Title: [242397] trunk
Revision
242397
Author
ysuz...@apple.com
Date
2019-03-04 15:27:43 -0800 (Mon, 04 Mar 2019)

Log Message

[JSC] Store bits for JSRopeString in 3 stores
https://bugs.webkit.org/show_bug.cgi?id=195234

Reviewed by Saam Barati.

JSTests:

* stress/null-rope-and-collectors.js: Added.

Source/_javascript_Core:

This patch cleans up the initialization of JSRopeString fields in DFG and FTL.
Previously, we store some part of data separately. Instead, this patch calculates
the data first by bit operations and store calculated data with fewer stores.

This patch also cleans up is8Bit and isSubstring flags. We put them in lower bits
of the first fiber instead of the upper 16 bits. Since we only have 3 bit flags, (isRope, is8Bit, isSubstring),
we can put them into the lower 3 bits, they are always empty due to alignment.

* bytecode/AccessCase.cpp:
(JSC::AccessCase::generateImpl): A bit clean up of StringLength IC to give a chance of unnecessary mov removal.
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::canBeRope):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileMakeRope):
* dfg/DFGSpeculativeJIT.h:
* ftl/FTLAbstractHeapRepository.cpp:
(JSC::FTL::AbstractHeapRepository::AbstractHeapRepository):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileMakeRope):
(JSC::FTL::DFG::LowerDFGToB3::isRopeString):
(JSC::FTL::DFG::LowerDFGToB3::isNotRopeString):
* runtime/JSString.cpp:
(JSC::JSString::visitChildren):
* runtime/JSString.h:
(JSC::JSString::is8Bit const):
(JSC::JSString::isSubstring const):
* tools/JSDollarVM.cpp:
(JSC::functionCreateNullRopeString):
(JSC::JSDollarVM::finishCreation):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (242396 => 242397)


--- trunk/JSTests/ChangeLog	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/JSTests/ChangeLog	2019-03-04 23:27:43 UTC (rev 242397)
@@ -1,3 +1,12 @@
+2019-03-04  Yusuke Suzuki  <ysuz...@apple.com>
+
+        [JSC] Store bits for JSRopeString in 3 stores
+        https://bugs.webkit.org/show_bug.cgi?id=195234
+
+        Reviewed by Saam Barati.
+
+        * stress/null-rope-and-collectors.js: Added.
+
 2019-03-01  Dominik Infuehr  <dinfu...@igalia.com>
 
         Unskip test read-dead-bytecode-locals-in-must-have-handle-values2.js on ARM/MIPS

Added: trunk/JSTests/stress/null-rope-and-collectors.js (0 => 242397)


--- trunk/JSTests/stress/null-rope-and-collectors.js	                        (rev 0)
+++ trunk/JSTests/stress/null-rope-and-collectors.js	2019-03-04 23:27:43 UTC (rev 242397)
@@ -0,0 +1,4 @@
+var array = [];
+for (var i = 0; i < 1e6; ++i) {
+    array.push($vm.createNullRopeString());
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (242396 => 242397)


--- trunk/Source/_javascript_Core/ChangeLog	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-03-04 23:27:43 UTC (rev 242397)
@@ -1,3 +1,41 @@
+2019-03-04  Yusuke Suzuki  <ysuz...@apple.com>
+
+        [JSC] Store bits for JSRopeString in 3 stores
+        https://bugs.webkit.org/show_bug.cgi?id=195234
+
+        Reviewed by Saam Barati.
+
+        This patch cleans up the initialization of JSRopeString fields in DFG and FTL.
+        Previously, we store some part of data separately. Instead, this patch calculates
+        the data first by bit operations and store calculated data with fewer stores.
+
+        This patch also cleans up is8Bit and isSubstring flags. We put them in lower bits
+        of the first fiber instead of the upper 16 bits. Since we only have 3 bit flags, (isRope, is8Bit, isSubstring),
+        we can put them into the lower 3 bits, they are always empty due to alignment.
+
+        * bytecode/AccessCase.cpp:
+        (JSC::AccessCase::generateImpl): A bit clean up of StringLength IC to give a chance of unnecessary mov removal.
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::canBeRope):
+        (JSC::DFG::SpeculativeJIT::compileGetArrayLength):
+        (JSC::DFG::SpeculativeJIT::compileMakeRope):
+        * dfg/DFGSpeculativeJIT.h:
+        * ftl/FTLAbstractHeapRepository.cpp:
+        (JSC::FTL::AbstractHeapRepository::AbstractHeapRepository):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileMakeRope):
+        (JSC::FTL::DFG::LowerDFGToB3::isRopeString):
+        (JSC::FTL::DFG::LowerDFGToB3::isNotRopeString):
+        * runtime/JSString.cpp:
+        (JSC::JSString::visitChildren):
+        * runtime/JSString.h:
+        (JSC::JSString::is8Bit const):
+        (JSC::JSString::isSubstring const):
+        * tools/JSDollarVM.cpp:
+        (JSC::functionCreateNullRopeString):
+        (JSC::JSDollarVM::finishCreation):
+
 2019-03-04  Joseph Pecoraro  <pecor...@apple.com>
 
         ITMLKit Inspector: Data Bindings / Associated Data for nodes

Modified: trunk/Source/_javascript_Core/bytecode/AccessCase.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/bytecode/AccessCase.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/bytecode/AccessCase.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -1215,14 +1215,14 @@
     case StringLength: {
         jit.loadPtr(CCallHelpers::Address(baseGPR, JSString::offsetOfValue()), scratchGPR);
         auto isRope = jit.branchIfRopeStringImpl(scratchGPR);
-        jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), scratchGPR);
+        jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), valueRegs.payloadGPR());
         auto done = jit.jump();
 
         isRope.link(&jit);
-        jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), scratchGPR);
+        jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), valueRegs.payloadGPR());
 
         done.link(&jit);
-        jit.boxInt32(scratchGPR, valueRegs);
+        jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
         state.succeed();
         return;
     }

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -6897,6 +6897,17 @@
     cellResult(resultGPR, node);
 }
 
+bool SpeculativeJIT::canBeRope(Edge& edge)
+{
+    if (m_state.forNode(edge).isType(SpecStringIdent))
+        return false;
+    // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
+    String string = edge->tryGetString(m_graph);
+    if (!string.isNull())
+        return false;
+    return true;
+}
+
 void SpeculativeJIT::compileGetArrayLength(Node* node)
 {
     switch (node->arrayMode().type()) {
@@ -6934,15 +6945,21 @@
         GPRReg resultGPR = result.gpr();
         GPRReg tempGPR = temp.gpr();
 
+        bool needsRopeCase = canBeRope(node->child1());
+
         m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSString::offsetOfValue()), tempGPR);
-        auto isRope = m_jit.branchIfRopeStringImpl(tempGPR);
+        CCallHelpers::Jump isRope;
+        if (needsRopeCase)
+            isRope = m_jit.branchIfRopeStringImpl(tempGPR);
         m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), resultGPR);
-        auto done = m_jit.jump();
+        if (needsRopeCase) {
+            auto done = m_jit.jump();
 
-        isRope.link(&m_jit);
-        m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
+            isRope.link(&m_jit);
+            m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
 
-        done.link(&m_jit);
+            done.link(&m_jit);
+        }
         int32Result(resultGPR, node);
         break;
     }
@@ -13425,43 +13442,28 @@
     Allocator allocatorValue = allocatorForNonVirtualConcurrently<JSRopeString>(*m_jit.vm(), sizeof(JSRopeString), AllocatorForMode::AllocatorIfExists);
     emitAllocateJSCell(resultGPR, JITAllocator::constant(allocatorValue), allocatorGPR, TrustedImmPtr(m_jit.graph().registerStructure(m_jit.vm()->stringStructure.get())), scratchGPR, slowPath);
 
-    m_jit.orPtr(TrustedImm32(JSString::isRopeInPointer), opGPRs[0], allocatorGPR);
-    m_jit.storePtr(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
+    // This puts nullptr for the first fiber. It makes visitChildren safe even if this JSRopeString is discarded due to the speculation failure in the following path.
+    m_jit.storePtr(TrustedImmPtr(JSString::isRopeInPointer), CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
 
-    m_jit.move(opGPRs[1], scratchGPR);
-    m_jit.store32(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1Lower()));
-    m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
-    m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1Upper()));
-
-    if (numOpGPRs == 3) {
-        m_jit.move(opGPRs[2], scratchGPR);
-        m_jit.store32(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Lower()));
-        m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
-        m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Upper()));
-    } else {
-        m_jit.storeZero32(CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Lower()));
-        m_jit.storeZero16(CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2Upper()));
-    }
-
     {
         if (JSString* string = edges[0]->dynamicCastConstant<JSString*>(*m_jit.vm())) {
             m_jit.move(TrustedImm32(string->is8Bit() ? StringImpl::flagIs8Bit() : 0), scratchGPR);
             m_jit.move(TrustedImm32(string->length()), allocatorGPR);
         } else {
-            bool canBeRope = !m_state.forNode(edges[0]).isType(SpecStringIdent);
+            bool needsRopeCase = canBeRope(edges[0]);
             m_jit.loadPtr(CCallHelpers::Address(opGPRs[0], JSString::offsetOfValue()), scratch2GPR);
             CCallHelpers::Jump isRope;
-            if (canBeRope)
+            if (needsRopeCase)
                 isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
 
             m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
             m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR);
 
-            if (canBeRope) {
+            if (needsRopeCase) {
                 auto done = m_jit.jump();
 
                 isRope.link(&m_jit);
-                m_jit.load16(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
+                m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
                 m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfLength()), allocatorGPR);
                 done.link(&m_jit);
             }
@@ -13484,23 +13486,23 @@
                     CCallHelpers::Overflow,
                     TrustedImm32(string->length()), allocatorGPR));
         } else {
-            bool canBeRope = !m_state.forNode(edges[i]).isType(SpecStringIdent);
+            bool needsRopeCase = canBeRope(edges[i]);
             m_jit.loadPtr(CCallHelpers::Address(opGPRs[i], JSString::offsetOfValue()), scratch2GPR);
             CCallHelpers::Jump isRope;
-            if (canBeRope)
+            if (needsRopeCase)
                 isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
 
-            m_jit.and16(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
+            m_jit.and32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
             speculationCheck(
                 Uncountable, JSValueSource(), nullptr,
                 m_jit.branchAdd32(
                     CCallHelpers::Overflow,
                     CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR));
-            if (canBeRope) {
+            if (needsRopeCase) {
                 auto done = m_jit.jump();
 
                 isRope.link(&m_jit);
-                m_jit.and16(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
+                m_jit.and32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
                 m_jit.load32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfLength()), scratch2GPR);
                 speculationCheck(
                     Uncountable, JSValueSource(), nullptr,
@@ -13510,7 +13512,7 @@
             }
         }
     }
-    m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFlags()));
+
     if (!ASSERT_DISABLED) {
         CCallHelpers::Jump ok = m_jit.branch32(
             CCallHelpers::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0));
@@ -13517,7 +13519,31 @@
         m_jit.abortWithReason(DFGNegativeStringLength);
         ok.link(&m_jit);
     }
-    m_jit.store32(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfLength()));
+
+    static_assert(StringImpl::flagIs8Bit() == JSRopeString::is8BitInPointer, "");
+    m_jit.and32(TrustedImm32(StringImpl::flagIs8Bit()), scratchGPR);
+    m_jit.orPtr(opGPRs[0], scratchGPR);
+    m_jit.orPtr(TrustedImmPtr(JSString::isRopeInPointer), scratchGPR);
+    m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
+
+    m_jit.move(opGPRs[1], scratchGPR);
+    m_jit.lshiftPtr(TrustedImm32(32), scratchGPR);
+    m_jit.orPtr(allocatorGPR, scratchGPR);
+    m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber1()));
+
+    if (numOpGPRs == 2) {
+        m_jit.move(opGPRs[1], scratchGPR);
+        m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
+        m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2()));
+    } else {
+        m_jit.move(opGPRs[1], scratchGPR);
+        m_jit.rshiftPtr(TrustedImm32(32), scratchGPR);
+        m_jit.move(opGPRs[2], scratch2GPR);
+        m_jit.lshiftPtr(TrustedImm32(16), scratch2GPR);
+        m_jit.orPtr(scratch2GPR, scratchGPR);
+        m_jit.storePtr(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber2()));
+    }
+
     auto isNonEmptyString = m_jit.branchTest32(CCallHelpers::NonZero, allocatorGPR);
 
     m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(&m_jit.graph().m_vm)), resultGPR);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (242396 => 242397)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2019-03-04 23:27:43 UTC (rev 242397)
@@ -577,6 +577,8 @@
     bool isKnownNotNumber(Node* node) { return !(m_state.forNode(node).m_type & SpecFullNumber); }
     bool isKnownNotCell(Node* node) { return !(m_state.forNode(node).m_type & SpecCell); }
     bool isKnownNotOther(Node* node) { return !(m_state.forNode(node).m_type & SpecOther); }
+
+    bool canBeRope(Edge&);
     
     UniquedStringImpl* identifierUID(unsigned index)
     {

Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -86,6 +86,7 @@
     JSCell_typeInfoFlags.changeParent(&JSCell_usefulBytes);
     JSCell_cellState.changeParent(&JSCell_usefulBytes);
     JSRopeString_flags.changeParent(&JSRopeString_fiber0);
+    JSRopeString_length.changeParent(&JSRopeString_fiber1);
 
     RELEASE_ASSERT(!JSCell_freeListNext.offset());
 }

Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (242396 => 242397)


--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2019-03-04 23:27:43 UTC (rev 242397)
@@ -89,12 +89,10 @@
     macro(JSPropertyNameEnumerator_endStructurePropertyIndex, JSPropertyNameEnumerator::endStructurePropertyIndexOffset()) \
     macro(JSPropertyNameEnumerator_indexLength, JSPropertyNameEnumerator::indexedLengthOffset()) \
     macro(JSRopeString_flags, JSRopeString::offsetOfFlags()) \
+    macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
     macro(JSRopeString_fiber0, JSRopeString::offsetOfFiber0()) \
-    macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
-    macro(JSRopeString_fiber1Lower, JSRopeString::offsetOfFiber1Lower()) \
-    macro(JSRopeString_fiber1Upper, JSRopeString::offsetOfFiber1Upper()) \
-    macro(JSRopeString_fiber2Lower, JSRopeString::offsetOfFiber2Lower()) \
-    macro(JSRopeString_fiber2Upper, JSRopeString::offsetOfFiber2Upper()) \
+    macro(JSRopeString_fiber1, JSRopeString::offsetOfFiber1()) \
+    macro(JSRopeString_fiber2, JSRopeString::offsetOfFiber2()) \
     macro(JSScope_next, JSScope::offsetOfNext()) \
     macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
     macro(JSWrapperObject_internalValue, JSWrapperObject::internalValueOffset()) \

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -6524,6 +6524,7 @@
             numKids = 2;
         }
         
+        LBasicBlock emptyCase = m_out.newBlock();
         LBasicBlock slowPath = m_out.newBlock();
         LBasicBlock continuation = m_out.newBlock();
         
@@ -6532,19 +6533,9 @@
         LValue result = allocateCell(
             m_out.constIntPtr(allocator.localAllocator()), vm().stringStructure.get(), slowPath);
         
-        m_out.storePtr(m_out.bitOr(kids[0], m_out.constIntPtr(JSString::isRopeInPointer)), result, m_heaps.JSRopeString_fiber0);
+        // This puts nullptr for the first fiber. It makes visitChildren safe even if this JSRopeString is discarded due to the speculation failure in the following path.
+        m_out.storePtr(m_out.constIntPtr(JSString::isRopeInPointer), result, m_heaps.JSRopeString_fiber0);
 
-        m_out.store32(m_out.castToInt32(kids[1]), result, m_heaps.JSRopeString_fiber1Lower);
-        m_out.store32As16(m_out.castToInt32(m_out.lShr(kids[1], m_out.constInt32(32))), result, m_heaps.JSRopeString_fiber1Upper);
-
-        if (numKids == 3) {
-            m_out.store32(m_out.castToInt32(kids[2]), result, m_heaps.JSRopeString_fiber2Lower);
-            m_out.store32As16(m_out.castToInt32(m_out.lShr(kids[2], m_out.constInt32(32))), result, m_heaps.JSRopeString_fiber2Upper);
-        } else {
-            m_out.store32(m_out.int32Zero, result, m_heaps.JSRopeString_fiber2Lower);
-            m_out.store32As16(m_out.int32Zero, result, m_heaps.JSRopeString_fiber2Upper);
-        }
-
         auto getFlagsAndLength = [&] (Edge& edge, LValue child) {
             if (JSString* string = edge->dynamicCastConstant<JSString*>(vm())) {
                 return FlagsAndLength {
@@ -6560,7 +6551,7 @@
             m_out.branch(isRopeString(child, edge), unsure(ropeCase), unsure(notRopeCase));
 
             LBasicBlock lastNext = m_out.appendTo(ropeCase, notRopeCase);
-            ValueFromBlock flagsForRope = m_out.anchor(m_out.load16ZeroExt32(child, m_heaps.JSRopeString_flags));
+            ValueFromBlock flagsForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_flags));
             ValueFromBlock lengthForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_length));
             m_out.jump(continuation);
 
@@ -6591,14 +6582,29 @@
             };
             flagsAndLength = mergeFlagsAndLength(edges[i], kids[i], flagsAndLength);
         }
-        m_out.store32As16(flagsAndLength.flags, result, m_heaps.JSRopeString_flags);
-        m_out.store32(flagsAndLength.length, result, m_heaps.JSRopeString_length);
+
+        m_out.storePtr(
+            m_out.bitOr(
+                m_out.bitOr(kids[0], m_out.constIntPtr(JSString::isRopeInPointer)),
+                m_out.bitAnd(m_out.constIntPtr(JSRopeString::is8BitInPointer), m_out.zeroExtPtr(flagsAndLength.flags))),
+            result, m_heaps.JSRopeString_fiber0);
+        m_out.storePtr(
+            m_out.bitOr(m_out.zeroExtPtr(flagsAndLength.length), m_out.shl(kids[1], m_out.constInt32(32))),
+            result, m_heaps.JSRopeString_fiber1);
+        if (numKids == 2)
+            m_out.storePtr(m_out.lShr(kids[1], m_out.constInt32(32)), result, m_heaps.JSRopeString_fiber2);
+        else
+            m_out.storePtr(m_out.bitOr(m_out.lShr(kids[1], m_out.constInt32(32)), m_out.shl(kids[2], m_out.constInt32(16))), result, m_heaps.JSRopeString_fiber2);
         
         mutatorFence();
-        ValueFromBlock fastResult = m_out.anchor(m_out.select(m_out.isZero32(flagsAndLength.length), weakPointer(jsEmptyString(&m_graph.m_vm)), result));
+        ValueFromBlock fastResult = m_out.anchor(result);
+        m_out.branch(m_out.isZero32(flagsAndLength.length), rarely(emptyCase), usually(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(emptyCase, slowPath);
+        ValueFromBlock emptyResult = m_out.anchor(weakPointer(jsEmptyString(&m_graph.m_vm)));
         m_out.jump(continuation);
         
-        LBasicBlock lastNext = m_out.appendTo(slowPath, continuation);
+        m_out.appendTo(slowPath, continuation);
         LValue slowResultValue;
         VM& vm = this->vm();
         switch (numKids) {
@@ -6626,7 +6632,7 @@
         m_out.jump(continuation);
         
         m_out.appendTo(continuation, lastNext);
-        setJSValue(m_out.phi(Int64, fastResult, slowResult));
+        setJSValue(m_out.phi(Int64, fastResult, emptyResult, slowResult));
     }
     
     void compileStringCharAt()
@@ -15680,6 +15686,11 @@
                 if (value.isCell() && value.asCell()->type() == StringType && !asString(value)->isRope())
                     return m_out.booleanFalse;
             }
+            String value = edge->tryGetString(m_graph);
+            if (!value.isNull()) {
+                // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
+                return m_out.booleanFalse;
+            }
         }
 
         return m_out.testNonZeroPtr(m_out.loadPtr(string, m_heaps.JSString_value), m_out.constIntPtr(JSString::isRopeInPointer));
@@ -15694,6 +15705,11 @@
                 if (value.isCell() && value.asCell()->type() == StringType && !asString(value)->isRope())
                     return m_out.booleanTrue;
             }
+            String value = edge->tryGetString(m_graph);
+            if (!value.isNull()) {
+                // If this value is LazyValue, it will be converted to JSString, and the result must be non-rope string.
+                return m_out.booleanTrue;
+            }
         }
 
         return m_out.testIsZeroPtr(m_out.loadPtr(string, m_heaps.JSString_value), m_out.constIntPtr(JSString::isRopeInPointer));

Modified: trunk/Source/_javascript_Core/runtime/JSString.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/runtime/JSString.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/runtime/JSString.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -114,7 +114,7 @@
     
     uintptr_t pointer = thisObject->m_fiber;
     if (pointer & isRopeInPointer) {
-        if ((pointer & JSRopeString::stringMask) == JSRopeString::substringSentinel()) {
+        if (pointer & JSRopeString::isSubstringInPointer) {
             visitor.appendUnbarriered(static_cast<JSRopeString*>(thisObject)->fiber1());
             return;
         }

Modified: trunk/Source/_javascript_Core/runtime/JSString.h (242396 => 242397)


--- trunk/Source/_javascript_Core/runtime/JSString.h	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/runtime/JSString.h	2019-03-04 23:27:43 UTC (rev 242397)
@@ -86,9 +86,9 @@
 //
 //              0                        8        10               16                       32                                     48
 // JSString     [   ID      ][  header  ][   String pointer      0]
-// JSRopeString [   ID      ][  header  ][ flags ][ 1st fiber    1][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
+// JSRopeString [   ID      ][  header  ][   1st fiber         xyz][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
 //                                                               ^
-//                                                            isRope bit
+//                                            x:(is8Bit),y:(isSubstring),z:(isRope) bit flags
 class JSString : public JSCell {
 public:
     friend class JIT;
@@ -266,13 +266,15 @@
 class JSRopeString final : public JSString {
     friend class JSString;
 public:
+    // We use lower 3bits of fiber0 for flags. These bits are usable due to alignment, and it is OK even in 32bit architecture.
+    static constexpr uintptr_t is8BitInPointer = static_cast<uintptr_t>(StringImpl::flagIs8Bit());
+    static constexpr uintptr_t isSubstringInPointer = 0x2;
+    static_assert(is8BitInPointer == 0b100, "");
+    static_assert(isSubstringInPointer == 0b010, "");
+    static_assert(isRopeInPointer == 0b001, "");
+    static constexpr uintptr_t stringMask = ~(isRopeInPointer | is8BitInPointer | isSubstringInPointer);
 #if CPU(ADDRESS64)
     static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
-    static constexpr uintptr_t flagMask = 0xffff000000000000ULL;
-    static constexpr uintptr_t stringMask = ~(flagMask | isRopeInPointer);
-    static_assert(StringImpl::flagIs8Bit() == 0b100, "");
-    static constexpr uintptr_t is8BitInPointer = static_cast<uintptr_t>(StringImpl::flagIs8Bit()) << 48;
-
     class CompactFibers {
     public:
         JSString* fiber1() const
@@ -289,13 +291,13 @@
 
         JSString* fiber2() const
         {
-            return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 32));
+            return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 16));
         }
         void initializeFiber2(JSString* fiber)
         {
             uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
-            m_fiber2Lower = static_cast<uint32_t>(pointer);
-            m_fiber2Upper = static_cast<uint16_t>(pointer >> 32);
+            m_fiber2Lower = static_cast<uint16_t>(pointer);
+            m_fiber2Upper = static_cast<uint32_t>(pointer >> 16);
         }
 
         unsigned length() const { return m_length; }
@@ -305,22 +307,18 @@
         }
 
         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
-        static ptrdiff_t offsetOfFiber1Lower() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Lower); }
-        static ptrdiff_t offsetOfFiber1Upper() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
-        static ptrdiff_t offsetOfFiber2Lower() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2Lower); }
-        static ptrdiff_t offsetOfFiber2Upper() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2Upper); }
+        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
+        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
 
     private:
         uint32_t m_length { 0 };
         uint32_t m_fiber1Lower { 0 };
         uint16_t m_fiber1Upper { 0 };
-        uint16_t m_fiber2Upper { 0 };
-        uint32_t m_fiber2Lower { 0 };
+        uint16_t m_fiber2Lower { 0 };
+        uint32_t m_fiber2Upper { 0 };
     };
     static_assert(sizeof(CompactFibers) == sizeof(void*) * 2, "");
 #else
-    static constexpr uintptr_t stringMask = ~(isRopeInPointer);
-
     class CompactFibers {
     public:
         JSString* fiber1() const
@@ -347,24 +345,12 @@
             m_length = length;
         }
 
-        void initializeIs8Bit(bool flag)
-        {
-            if (flag)
-                m_flags |= static_cast<uintptr_t>(StringImpl::flagIs8Bit());
-            else
-                m_flags &= ~static_cast<uintptr_t>(StringImpl::flagIs8Bit());
-        }
-
-        bool is8Bit()
-        {
-            return m_flags & static_cast<uintptr_t>(StringImpl::flagIs8Bit());
-        }
-
         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
+        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1); }
+        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2); }
 
     private:
         uint32_t m_length { 0 };
-        uint32_t m_flags { 0 };
         JSString* m_fiber1 { nullptr };
         JSString* m_fiber2 { nullptr };
     };
@@ -456,16 +442,20 @@
 
     void initializeIs8Bit(bool flag) const
     {
-#if CPU(ADDRESS64)
         if (flag)
             m_fiber |= is8BitInPointer;
         else
             m_fiber &= ~is8BitInPointer;
-#else
-        m_compactFibers.initializeIs8Bit(flag);
-#endif
     }
 
+    void initializeIsSubstring(bool flag) const
+    {
+        if (flag)
+            m_fiber |= isSubstringInPointer;
+        else
+            m_fiber &= ~isSubstringInPointer;
+    }
+
     ALWAYS_INLINE void initializeLength(unsigned length)
     {
         ASSERT(length <= MaxLength);
@@ -472,6 +462,17 @@
         m_compactFibers.initializeLength(length);
     }
 
+    JSRopeString(VM& vm)
+        : JSString(vm)
+    {
+        initializeIsSubstring(false);
+        initializeLength(0);
+        initializeIs8Bit(true);
+        initializeFiber0(nullptr);
+        initializeFiber1(nullptr);
+        initializeFiber2(nullptr);
+    }
+
     JSRopeString(VM& vm, JSString* s1, JSString* s2)
         : JSString(vm)
     {
@@ -549,26 +550,29 @@
 
 public:
     static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfLength(); } // 32byte width.
+    static ptrdiff_t offsetOfFlags() { return offsetOfValue(); }
     static ptrdiff_t offsetOfFiber0() { return offsetOfValue(); }
-#if CPU(ADDRESS64)
-    static ptrdiff_t offsetOfFlags() { return offsetOfValue() + sizeof(uint16_t) * 3; } // 16byte width.
-    static ptrdiff_t offsetOfFiber1Lower() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1Lower(); } // 32byte width.
-    static ptrdiff_t offsetOfFiber1Upper() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1Upper(); } // 16byte width.
-    static ptrdiff_t offsetOfFiber2Lower() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2Lower(); } // 32byte width.
-    static ptrdiff_t offsetOfFiber2Upper() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2Upper(); } // 16byte width.
-#elif USE(JSVALUE64)
-    // FIXME: This is an temporary workaround to make JSC built on ARM64_32. Once we start calculating bits before storing them to JSRopeString,
-    // we do not need to have such a detailed information as an offset. After that, what we only need is offsetOfFiber0, offsetOfFiber1, and offsetOfFiber2.
-    // https://bugs.webkit.org/show_bug.cgi?id=195234
-    static ptrdiff_t offsetOfFlags() { ASSERT_NOT_REACHED(); return 0; }
-    static ptrdiff_t offsetOfFiber1Lower() { ASSERT_NOT_REACHED(); return 0; }
-    static ptrdiff_t offsetOfFiber1Upper() { ASSERT_NOT_REACHED(); return 0; }
-    static ptrdiff_t offsetOfFiber2Lower() { ASSERT_NOT_REACHED(); return 0; }
-    static ptrdiff_t offsetOfFiber2Upper() { ASSERT_NOT_REACHED(); return 0; }
-#endif
+    static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1(); }
+    static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2(); }
 
     static constexpr unsigned s_maxInternalRopeLength = 3;
 
+    // This JSRopeString is only used to simulate half-baked JSRopeString in DFG and FTL MakeRope. If OSR exit happens in
+    // the middle of MakeRope due to string length overflow, we have half-baked JSRopeString which is the same to the result
+    // of this function. This half-baked JSRopeString will not be exposed to users, but still collectors can see it due to
+    // the conservative stack scan. This JSRopeString is used to test the collector with such a half-baked JSRopeString.
+    // Because this JSRopeString breaks the JSString's invariant (only one singleton JSString can be zero length), almost all the
+    // operations in JS fail to handle this string correctly.
+    static JSRopeString* createNullForTesting(VM& vm)
+    {
+        JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
+        newString->finishCreation(vm);
+        ASSERT(!newString->length());
+        ASSERT(newString->isRope());
+        ASSERT(fiber0() == nullptr);
+        return newString;
+    }
+
 private:
     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2)
     {
@@ -575,6 +579,7 @@
         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2);
         newString->finishCreation(vm);
         ASSERT(newString->length());
+        ASSERT(newString->isRope());
         return newString;
     }
     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
@@ -582,6 +587,7 @@
         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2, s3);
         newString->finishCreation(vm);
         ASSERT(newString->length());
+        ASSERT(newString->isRope());
         return newString;
     }
 
@@ -590,6 +596,7 @@
         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, base, offset, length);
         newString->finishCreationSubstring(vm, exec);
         ASSERT(newString->length());
+        ASSERT(newString->isRope());
         return newString;
     }
 
@@ -598,6 +605,7 @@
         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap, deferralContext)) JSRopeString(SubstringOfResolved, vm, base, offset, length);
         newString->finishCreationSubstringOfResolved(vm);
         ASSERT(newString->length());
+        ASSERT(newString->isRope());
         return newString;
     }
 
@@ -685,26 +693,6 @@
         return static_cast<unsigned>(bitwise_cast<uintptr_t>(fiber2()));
     }
 
-    static constexpr uintptr_t notSubstringSentinel()
-    {
-        return 0;
-    }
-
-    static constexpr uintptr_t substringSentinel()
-    {
-        return 2;
-    }
-
-    bool isSubstring() const
-    {
-        return (m_fiber & stringMask) == substringSentinel();
-    }
-
-    void initializeIsSubstring(bool isSubstring)
-    {
-        m_fiber |= (isSubstring ? substringSentinel() : notSubstringSentinel());
-    }
-
     static_assert(s_maxInternalRopeLength >= 2, "");
     mutable CompactFibers m_compactFibers;
 
@@ -726,14 +714,9 @@
 {
     uintptr_t pointer = m_fiber;
     if (pointer & isRopeInPointer) {
-#if CPU(ADDRESS64)
         // Do not load m_fiber twice. We should use the information in pointer.
         // Otherwise, JSRopeString may be converted to JSString between the first and second accesses.
         return pointer & JSRopeString::is8BitInPointer;
-#else
-        // It is OK to load flag since even if JSRopeString is converted to JSString, this flag still exists.
-        return jsCast<const JSRopeString*>(this)->m_compactFibers.is8Bit();
-#endif
     }
     return bitwise_cast<StringImpl*>(pointer)->is8Bit();
 }
@@ -1046,7 +1029,7 @@
 
 inline bool JSString::isSubstring() const
 {
-    return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
+    return m_fiber & JSRopeString::isSubstringInPointer;
 }
 
 // --- JSValue inlines ----------------------------

Modified: trunk/Source/_javascript_Core/tools/JSDollarVM.cpp (242396 => 242397)


--- trunk/Source/_javascript_Core/tools/JSDollarVM.cpp	2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/_javascript_Core/tools/JSDollarVM.cpp	2019-03-04 23:27:43 UTC (rev 242397)
@@ -1721,6 +1721,13 @@
     return JSValue::encode(array);
 }
 
+static EncodedJSValue JSC_HOST_CALL functionCreateNullRopeString(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    JSLockHolder lock(vm);
+    return JSValue::encode(JSRopeString::createNullForTesting(vm));
+}
+
 static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -2220,6 +2227,7 @@
     addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
     addFunction(vm, "createProxy", functionCreateProxy, 1);
     addFunction(vm, "createRuntimeArray", functionCreateRuntimeArray, 0);
+    addFunction(vm, "createNullRopeString", functionCreateNullRopeString, 0);
 
     addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
     addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to