[JSC] Constant folding of UInt32ToNumber is incorrect
https://bugs.webkit.org/show_bug.cgi?id=157011
rdar://problem/25769641

Patch by Benjamin Poulain <bpoulain@apple.com> on 2016-04-25
Reviewed by Geoffrey Garen.

UInt32ToNumber should return the unsigned 32bit value of
its child. The abstract interpreter fails to do that when handling
Int52.

None of the tests caught that because the bytecode generator already
fold the operation if given a constant. If the constant is not visible
from the bytecode generator (for example because it comes from an inlined call),
then the abstract interpreter folding was producing invalid results.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* tests/stress/uint32-to-number-constant-folding.js: Added.
(uint32ToNumberMinusOne):
(uint32ToNumberMinusOnePlusInteger):
(inlineMinusOne):
(uint32ToNumberOnHiddenMinusOne):
(uint32ToNumberOnHiddenMinusOnePlusInteger):
(inlineLargeNegativeNumber1):
(inlineLargeNegativeNumber2):
(inlineLargeNegativeNumber3):
(uint32ToNumberOnHiddenLargeNegativeNumber1):
(uint32ToNumberOnHiddenLargeNegativeNumber2):
(uint32ToNumberOnHiddenLargeNegativeNumber3):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@200071 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 060c364..a9fc163 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,35 @@
+2016-04-25  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Constant folding of UInt32ToNumber is incorrect
+        https://bugs.webkit.org/show_bug.cgi?id=157011
+        rdar://problem/25769641
+
+        Reviewed by Geoffrey Garen.
+
+        UInt32ToNumber should return the unsigned 32bit value of
+        its child. The abstract interpreter fails to do that when handling
+        Int52.
+
+        None of the tests caught that because the bytecode generator already
+        fold the operation if given a constant. If the constant is not visible
+        from the bytecode generator (for example because it comes from an inlined call),
+        then the abstract interpreter folding was producing invalid results.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * tests/stress/uint32-to-number-constant-folding.js: Added.
+        (uint32ToNumberMinusOne):
+        (uint32ToNumberMinusOnePlusInteger):
+        (inlineMinusOne):
+        (uint32ToNumberOnHiddenMinusOne):
+        (uint32ToNumberOnHiddenMinusOnePlusInteger):
+        (inlineLargeNegativeNumber1):
+        (inlineLargeNegativeNumber2):
+        (inlineLargeNegativeNumber3):
+        (uint32ToNumberOnHiddenLargeNegativeNumber1):
+        (uint32ToNumberOnHiddenLargeNegativeNumber2):
+        (uint32ToNumberOnHiddenLargeNegativeNumber3):
+
 2016-04-25  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         Heap corruption is detected when destructing JSGlobalObject
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 7abe5b8..2982c47 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -302,7 +302,8 @@
         if (doesOverflow(node->arithMode())) {
             if (enableInt52()) {
                 if (child && child.isAnyInt()) {
-                    setConstant(node, jsNumber(child.asAnyInt()));
+                    int64_t machineInt = child.asAnyInt();
+                    setConstant(node, jsNumber(static_cast<uint32_t>(machineInt)));
                     break;
                 }
                 forNode(node).setType(SpecAnyInt);
diff --git a/Source/JavaScriptCore/tests/stress/uint32-to-number-constant-folding.js b/Source/JavaScriptCore/tests/stress/uint32-to-number-constant-folding.js
new file mode 100644
index 0000000..ce0587a
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/uint32-to-number-constant-folding.js
@@ -0,0 +1,84 @@
+function uint32ToNumberMinusOne()
+{
+    return (-1) >>> 0;
+}
+noInline(uint32ToNumberMinusOne);
+
+function uint32ToNumberMinusOnePlusInteger(a)
+{
+    return ((-1) >>> 0) + a;
+}
+noInline(uint32ToNumberMinusOnePlusInteger);
+
+function inlineMinusOne()
+{
+    return -1;
+}
+
+function uint32ToNumberOnHiddenMinusOne()
+{
+    return inlineMinusOne() >>> 0;
+}
+noInline(uint32ToNumberOnHiddenMinusOne);
+
+function uint32ToNumberOnHiddenMinusOnePlusInteger(a)
+{
+    return (inlineMinusOne() >>> 0) + a;
+}
+noInline(uint32ToNumberOnHiddenMinusOnePlusInteger);
+
+function inlineLargeNegativeNumber1()
+{
+    return -2251799813685248;
+}
+
+function inlineLargeNegativeNumber2()
+{
+    return -2251799813685249;
+}
+
+function inlineLargeNegativeNumber3()
+{
+    return -2251799813685250;
+}
+
+function uint32ToNumberOnHiddenLargeNegativeNumber1()
+{
+    return inlineLargeNegativeNumber1() >>> 0;
+}
+noInline(uint32ToNumberOnHiddenLargeNegativeNumber1);
+
+function uint32ToNumberOnHiddenLargeNegativeNumber2()
+{
+    return inlineLargeNegativeNumber2() >>> 0;
+}
+noInline(uint32ToNumberOnHiddenLargeNegativeNumber2);
+
+function uint32ToNumberOnHiddenLargeNegativeNumber3()
+{
+    return inlineLargeNegativeNumber3() >>> 0;
+}
+noInline(uint32ToNumberOnHiddenLargeNegativeNumber3);
+
+for (let i = 0; i < 1e6; ++i) {
+    if (uint32ToNumberMinusOne() !== 4294967295)
+        throw "Failed uint32ToNumberMinusOne()";
+
+    if (uint32ToNumberMinusOnePlusInteger(1) !== 4294967296)
+        throw "Failed uint32ToNumberMinusOnePlusOne()";
+
+    if (uint32ToNumberOnHiddenMinusOne() !== 4294967295)
+        throw "Failed uint32ToNumberOnHiddenMinusOne()";
+
+    if (uint32ToNumberOnHiddenMinusOnePlusInteger(1) !== 4294967296)
+        throw "Failed uint32ToNumberOnHiddenMinusOnePlusInteger()";
+
+    if (uint32ToNumberOnHiddenLargeNegativeNumber1() !== 0)
+        throw "Failed uint32ToNumberOnHiddenLargeNegativeNumber1()";
+
+    if (uint32ToNumberOnHiddenLargeNegativeNumber2() !== 4294967295)
+        throw "Failed uint32ToNumberOnHiddenLargeNegativeNumber2()";
+
+    if (uint32ToNumberOnHiddenLargeNegativeNumber3() !== 4294967294)
+        throw "Failed uint32ToNumberOnHiddenLargeNegativeNumber3()";
+}
\ No newline at end of file