summaryrefslogtreecommitdiff
path: root/src/jit/valuenum.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/valuenum.cpp')
-rw-r--r--src/jit/valuenum.cpp375
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;