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