<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[242397] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/242397">242397</a></dd>
<dt>Author</dt> <dd>ysuzuki@apple.com</dd>
<dt>Date</dt> <dd>2019-03-04 15:27:43 -0800 (Mon, 04 Mar 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>[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/JavaScriptCore:

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):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeAccessCasecpp">trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITh">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositorycpp">trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositoryh">trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSStringcpp">trunk/Source/JavaScriptCore/runtime/JSString.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSStringh">trunk/Source/JavaScriptCore/runtime/JSString.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsJSDollarVMcpp">trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressnullropeandcollectorsjs">trunk/JSTests/stress/null-rope-and-collectors.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog  2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/JSTests/ChangeLog     2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2019-03-04  Yusuke Suzuki  <ysuzuki@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.
+
</ins><span class="cx"> 2019-03-01  Dominik Infuehr  <dinfuehr@igalia.com>
</span><span class="cx"> 
</span><span class="cx">         Unskip test read-dead-bytecode-locals-in-must-have-handle-values2.js on ARM/MIPS
</span></span></pre></div>
<a id="trunkJSTestsstressnullropeandcollectorsjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/null-rope-and-collectors.js (0 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,4 @@
</span><ins>+var array = [];
+for (var i = 0; i < 1e6; ++i) {
+    array.push($vm.createNullRopeString());
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog    2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/ChangeLog       2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -1,3 +1,41 @@
</span><ins>+2019-03-04  Yusuke Suzuki  <ysuzuki@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):
+
</ins><span class="cx"> 2019-03-04  Joseph Pecoraro  <pecoraro@apple.com>
</span><span class="cx"> 
</span><span class="cx">         ITMLKit Inspector: Data Bindings / Associated Data for nodes
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeAccessCasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp      2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp 2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -1215,14 +1215,14 @@
</span><span class="cx">     case StringLength: {
</span><span class="cx">         jit.loadPtr(CCallHelpers::Address(baseGPR, JSString::offsetOfValue()), scratchGPR);
</span><span class="cx">         auto isRope = jit.branchIfRopeStringImpl(scratchGPR);
</span><del>-        jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), scratchGPR);
</del><ins>+        jit.load32(CCallHelpers::Address(scratchGPR, StringImpl::lengthMemoryOffset()), valueRegs.payloadGPR());
</ins><span class="cx">         auto done = jit.jump();
</span><span class="cx"> 
</span><span class="cx">         isRope.link(&jit);
</span><del>-        jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), scratchGPR);
</del><ins>+        jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), valueRegs.payloadGPR());
</ins><span class="cx"> 
</span><span class="cx">         done.link(&jit);
</span><del>-        jit.boxInt32(scratchGPR, valueRegs);
</del><ins>+        jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
</ins><span class="cx">         state.succeed();
</span><span class="cx">         return;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp    2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp       2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -6897,6 +6897,17 @@
</span><span class="cx">     cellResult(resultGPR, node);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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;
+}
+
</ins><span class="cx"> void SpeculativeJIT::compileGetArrayLength(Node* node)
</span><span class="cx"> {
</span><span class="cx">     switch (node->arrayMode().type()) {
</span><span class="lines">@@ -6934,15 +6945,21 @@
</span><span class="cx">         GPRReg resultGPR = result.gpr();
</span><span class="cx">         GPRReg tempGPR = temp.gpr();
</span><span class="cx"> 
</span><ins>+        bool needsRopeCase = canBeRope(node->child1());
+
</ins><span class="cx">         m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSString::offsetOfValue()), tempGPR);
</span><del>-        auto isRope = m_jit.branchIfRopeStringImpl(tempGPR);
</del><ins>+        CCallHelpers::Jump isRope;
+        if (needsRopeCase)
+            isRope = m_jit.branchIfRopeStringImpl(tempGPR);
</ins><span class="cx">         m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), resultGPR);
</span><del>-        auto done = m_jit.jump();
</del><ins>+        if (needsRopeCase) {
+            auto done = m_jit.jump();
</ins><span class="cx"> 
</span><del>-        isRope.link(&m_jit);
-        m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
</del><ins>+            isRope.link(&m_jit);
+            m_jit.load32(CCallHelpers::Address(baseGPR, JSRopeString::offsetOfLength()), resultGPR);
</ins><span class="cx"> 
</span><del>-        done.link(&m_jit);
</del><ins>+            done.link(&m_jit);
+        }
</ins><span class="cx">         int32Result(resultGPR, node);
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="lines">@@ -13425,43 +13442,28 @@
</span><span class="cx">     Allocator allocatorValue = allocatorForNonVirtualConcurrently<JSRopeString>(*m_jit.vm(), sizeof(JSRopeString), AllocatorForMode::AllocatorIfExists);
</span><span class="cx">     emitAllocateJSCell(resultGPR, JITAllocator::constant(allocatorValue), allocatorGPR, TrustedImmPtr(m_jit.graph().registerStructure(m_jit.vm()->stringStructure.get())), scratchGPR, slowPath);
</span><span class="cx"> 
</span><del>-    m_jit.orPtr(TrustedImm32(JSString::isRopeInPointer), opGPRs[0], allocatorGPR);
-    m_jit.storePtr(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFiber0()));
</del><ins>+    // 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()));
</ins><span class="cx"> 
</span><del>-    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()));
-    }
-
</del><span class="cx">     {
</span><span class="cx">         if (JSString* string = edges[0]->dynamicCastConstant<JSString*>(*m_jit.vm())) {
</span><span class="cx">             m_jit.move(TrustedImm32(string->is8Bit() ? StringImpl::flagIs8Bit() : 0), scratchGPR);
</span><span class="cx">             m_jit.move(TrustedImm32(string->length()), allocatorGPR);
</span><span class="cx">         } else {
</span><del>-            bool canBeRope = !m_state.forNode(edges[0]).isType(SpecStringIdent);
</del><ins>+            bool needsRopeCase = canBeRope(edges[0]);
</ins><span class="cx">             m_jit.loadPtr(CCallHelpers::Address(opGPRs[0], JSString::offsetOfValue()), scratch2GPR);
</span><span class="cx">             CCallHelpers::Jump isRope;
</span><del>-            if (canBeRope)
</del><ins>+            if (needsRopeCase)
</ins><span class="cx">                 isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
</span><span class="cx"> 
</span><span class="cx">             m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
</span><span class="cx">             m_jit.load32(CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR);
</span><span class="cx"> 
</span><del>-            if (canBeRope) {
</del><ins>+            if (needsRopeCase) {
</ins><span class="cx">                 auto done = m_jit.jump();
</span><span class="cx"> 
</span><span class="cx">                 isRope.link(&m_jit);
</span><del>-                m_jit.load16(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
</del><ins>+                m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfFlags()), scratchGPR);
</ins><span class="cx">                 m_jit.load32(CCallHelpers::Address(opGPRs[0], JSRopeString::offsetOfLength()), allocatorGPR);
</span><span class="cx">                 done.link(&m_jit);
</span><span class="cx">             }
</span><span class="lines">@@ -13484,23 +13486,23 @@
</span><span class="cx">                     CCallHelpers::Overflow,
</span><span class="cx">                     TrustedImm32(string->length()), allocatorGPR));
</span><span class="cx">         } else {
</span><del>-            bool canBeRope = !m_state.forNode(edges[i]).isType(SpecStringIdent);
</del><ins>+            bool needsRopeCase = canBeRope(edges[i]);
</ins><span class="cx">             m_jit.loadPtr(CCallHelpers::Address(opGPRs[i], JSString::offsetOfValue()), scratch2GPR);
</span><span class="cx">             CCallHelpers::Jump isRope;
</span><del>-            if (canBeRope)
</del><ins>+            if (needsRopeCase)
</ins><span class="cx">                 isRope = m_jit.branchIfRopeStringImpl(scratch2GPR);
</span><span class="cx"> 
</span><del>-            m_jit.and16(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
</del><ins>+            m_jit.and32(CCallHelpers::Address(scratch2GPR, StringImpl::flagsOffset()), scratchGPR);
</ins><span class="cx">             speculationCheck(
</span><span class="cx">                 Uncountable, JSValueSource(), nullptr,
</span><span class="cx">                 m_jit.branchAdd32(
</span><span class="cx">                     CCallHelpers::Overflow,
</span><span class="cx">                     CCallHelpers::Address(scratch2GPR, StringImpl::lengthMemoryOffset()), allocatorGPR));
</span><del>-            if (canBeRope) {
</del><ins>+            if (needsRopeCase) {
</ins><span class="cx">                 auto done = m_jit.jump();
</span><span class="cx"> 
</span><span class="cx">                 isRope.link(&m_jit);
</span><del>-                m_jit.and16(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
</del><ins>+                m_jit.and32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfFlags()), scratchGPR);
</ins><span class="cx">                 m_jit.load32(CCallHelpers::Address(opGPRs[i], JSRopeString::offsetOfLength()), scratch2GPR);
</span><span class="cx">                 speculationCheck(
</span><span class="cx">                     Uncountable, JSValueSource(), nullptr,
</span><span class="lines">@@ -13510,7 +13512,7 @@
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">     }
</span><del>-    m_jit.store16(scratchGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfFlags()));
</del><ins>+
</ins><span class="cx">     if (!ASSERT_DISABLED) {
</span><span class="cx">         CCallHelpers::Jump ok = m_jit.branch32(
</span><span class="cx">             CCallHelpers::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0));
</span><span class="lines">@@ -13517,7 +13519,31 @@
</span><span class="cx">         m_jit.abortWithReason(DFGNegativeStringLength);
</span><span class="cx">         ok.link(&m_jit);
</span><span class="cx">     }
</span><del>-    m_jit.store32(allocatorGPR, CCallHelpers::Address(resultGPR, JSRopeString::offsetOfLength()));
</del><ins>+
+    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()));
+    }
+
</ins><span class="cx">     auto isNonEmptyString = m_jit.branchTest32(CCallHelpers::NonZero, allocatorGPR);
</span><span class="cx"> 
</span><span class="cx">     m_jit.move(TrustedImmPtr::weakPointer(m_jit.graph(), jsEmptyString(&m_jit.graph().m_vm)), resultGPR);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h      2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h 2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -577,6 +577,8 @@
</span><span class="cx">     bool isKnownNotNumber(Node* node) { return !(m_state.forNode(node).m_type & SpecFullNumber); }
</span><span class="cx">     bool isKnownNotCell(Node* node) { return !(m_state.forNode(node).m_type & SpecCell); }
</span><span class="cx">     bool isKnownNotOther(Node* node) { return !(m_state.forNode(node).m_type & SpecOther); }
</span><ins>+
+    bool canBeRope(Edge&);
</ins><span class="cx">     
</span><span class="cx">     UniquedStringImpl* identifierUID(unsigned index)
</span><span class="cx">     {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositorycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp    2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.cpp       2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -86,6 +86,7 @@
</span><span class="cx">     JSCell_typeInfoFlags.changeParent(&JSCell_usefulBytes);
</span><span class="cx">     JSCell_cellState.changeParent(&JSCell_usefulBytes);
</span><span class="cx">     JSRopeString_flags.changeParent(&JSRopeString_fiber0);
</span><ins>+    JSRopeString_length.changeParent(&JSRopeString_fiber1);
</ins><span class="cx"> 
</span><span class="cx">     RELEASE_ASSERT(!JSCell_freeListNext.offset());
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositoryh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h      2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h 2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -89,12 +89,10 @@
</span><span class="cx">     macro(JSPropertyNameEnumerator_endStructurePropertyIndex, JSPropertyNameEnumerator::endStructurePropertyIndexOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_indexLength, JSPropertyNameEnumerator::indexedLengthOffset()) \
</span><span class="cx">     macro(JSRopeString_flags, JSRopeString::offsetOfFlags()) \
</span><ins>+    macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
</ins><span class="cx">     macro(JSRopeString_fiber0, JSRopeString::offsetOfFiber0()) \
</span><del>-    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()) \
</del><ins>+    macro(JSRopeString_fiber1, JSRopeString::offsetOfFiber1()) \
+    macro(JSRopeString_fiber2, JSRopeString::offsetOfFiber2()) \
</ins><span class="cx">     macro(JSScope_next, JSScope::offsetOfNext()) \
</span><span class="cx">     macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
</span><span class="cx">     macro(JSWrapperObject_internalValue, JSWrapperObject::internalValueOffset()) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp      2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp 2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -6524,6 +6524,7 @@
</span><span class="cx">             numKids = 2;
</span><span class="cx">         }
</span><span class="cx">         
</span><ins>+        LBasicBlock emptyCase = m_out.newBlock();
</ins><span class="cx">         LBasicBlock slowPath = m_out.newBlock();
</span><span class="cx">         LBasicBlock continuation = m_out.newBlock();
</span><span class="cx">         
</span><span class="lines">@@ -6532,19 +6533,9 @@
</span><span class="cx">         LValue result = allocateCell(
</span><span class="cx">             m_out.constIntPtr(allocator.localAllocator()), vm().stringStructure.get(), slowPath);
</span><span class="cx">         
</span><del>-        m_out.storePtr(m_out.bitOr(kids[0], m_out.constIntPtr(JSString::isRopeInPointer)), result, m_heaps.JSRopeString_fiber0);
</del><ins>+        // 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);
</ins><span class="cx"> 
</span><del>-        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);
-        }
-
</del><span class="cx">         auto getFlagsAndLength = [&] (Edge& edge, LValue child) {
</span><span class="cx">             if (JSString* string = edge->dynamicCastConstant<JSString*>(vm())) {
</span><span class="cx">                 return FlagsAndLength {
</span><span class="lines">@@ -6560,7 +6551,7 @@
</span><span class="cx">             m_out.branch(isRopeString(child, edge), unsure(ropeCase), unsure(notRopeCase));
</span><span class="cx"> 
</span><span class="cx">             LBasicBlock lastNext = m_out.appendTo(ropeCase, notRopeCase);
</span><del>-            ValueFromBlock flagsForRope = m_out.anchor(m_out.load16ZeroExt32(child, m_heaps.JSRopeString_flags));
</del><ins>+            ValueFromBlock flagsForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_flags));
</ins><span class="cx">             ValueFromBlock lengthForRope = m_out.anchor(m_out.load32NonNegative(child, m_heaps.JSRopeString_length));
</span><span class="cx">             m_out.jump(continuation);
</span><span class="cx"> 
</span><span class="lines">@@ -6591,14 +6582,29 @@
</span><span class="cx">             };
</span><span class="cx">             flagsAndLength = mergeFlagsAndLength(edges[i], kids[i], flagsAndLength);
</span><span class="cx">         }
</span><del>-        m_out.store32As16(flagsAndLength.flags, result, m_heaps.JSRopeString_flags);
-        m_out.store32(flagsAndLength.length, result, m_heaps.JSRopeString_length);
</del><ins>+
+        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);
</ins><span class="cx">         
</span><span class="cx">         mutatorFence();
</span><del>-        ValueFromBlock fastResult = m_out.anchor(m_out.select(m_out.isZero32(flagsAndLength.length), weakPointer(jsEmptyString(&m_graph.m_vm)), result));
</del><ins>+        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)));
</ins><span class="cx">         m_out.jump(continuation);
</span><span class="cx">         
</span><del>-        LBasicBlock lastNext = m_out.appendTo(slowPath, continuation);
</del><ins>+        m_out.appendTo(slowPath, continuation);
</ins><span class="cx">         LValue slowResultValue;
</span><span class="cx">         VM& vm = this->vm();
</span><span class="cx">         switch (numKids) {
</span><span class="lines">@@ -6626,7 +6632,7 @@
</span><span class="cx">         m_out.jump(continuation);
</span><span class="cx">         
</span><span class="cx">         m_out.appendTo(continuation, lastNext);
</span><del>-        setJSValue(m_out.phi(Int64, fastResult, slowResult));
</del><ins>+        setJSValue(m_out.phi(Int64, fastResult, emptyResult, slowResult));
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     void compileStringCharAt()
</span><span class="lines">@@ -15680,6 +15686,11 @@
</span><span class="cx">                 if (value.isCell() && value.asCell()->type() == StringType && !asString(value)->isRope())
</span><span class="cx">                     return m_out.booleanFalse;
</span><span class="cx">             }
</span><ins>+            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;
+            }
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         return m_out.testNonZeroPtr(m_out.loadPtr(string, m_heaps.JSString_value), m_out.constIntPtr(JSString::isRopeInPointer));
</span><span class="lines">@@ -15694,6 +15705,11 @@
</span><span class="cx">                 if (value.isCell() && value.asCell()->type() == StringType && !asString(value)->isRope())
</span><span class="cx">                     return m_out.booleanTrue;
</span><span class="cx">             }
</span><ins>+            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;
+            }
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         return m_out.testIsZeroPtr(m_out.loadPtr(string, m_heaps.JSString_value), m_out.constIntPtr(JSString::isRopeInPointer));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSStringcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSString.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSString.cpp 2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/runtime/JSString.cpp    2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -114,7 +114,7 @@
</span><span class="cx">     
</span><span class="cx">     uintptr_t pointer = thisObject->m_fiber;
</span><span class="cx">     if (pointer & isRopeInPointer) {
</span><del>-        if ((pointer & JSRopeString::stringMask) == JSRopeString::substringSentinel()) {
</del><ins>+        if (pointer & JSRopeString::isSubstringInPointer) {
</ins><span class="cx">             visitor.appendUnbarriered(static_cast<JSRopeString*>(thisObject)->fiber1());
</span><span class="cx">             return;
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSStringh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSString.h (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSString.h   2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/runtime/JSString.h      2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -86,9 +86,9 @@
</span><span class="cx"> //
</span><span class="cx"> //              0                        8        10               16                       32                                     48
</span><span class="cx"> // JSString     [   ID      ][  header  ][   String pointer      0]
</span><del>-// JSRopeString [   ID      ][  header  ][ flags ][ 1st fiber    1][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
</del><ins>+// JSRopeString [   ID      ][  header  ][   1st fiber         xyz][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
</ins><span class="cx"> //                                                               ^
</span><del>-//                                                            isRope bit
</del><ins>+//                                            x:(is8Bit),y:(isSubstring),z:(isRope) bit flags
</ins><span class="cx"> class JSString : public JSCell {
</span><span class="cx"> public:
</span><span class="cx">     friend class JIT;
</span><span class="lines">@@ -266,13 +266,15 @@
</span><span class="cx"> class JSRopeString final : public JSString {
</span><span class="cx">     friend class JSString;
</span><span class="cx"> public:
</span><ins>+    // 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);
</ins><span class="cx"> #if CPU(ADDRESS64)
</span><span class="cx">     static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
</span><del>-    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;
-
</del><span class="cx">     class CompactFibers {
</span><span class="cx">     public:
</span><span class="cx">         JSString* fiber1() const
</span><span class="lines">@@ -289,13 +291,13 @@
</span><span class="cx"> 
</span><span class="cx">         JSString* fiber2() const
</span><span class="cx">         {
</span><del>-            return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 32));
</del><ins>+            return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 16));
</ins><span class="cx">         }
</span><span class="cx">         void initializeFiber2(JSString* fiber)
</span><span class="cx">         {
</span><span class="cx">             uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
</span><del>-            m_fiber2Lower = static_cast<uint32_t>(pointer);
-            m_fiber2Upper = static_cast<uint16_t>(pointer >> 32);
</del><ins>+            m_fiber2Lower = static_cast<uint16_t>(pointer);
+            m_fiber2Upper = static_cast<uint32_t>(pointer >> 16);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         unsigned length() const { return m_length; }
</span><span class="lines">@@ -305,22 +307,18 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
</span><del>-        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); }
</del><ins>+        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
+        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
</ins><span class="cx"> 
</span><span class="cx">     private:
</span><span class="cx">         uint32_t m_length { 0 };
</span><span class="cx">         uint32_t m_fiber1Lower { 0 };
</span><span class="cx">         uint16_t m_fiber1Upper { 0 };
</span><del>-        uint16_t m_fiber2Upper { 0 };
-        uint32_t m_fiber2Lower { 0 };
</del><ins>+        uint16_t m_fiber2Lower { 0 };
+        uint32_t m_fiber2Upper { 0 };
</ins><span class="cx">     };
</span><span class="cx">     static_assert(sizeof(CompactFibers) == sizeof(void*) * 2, "");
</span><span class="cx"> #else
</span><del>-    static constexpr uintptr_t stringMask = ~(isRopeInPointer);
-
</del><span class="cx">     class CompactFibers {
</span><span class="cx">     public:
</span><span class="cx">         JSString* fiber1() const
</span><span class="lines">@@ -347,24 +345,12 @@
</span><span class="cx">             m_length = length;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        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());
-        }
-
</del><span class="cx">         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
</span><ins>+        static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1); }
+        static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2); }
</ins><span class="cx"> 
</span><span class="cx">     private:
</span><span class="cx">         uint32_t m_length { 0 };
</span><del>-        uint32_t m_flags { 0 };
</del><span class="cx">         JSString* m_fiber1 { nullptr };
</span><span class="cx">         JSString* m_fiber2 { nullptr };
</span><span class="cx">     };
</span><span class="lines">@@ -456,16 +442,20 @@
</span><span class="cx"> 
</span><span class="cx">     void initializeIs8Bit(bool flag) const
</span><span class="cx">     {
</span><del>-#if CPU(ADDRESS64)
</del><span class="cx">         if (flag)
</span><span class="cx">             m_fiber |= is8BitInPointer;
</span><span class="cx">         else
</span><span class="cx">             m_fiber &= ~is8BitInPointer;
</span><del>-#else
-        m_compactFibers.initializeIs8Bit(flag);
-#endif
</del><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void initializeIsSubstring(bool flag) const
+    {
+        if (flag)
+            m_fiber |= isSubstringInPointer;
+        else
+            m_fiber &= ~isSubstringInPointer;
+    }
+
</ins><span class="cx">     ALWAYS_INLINE void initializeLength(unsigned length)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(length <= MaxLength);
</span><span class="lines">@@ -472,6 +462,17 @@
</span><span class="cx">         m_compactFibers.initializeLength(length);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    JSRopeString(VM& vm)
+        : JSString(vm)
+    {
+        initializeIsSubstring(false);
+        initializeLength(0);
+        initializeIs8Bit(true);
+        initializeFiber0(nullptr);
+        initializeFiber1(nullptr);
+        initializeFiber2(nullptr);
+    }
+
</ins><span class="cx">     JSRopeString(VM& vm, JSString* s1, JSString* s2)
</span><span class="cx">         : JSString(vm)
</span><span class="cx">     {
</span><span class="lines">@@ -549,26 +550,29 @@
</span><span class="cx"> 
</span><span class="cx"> public:
</span><span class="cx">     static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfLength(); } // 32byte width.
</span><ins>+    static ptrdiff_t offsetOfFlags() { return offsetOfValue(); }
</ins><span class="cx">     static ptrdiff_t offsetOfFiber0() { return offsetOfValue(); }
</span><del>-#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
</del><ins>+    static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1(); }
+    static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2(); }
</ins><span class="cx"> 
</span><span class="cx">     static constexpr unsigned s_maxInternalRopeLength = 3;
</span><span class="cx"> 
</span><ins>+    // 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;
+    }
+
</ins><span class="cx"> private:
</span><span class="cx">     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2)
</span><span class="cx">     {
</span><span class="lines">@@ -575,6 +579,7 @@
</span><span class="cx">         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2);
</span><span class="cx">         newString->finishCreation(vm);
</span><span class="cx">         ASSERT(newString->length());
</span><ins>+        ASSERT(newString->isRope());
</ins><span class="cx">         return newString;
</span><span class="cx">     }
</span><span class="cx">     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
</span><span class="lines">@@ -582,6 +587,7 @@
</span><span class="cx">         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2, s3);
</span><span class="cx">         newString->finishCreation(vm);
</span><span class="cx">         ASSERT(newString->length());
</span><ins>+        ASSERT(newString->isRope());
</ins><span class="cx">         return newString;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -590,6 +596,7 @@
</span><span class="cx">         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, base, offset, length);
</span><span class="cx">         newString->finishCreationSubstring(vm, exec);
</span><span class="cx">         ASSERT(newString->length());
</span><ins>+        ASSERT(newString->isRope());
</ins><span class="cx">         return newString;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -598,6 +605,7 @@
</span><span class="cx">         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap, deferralContext)) JSRopeString(SubstringOfResolved, vm, base, offset, length);
</span><span class="cx">         newString->finishCreationSubstringOfResolved(vm);
</span><span class="cx">         ASSERT(newString->length());
</span><ins>+        ASSERT(newString->isRope());
</ins><span class="cx">         return newString;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -685,26 +693,6 @@
</span><span class="cx">         return static_cast<unsigned>(bitwise_cast<uintptr_t>(fiber2()));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    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());
-    }
-
</del><span class="cx">     static_assert(s_maxInternalRopeLength >= 2, "");
</span><span class="cx">     mutable CompactFibers m_compactFibers;
</span><span class="cx"> 
</span><span class="lines">@@ -726,14 +714,9 @@
</span><span class="cx"> {
</span><span class="cx">     uintptr_t pointer = m_fiber;
</span><span class="cx">     if (pointer & isRopeInPointer) {
</span><del>-#if CPU(ADDRESS64)
</del><span class="cx">         // Do not load m_fiber twice. We should use the information in pointer.
</span><span class="cx">         // Otherwise, JSRopeString may be converted to JSString between the first and second accesses.
</span><span class="cx">         return pointer & JSRopeString::is8BitInPointer;
</span><del>-#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
</del><span class="cx">     }
</span><span class="cx">     return bitwise_cast<StringImpl*>(pointer)->is8Bit();
</span><span class="cx"> }
</span><span class="lines">@@ -1046,7 +1029,7 @@
</span><span class="cx"> 
</span><span class="cx"> inline bool JSString::isSubstring() const
</span><span class="cx"> {
</span><del>-    return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
</del><ins>+    return m_fiber & JSRopeString::isSubstringInPointer;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // --- JSValue inlines ----------------------------
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsJSDollarVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp (242396 => 242397)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp 2019-03-04 23:11:58 UTC (rev 242396)
+++ trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp    2019-03-04 23:27:43 UTC (rev 242397)
</span><span class="lines">@@ -1721,6 +1721,13 @@
</span><span class="cx">     return JSValue::encode(array);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionCreateNullRopeString(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    JSLockHolder lock(vm);
+    return JSValue::encode(JSRopeString::createNullForTesting(vm));
+}
+
</ins><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx">     VM& vm = exec->vm();
</span><span class="lines">@@ -2220,6 +2227,7 @@
</span><span class="cx">     addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
</span><span class="cx">     addFunction(vm, "createProxy", functionCreateProxy, 1);
</span><span class="cx">     addFunction(vm, "createRuntimeArray", functionCreateRuntimeArray, 0);
</span><ins>+    addFunction(vm, "createNullRopeString", functionCreateNullRopeString, 0);
</ins><span class="cx"> 
</span><span class="cx">     addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
</span><span class="cx">     addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
</span></span></pre>
</div>
</div>

</body>
</html>