diff options
Diffstat (limited to 'src/jit/valuenum.cpp')
-rw-r--r-- | src/jit/valuenum.cpp | 375 |
1 files changed, 313 insertions, 62 deletions
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index aba29c4411..03bc204070 100644 --- a/src/jit/valuenum.cpp +++ b/src/jit/valuenum.cpp @@ -32,7 +32,7 @@ VNFunc GetVNFuncForOper(genTreeOps oper, bool isUnsigned) case GT_LE: return VNF_LE_UN; case GT_GE: - return VNF_GT_UN; + return VNF_GE_UN; case GT_GT: return VNF_GT_UN; case GT_ADD: @@ -206,6 +206,52 @@ T ValueNumStore::EvalOp(VNFunc vnf, T v0, T v1, ValueNum* pExcSet) } } +struct FloatTraits +{ + static float NaN() + { + unsigned bits = 0xFFC00000u; + float result; + static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned) must equal sizeof(float)"); + memcpy(&result, &bits, sizeof(result)); + return result; + } +}; + +struct DoubleTraits +{ + static double NaN() + { + unsigned long long bits = 0xFFF8000000000000ull; + double result; + static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned long long) must equal sizeof(double)"); + memcpy(&result, &bits, sizeof(result)); + return result; + } +}; + +template <typename TFp, typename TFpTraits> +TFp FpRem(TFp dividend, TFp divisor) +{ + // From the ECMA standard: + // + // If [divisor] is zero or [dividend] is infinity + // the result is NaN. + // If [divisor] is infinity, + // the result is [dividend] + + if (divisor == 0 || !_finite(dividend)) + { + return TFpTraits::NaN(); + } + else if (!_finite(divisor) && !_isnan(divisor)) + { + return dividend; + } + + return (TFp)fmod((double)dividend, (double)divisor); +} + // Specialize for double for floating operations, that doesn't involve unsigned. template <> double ValueNumStore::EvalOp<double>(VNFunc vnf, double v0, double v1, ValueNum* pExcSet) @@ -223,7 +269,31 @@ double ValueNumStore::EvalOp<double>(VNFunc vnf, double v0, double v1, ValueNum* case GT_DIV: return v0 / v1; case GT_MOD: - return fmod(v0, v1); + return FpRem<double, DoubleTraits>(v0, v1); + + default: + unreached(); + } +} + +// Specialize for float for floating operations, that doesn't involve unsigned. +template <> +float ValueNumStore::EvalOp<float>(VNFunc vnf, float v0, float v1, ValueNum* pExcSet) +{ + genTreeOps oper = genTreeOps(vnf); + // Here we handle those that are the same for floating-point types. + switch (oper) + { + case GT_ADD: + return v0 + v1; + case GT_SUB: + return v0 - v1; + case GT_MUL: + return v0 * v1; + case GT_DIV: + return v0 / v1; + case GT_MOD: + return FpRem<float, FloatTraits>(v0, v1); default: unreached(); @@ -833,7 +903,7 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, unsigned handleFlags) } // Returns the value number for zero of the given "typ". -// It has an unreached() for a "typ" that has no zero value, such as TYP_BYREF. +// It has an unreached() for a "typ" that has no zero value, such as TYP_VOID. ValueNum ValueNumStore::VNZeroForType(var_types typ) { switch (typ) @@ -861,6 +931,8 @@ ValueNum ValueNumStore::VNZeroForType(var_types typ) case TYP_REF: case TYP_ARRAY: return VNForNull(); + case TYP_BYREF: + return VNForByrefCon(0); case TYP_STRUCT: #ifdef FEATURE_SIMD // TODO-CQ: Improve value numbering for SIMD types. @@ -959,6 +1031,17 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN) } } +// Windows x86 and Windows ARM/ARM64 may not define _isnanf() but they do define _isnan(). +// We will redirect the macros to these other functions if the macro is not defined for the +// platform. This has the side effect of a possible implicit upcasting for arguments passed. +#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)) && !defined(FEATURE_PAL) + +#if !defined(_isnanf) +#define _isnanf _isnan +#endif + +#endif + ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN) { assert(arg0VN != NoVN && arg1VN != NoVN); @@ -986,8 +1069,12 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V // We don't try to fold a binary operation when one of the constant operands // is a floating-point constant and the other is not. // - bool arg0IsFloating = varTypeIsFloating(TypeOfVN(arg0VN)); - bool arg1IsFloating = varTypeIsFloating(TypeOfVN(arg1VN)); + var_types arg0VNtyp = TypeOfVN(arg0VN); + bool arg0IsFloating = varTypeIsFloating(arg0VNtyp); + + var_types arg1VNtyp = TypeOfVN(arg1VN); + bool arg1IsFloating = varTypeIsFloating(arg1VNtyp); + if (arg0IsFloating != arg1IsFloating) { canFold = false; @@ -997,8 +1084,10 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V // comparison would return false, an unordered comparison // will return true if any operands are a NaN. We only perform // ordered NaN comparison in EvalComparison. - if ((arg0IsFloating && _isnan(GetConstantDouble(arg0VN))) || - (arg1IsFloating && _isnan(GetConstantDouble(arg1VN)))) + if ((arg0IsFloating && (((arg0VNtyp == TYP_FLOAT) && _isnanf(GetConstantSingle(arg0VN))) || + ((arg0VNtyp == TYP_DOUBLE) && _isnan(GetConstantDouble(arg0VN))))) || + (arg1IsFloating && (((arg1VNtyp == TYP_FLOAT) && _isnanf(GetConstantSingle(arg1VN))) || + ((arg0VNtyp == TYP_DOUBLE) && _isnan(GetConstantDouble(arg1VN)))))) { canFold = false; } @@ -1607,27 +1696,24 @@ INT64 ValueNumStore::GetConstantInt64(ValueNum argVN) return result; } -// Given a float or a double constant value number return its value as a double. +// Given a double constant value number return its value as a double. // double ValueNumStore::GetConstantDouble(ValueNum argVN) { assert(IsVNConstant(argVN)); - var_types argVNtyp = TypeOfVN(argVN); + assert(TypeOfVN(argVN) == TYP_DOUBLE); - double result = 0; + return ConstantValue<double>(argVN); +} - switch (argVNtyp) - { - case TYP_FLOAT: - result = (double)ConstantValue<float>(argVN); - break; - case TYP_DOUBLE: - result = ConstantValue<double>(argVN); - break; - default: - unreached(); - } - return result; +// Given a float constant value number return its value as a float. +// +float ValueNumStore::GetConstantSingle(ValueNum argVN) +{ + assert(IsVNConstant(argVN)); + assert(TypeOfVN(argVN) == TYP_FLOAT); + + return ConstantValue<float>(argVN); } // Compute the proper value number when the VNFunc has all constant arguments @@ -1796,40 +1882,52 @@ ValueNum ValueNumStore::EvalFuncForConstantFPArgs(var_types typ, VNFunc func, Va assert(CanEvalForConstantArgs(func)); assert(IsVNConstant(arg0VN) && IsVNConstant(arg1VN)); - // We expect both argument types to be floating point types + // We expect both argument types to be floating-point types var_types arg0VNtyp = TypeOfVN(arg0VN); var_types arg1VNtyp = TypeOfVN(arg1VN); assert(varTypeIsFloating(arg0VNtyp)); assert(varTypeIsFloating(arg1VNtyp)); - double arg0Val = GetConstantDouble(arg0VN); - double arg1Val = GetConstantDouble(arg1VN); + // We also expect both arguments to be of the same floating-point type + assert(arg0VNtyp == arg1VNtyp); ValueNum result; // left uninitialized, we are required to initialize it on all paths below. if (VNFuncIsComparison(func)) { assert(genActualType(typ) == TYP_INT); - result = VNForIntCon(EvalComparison(func, arg0Val, arg1Val)); + + if (arg0VNtyp == TYP_FLOAT) + { + result = VNForIntCon(EvalComparison(func, GetConstantSingle(arg0VN), GetConstantSingle(arg1VN))); + } + else + { + assert(arg0VNtyp == TYP_DOUBLE); + result = VNForIntCon(EvalComparison(func, GetConstantDouble(arg0VN), GetConstantDouble(arg1VN))); + } } else { - assert(varTypeIsFloating(typ)); // We must be computing a floating point result + // We expect the return type to be the same as the argument type + assert(varTypeIsFloating(typ)); + assert(arg0VNtyp == typ); - // We always compute the result using a double - ValueNum exception = VNForEmptyExcSet(); - double doubleResultVal = EvalOp(func, arg0Val, arg1Val, &exception); - assert(exception == VNForEmptyExcSet()); // Floating point ops don't throw. + ValueNum exception = VNForEmptyExcSet(); if (typ == TYP_FLOAT) { - float floatResultVal = float(doubleResultVal); - result = VNForFloatCon(floatResultVal); + float floatResultVal = EvalOp(func, GetConstantSingle(arg0VN), GetConstantSingle(arg1VN), &exception); + assert(exception == VNForEmptyExcSet()); // Floating point ops don't throw. + result = VNForFloatCon(floatResultVal); } else { assert(typ == TYP_DOUBLE); + + double doubleResultVal = EvalOp(func, GetConstantDouble(arg0VN), GetConstantDouble(arg1VN), &exception); + assert(exception == VNForEmptyExcSet()); // Floating point ops don't throw. result = VNForDoubleCon(doubleResultVal); } } @@ -1876,6 +1974,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu { #ifndef _TARGET_64BIT_ case TYP_REF: + case TYP_BYREF: #endif case TYP_INT: { @@ -1934,6 +2033,9 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu else return VNForLongCon(INT64(arg0Val)); #endif + case TYP_BYREF: + assert(typ == TYP_BYREF); + return VNForByrefCon((INT64)arg0Val); case TYP_FLOAT: assert(typ == TYP_FLOAT); if (srcIsUnsigned) @@ -1962,6 +2064,7 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu { #ifdef _TARGET_64BIT_ case TYP_REF: + case TYP_BYREF: #endif case TYP_LONG: INT64 arg0Val = GetConstantInt64(arg0VN); @@ -1992,6 +2095,9 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu case TYP_ULONG: assert(typ == TYP_LONG); return arg0VN; + case TYP_BYREF: + assert(typ == TYP_BYREF); + return VNForByrefCon((INT64)arg0Val); case TYP_FLOAT: assert(typ == TYP_FLOAT); if (srcIsUnsigned) @@ -2017,6 +2123,47 @@ ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, Valu } } case TYP_FLOAT: + { + float arg0Val = GetConstantSingle(arg0VN); + + switch (castToType) + { + case TYP_BYTE: + assert(typ == TYP_INT); + return VNForIntCon(INT8(arg0Val)); + case TYP_BOOL: + case TYP_UBYTE: + assert(typ == TYP_INT); + return VNForIntCon(UINT8(arg0Val)); + case TYP_SHORT: + assert(typ == TYP_INT); + return VNForIntCon(INT16(arg0Val)); + case TYP_CHAR: + case TYP_USHORT: + assert(typ == TYP_INT); + return VNForIntCon(UINT16(arg0Val)); + case TYP_INT: + assert(typ == TYP_INT); + return VNForIntCon(INT32(arg0Val)); + case TYP_UINT: + assert(typ == TYP_INT); + return VNForIntCon(UINT32(arg0Val)); + case TYP_LONG: + assert(typ == TYP_LONG); + return VNForLongCon(INT64(arg0Val)); + case TYP_ULONG: + assert(typ == TYP_LONG); + return VNForLongCon(UINT64(arg0Val)); + case TYP_FLOAT: + assert(typ == TYP_FLOAT); + return VNForFloatCon(arg0Val); + case TYP_DOUBLE: + assert(typ == TYP_DOUBLE); + return VNForDoubleCon(double(arg0Val)); + default: + unreached(); + } + } case TYP_DOUBLE: { double arg0Val = GetConstantDouble(arg0VN); @@ -3062,6 +3209,53 @@ void ValueNumStore::GetConstantBoundInfo(ValueNum vn, ConstantBoundInfo* info) } } +//------------------------------------------------------------------------ +// IsVNArrLenUnsignedBound: Checks if the specified vn represents an expression +// such as "(uint)i < (uint)a.len" that implies that the array index is valid +// (0 <= i && i < a.len). +// +// Arguments: +// vn - Value number to query +// info - Pointer to an ArrLenUnsignedBoundInfo object to return information about +// the expression. Not populated if the vn expression isn't suitable (e.g. i <= a.len). +// This enables optCreateJTrueBoundAssertion to immediatly create an OAK_NO_THROW +// assertion instead of the OAK_EQUAL/NOT_EQUAL assertions created by signed compares +// (IsVNArrLenBound, IsVNArrLenArithBound) that require further processing. + +bool ValueNumStore::IsVNArrLenUnsignedBound(ValueNum vn, ArrLenUnsignedBoundInfo* info) +{ + VNFuncApp funcApp; + + if (GetVNFunc(vn, &funcApp)) + { + if ((funcApp.m_func == VNF_LT_UN) || (funcApp.m_func == VNF_GE_UN)) + { + // We only care about "(uint)i < (uint)a.len" and its negation "(uint)i >= (uint)a.len" + if (IsVNArrLen(funcApp.m_args[1])) + { + info->vnIdx = funcApp.m_args[0]; + info->cmpOper = funcApp.m_func; + info->vnLen = funcApp.m_args[1]; + return true; + } + } + else if ((funcApp.m_func == VNF_GT_UN) || (funcApp.m_func == VNF_LE_UN)) + { + // We only care about "(uint)a.len > (uint)i" and its negation "(uint)a.len <= (uint)i" + if (IsVNArrLen(funcApp.m_args[0])) + { + info->vnIdx = funcApp.m_args[1]; + // Let's keep a consistent operand order - it's always i < a.len, never a.len > i + info->cmpOper = (funcApp.m_func == VNF_GT_UN) ? VNF_LT_UN : VNF_GE_UN; + info->vnLen = funcApp.m_args[0]; + return true; + } + } + } + + return false; +} + bool ValueNumStore::IsVNArrLenBound(ValueNum vn) { // Do we have "var < a.len"? @@ -3257,48 +3451,103 @@ bool ValueNumStore::IsVNArrLen(ValueNum vn) ValueNum ValueNumStore::EvalMathFuncUnary(var_types typ, CorInfoIntrinsics gtMathFN, ValueNum arg0VN) { assert(arg0VN == VNNormVal(arg0VN)); + + // If the math intrinsic is not implemented by target-specific instructions, such as implemented + // by user calls, then don't do constant folding on it. This minimizes precision loss. + if (IsVNConstant(arg0VN) && Compiler::IsTargetIntrinsic(gtMathFN)) { - // If the math intrinsic is not implemented by target-specific instructions, such as implemented - // by user calls, then don't do constant folding on it. This minimizes precision loss. - // I *may* need separate tracks for the double/float -- if the intrinsic funcs have overloads for these. - double arg0Val = GetConstantDouble(arg0VN); + assert(varTypeIsFloating(TypeOfVN(arg0VN))); - double res = 0.0; - switch (gtMathFN) - { - case CORINFO_INTRINSIC_Sin: - res = sin(arg0Val); - break; - case CORINFO_INTRINSIC_Cos: - res = cos(arg0Val); - break; - case CORINFO_INTRINSIC_Sqrt: - res = sqrt(arg0Val); - break; - case CORINFO_INTRINSIC_Abs: - res = fabs(arg0Val); // The result and params are doubles. - break; - case CORINFO_INTRINSIC_Round: - res = FloatingPointUtils::round(arg0Val); - break; - default: - unreached(); // the above are the only math intrinsics at the time of this writing. - } if (typ == TYP_DOUBLE) { + // Both operand and its result must be of the same floating point type. + assert(typ == TypeOfVN(arg0VN)); + double arg0Val = GetConstantDouble(arg0VN); + + double res = 0.0; + switch (gtMathFN) + { + case CORINFO_INTRINSIC_Sin: + res = sin(arg0Val); + break; + case CORINFO_INTRINSIC_Cos: + res = cos(arg0Val); + break; + case CORINFO_INTRINSIC_Sqrt: + res = sqrt(arg0Val); + break; + case CORINFO_INTRINSIC_Abs: + res = fabs(arg0Val); + break; + case CORINFO_INTRINSIC_Round: + res = FloatingPointUtils::round(arg0Val); + break; + default: + unreached(); // the above are the only math intrinsics at the time of this writing. + } + return VNForDoubleCon(res); } else if (typ == TYP_FLOAT) { - return VNForFloatCon(float(res)); + // Both operand and its result must be of the same floating point type. + assert(typ == TypeOfVN(arg0VN)); + float arg0Val = GetConstantSingle(arg0VN); + + float res = 0.0f; + switch (gtMathFN) + { + case CORINFO_INTRINSIC_Sin: + res = sinf(arg0Val); + break; + case CORINFO_INTRINSIC_Cos: + res = cosf(arg0Val); + break; + case CORINFO_INTRINSIC_Sqrt: + res = sqrtf(arg0Val); + break; + case CORINFO_INTRINSIC_Abs: + res = fabsf(arg0Val); + break; + case CORINFO_INTRINSIC_Round: + res = FloatingPointUtils::round(arg0Val); + break; + default: + unreached(); // the above are the only math intrinsics at the time of this writing. + } + + return VNForFloatCon(res); } else { + // CORINFO_INTRINSIC_Round is currently the only intrinsic that takes floating-point arguments + // and that returns a non floating-point result. + assert(typ == TYP_INT); assert(gtMathFN == CORINFO_INTRINSIC_Round); - return VNForIntCon(int(res)); + int res = 0; + + switch (TypeOfVN(arg0VN)) + { + case TYP_DOUBLE: + { + double arg0Val = GetConstantDouble(arg0VN); + res = int(FloatingPointUtils::round(arg0Val)); + break; + } + case TYP_FLOAT: + { + float arg0Val = GetConstantSingle(arg0VN); + res = int(FloatingPointUtils::round(arg0Val)); + break; + } + default: + unreached(); + } + + return VNForIntCon(res); } } else @@ -7388,11 +7637,9 @@ VNFunc Compiler::fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc) case CORINFO_HELP_READYTORUN_STATIC_BASE: vnf = VNF_ReadyToRunStaticBase; break; -#if COR_JIT_EE_VERSION > 460 case CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE: vnf = VNF_ReadyToRunGenericStaticBase; break; -#endif // COR_JIT_EE_VERSION > 460 case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: vnf = VNF_GetsharedGcstaticBaseDynamicclass; break; @@ -7466,6 +7713,10 @@ VNFunc Compiler::fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc) vnf = VNF_IsInstanceOf; break; + case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: + vnf = VNF_TypeHandleToRuntimeType; + break; + case CORINFO_HELP_READYTORUN_ISINSTANCEOF: vnf = VNF_ReadyToRunIsInstanceOf; break; |