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);