diff options
author | Kyungwoo Lee <kyulee@microsoft.com> | 2016-04-18 10:42:51 -0700 |
---|---|---|
committer | Kyungwoo Lee <kyulee@microsoft.com> | 2016-04-18 17:46:40 -0700 |
commit | 75c97cff492584075b6e13d67a901c69c34ad4f9 (patch) | |
tree | 45d58e70072962b1c4c15dc53c5980c63b7416d6 /src | |
parent | 83e52facfec1ae6002ed9d0a474c300ab3dbe2fe (diff) | |
download | coreclr-75c97cff492584075b6e13d67a901c69c34ad4f9.tar.gz coreclr-75c97cff492584075b6e13d67a901c69c34ad4f9.tar.bz2 coreclr-75c97cff492584075b6e13d67a901c69c34ad4f9.zip |
ARM64: Fix Round Operation
Math.Round is implemented as a target intrinsic for Arm64.
JIT emitted `frinta` which rounds the ties to away.
As described in MSDN
https://msdn.microsoft.com/en-us/library/system.math.round(v=vs.110).aspx#Round2_Example,
we should round the ties to even.
So, I corrected `frintn` instruction instead for such intrinsic expansion.
I've also identified that `EvalMathFuncUnary` incorrectly folds the round
operation when the argument is constant at the compile time.
The logic itself is not the right semantic as described above in MSDN.
I made a separate helper function which is essentially a duplicate of
classlib. This is not Arm64 specific fix but applies to all other targets.
So, I just fixed it while I'm here.
Diffstat (limited to 'src')
-rw-r--r-- | src/jit/codegenarm64.cpp | 2 | ||||
-rw-r--r-- | src/jit/utils.cpp | 26 | ||||
-rw-r--r-- | src/jit/utils.h | 2 | ||||
-rw-r--r-- | src/jit/valuenum.cpp | 2 |
4 files changed, 30 insertions, 2 deletions
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index f6305b8966..1473171863 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -6519,7 +6519,7 @@ CodeGen::genIntrinsic(GenTreePtr treeNode) case CORINFO_INTRINSIC_Round: genConsumeOperands(treeNode->AsOp()); - getEmitter()->emitInsBinary(INS_frinta, emitTypeSize(treeNode), treeNode, srcNode); + getEmitter()->emitInsBinary(INS_frintn, emitTypeSize(treeNode), treeNode, srcNode); break; case CORINFO_INTRINSIC_Sqrt: diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp index c86d124b1b..f92335e9b6 100644 --- a/src/jit/utils.cpp +++ b/src/jit/utils.cpp @@ -1614,3 +1614,29 @@ unsigned __int64 FloatingPointUtils::convertDoubleToUInt64(double d) { return u64; } + +// Rounds a double-precision floating-point value to the nearest integer, +// and rounds midpoint values to the nearest even number. +// Note this should align with classlib in floatnative.cpp +// Specializing for x86 using a x87 instruction is optional since +// this outcome is identical across targets. +double FloatingPointUtils::round(double d) +{ + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + if (d == (double)(__int64)d) + return d; + + double tempVal = (d + 0.5); + //We had a number that was equally close to 2 integers. + //We need to return the even one. + double flrTempVal = floor(tempVal); + if (flrTempVal == tempVal) { + if (fmod(tempVal, 2.0) != 0) { + flrTempVal -= 1.0; + } + } + + flrTempVal = _copysign(flrTempVal, d); + return flrTempVal; +} diff --git a/src/jit/utils.h b/src/jit/utils.h index 7f141637f9..242a862b2b 100644 --- a/src/jit/utils.h +++ b/src/jit/utils.h @@ -538,6 +538,8 @@ public: static float convertUInt64ToFloat(unsigned __int64 u64); static unsigned __int64 convertDoubleToUInt64(double d); + + static double round(double d); }; #endif // _UTILS_H_ diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index 6471075ecd..f928d607ee 100644 --- a/src/jit/valuenum.cpp +++ b/src/jit/valuenum.cpp @@ -3082,7 +3082,7 @@ ValueNum ValueNumStore::EvalMathFuncUnary(var_types typ, CorInfoIntrinsics gtMat res = fabs(arg0Val); // The result and params are doubles. break; case CORINFO_INTRINSIC_Round: - res = (arg0Val > 0.0 ? floor(arg0Val + 0.5) : ceil(arg0Val - 0.5)); + res = FloatingPointUtils::round(arg0Val); break; default: unreached(); // the above are the only math intrinsics at the time of this writing. |