| /* |
| * 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 Intel, Pavel A. Ozhdikhin |
| * |
| */ |
| |
| #include "constantfolder.h" |
| #include "Inst.h" |
| |
| #define CAST(a, b) ((a)b) |
| |
| #include <float.h> |
| #include <math.h> |
| #include "PlatformDependant.h" |
| |
| /* |
| * The constant folding optimization is described in [S.Muchnick. Advanced Compiler |
| * Design and Implementation. Morgan Kaufmann, San Francisco, CA, 1997]. |
| */ |
| |
| namespace Jitrino { |
| #ifdef PLATFORM_POSIX |
| |
| using namespace std; |
| |
| inline float _chgsign(float f) { |
| return copysignf(f, signbit(f) ? (float)1.0 : (float)-1.0 ); |
| } |
| |
| inline double _chgsign(double d) { |
| return copysign(d, signbit(d) ? 1.0 : -1.0 ); |
| } |
| #endif |
| |
| // use templates here to decrease code to write for float2int below. |
| template <typename tointtype> tointtype minint(tointtype); |
| template < > inline I_32 minint<I_32>(I_32) { return 0x80000000; } |
| template < > inline int64 minint<int64>(int64) { |
| return __INT64_C(0x8000000000000000); |
| } |
| template <typename tointtype> tointtype maxint(tointtype); |
| template < > inline I_32 maxint<I_32>(I_32) { return 0x7fffffff; } |
| template < > inline int64 maxint<int64>(int64) { |
| return __INT64_C(0x7fffffffffffffff); |
| } |
| template <typename tointtype> tointtype maxuint(tointtype); |
| template < > inline U_32 maxuint<U_32>(U_32) { return 0xffffffff; } |
| template < > inline uint64 maxuint<uint64>(uint64) { |
| return __UINT64_C(0xffffffffffffffff); |
| } |
| // get around VC++ problem with uint64->float conversions: |
| template <typename tointtype> double maxuintasfloat(tointtype); |
| template < > inline double maxuintasfloat<U_32>(U_32) { |
| return 4294967295.0; |
| } |
| template < > inline double maxuintasfloat<uint64>(uint64) { |
| return 18446744073709551615.0; |
| } |
| |
| // do float->int conversions and make sure we do it like Java would. |
| template <typename tointtype, typename fromfloattype> |
| inline tointtype float2int(fromfloattype f) |
| { |
| if (isnan(f)) return (tointtype) 0; |
| if (finite(f) && |
| (((fromfloattype(minint<tointtype>(0))) < f) && |
| (f < fromfloattype(maxint<tointtype>(0))))) |
| return (tointtype) f; // both C++ and Java truncate |
| if (f < 0.0) return minint<tointtype>(0); |
| return maxint<tointtype>(0); |
| } |
| |
| template <typename tointtype, typename fromfloattype> |
| inline tointtype float2uint(fromfloattype s) |
| { |
| if (isnan(s) || (s < 0.0)) return (tointtype) 0; |
| if (finite(s) && (s < maxuintasfloat<tointtype>(0))) |
| return (tointtype) s; |
| return maxuint<tointtype>(0); |
| } |
| |
| template <typename uinttype, typename uhalftype, int bitsdiv2> |
| inline uinttype mulhu(uinttype u, uinttype v) |
| { |
| uhalftype w1, w2, w3, u0, u1, v0, v1; |
| uinttype k, t; |
| |
| u0 = (uhalftype) u; |
| u1 = (uhalftype) (u >> bitsdiv2); |
| v0 = (uhalftype) v; |
| v1 = (uhalftype) (v >> bitsdiv2); |
| |
| t = u0*v0; |
| k = t >> bitsdiv2; |
| |
| t = u1*v0; |
| w1 = (uhalftype) t; |
| k = t >> bitsdiv2; |
| w2 = (uhalftype) k; |
| |
| t = u0*v1 + w1; |
| w1 = (uhalftype) t; |
| k = t >> bitsdiv2; |
| |
| t = u1*v1 + w2+k; |
| w2 = (uhalftype) t; |
| k = t >> bitsdiv2; |
| w3 = (uhalftype) k; |
| |
| uinttype r = ((uinttype)w3 << bitsdiv2) | w2; |
| return r; |
| } |
| |
| template <typename inttype, typename uinttype, int bitsdiv2> |
| inline inttype mulhs(inttype u, inttype v) { |
| uinttype u0, v0, w0; |
| inttype u1, v1, w1, w2, t; |
| |
| // e.g., 0xffff if bitsdiv2==16 |
| uinttype halfmask = ((uinttype)-1) >> bitsdiv2; |
| u0 = u & halfmask; u1 = u >> bitsdiv2; |
| v0 = v & halfmask; v1 = v >> bitsdiv2; |
| w0 = u0*v0; |
| |
| t = u1*v0 + (w0 >> bitsdiv2); |
| w1 = t & halfmask; |
| w2 = t >> bitsdiv2; |
| w1 = u0*v1 + w1; |
| return u1*v1 + w2 + (w1 >> bitsdiv2); |
| } |
| |
| bool |
| ConstantFolder::isConstant(Opnd* opnd) { |
| return opnd->getInst()->isConst(); |
| } |
| bool |
| ConstantFolder::isConstantZero(Opnd* opnd) { |
| ConstInst* inst = opnd->getInst()->asConstInst(); |
| if (inst == NULL) |
| return false; |
| ConstInst::ConstValue value = inst->getValue(); |
| switch (inst->getType()) { |
| case Type::Int8: |
| case Type::UInt8: |
| case Type::Int16: |
| case Type::UInt16: |
| case Type::Int32: |
| case Type::UInt32: |
| return value.i4 == 0; |
| case Type::UInt64: |
| case Type::Int64: |
| return value.i8 == 0L; |
| case Type::IntPtr: |
| return value.i == 0; |
| case Type::NullObject: |
| return true; |
| case Type::Array: |
| case Type::Object: |
| return value.i == 0; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::isConstantOne(Opnd* opnd) { |
| ConstInst* inst = opnd->getInst()->asConstInst(); |
| if (inst == NULL) |
| return false; |
| ConstInst::ConstValue value = inst->getValue(); |
| switch (inst->getType()) { |
| case Type::Int32: |
| return value.i4 == 1; |
| case Type::Int64: |
| return value.i8 == 1; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::isConstantAllOnes(Opnd* opnd) { |
| ConstInst* inst = opnd->getInst()->asConstInst(); |
| if (inst == NULL) |
| return false; |
| ConstInst::ConstValue value = inst->getValue(); |
| switch (inst->getType()) { |
| case Type::Int32: |
| return value.i4 == (I_32) -1; |
| case Type::Int64: |
| return value.i8 == (int64) -1; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::isConstant(Inst* inst, I_32& value) { |
| ConstInst* constInst = inst->asConstInst(); |
| if (constInst == NULL || constInst->getType() != Type::Int32) |
| return false; |
| value = constInst->getValue().i4; |
| return true; |
| } |
| |
| bool |
| ConstantFolder::isConstant(Inst* inst, int64& value) { |
| ConstInst* constInst = inst->asConstInst(); |
| if (constInst == NULL || constInst->getType() != Type::Int64) |
| return false; |
| value = constInst->getValue().i8; |
| return true; |
| } |
| |
| bool |
| ConstantFolder::isConstant(Inst* inst, ConstInst::ConstValue& value) { |
| ConstInst* constInst = inst->asConstInst(); |
| if (constInst == NULL) |
| return false; |
| value = constInst->getValue(); |
| return true; |
| } |
| |
| bool |
| ConstantFolder::hasConstant(Inst* inst) { |
| return (isConstant(inst->getSrc(0)) || isConstant(inst->getSrc(1))); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Utilities for constant folding |
| //----------------------------------------------------------------------------- |
| bool |
| ConstantFolder::fold8(Opcode opc, I_8 c1, I_8 c2, I_32& result, bool is_signed) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_And: result = c1 & c2; return true; |
| case Op_Or: result = c1 | c2; return true; |
| case Op_Xor: result = c1 ^ c2; return true; |
| case Op_Mul: |
| // mul, div, and rem are different for unsigned values |
| if (is_signed) { |
| result = c1 * c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, (CAST(U_8, c1)*CAST(U_8, c2)))); return true; |
| } |
| // for div and rem, be careful c2 not be 0 |
| // also, need to handle signed/unsigned based on SignedModifier |
| case Op_TauDiv: |
| if (c2 == (I_8)0) return false; |
| if (is_signed) { |
| result = c1 / c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(U_8, c1) / CAST(U_8, c2))); return true; |
| } |
| case Op_TauRem: |
| if (c2 == (I_8)0) return false; |
| if (is_signed) { |
| result = c1 % c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(U_8, c1) % CAST(U_8, c2))); return true; |
| } |
| case Op_MulHi: result = (I_32)(I_8)((((int16)c1) * ((int16)c2)) >> 8); |
| return true; |
| case Op_Min: |
| result = ::std::min(c1,c2); return true; |
| case Op_Max: |
| result = ::std::max(c1,c2); return true; |
| default: |
| return true; |
| } |
| } |
| bool |
| ConstantFolder::fold16(Opcode opc, int16 c1, int16 c2, I_32& result, bool is_signed) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_And: result = c1 & c2; return true; |
| case Op_Or: result = c1 | c2; return true; |
| case Op_Xor: result = c1 ^ c2; return true; |
| case Op_Mul: |
| // mul, div, and rem are different for unsigned values |
| if (is_signed) { |
| result = c1 * c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(uint16, c1) * CAST(uint16, c2))); return true; |
| } |
| // for div and rem, be careful c2 not be 0 |
| // also, need to handle signed/unsigned based on SignedModifier |
| case Op_TauDiv: |
| if (c2 == (int16)0) return false; |
| if (is_signed) { |
| result = c1 / c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(uint16, c1) / CAST(uint16, c2))); return true; |
| } |
| case Op_TauRem: |
| if (c2 == (int16)0) return false; |
| if (is_signed) { |
| result = c1 % c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(uint16, c1) % CAST(uint16, c2))); return true; |
| } |
| case Op_MulHi: result = (I_32)((int16)(((I_32)c1) * ((I_32)c2)) >> 16); |
| return true; |
| case Op_Min: |
| result = ::std::min(c1,c2); return true; |
| case Op_Max: |
| result = ::std::max(c1,c2); return true; |
| default: |
| return true; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold32(Opcode opc, I_32 c1, I_32 c2, I_32& result, bool is_signed) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_And: result = c1 & c2; return true; |
| case Op_Or: result = c1 | c2; return true; |
| case Op_Xor: result = c1 ^ c2; return true; |
| case Op_Mul: |
| // mul, div, and rem are different for unsigned values |
| if (is_signed) { |
| result = c1 * c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(U_32, c1) * CAST(U_32, c2))); return true; |
| } |
| // for div and rem, be careful c2 not be 0 |
| // also, need to handle signed/unsigned based on SignedModifier |
| case Op_TauDiv: |
| if (c2 == (I_32)0) return false; |
| if (is_signed) { |
| if ((c1 == (I_32)0x80000000) && (c2 == -1)) { |
| result = c1; |
| return true; |
| } |
| |
| result = c1 / c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(U_32, c1 / CAST(U_32, c2)))); return true; |
| } |
| case Op_TauRem: |
| if (c2 == (I_32)0) return false; |
| if (is_signed) { |
| if ((c1 == (I_32)0x80000000) && (c2 == -1)) { |
| result = 0; |
| return true; |
| } |
| |
| result = c1 % c2; return true; |
| } else { |
| result = CAST(I_32, CAST(U_32, CAST(U_32, c1) % CAST(U_32, c2))); return true; |
| } |
| case Op_MulHi: |
| { |
| int64 res = ((int64)c1) * ((int64)c2); |
| int64 res2 = (int64)((I_32)(res >> 32)); |
| result = (I_32) res2; |
| return true; |
| } |
| case Op_Min: |
| result = ::std::min(c1,c2); return true; |
| case Op_Max: |
| result = ::std::max(c1,c2); return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold64(Opcode opc, int64 c1, int64 c2, int64& result, bool is_signed) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_And: result = c1 & c2; return true; |
| case Op_Or: result = c1 | c2; return true; |
| case Op_Xor: result = c1 ^ c2; return true; |
| case Op_Mul: |
| // mul, div, and rem are different for unsigned values |
| if (is_signed) { |
| result = c1 * c2; |
| } else { |
| result = CAST(int64, CAST(uint64, c1) * CAST(uint64, c2)); |
| } |
| return true; |
| // for div and rem, be careful c2 not be 0 |
| case Op_TauDiv: |
| if (c2 == (int64)0) return false; |
| // LONG.MIN_VALUE / -1 == LONG.MIN_VALUE |
| if ((c2 == (int64)-1) && (c1 == ((int64)1<<63))) { |
| result = c1; |
| return true; |
| } |
| if (is_signed) { |
| result = c1 / c2; |
| } else { |
| result = CAST(int64, CAST(uint64, c1) / CAST(uint64, c2)); |
| } |
| return true; |
| case Op_TauRem: |
| if (c2 == (int64)0) return false; |
| // LONG.MIN_VALUE % -1 == 0 |
| if ((c2 == (int64)-1) && (c1 == ((int64)1<<63))) { |
| result = 0; |
| return true; |
| } |
| if (is_signed) { |
| result = c1 % c2; |
| } else { |
| result = CAST(int64, CAST(uint64, c1) % CAST(uint64, c2)); |
| } |
| return true; |
| case Op_MulHi: |
| { |
| if (is_signed) { |
| result = mulhs<int64, uint64, 32>(c1, c2); |
| } else { |
| result = mulhu<uint64, U_32, 32>(CAST(uint64, c1), |
| CAST(uint64, c2)); |
| } |
| return true; |
| } |
| case Op_Min: |
| result = ::std::min(c1,c2); return true; |
| case Op_Max: |
| result = ::std::max(c1,c2); return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::foldSingle(Opcode opc, float c1, float c2, float& result) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_Mul: result = c1 * c2; return true; |
| case Op_TauDiv: |
| if (c2 == 0.0) return false; |
| result = c1 / c2; return true; |
| case Op_Min: |
| case Op_Max: |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::foldDouble(Opcode opc, double c1, double c2, double& result) { |
| switch (opc) { |
| case Op_Add: result = c1 + c2; return true; |
| case Op_Sub: result = c1 - c2; return true; |
| case Op_Mul: result = c1 * c2; return true; |
| case Op_TauDiv: |
| if (c2 == ((double)0.0)) return false; |
| result = c1 / c2; return true; |
| case Op_Min: |
| case Op_Max: |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold8(Opcode opc, I_8 c, I_32& result) { |
| switch (opc) { |
| case Op_Not: result = ~c; return true; |
| case Op_Neg: result = -c; return true; |
| case Op_Abs: result = (c < 0) ? -c : c; return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold16(Opcode opc, int16 c, I_32& result) { |
| switch (opc) { |
| case Op_Not: result = ~c; return true; |
| case Op_Neg: result = -c; return true; |
| case Op_Abs: result = (c < 0) ? -c : c; return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold32(Opcode opc, I_32 c, I_32& result) { |
| switch (opc) { |
| case Op_Not: result = ~c; return true; |
| case Op_Neg: result = -c; return true; |
| case Op_Abs: result = (c < 0) ? -c : c; return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::fold64(Opcode opc, int64 c, int64& result) { |
| switch (opc) { |
| case Op_Not: result = ~c; return true; |
| case Op_Neg: result = -c; return true; |
| case Op_Abs: result = (c < 0) ? -c : c; return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::foldSingle(Opcode opc, float c, float& result) { |
| if( Op_Neg == opc) { |
| result = (float)_chgsign(c); |
| return true; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldDouble(Opcode opc, double c, double& result) { |
| if( Op_Neg == opc) { |
| result = _chgsign(c); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ConstantFolder::foldConv(Type::Tag fromType, Type::Tag toType, Modifier mod, |
| ConstInst::ConstValue src, |
| ConstInst::ConstValue& res) |
| { |
| bool ovfm = (mod.getOverflowModifier()!=Overflow_None); |
| switch (toType) { |
| case Type::Int32: |
| #ifndef PONTER64 |
| case Type::IntPtr: |
| #endif |
| if (Type::isInteger(fromType)) { |
| if (Type::isIntegerOf4Signed(fromType)) { |
| res.i4 = src.i4; return true; |
| } else if (Type::isIntegerOf4Unsigned(fromType)) { |
| res.i4 = src.i4; |
| if (ovfm && (src.i4 < 0)) return false; |
| return true; |
| } else if (Type::isIntegerOf8Signed(fromType)) { |
| res.i4 = CAST(I_32, src.i8); |
| if (ovfm && (src.i8 != CAST(int64, res.i4))) return false; |
| return true; |
| } else if (Type::isIntegerOf8Unsigned(fromType)) { |
| res.i4 = CAST(I_32, CAST(uint64, src.i8)); |
| if (ovfm && (CAST(uint64, src.i8) != CAST(uint64, res.i4))) return false; |
| return true; |
| } |
| assert(0); |
| } else if (fromType == Type::Single) { |
| res.i4 = float2int<I_32, float>(src.s); |
| if (ovfm && (src.s != CAST(float, res.i4))) return false; |
| return true; |
| } else if (fromType == Type::Double) { |
| res.i4 = float2int<I_32, double>(src.d); |
| if (ovfm && (src.d != CAST(double, res.i4))) return false; |
| return true; |
| } |
| break; |
| case Type::UInt32: |
| #ifndef PONTER64 |
| case Type::UIntPtr: |
| #endif |
| if (Type::isInteger(fromType)) { |
| if (Type::isIntegerOf4Bytes(fromType)) { |
| res.i4 = src.i4; return true; |
| } else if (Type::isIntegerOf8Signed(fromType)) { |
| res.i4 = CAST(I_32, CAST(U_32, src.i8)); |
| if (ovfm && (src.i8 != CAST(int64, CAST(U_32, res.i4)))) return false; |
| return true; |
| } else if (Type::isIntegerOf8Unsigned(fromType)) { |
| res.i4 = CAST(I_32, CAST(U_32, CAST(uint64, src.i8))); |
| if (ovfm && (CAST(uint64, src.i8) != CAST(uint64, CAST(U_32, res.i4)))) return false; |
| return true; |
| } |
| assert(0); |
| } else if (fromType == Type::Single) { |
| res.i4 = float2uint<U_32, float>(src.s); |
| if (ovfm && (src.s != CAST(float, CAST(U_32, res.i4)))) return false; |
| return true; |
| } else if (fromType == Type::Double) { |
| res.i4 = float2uint<U_32, double>(src.d); |
| if (ovfm && (src.d != CAST(double, CAST(U_32, res.i4)))) return false; |
| return true; |
| } |
| break; |
| case Type::Int64: |
| #ifdef PONTER64 |
| case Type::IntPtr: |
| #endif |
| assert(!ovfm || mod.getOverflowModifier()==Overflow_Signed); |
| if (Type::isInteger(fromType)) { |
| if (Type::isIntegerOf4Signed(fromType)) { |
| res.i8 = src.i4; return true; |
| } else if (Type::isIntegerOf4Unsigned(fromType)) { |
| res.i8 = CAST(U_32, src.i4); |
| if (ovfm && (src.i4 < 0)) return false; |
| return true; |
| } else if (Type::isIntegerOf8Signed(fromType)) { |
| res.i8 = src.i8; return true; |
| } else if (Type::isIntegerOf8Unsigned(fromType)) { |
| res.i8 = src.i8; |
| if (ovfm && (src.i8 < 0)) return false; |
| return true; |
| } |
| assert(0); |
| } else if (fromType == Type::Single) { |
| res.i8 = float2int<int64, float>(src.s); |
| if (ovfm && (src.s != CAST(float, res.i8))) return false; |
| return true; |
| } else if (fromType == Type::Double) { |
| res.i8 = float2int<int64, double>(src.d); |
| if (ovfm && (src.d != CAST(double, res.i8))) return false; |
| return true; |
| } |
| break; |
| case Type::UInt64: |
| #ifdef PONTER64 |
| case Type::UIntPtr: |
| #endif |
| assert(!ovfm || mod.getOverflowModifier()==Overflow_Unsigned); |
| if (Type::isInteger(fromType)) { |
| if (Type::isIntegerOf4Signed(fromType)) { |
| res.i8 = CAST(uint64, CAST(U_32, src.i4)); |
| if (ovfm && (src.i4 < 0)) return false; |
| return true; |
| } else if (Type::isIntegerOf4Unsigned(fromType)) { |
| res.i8 = CAST(uint64, CAST(U_32, src.i4)); return true; |
| } else if (Type::isIntegerOf8Bytes(fromType)) { |
| res.i8 = src.i8; return true; |
| } |
| } else if (fromType == Type::Single) { |
| res.i8 = float2uint<uint64, float>(src.s); |
| if (ovfm) return false; |
| return true; |
| } else if (fromType == Type::Double) { |
| return false; |
| } |
| break; |
| case Type::Single: |
| switch (fromType) { |
| case Type::Int32: |
| res.s = CAST(float, src.i4); return true; |
| case Type::UInt32: |
| res.s = CAST(float, CAST(U_32, src.i4)); return true; |
| case Type::Int64: |
| res.s = CAST(float, src.i8); |
| if (ovfm && (src.i8 != float2int<int64, float>(res.s))) return false; |
| return true; |
| case Type::UInt64: |
| return false; |
| case Type::Single: |
| res.s = src.s; return true; |
| case Type::Double: |
| res.s = CAST(float, src.d); |
| if (ovfm && (src.d != CAST(double, res.s))) return false; |
| return true; |
| case Type::Float: |
| default: |
| break; |
| } |
| break; |
| case Type::Double: |
| switch (fromType) { |
| case Type::Int32: |
| res.d = CAST(double, src.i4); return true; |
| case Type::UInt32: |
| res.d = CAST(double, CAST(U_32, src.i4)); return true; |
| case Type::Int64: |
| res.d = CAST(double, src.i8); return true; |
| case Type::UInt64: |
| return false; |
| case Type::Single: |
| res.d = CAST(double, src.s); return true; |
| case Type::Double: |
| res.d = src.d; return true; |
| case Type::Float: |
| default: |
| break; |
| } |
| break; |
| case Type::UnmanagedPtr: |
| // Let's accept the conversion from properly sized integer |
| #ifdef PONTER64 |
| if (Type::isIntegerOf8Bytes(fromType)) { |
| #else |
| if (Type::isIntegerOf4Bytes(fromType)) { |
| #endif |
| return true; |
| } |
| break; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmp32(ComparisonModifier mod, I_32 c1, I_32 c2, I_32& result) { |
| switch (mod) { |
| case Cmp_EQ: result = ((c1 == c2)?1:0); return true; |
| case Cmp_NE_Un: result = ((c1 != c2)?1:0); return true; |
| case Cmp_GT: result = ((c1 > c2)?1:0); return true; |
| case Cmp_GT_Un: result = ((CAST(U_32, c1) > CAST(U_32, c2))?1:0); return true; |
| case Cmp_GTE: result = ((c1 >= c2)?1:0); return true; |
| case Cmp_GTE_Un:result = ((CAST(U_32, c1) >= CAST(U_32, c2))?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmpRef(ComparisonModifier mod, void* c1, void* c2, I_32& result) { |
| switch (mod) { |
| case Cmp_EQ: result = ((c1 == c2)?1:0); return true; |
| case Cmp_NE_Un: result = ((c1 != c2)?1:0); return true; |
| case Cmp_GT: result = ((c1 > c2)?1:0); return true; |
| case Cmp_GT_Un: result = ((c1 > c2)?1:0); return true; |
| case Cmp_GTE: result = ((c1 >= c2)?1:0); return true; |
| case Cmp_GTE_Un:result = ((c1 >= c2)?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmp64(ComparisonModifier mod, int64 c1, int64 c2, I_32& result) { |
| switch (mod) { |
| case Cmp_EQ: result = ((c1 == c2)?1:0); return true; |
| case Cmp_NE_Un: result = ((c1 != c2)?1:0); return true; |
| case Cmp_GT: result = ((c1 > c2)?1:0); return true; |
| case Cmp_GT_Un: result = ((CAST(uint64, c1) > CAST(uint64, c2))?1:0); return true; |
| case Cmp_GTE: result = ((c1 >= c2)?1:0); return true; |
| case Cmp_GTE_Un:result = ((CAST(uint64, c1) >= CAST(uint64, c2))?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmpSingle(ComparisonModifier mod, float c1, float c2, I_32& result) { |
| switch (mod) { |
| case Cmp_EQ: |
| if (isnan(c1) || isnan(c2)) { result = false; } |
| else { result = ((c1 == c2)?1:0); } |
| return true; |
| case Cmp_NE_Un: |
| if (isnan(c1) || isnan(c2)) { result = true; } |
| else { result = ((c1 != c2)?1:0); } |
| return true; |
| case Cmp_GT: |
| case Cmp_GT_Un: |
| if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GT_Un); } |
| else { result = ((c1 > c2)?1:0); } |
| return true; |
| case Cmp_GTE: |
| case Cmp_GTE_Un: |
| if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GTE_Un); } |
| else { result = ((c1 >= c2)?1:0); } |
| return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmpDouble(ComparisonModifier mod, double c1, double c2, I_32& result){ |
| switch (mod) { |
| case Cmp_EQ: |
| if (isnan(c1) || isnan(c2)) { result = false; } |
| else { result = ((c1 == c2)?1:0); } |
| return true; |
| case Cmp_NE_Un: |
| if (isnan(c1) || isnan(c2)) { result = true; } |
| else { result = ((c1 != c2)?1:0); } |
| return true; |
| case Cmp_GT: |
| case Cmp_GT_Un: |
| if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GT_Un); } |
| else { result = ((c1 > c2)?1:0); } |
| return true; |
| case Cmp_GTE: |
| case Cmp_GTE_Un: |
| if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GTE_Un); } |
| else { result = ((c1 >= c2)?1:0); } |
| return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmp32(ComparisonModifier mod, I_32 c, I_32& result) { |
| switch (mod) { |
| case Cmp_Zero: result = ((c == 0)?1:0); return true; |
| case Cmp_NonZero: result = ((c != 0)?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmp64(ComparisonModifier mod, int64 c, I_32& result) { |
| switch (mod) { |
| case Cmp_Zero: result = ((c == 0)?1:0); return true; |
| case Cmp_NonZero: result = ((c != 0)?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmpRef(ComparisonModifier mod, void* c, I_32& result) { |
| switch (mod) { |
| case Cmp_Zero: result = ((c == NULL)?1:0); return true; |
| case Cmp_NonZero: result = ((c != NULL)?1:0); return true; |
| default: break; |
| } |
| return false; |
| } |
| |
| bool |
| ConstantFolder::foldCmp(Type::Tag cmpTypeTag, |
| ComparisonModifier mod, |
| ConstInst::ConstValue val, |
| ConstInst::ConstValue& result) { |
| switch (cmpTypeTag) { |
| case Type::Int32: return foldCmp32(mod, val.i4, result.i4); |
| case Type::Int64: return foldCmp64(mod, val.i8, result.i4); |
| case Type::UInt32: return foldCmp32(mod, val.i4, result.i4); |
| case Type::UInt64: return foldCmp64(mod, val.i8, result.i4); |
| default: break; |
| } |
| // fold comparisons of references against null |
| if (Type::isObject(cmpTypeTag)) |
| return foldCmpRef(mod, val.i, result.i4); |
| return false; |
| } |
| // |
| // binary comparison |
| // |
| bool |
| ConstantFolder::foldCmp(Type::Tag cmpTypeTag, |
| ComparisonModifier mod, |
| ConstInst::ConstValue val1, |
| ConstInst::ConstValue val2, |
| ConstInst::ConstValue& result) { |
| switch (cmpTypeTag) { |
| case Type::Int32: case Type::UInt32: return foldCmp32(mod, val1.i4, val2.i4, result.i4); |
| case Type::Int64: case Type::UInt64: return foldCmp64(mod, val1.i8, val2.i8, result.i4); |
| case Type::Single: return foldCmpSingle(mod, val1.s, val2.s, result.i4); |
| case Type::Double: return foldCmpDouble(mod, val1.d, val2.d, result.i4); |
| default: break; |
| } |
| return false; |
| } |
| |
| |
| |
| bool |
| ConstantFolder::foldConstant(Type::Tag type, |
| Opcode opc, |
| ConstInst::ConstValue val1, |
| ConstInst::ConstValue val2, |
| ConstInst::ConstValue& result, |
| bool is_signed) { |
| |
| switch (type) { |
| case Type::Int8: |
| case Type::UInt8: return fold8(opc, (I_8)val1.i4, (I_8)val2.i4, result.i4, is_signed); |
| case Type::Int16: |
| case Type::UInt16: return fold16(opc, (int16)val1.i4, (int16)val2.i4, result.i4, is_signed); |
| case Type::Int32: |
| case Type::UInt32: return fold32(opc, val1.i4, val2.i4, result.i4, is_signed); |
| case Type::Int64: |
| case Type::UInt64: return fold64(opc, val1.i8, val2.i8, result.i8, is_signed); |
| case Type::IntPtr: |
| case Type::UIntPtr: { |
| int psi = sizeof(POINTER_SIZE_INT); |
| switch (psi) { |
| case 1: return fold8(opc, (I_8)val1.i4, (I_8)val2.i4, result.i4, is_signed); |
| case 2: return fold16(opc, (int16)val1.i4, (int16)val2.i4, result.i4, is_signed); |
| case 4: return fold32(opc, val1.i4, val2.i4, result.i4, is_signed); |
| case 8: return fold64(opc, val1.i8, val2.i8, result.i8, is_signed); |
| default: return false; |
| } |
| } |
| case Type::Single: return foldSingle(opc, val1.s, val2.s, result.s); |
| case Type::Double: return foldDouble(opc, val1.d, val2.d, result.d); |
| default: return false; |
| } |
| } |
| |
| bool |
| ConstantFolder::foldConstant(Type::Tag type, |
| Opcode opc, |
| ConstInst::ConstValue val, |
| ConstInst::ConstValue& result) { |
| switch (type) { |
| case Type::Int32: |
| case Type::UInt32: |
| return fold32(opc, val.i4, result.i4); |
| case Type::Int64: |
| case Type::UInt64: |
| return fold64(opc, val.i8, result.i8); |
| case Type::Single: |
| return foldSingle(opc, val.s, result.s); |
| case Type::Double: |
| return foldDouble(opc, val.d, result.d); |
| default: break; |
| } |
| return false; |
| } |
| // |
| // unary comparison |
| // |
| |
| // |
| // Tries to constant fold the instruction, setting the resulting constant |
| // value to result. Returns true if instruction was folded. |
| // |
| bool |
| ConstantFolder::fold(Inst* inst, ConstInst::ConstValue& result) { |
| U_32 numSrcs = inst->getNumSrcOperands(); |
| if (numSrcs == 0) |
| return false; |
| ConstInst* constSrc0 = inst->getSrc(0)->getInst()->asConstInst(); |
| if (constSrc0 == NULL) |
| return false; |
| Opcode opc = inst->getOpcode(); |
| if (numSrcs == 1) { |
| if (opc == Op_Cmp) { |
| return foldCmp(constSrc0->getType(), |
| inst->getComparisonModifier(), |
| constSrc0->getValue(), |
| result); |
| } |
| Modifier mod = inst->getModifier(); |
| return foldConstant(inst->getType(), |
| inst->getOpcode(), |
| constSrc0->getValue(), |
| result); |
| } |
| ConstInst* constSrc1 = inst->getSrc(1)->getInst()->asConstInst(); |
| if (constSrc1 == NULL) |
| return false; |
| if (numSrcs == 2) { |
| if (opc == Op_Cmp) { |
| assert(constSrc0->getType() == constSrc1->getType()); |
| return foldCmp(inst->getType(), |
| inst->getComparisonModifier(), |
| constSrc0->getValue(), |
| constSrc1->getValue(), |
| result); |
| } |
| Modifier mod = inst->getModifier(); |
| bool is_signed = mod.isSigned(); |
| return foldConstant(inst->getType(), |
| inst->getOpcode(), |
| constSrc0->getValue(), |
| constSrc1->getValue(), |
| result, |
| is_signed); |
| } |
| return false; |
| } |
| |
| |
| } //namespace Jitrino |