Title: [282239] trunk
Revision
282239
Author
[email protected]
Date
2021-09-09 14:36:38 -0700 (Thu, 09 Sep 2021)

Log Message

[JSC] Optimize op_get_property_enumerator further
https://bugs.webkit.org/show_bug.cgi?id=230086

Reviewed by Saam Barati.

JSTests:

* stress/for-in-cell-other.js: Added.
(shouldBe):
(forIn):
* stress/for-in-null-undefined.js: Added.
(shouldBe):
(forIn):

Source/_javascript_Core:

1. This patch adds fast path of op_get_property_enumerator to LLInt and Baseline. Previously, we only had this fast path in DFG and FTL.
2. From the profiled data, Speedometer2/React-Redux-TodoMVC has GetPropertyEnumerator(CellOrOther). However, DFG and FTL only optimized
   GetPropertyEnumerator(Cell). We add CellOrOther and Other cases: if the argument is Other, then we can constant fold it to the
   empty enumerator. If the argument is CellOrOther, we can check first, and return empty enumerator for Other case.
3. This patch also cleans up StructureRareData lookup by introducing StructureType to JSType.

----------------------------------------------------------------------------------------------------------------------------------
|               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
----------------------------------------------------------------------------------------------------------------------------------
| Elm-TodoMVC                          |117.388333   |117.680000   |1.002485  | 0.269607                                         |
| VueJS-TodoMVC                        |24.918333    |24.651667    |0.989298  | 0.157665                                         |
| EmberJS-TodoMVC                      |126.430000   |126.296667   |0.998945  | 0.673803                                         |
| BackboneJS-TodoMVC                   |48.695000    |48.411667    |0.994181  | 0.019164                                         |
| Preact-TodoMVC                       |17.268333    |17.511667    |1.014091  | 0.199775                                         |
| AngularJS-TodoMVC                    |130.246667   |129.850000   |0.996954  | 0.261543                                         |
| Vanilla-ES2015-TodoMVC               |63.626667    |63.611667    |0.999764  | 0.912112                                         |
| Inferno-TodoMVC                      |63.881667    |63.600000    |0.995591  | 0.385440                                         |
| Flight-TodoMVC                       |78.158333    |78.606667    |1.005736  | 0.284177                                         |
| Angular2-TypeScript-TodoMVC          |39.448333    |39.411667    |0.999071  | 0.890825                                         |
| VanillaJS-TodoMVC                    |50.858333    |51.130000    |1.005342  | 0.195409                                         |
| jQuery-TodoMVC                       |225.318333   |226.256667   |1.004164  | 0.011190                                         |
| EmberJS-Debug-TodoMVC                |340.150000   |338.450000   |0.995002  | 0.000063 (significant)                           |
| React-TodoMVC                        |85.703333    |85.606667    |0.998872  | 0.549298                                         |
| React-Redux-TodoMVC                  |141.985000   |140.418333   |0.988966  | 0.000000 (significant)                           |
| Vanilla-ES2015-Babel-Webpack-TodoMVC |61.505000    |61.705000    |1.003252  | 0.079817                                         |
----------------------------------------------------------------------------------------------------------------------------------
a mean = 260.98021
b mean = 261.16020
pValue = 0.4985041089
(Bigger means are better.)
1.001 times better
Results ARE NOT significant

* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
(JSC::DFG::SpeculativeJIT::compileObjectKeysOrObjectGetOwnPropertyNames):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileObjectKeysOrObjectGetOwnPropertyNames):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfStructure):
(JSC::AssemblyHelpers::branchIfNotStructure):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_property_enumerator):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_get_property_enumerator):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/BrandedStructure.cpp:
(JSC::BrandedStructure::create):
* runtime/JSType.cpp:
(WTF::printInternal):
* runtime/JSType.h:
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::create):
* runtime/Structure.h:
(JSC::Structure::isRareData):
(JSC::Structure::isRareData const): Deleted.
* runtime/StructureInlines.h:
(JSC::Structure::create):
(JSC::Structure::createStructure):
* runtime/StructureRareData.h:
* runtime/VM.cpp:
(JSC::VM::VM):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (282238 => 282239)


--- trunk/JSTests/ChangeLog	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/JSTests/ChangeLog	2021-09-09 21:36:38 UTC (rev 282239)
@@ -1,3 +1,17 @@
+2021-09-09  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Optimize op_get_property_enumerator further
+        https://bugs.webkit.org/show_bug.cgi?id=230086
+
+        Reviewed by Saam Barati.
+
+        * stress/for-in-cell-other.js: Added.
+        (shouldBe):
+        (forIn):
+        * stress/for-in-null-undefined.js: Added.
+        (shouldBe):
+        (forIn):
+
 2021-09-09  Justin Michaud  <[email protected]>
 
         Differential testing: incorrect constant propagation around Uint8ClampedArray

Added: trunk/JSTests/stress/for-in-cell-other.js (0 => 282239)


--- trunk/JSTests/stress/for-in-cell-other.js	                        (rev 0)
+++ trunk/JSTests/stress/for-in-cell-other.js	2021-09-09 21:36:38 UTC (rev 282239)
@@ -0,0 +1,22 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function forIn(object)
+{
+    var iteration = 0;
+    for (var i in object)
+        ++iteration;
+    return iteration;
+}
+noInline(forIn);
+
+var object = { i: 42, i2: 43, i3: 44 };
+var object2 = { i: 42, i2: 43, i3: 44, i4:45 };
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(forIn(null), 0);
+    shouldBe(forIn(object), 3);
+    shouldBe(forIn(undefined), 0);
+    shouldBe(forIn(object2), 4);
+}

Added: trunk/JSTests/stress/for-in-null-undefined.js (0 => 282239)


--- trunk/JSTests/stress/for-in-null-undefined.js	                        (rev 0)
+++ trunk/JSTests/stress/for-in-null-undefined.js	2021-09-09 21:36:38 UTC (rev 282239)
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function forIn(object)
+{
+    var iteration = 0;
+    for (var i in object)
+        ++iteration;
+    return iteration;
+}
+noInline(forIn);
+
+for (var i = 0; i < 1e6; ++i) {
+    shouldBe(forIn(null), 0);
+    shouldBe(forIn(undefined), 0);
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (282238 => 282239)


--- trunk/Source/_javascript_Core/ChangeLog	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-09-09 21:36:38 UTC (rev 282239)
@@ -1,3 +1,84 @@
+2021-09-09  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Optimize op_get_property_enumerator further
+        https://bugs.webkit.org/show_bug.cgi?id=230086
+
+        Reviewed by Saam Barati.
+
+        1. This patch adds fast path of op_get_property_enumerator to LLInt and Baseline. Previously, we only had this fast path in DFG and FTL.
+        2. From the profiled data, Speedometer2/React-Redux-TodoMVC has GetPropertyEnumerator(CellOrOther). However, DFG and FTL only optimized
+           GetPropertyEnumerator(Cell). We add CellOrOther and Other cases: if the argument is Other, then we can constant fold it to the
+           empty enumerator. If the argument is CellOrOther, we can check first, and return empty enumerator for Other case.
+        3. This patch also cleans up StructureRareData lookup by introducing StructureType to JSType.
+
+        ----------------------------------------------------------------------------------------------------------------------------------
+        |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        | Elm-TodoMVC                          |117.388333   |117.680000   |1.002485  | 0.269607                                         |
+        | VueJS-TodoMVC                        |24.918333    |24.651667    |0.989298  | 0.157665                                         |
+        | EmberJS-TodoMVC                      |126.430000   |126.296667   |0.998945  | 0.673803                                         |
+        | BackboneJS-TodoMVC                   |48.695000    |48.411667    |0.994181  | 0.019164                                         |
+        | Preact-TodoMVC                       |17.268333    |17.511667    |1.014091  | 0.199775                                         |
+        | AngularJS-TodoMVC                    |130.246667   |129.850000   |0.996954  | 0.261543                                         |
+        | Vanilla-ES2015-TodoMVC               |63.626667    |63.611667    |0.999764  | 0.912112                                         |
+        | Inferno-TodoMVC                      |63.881667    |63.600000    |0.995591  | 0.385440                                         |
+        | Flight-TodoMVC                       |78.158333    |78.606667    |1.005736  | 0.284177                                         |
+        | Angular2-TypeScript-TodoMVC          |39.448333    |39.411667    |0.999071  | 0.890825                                         |
+        | VanillaJS-TodoMVC                    |50.858333    |51.130000    |1.005342  | 0.195409                                         |
+        | jQuery-TodoMVC                       |225.318333   |226.256667   |1.004164  | 0.011190                                         |
+        | EmberJS-Debug-TodoMVC                |340.150000   |338.450000   |0.995002  | 0.000063 (significant)                           |
+        | React-TodoMVC                        |85.703333    |85.606667    |0.998872  | 0.549298                                         |
+        | React-Redux-TodoMVC                  |141.985000   |140.418333   |0.988966  | 0.000000 (significant)                           |
+        | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.505000    |61.705000    |1.003252  | 0.079817                                         |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        a mean = 260.98021
+        b mean = 261.16020
+        pValue = 0.4985041089
+        (Bigger means are better.)
+        1.001 times better
+        Results ARE NOT significant
+
+        * bytecode/SpeculatedType.cpp:
+        (JSC::dumpSpeculation):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
+        (JSC::DFG::SpeculativeJIT::compileObjectKeysOrObjectGetOwnPropertyNames):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileObjectKeysOrObjectGetOwnPropertyNames):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfStructure):
+        (JSC::AssemblyHelpers::branchIfNotStructure):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_get_property_enumerator):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_get_property_enumerator):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/BrandedStructure.cpp:
+        (JSC::BrandedStructure::create):
+        * runtime/JSType.cpp:
+        (WTF::printInternal):
+        * runtime/JSType.h:
+        * runtime/Structure.cpp:
+        (JSC::Structure::Structure):
+        (JSC::Structure::create):
+        * runtime/Structure.h:
+        (JSC::Structure::isRareData):
+        (JSC::Structure::isRareData const): Deleted.
+        * runtime/StructureInlines.h:
+        (JSC::Structure::create):
+        (JSC::Structure::createStructure):
+        * runtime/StructureRareData.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+
 2021-09-09  Patrick Angle  <[email protected]>
 
         run-webkit-archive crashes with dyld error

Modified: trunk/Source/_javascript_Core/bytecode/SpeculatedType.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/bytecode/SpeculatedType.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/bytecode/SpeculatedType.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -250,6 +250,11 @@
             strOut.print("Symbol");
         else
             isTop = false;
+
+        if (value & SpecHeapBigInt)
+            strOut.print("HeapBigInt");
+        else
+            isTop = false;
     }
     
     if (value == SpecInt32Only)
@@ -285,20 +290,11 @@
             isTop = false;
     }
 
-    if ((value & SpecBigInt) == SpecBigInt)
-        strOut.print("BigInt");
 #if USE(BIGINT32)
-    else {
-        if (value & SpecBigInt32)
-            strOut.print("BigInt32");
-        else
-            isTop = false;
-
-        if (value & SpecHeapBigInt)
-            strOut.print("HeapBigInt");
-        else
-            isTop = false;
-    }
+    if (value & SpecBigInt32)
+        strOut.print("BigInt32");
+    else
+        isTop = false;
 #endif
 
     if (value & SpecDoubleImpureNaN)

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -2274,6 +2274,11 @@
         case GetPropertyEnumerator: {
             if (node->child1()->shouldSpeculateCell())
                 fixEdge<CellUse>(node->child1());
+            else if (node->child1()->shouldSpeculateOther()) {
+                insertCheck<OtherUse>(node->child1().node());
+                m_graph.convertToConstant(node, m_graph.freeze(vm().emptyPropertyNameEnumerator()));
+            } else if (node->child1()->shouldSpeculateCellOrOther())
+                fixEdge<CellOrOtherUse>(node->child1());
             break;
         }
 

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -14006,17 +14006,27 @@
 
 void SpeculativeJIT::compileGetPropertyEnumerator(Node* node)
 {
-    if (node->child1().useKind() == CellUse) {
-        SpeculateCellOperand base(this, node->child1());
+    if (node->child1().useKind() == CellUse || node->child1().useKind() == CellOrOtherUse) {
+        JSValueOperand base(this, node->child1(), ManualOperandSpeculation);
         GPRTemporary scratch1(this);
         GPRTemporary scratch2(this);
 
-        GPRReg baseGPR = base.gpr();
+        speculate(node, node->child1());
+
+        JSValueRegs baseRegs = base.jsValueRegs();
         GPRReg scratch1GPR = scratch1.gpr();
         GPRReg scratch2GPR = scratch2.gpr();
 
         CCallHelpers::JumpList slowCases;
+        CCallHelpers::JumpList doneCases;
 
+        if (node->child1().useKind() == CellOrOtherUse) {
+            auto notOther = m_jit.branchIfNotOther(baseRegs, scratch1GPR);
+            m_jit.move(TrustedImmPtr::weakPointer(m_graph, vm().emptyPropertyNameEnumerator()), scratch1GPR);
+            doneCases.append(m_jit.jump());
+            notOther.link(&m_jit);
+        }
+
         // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
         static_assert(!NonArray);
         static_assert(ArrayClass == 1);
@@ -14025,26 +14035,56 @@
         static_assert(NonArray <= ArrayWithUndecided);
         static_assert(ArrayClass <= ArrayWithUndecided);
         static_assert(ArrayWithUndecided <= ArrayWithUndecided);
-        m_jit.load8(CCallHelpers::Address(baseGPR, JSCell::indexingTypeAndMiscOffset()), scratch1GPR);
-        m_jit.and32(CCallHelpers::TrustedImm32(IndexingTypeMask), scratch1GPR);
-        slowCases.append(m_jit.branch32(CCallHelpers::Above, scratch1GPR, CCallHelpers::TrustedImm32(ArrayWithUndecided)));
-        m_jit.emitLoadStructure(vm(), baseGPR, scratch1GPR, scratch2GPR);
-        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, Structure::previousOrRareDataOffset()), scratch1GPR);
 
+        AbstractValue& baseValue = m_state.forNode(node->child1());
+        RegisteredStructure onlyStructure;
+        StructureRareData* rareData = nullptr;
+        bool skipIndexingMaskCheck = false;
+        if (baseValue.isType(SpecObject) && baseValue.m_structure.isFinite()) {
+            bool hasIndexing = false;
+            baseValue.m_structure.forEach([&] (RegisteredStructure structure) {
+                if (structure->indexingType() > ArrayWithUndecided)
+                    hasIndexing = true;
+            });
+            if (!hasIndexing)
+                skipIndexingMaskCheck = true;
+            _onlyStructure_ = baseValue.m_structure.onlyStructure();
+            if (onlyStructure)
+                rareData = onlyStructure->tryRareData();
+        }
+
+        if (!skipIndexingMaskCheck) {
+            m_jit.load8(CCallHelpers::Address(baseRegs.payloadGPR(), JSCell::indexingTypeAndMiscOffset()), scratch1GPR);
+            m_jit.and32(CCallHelpers::TrustedImm32(IndexingTypeMask), scratch1GPR);
+            slowCases.append(m_jit.branch32(CCallHelpers::Above, scratch1GPR, CCallHelpers::TrustedImm32(ArrayWithUndecided)));
+        }
+
+        if (rareData) {
+            FrozenValue* frozenRareData = m_graph.freeze(rareData);
+            m_jit.move(TrustedImmPtr(frozenRareData), scratch1GPR);
+            m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, StructureRareData::offsetOfCachedPropertyNameEnumerator()), scratch1GPR);
+        } else {
+            if (onlyStructure)
+                m_jit.move(TrustedImmPtr(onlyStructure), scratch1GPR);
+            else
+                m_jit.emitLoadStructure(vm(), baseRegs.payloadGPR(), scratch1GPR, scratch2GPR);
+            m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, Structure::previousOrRareDataOffset()), scratch1GPR);
+            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
+            slowCases.append(m_jit.branchIfStructure(scratch1GPR));
+            m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, StructureRareData::offsetOfCachedPropertyNameEnumerator()), scratch1GPR);
+        }
+
         slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
-        slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratch1GPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(vm().structureStructure->structureID()))));
-        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, StructureRareData::offsetOfCachedPropertyNameEnumerator()), scratch1GPR);
-        slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
         slowCases.append(m_jit.branchTest32(CCallHelpers::Zero, CCallHelpers::Address(scratch1GPR, JSPropertyNameEnumerator::flagsOffset()), CCallHelpers::TrustedImm32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)));
-        auto done = m_jit.jump();
+        doneCases.append(m_jit.jump());
 
         slowCases.link(&m_jit);
         silentSpillAllRegisters(scratch1GPR, scratch2GPR);
-        callOperation(operationGetPropertyEnumeratorCell, scratch1GPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR);
+        callOperation(operationGetPropertyEnumeratorCell, scratch1GPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseRegs.payloadGPR());
         silentFillAllRegisters();
         m_jit.exceptionCheck();
 
-        done.link(&m_jit);
+        doneCases.link(&m_jit);
         cellResult(scratch1GPR, node);
         return;
     }
@@ -14305,7 +14345,7 @@
             m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR);
 
             slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
-            slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(vm().structureStructure->structureID()))));
+            slowCases.append(m_jit.branchIfStructure(scratchGPR));
 
             m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedPropertyNames(node->op() == ObjectKeys ? CachedPropertyNamesKind::Keys : CachedPropertyNamesKind::GetOwnPropertyNames)), scratchGPR);
 

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -7207,8 +7207,8 @@
 
                 LBasicBlock lastNext = m_out.appendTo(notNullCase, rareDataCase);
                 m_out.branch(
-                    m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
-                    unsure(rareDataCase), unsure(slowCase));
+                    isCellWithType(previousOrRareData, StructureType, std::nullopt),
+                    unsure(slowCase), unsure(rareDataCase));
 
                 m_out.appendTo(rareDataCase, useCacheCase);
                 ASSERT(bitwise_cast<uintptr_t>(StructureRareData::cachedPropertyNamesSentinel()) == 1);
@@ -13314,7 +13314,7 @@
     void compileGetPropertyEnumerator()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
-        if (m_node->child1().useKind() == CellUse) {
+        if (m_node->child1().useKind() == CellUse || m_node->child1().useKind() == CellOrOtherUse) {
             LBasicBlock checkExistingCase = m_out.newBlock();
             LBasicBlock notNullCase = m_out.newBlock();
             LBasicBlock rareDataCase = m_out.newBlock();
@@ -13321,9 +13321,19 @@
             LBasicBlock validationCase = m_out.newBlock();
             LBasicBlock genericCase = m_out.newBlock();
             LBasicBlock continuation = m_out.newBlock();
+            LBasicBlock lastNext = nullptr;
 
-            LValue cell = lowCell(m_node->child1());
+            LValue base = lowJSValue(m_node->child1(), ManualOperandSpeculation);
+            speculate(m_node->child1());
 
+            Vector<ValueFromBlock, 3> results;
+            if (m_node->child1().useKind() == CellOrOtherUse) {
+                LBasicBlock cellCase = m_out.newBlock();
+                results.append(m_out.anchor(weakPointer(m_graph.m_vm.emptyPropertyNameEnumerator())));
+                m_out.branch(isOther(base, provenType(m_node->child1())), unsure(continuation), unsure(cellCase));
+                lastNext = m_out.appendTo(cellCase, checkExistingCase);
+            }
+
             // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
             static_assert(!NonArray);
             static_assert(ArrayClass == 1);
@@ -13332,18 +13342,56 @@
             static_assert(NonArray <= ArrayWithUndecided);
             static_assert(ArrayClass <= ArrayWithUndecided);
             static_assert(ArrayWithUndecided <= ArrayWithUndecided);
-            LValue indexingType = m_out.bitAnd(m_out.load8ZeroExt32(cell, m_heaps.JSCell_indexingTypeAndMisc), m_out.constInt32(IndexingTypeMask));
-            m_out.branch(m_out.belowOrEqual(indexingType, m_out.constInt32(ArrayWithUndecided)), unsure(checkExistingCase), unsure(genericCase));
 
-            LBasicBlock lastNext = m_out.appendTo(checkExistingCase, notNullCase);
-            LValue structure = loadStructure(cell);
-            LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+            AbstractValue& baseValue = m_state.forNode(m_node->child1());
+            RegisteredStructure onlyStructure;
+            StructureRareData* rareData = nullptr;
+            bool skipIndexingMaskCheck = false;
+            if (baseValue.isType(SpecObject) && baseValue.m_structure.isFinite()) {
+                bool hasIndexing = false;
+                baseValue.m_structure.forEach([&] (RegisteredStructure structure) {
+                    if (structure->indexingType() > ArrayWithUndecided)
+                        hasIndexing = true;
+                });
+                if (!hasIndexing)
+                    skipIndexingMaskCheck = true;
+                _onlyStructure_ = baseValue.m_structure.onlyStructure();
+                if (onlyStructure)
+                    rareData = onlyStructure->tryRareData();
+            }
+
+            LValue notHavingIndexing = nullptr;
+            if (skipIndexingMaskCheck)
+                notHavingIndexing = m_out.booleanTrue;
+            else {
+                LValue indexingType = m_out.bitAnd(m_out.load8ZeroExt32(base, m_heaps.JSCell_indexingTypeAndMisc), m_out.constInt32(IndexingTypeMask));
+                notHavingIndexing = m_out.belowOrEqual(indexingType, m_out.constInt32(ArrayWithUndecided));
+            }
+            m_out.branch(notHavingIndexing, unsure(checkExistingCase), unsure(genericCase));
+
+            LBasicBlock lastNextCandidate = m_out.appendTo(checkExistingCase, notNullCase);
+            if (!lastNext)
+                lastNext = lastNextCandidate;
+            LValue previousOrRareData = nullptr;
+            if (rareData)
+                previousOrRareData = weakPointer(rareData);
+            else {
+                LValue structure = nullptr;
+                if (onlyStructure)
+                    structure = weakStructure(onlyStructure);
+                else
+                    structure = loadStructure(base);
+                previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+            }
             m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(genericCase));
 
             m_out.appendTo(notNullCase, rareDataCase);
-            m_out.branch(
-                m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
-                unsure(rareDataCase), unsure(genericCase));
+            LValue isRareData = nullptr;
+            if (rareData)
+                isRareData = m_out.booleanTrue;
+            else
+                isRareData = m_out.logicalNot(isCellWithType(previousOrRareData, StructureType, std::nullopt));
+            m_out.branch(isRareData, unsure(rareDataCase), unsure(genericCase));
 
             m_out.appendTo(rareDataCase, validationCase);
             LValue cached = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedPropertyNameEnumerator);
@@ -13350,15 +13398,15 @@
             m_out.branch(m_out.notNull(cached), unsure(validationCase), unsure(genericCase));
 
             m_out.appendTo(validationCase, genericCase);
-            ValueFromBlock fastResult = m_out.anchor(cached);
+            results.append(m_out.anchor(cached));
             m_out.branch(m_out.testNonZero32(m_out.load32(cached, m_heaps.JSPropertyNameEnumerator_flags), m_out.constInt32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)), unsure(continuation), unsure(genericCase));
 
             m_out.appendTo(genericCase, continuation);
-            ValueFromBlock genericResult = m_out.anchor(vmCall(pointerType(), operationGetPropertyEnumeratorCell, weakPointer(globalObject), cell));
+            results.append(m_out.anchor(vmCall(pointerType(), operationGetPropertyEnumeratorCell, weakPointer(globalObject), base)));
             m_out.jump(continuation);
 
             m_out.appendTo(continuation, lastNext);
-            setJSValue(m_out.phi(pointerType(), fastResult, genericResult));
+            setJSValue(m_out.phi(pointerType(), results));
             return;
         }
         setJSValue(vmCall(Int64, operationGetPropertyEnumerator, weakPointer(globalObject), lowJSValue(m_node->child1())));

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.h (282238 => 282239)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -934,6 +934,8 @@
     Jump branchIfNotHeapBigInt(GPRReg cellGPR) { return branchIfNotType(cellGPR, HeapBigIntType); }
     Jump branchIfFunction(GPRReg cellGPR) { return branchIfType(cellGPR, JSFunctionType); }
     Jump branchIfNotFunction(GPRReg cellGPR) { return branchIfNotType(cellGPR, JSFunctionType); }
+    Jump branchIfStructure(GPRReg cellGPR) { return branchIfType(cellGPR, StructureType); }
+    Jump branchIfNotStructure(GPRReg cellGPR) { return branchIfNotType(cellGPR, StructureType); }
     
     void isEmpty(GPRReg gpr, GPRReg dst)
     {

Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/jit/JIT.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -308,7 +308,6 @@
         DEFINE_SLOW_OP(new_array_with_spread)
         DEFINE_SLOW_OP(new_array_buffer)
         DEFINE_SLOW_OP(spread)
-        DEFINE_SLOW_OP(get_property_enumerator)
         DEFINE_SLOW_OP(create_direct_arguments)
         DEFINE_SLOW_OP(create_scoped_arguments)
         DEFINE_SLOW_OP(create_cloned_arguments)
@@ -362,6 +361,7 @@
         DEFINE_OP(op_get_by_id_with_this)
         DEFINE_OP(op_get_by_id_direct)
         DEFINE_OP(op_get_by_val)
+        DEFINE_OP(op_get_property_enumerator)
         DEFINE_OP(op_enumerator_next)
         DEFINE_OP(op_enumerator_get_by_val)
         DEFINE_OP(op_enumerator_in_by_val)

Modified: trunk/Source/_javascript_Core/jit/JIT.h (282238 => 282239)


--- trunk/Source/_javascript_Core/jit/JIT.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/jit/JIT.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -545,6 +545,7 @@
         template<typename OpcodeType>
         void generateGetByValSlowCase(const OpcodeType&, Vector<SlowCaseEntry>::iterator&);
 
+        void emit_op_get_property_enumerator(const Instruction*);
         void emit_op_enumerator_next(const Instruction*);
         void emit_op_enumerator_get_by_val(const Instruction*);
         void emitSlow_op_enumerator_get_by_val(const Instruction*, Vector<SlowCaseEntry>::iterator&);

Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -2786,6 +2786,40 @@
 
 template void JIT::emit_op_put_by_val<OpPutByVal>(const Instruction*);
 
+void JIT::emit_op_get_property_enumerator(const Instruction* currentInstruction)
+{
+    auto bytecode = currentInstruction->as<OpGetPropertyEnumerator>();
+
+    VirtualRegister base = bytecode.m_base;
+    VirtualRegister dst = bytecode.m_dst;
+
+    JumpList doneCases;
+    JumpList genericCases;
+
+    emitGetVirtualRegister(base, regT0);
+    genericCases.append(branchIfNotCell(regT0));
+    load8(Address(regT0, JSCell::indexingTypeAndMiscOffset()), regT1);
+    and32(TrustedImm32(IndexingTypeMask), regT1);
+    genericCases.append(branch32(Above, regT1, TrustedImm32(ArrayWithUndecided)));
+
+    emitLoadStructure(vm(), regT0, regT1, regT2);
+    loadPtr(Address(regT1, Structure::previousOrRareDataOffset()), regT1);
+    genericCases.append(branchTestPtr(Zero, regT1));
+    genericCases.append(branchIfStructure(regT1));
+    loadPtr(Address(regT1, StructureRareData::offsetOfCachedPropertyNameEnumerator()), regT1);
+
+    genericCases.append(branchTestPtr(Zero, regT1));
+    genericCases.append(branchTest32(Zero, Address(regT1, JSPropertyNameEnumerator::flagsOffset()), TrustedImm32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)));
+    emitPutVirtualRegister(dst, regT1);
+    doneCases.append(jump());
+
+    genericCases.link(this);
+    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_get_property_enumerator);
+    slowPathCall.call();
+
+    doneCases.link(this);
+}
+
 void JIT::emit_op_enumerator_next(const Instruction* currentInstruction)
 {
     auto bytecode = currentInstruction->as<OpEnumeratorNext>();

Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -1385,6 +1385,12 @@
 
 template void JIT::emit_op_put_by_val<OpPutByVal>(const Instruction*);
 
+void JIT::emit_op_get_property_enumerator(const Instruction* currentInstruction)
+{
+    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_get_property_enumerator);
+    slowPathCall.call();
+}
+
 void JIT::emit_op_enumerator_next(const Instruction* currentInstruction)
 {
     JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_enumerator_next);

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (282238 => 282239)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm	2021-09-09 21:36:38 UTC (rev 282239)
@@ -525,6 +525,7 @@
 # Constant for reasoning about butterflies.
 const IsArray                  = constexpr IsArray
 const IndexingShapeMask        = constexpr IndexingShapeMask
+const IndexingTypeMask         = constexpr IndexingTypeMask
 const NoIndexingShape          = constexpr NoIndexingShape
 const Int32Shape               = constexpr Int32Shape
 const DoubleShape              = constexpr DoubleShape
@@ -532,8 +533,10 @@
 const ArrayStorageShape        = constexpr ArrayStorageShape
 const SlowPutArrayStorageShape = constexpr SlowPutArrayStorageShape
 const CopyOnWrite              = constexpr CopyOnWrite
+const ArrayWithUndecided       = constexpr ArrayWithUndecided
 
 # Type constants.
+const StructureType = constexpr StructureType
 const StringType = constexpr StringType
 const SymbolType = constexpr SymbolType
 const ObjectType = constexpr ObjectType
@@ -2051,7 +2054,6 @@
 slowPathOp(define_accessor_property)
 slowPathOp(define_data_property)
 slowPathOp(get_by_val_with_this)
-slowPathOp(get_property_enumerator)
 
 if not JSVALUE64
     slowPathOp(get_prototype_of)

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (282238 => 282239)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2021-09-09 21:36:38 UTC (rev 282239)
@@ -3079,6 +3079,7 @@
     dispatch()
 end)
 
+slowPathOp(get_property_enumerator)
 slowPathOp(enumerator_next)
 slowPathOp(enumerator_get_by_val)
 slowPathOp(enumerator_in_by_val)

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (282238 => 282239)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2021-09-09 21:36:38 UTC (rev 282239)
@@ -3093,6 +3093,30 @@
     dispatch()
 end)
 
+llintOpWithReturn(op_get_property_enumerator, OpGetPropertyEnumerator, macro (size, get, dispatch, return)
+    get(m_base, t1)
+    loadConstantOrVariableCell(size, t1, t0, .slowPath)
+
+    loadb JSCell::m_indexingTypeAndMisc[t0], t1
+    andi IndexingTypeMask, t1
+    bia t1, ArrayWithUndecided, .slowPath
+
+    loadStructureWithScratch(t0, t1, t2, t3)
+    loadp Structure::m_previousOrRareData[t1], t1
+    btpz t1, .slowPath
+    bbeq JSCell::m_type[t1], StructureType, .slowPath
+
+    loadp StructureRareData::m_cachedPropertyNameEnumerator[t1], t1
+    btpz t1, .slowPath
+    btiz JSPropertyNameEnumerator::m_flags[t1], (constexpr JSPropertyNameEnumerator::ValidatedViaWatchpoint), .slowPath
+
+    return(t1)
+
+.slowPath:
+    callSlowPath(_slow_path_get_property_enumerator)
+    dispatch()
+end)
+
 llintOp(op_enumerator_next, OpEnumeratorNext, macro (size, get, dispatch)
     # Note: this will always call the slow path on at least the first/last execution of EnumeratorNext for any given loop.
     # The upside this is that we don't have to record any metadata or mode information here as the slow path will do it for us when transitioning from InitMode/IndexedMode to OwnStructureMode, or from OwnStructureMode to GenericMode.

Modified: trunk/Source/_javascript_Core/runtime/BrandedStructure.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/BrandedStructure.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/BrandedStructure.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -53,6 +53,7 @@
     ASSERT(vm.structureStructure);
     BrandedStructure* newStructure = new (NotNull, allocateCell<BrandedStructure>(vm.heap)) BrandedStructure(vm, previous, brandUid, deferred);
     newStructure->finishCreation(vm, previous);
+    ASSERT(newStructure->type() == StructureType);
     return newStructure;
 }
 

Modified: trunk/Source/_javascript_Core/runtime/JSType.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/JSType.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/JSType.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -39,6 +39,7 @@
 {
     switch (type) {
     CASE(CellType)
+    CASE(StructureType)
     CASE(StringType)
     CASE(SymbolType)
     CASE(HeapBigIntType)

Modified: trunk/Source/_javascript_Core/runtime/JSType.h (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/JSType.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/JSType.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -25,6 +25,7 @@
 enum JSType : uint8_t {
     // The CellType value must come before any JSType that is a JSCell.
     CellType,
+    StructureType,
     StringType,
     HeapBigIntType,
     LastMaybeFalsyCellPrimitive = HeapBigIntType,

Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/Structure.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -236,7 +236,7 @@
 
 const ClassInfo Structure::s_info = { "Structure", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(Structure) };
 
-Structure::Structure(VM& vm)
+Structure::Structure(VM& vm, CreatingEarlyCellTag)
     : JSCell(CreatingEarlyCell)
     , m_inlineCapacity(0)
     , m_bitField(0)
@@ -263,7 +263,7 @@
     setTransitionOffset(vm, invalidOffset);
     setMaxOffset(vm, invalidOffset);
  
-    TypeInfo typeInfo = TypeInfo(CellType, StructureFlags);
+    TypeInfo typeInfo = TypeInfo(StructureType, StructureFlags);
     m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo);
     m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
 
@@ -349,6 +349,7 @@
             result->setMaxOffset(vm, newMaxOffset);
         });
 
+    ASSERT(result->type() == StructureType);
     return result;
 }
 

Modified: trunk/Source/_javascript_Core/runtime/Structure.h (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/Structure.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/Structure.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -742,7 +742,7 @@
     friend class LLIntOffsetsExtractor;
 
     JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
-    Structure(VM&);
+    Structure(VM&, CreatingEarlyCellTag);
 
     static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
     
@@ -836,9 +836,9 @@
     JS_EXPORT_PRIVATE void pin(const AbstractLocker&, VM&, PropertyTable*);
     void pinForCaching(const AbstractLocker&, VM&, PropertyTable*);
     
-    bool isRareData(JSCell* cell) const
+    static bool isRareData(JSCell* cell)
     {
-        return cell && cell->structureID() != structureID();
+        return cell && cell->type() != StructureType;
     }
 
     template<typename DetailsFunc>

Modified: trunk/Source/_javascript_Core/runtime/StructureInlines.h (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/StructureInlines.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/StructureInlines.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -62,6 +62,7 @@
 
     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingModeIncludingHistory, inlineCapacity);
     structure->finishCreation(vm);
+    ASSERT(structure->type() == StructureType);
     return structure;
 }
 
@@ -68,7 +69,7 @@
 inline Structure* Structure::createStructure(VM& vm)
 {
     ASSERT(!vm.structureStructure);
-    Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm);
+    Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, CreatingEarlyCell);
     structure->finishCreation(vm, CreatingEarlyCell);
     return structure;
 }

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.h (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.h	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.h	2021-09-09 21:36:38 UTC (rev 282239)
@@ -35,6 +35,7 @@
 namespace JSC {
 
 class JSPropertyNameEnumerator;
+class LLIntOffsetsExtractor;
 class Structure;
 class StructureChain;
 class CachedSpecialPropertyAdaptiveStructureWatchpoint;
@@ -119,6 +120,7 @@
     void invalidateWatchpointBasedValidation();
 
 private:
+    friend class LLIntOffsetsExtractor;
     friend class Structure;
     friend class CachedSpecialPropertyAdaptiveStructureWatchpoint;
     friend class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint;

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (282238 => 282239)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2021-09-09 20:58:37 UTC (rev 282238)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2021-09-09 21:36:38 UTC (rev 282239)
@@ -486,6 +486,7 @@
     if (Options::useJIT()) {
         sentinelMapBucket();
         sentinelSetBucket();
+        emptyPropertyNameEnumerator();
     }
     {
         auto* bigInt = JSBigInt::tryCreateFrom(*this, 1);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to