summaryrefslogtreecommitdiff
path: root/src/jit/valuenum.cpp
diff options
context:
space:
mode:
authorEgor Chesakov <Egor.Chesakov@microsoft.com>2018-08-08 17:57:26 -0700
committerEgor Chesakov <Egor.Chesakov@microsoft.com>2018-08-18 18:03:38 -0700
commit3477e6af5bf735c7bef12791dcad323f7e1600ff (patch)
tree60a06200b74bfb2d0a9626957545bf726f002f33 /src/jit/valuenum.cpp
parent7b6d24ede2810d29338b6097bf04aa67471f637d (diff)
downloadcoreclr-3477e6af5bf735c7bef12791dcad323f7e1600ff.tar.gz
coreclr-3477e6af5bf735c7bef12791dcad323f7e1600ff.tar.bz2
coreclr-3477e6af5bf735c7bef12791dcad323f7e1600ff.zip
Return "default" target-specific NaN value during constant folding of arithmetic expressions on ARM32 and ARM64
Diffstat (limited to 'src/jit/valuenum.cpp')
-rw-r--r--src/jit/valuenum.cpp200
1 files changed, 190 insertions, 10 deletions
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp
index a65a8787f0..29781b0e61 100644
--- a/src/jit/valuenum.cpp
+++ b/src/jit/valuenum.cpp
@@ -199,12 +199,38 @@ T ValueNumStore::EvalOp(VNFunc vnf, T v0, T v1, ValueNum* pExcSet)
}
}
+// We need to use target-specific NaN values when statically compute expressions.
+// Otherwise, cross crossgen (e.g. x86_arm) would have different binary outputs
+// from native crossgen (i.e. arm_arm) when the NaN got "embedded" into code.
+//
+// For example, when placing NaN value in r3 register
+// x86_arm crossgen would emit
+// movw r3, 0x00
+// movt r3, 0xfff8
+// while arm_arm crossgen (and JIT) output is
+// movw r3, 0x00
+// movt r3, 0x7ff8
+
struct FloatTraits
{
+ //------------------------------------------------------------------------
+ // NaN: Return target-specific float NaN value
+ //
+ // Notes:
+ // "Default" NaN value returned by expression 0.0f / 0.0f on x86/x64 has
+ // different binary representation (0xffc00000) than NaN on
+ // ARM32/ARM64 (0x7fc00000).
+
static float NaN()
{
+#if defined(_TARGET_XARCH_)
unsigned bits = 0xFFC00000u;
- float result;
+#elif defined(_TARGET_ARMARCH_)
+ unsigned bits = 0x7FC00000u;
+#else
+#error Unsupported or unset target architecture
+#endif
+ float result;
static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned) must equal sizeof(float)");
memcpy(&result, &bits, sizeof(result));
return result;
@@ -213,16 +239,170 @@ struct FloatTraits
struct DoubleTraits
{
+ //------------------------------------------------------------------------
+ // NaN: Return target-specific double NaN value
+ //
+ // Notes:
+ // "Default" NaN value returned by expression 0.0 / 0.0 on x86/x64 has
+ // different binary representation (0xfff8000000000000) than NaN on
+ // ARM32/ARM64 (0x7ff8000000000000).
+
static double NaN()
{
+#if defined(_TARGET_XARCH_)
unsigned long long bits = 0xFFF8000000000000ull;
- double result;
+#elif defined(_TARGET_ARMARCH_)
+ unsigned long long bits = 0x7FF8000000000000ull;
+#else
+#error Unsupported or unset target architecture
+#endif
+ double result;
static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned long long) must equal sizeof(double)");
memcpy(&result, &bits, sizeof(result));
return result;
}
};
+//------------------------------------------------------------------------
+// FpAdd: Computes value1 + value2
+//
+// Return Value:
+// TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
+// value1 + value2 - Otherwise
+//
+// Notes:
+// See FloatTraits::NaN() and DoubleTraits::NaN() notes.
+
+template <typename TFp, typename TFpTraits>
+TFp FpAdd(TFp value1, TFp value2)
+{
+#ifdef _TARGET_ARMARCH_
+ // If [value1] is negative infinity and [value2] is positive infinity
+ // the result is NaN.
+ // If [value1] is positive infinity and [value2] is negative infinity
+ // the result is NaN.
+
+ if (!_finite(value1) && !_finite(value2))
+ {
+ if (value1 < 0 && value2 > 0)
+ {
+ return TFpTraits::NaN();
+ }
+
+ if (value1 > 0 && value2 < 0)
+ {
+ return TFpTraits::NaN();
+ }
+ }
+#endif // _TARGET_ARMARCH_
+
+ return value1 + value2;
+}
+
+//------------------------------------------------------------------------
+// FpSub: Computes value1 - value2
+//
+// Return Value:
+// TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
+// value1 - value2 - Otherwise
+//
+// Notes:
+// See FloatTraits::NaN() and DoubleTraits::NaN() notes.
+
+template <typename TFp, typename TFpTraits>
+TFp FpSub(TFp value1, TFp value2)
+{
+#ifdef _TARGET_ARMARCH_
+ // If [value1] is positive infinity and [value2] is positive infinity
+ // the result is NaN.
+ // If [value1] is negative infinity and [value2] is negative infinity
+ // the result is NaN.
+
+ if (!_finite(value1) && !_finite(value2))
+ {
+ if (value1 > 0 && value2 > 0)
+ {
+ return TFpTraits::NaN();
+ }
+
+ if (value1 < 0 && value2 < 0)
+ {
+ return TFpTraits::NaN();
+ }
+ }
+#endif // _TARGET_ARMARCH_
+
+ return value1 - value2;
+}
+
+//------------------------------------------------------------------------
+// FpMul: Computes value1 * value2
+//
+// Return Value:
+// TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
+// value1 * value2 - Otherwise
+//
+// Notes:
+// See FloatTraits::NaN() and DoubleTraits::NaN() notes.
+
+template <typename TFp, typename TFpTraits>
+TFp FpMul(TFp value1, TFp value2)
+{
+#ifdef _TARGET_ARMARCH_
+ // From the ECMA standard:
+ //
+ // If [value1] is zero and [value2] is infinity
+ // the result is NaN.
+ // If [value1] is infinity and [value2] is zero
+ // the result is NaN.
+
+ if (value1 == 0 && !_finite(value2) && !_isnan(value2))
+ {
+ return TFpTraits::NaN();
+ }
+ if (!_finite(value1) && !_isnan(value1) && value2 == 0)
+ {
+ return TFpTraits::NaN();
+ }
+#endif // _TARGET_ARMARCH_
+
+ return value1 * value2;
+}
+
+//------------------------------------------------------------------------
+// FpDiv: Computes value1 / value2
+//
+// Return Value:
+// TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
+// value1 / value2 - Otherwise
+//
+// Notes:
+// See FloatTraits::NaN() and DoubleTraits::NaN() notes.
+
+template <typename TFp, typename TFpTraits>
+TFp FpDiv(TFp dividend, TFp divisor)
+{
+#ifdef _TARGET_ARMARCH_
+ // From the ECMA standard:
+ //
+ // If [dividend] is zero and [divisor] is zero
+ // the result is NaN.
+ // If [dividend] is infinity and [divisor] is infinity
+ // the result is NaN.
+
+ if (dividend == 0 && divisor == 0)
+ {
+ return TFpTraits::NaN();
+ }
+ else if (!_finite(dividend) && !_isnan(dividend) && !_finite(divisor) && !_isnan(divisor))
+ {
+ return TFpTraits::NaN();
+ }
+#endif // _TARGET_ARMARCH_
+
+ return dividend / divisor;
+}
+
template <typename TFp, typename TFpTraits>
TFp FpRem(TFp dividend, TFp divisor)
{
@@ -254,13 +434,13 @@ double ValueNumStore::EvalOp<double>(VNFunc vnf, double v0, double v1, ValueNum*
switch (oper)
{
case GT_ADD:
- return v0 + v1;
+ return FpAdd<double, DoubleTraits>(v0, v1);
case GT_SUB:
- return v0 - v1;
+ return FpSub<double, DoubleTraits>(v0, v1);
case GT_MUL:
- return v0 * v1;
+ return FpMul<double, DoubleTraits>(v0, v1);
case GT_DIV:
- return v0 / v1;
+ return FpDiv<double, DoubleTraits>(v0, v1);
case GT_MOD:
return FpRem<double, DoubleTraits>(v0, v1);
@@ -278,13 +458,13 @@ float ValueNumStore::EvalOp<float>(VNFunc vnf, float v0, float v1, ValueNum* pEx
switch (oper)
{
case GT_ADD:
- return v0 + v1;
+ return FpAdd<float, FloatTraits>(v0, v1);
case GT_SUB:
- return v0 - v1;
+ return FpSub<float, FloatTraits>(v0, v1);
case GT_MUL:
- return v0 * v1;
+ return FpMul<float, FloatTraits>(v0, v1);
case GT_DIV:
- return v0 / v1;
+ return FpDiv<float, FloatTraits>(v0, v1);
case GT_MOD:
return FpRem<float, FloatTraits>(v0, v1);