blob: 78f6f9e4e95963ba29271a02d9570df4fd0edfc2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Alexander V. Astapchuk
*/
/**
* @file
* @brief Implementation of arithmetic helpers declared in arith_rt.h.
*/
#include "arith_rt.h"
#include "Algorithms.h"
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <assert.h>
#ifndef PLATFORM_POSIX
#define isnan _isnan
#define finite _finite
#endif
#include "trace.h"
#undef isnan
#define isnan(x) ((x) != (x))
namespace Jitrino {
namespace Jet {
double __stdcall rt_h_neg_dbl64(double v)
{
return -v;
}
float __stdcall rt_h_neg_flt32(float v)
{
return -v;
}
jlong __stdcall rt_h_neg_i64(jlong v)
{
return -v;
}
int __stdcall rt_h_neg_i32(int v)
{
return -v;
}
int __stdcall rt_h_lcmp(jlong v1, jlong v2)
{
if (v1 > v2) return 1;
if (v1 < v2) return -1;
return 0;
}
int __stdcall rt_h_fcmp_g(float v1, float v2)
{
if (isnan(v1) || isnan(v2)) {
return 1;
}
if (v1 > v2) return 1;
if (v1 < v2) return -1;
return 0;
}
int __stdcall rt_h_fcmp_l(float v1, float v2)
{
if (isnan(v1) || isnan(v2)) {
return -1;
}
return rt_h_fcmp_g(v1,v2);
}
int __stdcall rt_h_dcmp_g(double v1, double v2)
{
if (isnan(v1) || isnan(v2)) {
return 1;
}
if (v1 > v2) {
return 1;
}
if (v1 < v2) {
return -1;
}
return 0;
}
int __stdcall rt_h_dcmp_l(double v1, double v2)
{
if (isnan(v1) || isnan(v2)) { return -1; }
return rt_h_dcmp_g(v1,v2);
}
double __stdcall rt_h_dbl_a(double v1, double v2, JavaByteCodes op)
{
//rt_dbg_out("d%s: %f %f", instrs[op].name, v1, v2);
switch(op) {
case OPCODE_IADD:
return v1 + v2;
case OPCODE_ISUB:
return v1 - v2;
case OPCODE_IMUL:
return v1 * v2;
case OPCODE_IDIV:
return v1 / v2;
default:
assert(op == OPCODE_IREM);
break;
}
assert(op == OPCODE_IREM);
// return NaN if either of args is NaN
if (isnan(v1)) {
return v1;
}
if (isnan(v2)) {
return v2;
}
// if v2 is infinity, then result is v1
if (finite(v1) && !finite(v2)) {
return v1;
}
return jitrino_ieee754_fmod_double(v1,v2);
}
float __stdcall rt_h_flt_a(float v1, float v2, JavaByteCodes op)
{
//dbg_rt("f%s: %f %f", instrs[op].name, v1, v2);
switch( op) {
case OPCODE_IADD:
return v1 + v2;
case OPCODE_ISUB:
return v1 - v2;
case OPCODE_IMUL:
return v1 * v2;
case OPCODE_IDIV:
return v1 / v2;
default:
assert(op == OPCODE_IREM); break;
}
assert(op == OPCODE_IREM);
// return NaN if either of args is NaN
if (isnan(v1)) {
return v1;
}
if (isnan(v2)) {
return v2;
}
// if v2 is infinity, then result is v1
if (finite(v1) && !finite(v2)) {
return v1;
}
return (float)fmod((double)v1,(double)v2);
}
jlong __stdcall rt_h_i64_shift(jlong v1, int v2, JavaByteCodes op)
{
switch(op) {
case OPCODE_ISHL:
return v1 << (v2&0x3F);
case OPCODE_ISHR:
return v1 >> (v2&0x3F);
// case OPCODE_IUSHR: return ( v1 >= 0 ) ? v1 >> (v2&0x3F) : (v1 >> (v2&0x3F)) + (2L << ~(v2&0x3F));
case OPCODE_IUSHR:
return ((uint64)v1) >> (v2&0x3F);
default:
break;
}
assert(false);
return 0;
}
jlong __stdcall rt_h_i64_a(jlong v1, jlong v2, JavaByteCodes op)
{
switch(op) {
case OPCODE_IADD:
return v1 + v2;
case OPCODE_ISUB:
return v1 - v2;
case OPCODE_IOR:
return v1 | v2;
case OPCODE_IXOR:
return v1 ^ v2;
case OPCODE_IAND:
return v1 & v2;
case OPCODE_IMUL:
return v1 * v2;
// special cases according to JVM Spec
case OPCODE_IDIV:
return (v2 == -1 && v1 == jLONG_MIN) ? v1 : v1 / v2;
case OPCODE_IREM:
return (v2 == -1 && v1 == jLONG_MIN) ? 0 : v1 % v2;
default:
break;
}
assert(false);
return 0;
}
int __stdcall rt_h_i32_a(int v1, int v2, JavaByteCodes op)
{
//dbg_rt("i%s: %u %u", instrs[op].name, v1, v2);
switch(op) {
case OPCODE_IADD:
return v1 + v2;
case OPCODE_ISUB:
return v1 - v2;
case OPCODE_IMUL:
return v1 * v2;
case OPCODE_IOR:
return v1 | v2;
case OPCODE_IXOR:
return v1 ^ v2;
case OPCODE_IAND:
return v1 & v2;
case OPCODE_ISHL:
return v1 << (v2&0x1F);
case OPCODE_ISHR:
return v1 >> (v2&0x1F);
case OPCODE_IUSHR:
return (v1 >= 0) ? v1 >> (v2&0x1F) :
(v1 >> (v2&0x1F)) + (2 << ~(v2&0x1F));
// special cases according to JVM Spec
case OPCODE_IDIV:
// With v2==0 may be called from gen_a(). In this case, the return
// value will be ignoread anyway.
if (v2 == 0) return -1;
return (v2 == -1 && v1 == INT_MIN) ? v1 : v1 / v2;
case OPCODE_IREM:
// With v2==0 may be called from gen_a(). In this case, the return
// value will be ignoread anyway.
if (v2 == 0) return -1;
return (v2 == -1 && v1 == INT_MIN) ? 0 : v1 % v2;
default:
break;
}
assert(false);
return 0;
}
// i32 ->
int __stdcall rt_h_i32_2_i8(int i) { return (char)i; };
int __stdcall rt_h_i32_2_i16(int i) { return (short)i; };
int __stdcall rt_h_i32_2_u16(int i) { return (unsigned short)i; };
jlong __stdcall rt_h_i32_2_i64(int i) { return (jlong)i; };
float __stdcall rt_h_i32_2_flt(int i) { return (float)i; };
double __stdcall rt_h_i32_2_dbl(int i) { return (double)i; };
// i64 ->
// Workaround for bug in gcc 4.1.x. It doesn't clear upper half of the long
// register when it returns value from rt_h_i64_2_i32
int __stdcall rt_h_i64_2_i32(int64 i) { return (I_32)(i & 0xffffffff); };
float __stdcall rt_h_i64_2_flt(int64 i) { return (float)i; };
double __stdcall rt_h_i64_2_dbl(int64 i) { return (double)i; };
// flt ->
int __stdcall rt_h_flt_2_i32(float i)
{
if (isnan(i)) {
return 0;
}
return i<(double)INT_MIN ?
INT_MIN : (i>=(double)INT_MAX ? INT_MAX : (I_32)i);
}
jlong __stdcall rt_h_flt_2_i64(float i)
{
if (isnan(i)) {
return 0;
}
return i<(double)jLONG_MIN ?
jLONG_MIN : (i>=(double)jLONG_MAX? jLONG_MAX : (jlong)i);
}
double __stdcall rt_h_flt_2_dbl(float i)
{
return (double)i;
}
// dbl ->
int __stdcall rt_h_dbl_2_i32(double i)
{
if (isnan(i)) {
return 0;
}
return i<(double)INT_MIN ?
INT_MIN : (i>=(double)INT_MAX ? INT_MAX : (int)i);
}
jlong __stdcall rt_h_dbl_2_i64(double i)
{
if (isnan(i)) {
return 0;
}
return i<(double)jLONG_MIN ?
jLONG_MIN : (i>=(double)jLONG_MAX? jLONG_MAX : (jlong)i);
}
float __stdcall rt_h_dbl_2_flt(double i)
{
return (float)i;
}
const void * cnv_matrix_impls[num_jtypes][num_jtypes] = {
{NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{(void*)&rt_h_i32_2_i8, (void*)&rt_h_i32_2_i16, (void*)&rt_h_i32_2_u16, NULL,
(void*)&rt_h_i32_2_i64, (void*)&rt_h_i32_2_flt, (void*)&rt_h_i32_2_dbl },
{NULL, NULL, NULL, (void*)&rt_h_i64_2_i32,
NULL, (void*)&rt_h_i64_2_flt, (void*)&rt_h_i64_2_dbl },
{NULL, NULL, NULL, (void*)&rt_h_flt_2_i32,
(void*)&rt_h_flt_2_i64, NULL, (void*)&rt_h_flt_2_dbl },
{NULL, NULL, NULL, (void*)&rt_h_dbl_2_i32,
(void*)&rt_h_dbl_2_i64, (void*)&rt_h_dbl_2_flt, NULL },
};
}}; // ~namespace Jitrino::Jet