summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKyungwoo Lee <kyulee@microsoft.com>2016-04-18 10:42:51 -0700
committerKyungwoo Lee <kyulee@microsoft.com>2016-04-18 17:46:40 -0700
commit75c97cff492584075b6e13d67a901c69c34ad4f9 (patch)
tree45d58e70072962b1c4c15dc53c5980c63b7416d6 /src
parent83e52facfec1ae6002ed9d0a474c300ab3dbe2fe (diff)
downloadcoreclr-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.cpp2
-rw-r--r--src/jit/utils.cpp26
-rw-r--r--src/jit/utils.h2
-rw-r--r--src/jit/valuenum.cpp2
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.