Title: [184960] trunk/Source/_javascript_Core
Revision
184960
Author
[email protected]
Date
2015-05-28 13:57:48 -0700 (Thu, 28 May 2015)

Log Message

[iOS8][ARMv7(s)] Optimized Object.create in 'use strict' context sometimes breaks.
https://bugs.webkit.org/show_bug.cgi?id=138038

Reviewed by Michael Saboff.

TL;DR: sometimes the baseline JIT could accidentally nuke the tag before calling
       to C++, making put_by_id behave erratically.

The bug was that put_by_id would randomly not work correctly in 32bits. It happened
in the baseline JIT if we were unlucky enough:
-The code get hot enough and the structure is stable so we get a fast path for
 put_by_id.
-We repatch the fast-path branch with a stub generated by
 emitPutTransitionStubAndGetOldStructure().
-In emitPutTransitionStubAndGetOldStructure(), we only preserve the payload of the base
 register, the tag register is ignored.
-emitPutTransitionStubAndGetOldStructure() allocate 2 to 3 registers. Any of those
 could be the one used for the base's tag before the fast path and the value is trashed.
-If we hit one of the failure case, we fallback to the slow path, but we destroyed
 the tag pointer.
-We now have unrelated bits in the tag, the most likely value type is now "double"
 and we fail the put_by_id because we try to set a property on a number.

The most obvious solution would be to change emitPutTransitionStubAndGetOldStructure()
to preserve the tag register in addition to the value register.
I decided against that option because of the added complexity. The DFG does not need
that case, so I would have to add branches everywhere to distinguish the cases
were we need to preserve the tag or not.

Instead, I just load the tag back from memory in the slow path. The function in the slow
path is several order of magnitude slower than a load, it is not worth eliminating it,
especially in baseline JIT.

I also discovered 4 useless loads in the fast path, so even with my extra load, this patch
makes the baseline faster :)

* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitSlow_op_put_by_id):
(JSC::JIT::emit_op_put_by_id): Deleted.
* tests/stress/put-by-id-on-new-object-after-prototype-transition-non-strict.js: Added.
(opaqueNewObject):
(putValueOnNewObject):
* tests/stress/put-by-id-on-new-object-after-prototype-transition-strict.js: Added.
(string_appeared_here.opaqueNewObject):
(putValueOnNewObject):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (184959 => 184960)


--- trunk/Source/_javascript_Core/ChangeLog	2015-05-28 20:46:14 UTC (rev 184959)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-05-28 20:57:48 UTC (rev 184960)
@@ -1,5 +1,53 @@
 2015-05-28  Benjamin Poulain  <[email protected]>
 
+        [iOS8][ARMv7(s)] Optimized Object.create in 'use strict' context sometimes breaks.
+        https://bugs.webkit.org/show_bug.cgi?id=138038
+
+        Reviewed by Michael Saboff.
+
+        TL;DR: sometimes the baseline JIT could accidentally nuke the tag before calling
+               to C++, making put_by_id behave erratically.
+
+        The bug was that put_by_id would randomly not work correctly in 32bits. It happened
+        in the baseline JIT if we were unlucky enough:
+        -The code get hot enough and the structure is stable so we get a fast path for
+         put_by_id.
+        -We repatch the fast-path branch with a stub generated by
+         emitPutTransitionStubAndGetOldStructure().
+        -In emitPutTransitionStubAndGetOldStructure(), we only preserve the payload of the base
+         register, the tag register is ignored.
+        -emitPutTransitionStubAndGetOldStructure() allocate 2 to 3 registers. Any of those
+         could be the one used for the base's tag before the fast path and the value is trashed.
+        -If we hit one of the failure case, we fallback to the slow path, but we destroyed
+         the tag pointer.
+        -We now have unrelated bits in the tag, the most likely value type is now "double"
+         and we fail the put_by_id because we try to set a property on a number.
+
+        The most obvious solution would be to change emitPutTransitionStubAndGetOldStructure()
+        to preserve the tag register in addition to the value register.
+        I decided against that option because of the added complexity. The DFG does not need
+        that case, so I would have to add branches everywhere to distinguish the cases
+        were we need to preserve the tag or not.
+
+        Instead, I just load the tag back from memory in the slow path. The function in the slow
+        path is several order of magnitude slower than a load, it is not worth eliminating it,
+        especially in baseline JIT.
+
+        I also discovered 4 useless loads in the fast path, so even with my extra load, this patch
+        makes the baseline faster :)
+
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emitSlow_op_put_by_id):
+        (JSC::JIT::emit_op_put_by_id): Deleted.
+        * tests/stress/put-by-id-on-new-object-after-prototype-transition-non-strict.js: Added.
+        (opaqueNewObject):
+        (putValueOnNewObject):
+        * tests/stress/put-by-id-on-new-object-after-prototype-transition-strict.js: Added.
+        (string_appeared_here.opaqueNewObject):
+        (putValueOnNewObject):
+
+2015-05-28  Benjamin Poulain  <[email protected]>
+
         [JSC] reduction the iteration count of the DoubleRep stress tests
 
         Once again, I used big numbers for manual testing and I forgot to fix them before landing.

Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp (184959 => 184960)


--- trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2015-05-28 20:46:14 UTC (rev 184959)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2015-05-28 20:57:48 UTC (rev 184960)
@@ -535,9 +535,6 @@
     emitLoad2(base, regT1, regT0, value, regT3, regT2);
     
     emitJumpSlowCaseIfNotJSCell(base, regT1);
-    
-    emitLoad(base, regT1, regT0);
-    emitLoad(value, regT3, regT2);
 
     JITPutByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
@@ -559,7 +556,10 @@
     linkSlowCase(iter);
     
     Label coldPathBegin(this);
-    
+
+    // JITPutByIdGenerator only preserve the value and the base's payload, we have to reload the tag.
+    emitLoadTag(base, regT1);
+
     JITPutByIdGenerator& gen = m_putByIds[m_putByIdIndex++];
     
     Call call = callOperation(

Added: trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-non-strict.js (0 => 184960)


--- trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-non-strict.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-non-strict.js	2015-05-28 20:57:48 UTC (rev 184960)
@@ -0,0 +1,31 @@
+function opaqueNewObject(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(opaqueNewObject);
+
+function putValueOnNewObject(value, prototype)
+{
+    var object = opaqueNewObject(prototype);
+    object.myProperty = value;
+    return object;
+}
+noInline(putValueOnNewObject)
+
+for (var i = 0; i < 1e4; ++i) {
+    var initialPrototype = new Object;
+    for (var j = 0; j < 5; ++j) {
+        var object = putValueOnNewObject(j, initialPrototype);
+        if (object["myProperty"] !== j) {
+            throw "Ooops, we mess up before the prototype change at iteration i = " + i + " j = " + j;
+        }
+    }
+
+    initialPrototype.foo = "bar";
+    for (var j = 0; j < 5; ++j) {
+        var object = putValueOnNewObject(j, initialPrototype);
+        if (object["myProperty"] !== j) {
+            throw "Ooops, we mess up at iteration i = " + i + " j = " + j;
+        }
+    }
+}
\ No newline at end of file

Added: trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-strict.js (0 => 184960)


--- trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-strict.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/put-by-id-on-new-object-after-prototype-transition-strict.js	2015-05-28 20:57:48 UTC (rev 184960)
@@ -0,0 +1,33 @@
+"use strict"
+
+function opaqueNewObject(prototype)
+{
+    return Object.create(prototype);
+}
+noInline(opaqueNewObject);
+
+function putValueOnNewObject(value, prototype)
+{
+    var object = opaqueNewObject(prototype);
+    object.myProperty = value;
+    return object;
+}
+noInline(putValueOnNewObject)
+
+for (var i = 0; i < 1e4; ++i) {
+    var initialPrototype = new Object;
+    for (var j = 0; j < 5; ++j) {
+        var object = putValueOnNewObject(j, initialPrototype);
+        if (object["myProperty"] !== j) {
+            throw "Ooops, we mess up before the prototype change at iteration i = " + i + " j = " + j;
+        }
+    }
+
+    initialPrototype.foo = "bar";
+    for (var j = 0; j < 5; ++j) {
+        var object = putValueOnNewObject(j, initialPrototype);
+        if (object["myProperty"] !== j) {
+            throw "Ooops, we mess up at iteration i = " + i + " j = " + j;
+        }
+    }
+}
\ No newline at end of file
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to