Diff
Modified: trunk/JSTests/ChangeLog (254935 => 254936)
--- trunk/JSTests/ChangeLog 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/JSTests/ChangeLog 2020-01-22 20:45:37 UTC (rev 254936)
@@ -1,3 +1,14 @@
+2020-01-22 Yusuke Suzuki <[email protected]>
+
+ [JSC] Add CheckArrayOrEmpty to handle the case when hoisting CheckArray for places where input can be empty
+ https://bugs.webkit.org/show_bug.cgi?id=206571
+ <rdar://problem/58757016>
+
+ Reviewed by Saam Barati.
+
+ * stress/check-array-or-empty.js: Added.
+ (C):
+
2020-01-21 Tadeu Zagallo <[email protected]>
Object allocation sinking is missing PutHint for allocations unreachable in the graph
Added: trunk/JSTests/stress/check-array-or-empty.js (0 => 254936)
--- trunk/JSTests/stress/check-array-or-empty.js (rev 0)
+++ trunk/JSTests/stress/check-array-or-empty.js 2020-01-22 20:45:37 UTC (rev 254936)
@@ -0,0 +1,24 @@
+//@ runDefault("--useGenerationalGC=0", "--useConcurrentGC=0", "--collectContinuously=1", "--useConcurrentJIT=0")
+class C extends Object {
+ constructor(beforeSuper) {
+ let f = () => {
+ for (let j = 0; j < 100; j++) {
+ try {
+ this[0] = {};
+ } catch (e) {
+ }
+ }
+ };
+ if (beforeSuper) {
+ f();
+ super();
+ } else {
+ super();
+ f();
+ }
+ }
+}
+for (let i = 0; i < 100; i++) {
+ new C(false);
+ new C(true);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (254935 => 254936)
--- trunk/Source/_javascript_Core/ChangeLog 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-01-22 20:45:37 UTC (rev 254936)
@@ -1,5 +1,56 @@
2020-01-22 Yusuke Suzuki <[email protected]>
+ [JSC] Add CheckArrayOrEmpty to handle the case when hoisting CheckArray for places where input can be empty
+ https://bugs.webkit.org/show_bug.cgi?id=206571
+ <rdar://problem/58757016>
+
+ Reviewed by Saam Barati.
+
+ Since we hoist CheckArray too in DFGTypeCheckHoistingPhase, we have the same problem to CheckStructureOrEmpty: we
+ could insert CheckArray where the input can include Empty. We should have CheckArrayOrEmpty as we have CheckStructureOrEmpty
+ for CheckStructure: CheckArrayOrEmpty accepts empty or cell with specified array-modes.
+
+ * dfg/DFGAbstractInterpreter.h:
+ (JSC::DFG::AbstractInterpreter::filterArrayModes):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::filterArrayModes):
+ * dfg/DFGAbstractValue.cpp:
+ (JSC::DFG::AbstractValue::filterArrayModes):
+ * dfg/DFGAbstractValue.h:
+ * dfg/DFGArgumentsEliminationPhase.cpp:
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertCheckArrayOrEmptyToCheckArray):
+ (JSC::DFG::Node::hasArrayMode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::checkArray):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::run):
+ * dfg/DFGValidate.cpp:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileCheckArrayOrEmpty):
+
+2020-01-22 Yusuke Suzuki <[email protected]>
+
[JSC] Attempt to fix BytecodeIndex handling in 32bit
https://bugs.webkit.org/show_bug.cgi?id=206577
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreter.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreter.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreter.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -182,9 +182,9 @@
}
template<typename T>
- FiltrationResult filterArrayModes(T node, ArrayModes arrayModes)
+ FiltrationResult filterArrayModes(T node, ArrayModes arrayModes, SpeculatedType admittedTypes = SpecNone)
{
- return filterArrayModes(forNode(node), arrayModes);
+ return filterArrayModes(forNode(node), arrayModes, admittedTypes);
}
template<typename T>
@@ -206,7 +206,7 @@
}
FiltrationResult filter(AbstractValue&, const RegisteredStructureSet&, SpeculatedType admittedTypes = SpecNone);
- FiltrationResult filterArrayModes(AbstractValue&, ArrayModes);
+ FiltrationResult filterArrayModes(AbstractValue&, ArrayModes, SpeculatedType admittedTypes = SpecNone);
FiltrationResult filter(AbstractValue&, SpeculatedType);
FiltrationResult filterByValue(AbstractValue&, FrozenValue);
FiltrationResult filterClassInfo(AbstractValue&, const ClassInfo*);
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -3379,14 +3379,27 @@
break;
}
+ case CheckArrayOrEmpty:
case CheckArray: {
- if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) {
+ AbstractValue& value = forNode(node->child1());
+
+ SpeculatedType admittedTypes = SpecNone;
+ if (node->op() == CheckArrayOrEmpty) {
+ bool mayBeEmpty = value.m_type & SpecEmpty;
+ if (!mayBeEmpty)
+ m_state.setShouldTryConstantFolding(true);
+ else
+ admittedTypes = SpecEmpty;
+ }
+
+ if (node->arrayMode().alreadyChecked(m_graph, node, value)) {
m_state.setShouldTryConstantFolding(true);
break;
}
+
switch (node->arrayMode().type()) {
case Array::String:
- filter(node->child1(), SpecString);
+ filter(node->child1(), SpecString | admittedTypes);
break;
case Array::Int32:
case Array::Double:
@@ -3396,46 +3409,46 @@
case Array::SlowPutArrayStorage:
break;
case Array::DirectArguments:
- filter(node->child1(), SpecDirectArguments);
+ filter(node->child1(), SpecDirectArguments | admittedTypes);
break;
case Array::ScopedArguments:
- filter(node->child1(), SpecScopedArguments);
+ filter(node->child1(), SpecScopedArguments | admittedTypes);
break;
case Array::Int8Array:
- filter(node->child1(), SpecInt8Array);
+ filter(node->child1(), SpecInt8Array | admittedTypes);
break;
case Array::Int16Array:
- filter(node->child1(), SpecInt16Array);
+ filter(node->child1(), SpecInt16Array | admittedTypes);
break;
case Array::Int32Array:
- filter(node->child1(), SpecInt32Array);
+ filter(node->child1(), SpecInt32Array | admittedTypes);
break;
case Array::Uint8Array:
- filter(node->child1(), SpecUint8Array);
+ filter(node->child1(), SpecUint8Array | admittedTypes);
break;
case Array::Uint8ClampedArray:
- filter(node->child1(), SpecUint8ClampedArray);
+ filter(node->child1(), SpecUint8ClampedArray | admittedTypes);
break;
case Array::Uint16Array:
- filter(node->child1(), SpecUint16Array);
+ filter(node->child1(), SpecUint16Array | admittedTypes);
break;
case Array::Uint32Array:
- filter(node->child1(), SpecUint32Array);
+ filter(node->child1(), SpecUint32Array | admittedTypes);
break;
case Array::Float32Array:
- filter(node->child1(), SpecFloat32Array);
+ filter(node->child1(), SpecFloat32Array | admittedTypes);
break;
case Array::Float64Array:
- filter(node->child1(), SpecFloat64Array);
+ filter(node->child1(), SpecFloat64Array | admittedTypes);
break;
case Array::AnyTypedArray:
- filter(node->child1(), SpecTypedArrayView);
+ filter(node->child1(), SpecTypedArrayView | admittedTypes);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
- filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering());
+ filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering(), admittedTypes);
break;
}
@@ -4464,9 +4477,9 @@
template<typename AbstractStateType>
FiltrationResult AbstractInterpreter<AbstractStateType>::filterArrayModes(
- AbstractValue& value, ArrayModes arrayModes)
+ AbstractValue& value, ArrayModes arrayModes, SpeculatedType admittedTypes)
{
- if (value.filterArrayModes(arrayModes) == FiltrationOK)
+ if (value.filterArrayModes(arrayModes, admittedTypes) == FiltrationOK)
return FiltrationOK;
m_state.setIsValid(false);
return Contradiction;
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -266,14 +266,15 @@
return normalizeClarity(graph);
}
-FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes)
+FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes, SpeculatedType admittedTypes)
{
ASSERT(arrayModes);
+ ASSERT(!(admittedTypes & SpecCell));
if (isClear())
return FiltrationOK;
- m_type &= SpecCell;
+ m_type &= SpecCell | admittedTypes;
m_arrayModes &= arrayModes;
return normalizeClarity();
}
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -336,7 +336,7 @@
// with SpecCell.
FiltrationResult filter(Graph&, const RegisteredStructureSet&, SpeculatedType admittedTypes = SpecNone);
- FiltrationResult filterArrayModes(ArrayModes);
+ FiltrationResult filterArrayModes(ArrayModes, SpeculatedType admittedTypes = SpecNone);
ALWAYS_INLINE FiltrationResult filter(SpeculatedType type)
{
Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -409,6 +409,7 @@
case FilterInByIdStatus:
break;
+ case CheckArrayOrEmpty:
case CheckArray:
escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
break;
@@ -1259,6 +1260,7 @@
break;
}
+ case CheckArrayOrEmpty:
case CheckArray:
case GetButterfly:
case FilterGetByStatus:
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -1096,6 +1096,7 @@
read(JSCell_structureID);
return;
+ case CheckArrayOrEmpty:
case CheckArray:
read(JSCell_indexingType);
read(JSCell_typeInfoType);
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -275,7 +275,20 @@
}
break;
}
-
+
+ case CheckArrayOrEmpty: {
+ const AbstractValue& value = m_state.forNode(node->child1());
+ if (!(value.m_type & SpecEmpty)) {
+ node->convertCheckArrayOrEmptyToCheckArray();
+ changed = true;
+ }
+ // Even if the input includes SpecEmpty, we can fall through to CheckArray and remove the node.
+ // CheckArrayOrEmpty can be removed when arrayMode meets the requirement. In that case, CellUse's
+ // check just remains, and it works as CheckArrayOrEmpty without ArrayMode checking.
+ ASSERT(typeFilterFor(node->child1().useKind()) & SpecEmpty);
+ FALLTHROUGH;
+ }
+
case CheckArray:
case Arrayify: {
if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -117,6 +117,7 @@
case GetButterfly:
case CheckSubClass:
case CheckArray:
+ case CheckArrayOrEmpty:
case CheckNeutered:
case GetScope:
case SkipScope:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -1945,6 +1945,7 @@
case PutHint:
case CheckStructureImmediate:
case CheckStructureOrEmpty:
+ case CheckArrayOrEmpty:
case MaterializeNewObject:
case MaterializeCreateActivation:
case MaterializeNewInternalFieldObject:
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -479,6 +479,12 @@
m_op = CheckStructureImmediate;
children.setChild1(Edge(structure, CellUse));
}
+
+ void convertCheckArrayOrEmptyToCheckArray()
+ {
+ ASSERT(op() == CheckArrayOrEmpty);
+ setOpAndDefaultFlags(CheckArray);
+ }
void replaceWith(Graph&, Node* other);
void replaceWithWithoutChecks(Node* other);
@@ -2145,6 +2151,7 @@
case StringCharCodeAt:
case StringCodePointAt:
case CheckArray:
+ case CheckArrayOrEmpty:
case Arrayify:
case ArrayifyToStructure:
case ArrayPush:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -238,6 +238,7 @@
macro(GetButterfly, NodeResultStorage) \
macro(NukeStructureAndSetButterfly, NodeMustGenerate) \
macro(CheckArray, NodeMustGenerate) \
+ macro(CheckArrayOrEmpty, NodeMustGenerate) \
/* This checks if the edge is a typed array and if it is neutered. */ \
macro(CheckNeutered, NodeMustGenerate) \
macro(Arrayify, NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -1292,6 +1292,7 @@
case PutHint:
case CheckStructureImmediate:
case CheckStructureOrEmpty:
+ case CheckArrayOrEmpty:
case MaterializeNewObject:
case MaterializeCreateActivation:
case MaterializeNewInternalFieldObject:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-01-22 20:45:37 UTC (rev 254936)
@@ -161,6 +161,7 @@
switch (node->op()) {
case CheckNotEmpty:
case CheckStructureOrEmpty:
+ case CheckArrayOrEmpty:
break;
default:
return false;
@@ -278,6 +279,7 @@
case CallDOM:
case CheckSubClass:
case CheckArray:
+ case CheckArrayOrEmpty:
case CheckNeutered:
case Arrayify:
case ArrayifyToStructure:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -829,11 +829,39 @@
GPRReg baseReg = base.gpr();
if (node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))) {
+ // We can purge Empty check completely in this case of CheckArrayOrEmpty since CellUse only accepts SpecCell | SpecEmpty.
+ ASSERT(typeFilterFor(node->child1().useKind()) & SpecEmpty);
noResult(m_currentNode);
return;
}
-
+
+ Optional<GPRTemporary> temp;
+ Optional<GPRReg> tempGPR;
switch (node->arrayMode().type()) {
+ case Array::Int32:
+ case Array::Double:
+ case Array::Contiguous:
+ case Array::Undecided:
+ case Array::ArrayStorage:
+ case Array::SlowPutArrayStorage: {
+ temp.emplace(this);
+ tempGPR = temp->gpr();
+ break;
+ }
+ default:
+ break;
+ }
+
+ CCallHelpers::Jump isEmpty;
+
+#if USE(JSVALUE64)
+ if (node->op() == CheckArrayOrEmpty) {
+ if (m_interpreter.forNode(node->child1()).m_type & SpecEmpty)
+ isEmpty = m_jit.branchIfEmpty(baseReg);
+ }
+#endif
+
+ switch (node->arrayMode().type()) {
case Array::AnyTypedArray:
case Array::String:
RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:)
@@ -844,31 +872,28 @@
case Array::Undecided:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
- GPRTemporary temp(this);
- GPRReg tempGPR = temp.gpr();
- m_jit.load8(MacroAssembler::Address(baseReg, JSCell::indexingTypeAndMiscOffset()), tempGPR);
+ m_jit.load8(MacroAssembler::Address(baseReg, JSCell::indexingTypeAndMiscOffset()), tempGPR.value());
speculationCheck(
BadIndexingType, JSValueSource::unboxedCell(baseReg), 0,
- jumpSlowForUnwantedArrayMode(tempGPR, node->arrayMode()));
-
- noResult(m_currentNode);
- return;
+ jumpSlowForUnwantedArrayMode(tempGPR.value(), node->arrayMode()));
+ break;
}
case Array::DirectArguments:
speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, DirectArgumentsType);
- noResult(m_currentNode);
- return;
+ break;
case Array::ScopedArguments:
speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, ScopedArgumentsType);
- noResult(m_currentNode);
- return;
+ break;
default:
speculateCellTypeWithoutTypeFiltering(
node->child1(), baseReg,
typeForTypedArrayType(node->arrayMode().typedArrayType()));
- noResult(m_currentNode);
- return;
+ break;
}
+
+ if (isEmpty.isSet())
+ isEmpty.link(&m_jit);
+ noResult(m_currentNode);
}
void SpeculativeJIT::arrayify(Node* node, GPRReg baseReg, GPRReg propertyReg)
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -4181,6 +4181,10 @@
case CheckStructureOrEmpty:
DFG_CRASH(m_jit.graph(), node, "CheckStructureOrEmpty only used in 64-bit DFG");
break;
+
+ case CheckArrayOrEmpty:
+ DFG_CRASH(m_jit.graph(), node, "CheckArrayOrEmpty only used in 64-bit DFG");
+ break;
case FilterCallLinkStatus:
case FilterGetByStatus:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -2375,7 +2375,8 @@
compileCheckNeutered(node);
break;
}
-
+
+ case CheckArrayOrEmpty:
case CheckArray: {
checkArray(node);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -143,18 +143,24 @@
Node* getLocal = insertionSet.insertNode(
indexInBlock + 1, variable->prediction(), GetLocal, origin,
OpInfo(variable), Edge(node));
+
+ auto needsEmptyCheck = [](Node* node) -> bool {
+ if (!(SpecCellCheck & SpecEmpty))
+ return false;
+ VirtualRegister local = node->variableAccessData()->operand().virtualRegister();
+ auto* inlineCallFrame = node->origin.semantic.inlineCallFrame();
+ if ((local - (inlineCallFrame ? inlineCallFrame->stackOffset : 0)) == virtualRegisterForArgumentIncludingThis(0)) {
+ // |this| can be the TDZ value. The call entrypoint won't have |this| as TDZ,
+ // but a catch or a loop OSR entry may have |this| be TDZ.
+ return true;
+ }
+ return false;
+ };
+
if (iter->value.m_structure) {
auto checkOp = CheckStructure;
- if (SpecCellCheck & SpecEmpty) {
- VirtualRegister local = node->variableAccessData()->operand().virtualRegister();
- auto* inlineCallFrame = node->origin.semantic.inlineCallFrame();
- if ((local - (inlineCallFrame ? inlineCallFrame->stackOffset : 0)) == virtualRegisterForArgumentIncludingThis(0)) {
- // |this| can be the TDZ value. The call entrypoint won't have |this| as TDZ,
- // but a catch or a loop OSR entry may have |this| be TDZ.
- checkOp = CheckStructureOrEmpty;
- }
- }
-
+ if (needsEmptyCheck(node))
+ checkOp = CheckStructureOrEmpty;
insertionSet.insertNode(
indexInBlock + 1, SpecNone, checkOp, origin,
OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
@@ -161,8 +167,11 @@
Edge(getLocal, CellUse));
} else if (iter->value.m_arrayModeIsValid) {
ASSERT(iter->value.m_arrayModeHoistingOkay);
+ auto checkOp = CheckArray;
+ if (needsEmptyCheck(node))
+ checkOp = CheckArrayOrEmpty;
insertionSet.insertNode(
- indexInBlock + 1, SpecNone, CheckArray, origin,
+ indexInBlock + 1, SpecNone, checkOp, origin,
OpInfo(iter->value.m_arrayMode.asWord()),
Edge(getLocal, CellUse));
} else
@@ -188,12 +197,12 @@
NodeOrigin origin = node->origin;
Edge child1 = node->child1();
+ // Note: On 64-bit platforms, cell checks allow the empty value to flow through.
+ // This means that this structure/array check may see the empty value as input. We need
+ // to emit a node that explicitly handles the empty value. Most of the time, CheckStructureOrEmpty/CheckArrayOrEmpty
+ // will be folded to CheckStructure/CheckArray because AI proves that the incoming value is
+ // definitely not empty.
if (iter->value.m_structure) {
- // Note: On 64-bit platforms, cell checks allow the empty value to flow through.
- // This means that this structure check may see the empty value as input. We need
- // to emit a node that explicitly handles the empty value. Most of the time, CheckStructureOrEmpty
- // will be folded to CheckStructure because AI proves that the incoming value is
- // definitely not empty.
insertionSet.insertNode(
indexForChecks, SpecNone, (SpecCellCheck & SpecEmpty) ? CheckStructureOrEmpty : CheckStructure,
originForChecks.withSemantic(origin.semantic),
@@ -202,7 +211,7 @@
} else if (iter->value.m_arrayModeIsValid) {
ASSERT(iter->value.m_arrayModeHoistingOkay);
insertionSet.insertNode(
- indexForChecks, SpecNone, CheckArray,
+ indexForChecks, SpecNone, (SpecCellCheck & SpecEmpty) ? CheckArrayOrEmpty : CheckArray,
originForChecks.withSemantic(origin.semantic),
OpInfo(iter->value.m_arrayMode.asWord()),
Edge(child1.node(), CellUse));
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -286,6 +286,11 @@
VALIDATE((node), !!node->child1());
VALIDATE((node), !!node->cellOperand()->value() && node->cellOperand()->value().isCell());
break;
+ case CheckArrayOrEmpty:
+ VALIDATE((node), is64Bit());
+ VALIDATE((node), !!node->child1());
+ VALIDATE((node), node->child1().useKind() == CellUse);
+ break;
case CheckStructureOrEmpty:
VALIDATE((node), is64Bit());
VALIDATE((node), !!node->child1());
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -203,6 +203,7 @@
case Check:
case CheckVarargs:
case CheckArray:
+ case CheckArrayOrEmpty:
case CheckNeutered:
case CountExecution:
case SuperSamplerBegin:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (254935 => 254936)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-01-22 20:39:09 UTC (rev 254935)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-01-22 20:45:37 UTC (rev 254936)
@@ -967,6 +967,9 @@
case CheckArray:
compileCheckArray();
break;
+ case CheckArrayOrEmpty:
+ compileCheckArrayOrEmpty();
+ break;
case CheckNeutered:
compileCheckNeutered();
break;
@@ -4067,6 +4070,37 @@
m_out.logicalNot(isArrayTypeForCheckArray(cell, m_node->arrayMode())));
}
+ void compileCheckArrayOrEmpty()
+ {
+ Edge edge = m_node->child1();
+ LValue cell = lowCell(edge);
+
+ if (m_node->arrayMode().alreadyChecked(m_graph, m_node, abstractValue(edge))) {
+ // We can purge Empty check of CheckArrayOrEmpty completely in this case since CellUse only accepts SpecCell | SpecEmpty.
+ ASSERT(typeFilterFor(m_node->child1().useKind()) & SpecEmpty);
+ return;
+ }
+
+ bool maySeeEmptyValue = m_interpreter.forNode(m_node->child1()).m_type & SpecEmpty;
+ LBasicBlock continuation = nullptr;
+ LBasicBlock lastNext = nullptr;
+ if (maySeeEmptyValue) {
+ LBasicBlock notEmpty = m_out.newBlock();
+ continuation = m_out.newBlock();
+ m_out.branch(m_out.isZero64(cell), unsure(continuation), unsure(notEmpty));
+ lastNext = m_out.appendTo(notEmpty, continuation);
+ }
+
+ speculate(
+ BadIndexingType, jsValueValue(cell), 0,
+ m_out.logicalNot(isArrayTypeForCheckArray(cell, m_node->arrayMode())));
+
+ if (maySeeEmptyValue) {
+ m_out.jump(continuation);
+ m_out.appendTo(continuation, lastNext);
+ }
+ }
+
void compileCheckNeutered()
{
Edge edge = m_node->child1();