Diff
Modified: trunk/JSTests/ChangeLog (259582 => 259583)
--- trunk/JSTests/ChangeLog 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/JSTests/ChangeLog 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1,3 +1,62 @@
+2020-04-06 Justin Michaud <[email protected]>
+
+ Allow deleteById to be cached in the DFG
+ https://bugs.webkit.org/show_bug.cgi?id=208664
+
+ Reviewed by Saam Barati.
+
+ * microbenchmarks/delete-property-allocation-sinking.js: Added.
+ (assert):
+ (noInline.assert.blackbox):
+ (noInline.blackbox.doAlloc1):
+ (noInline.doAlloc1):
+ * microbenchmarks/polyvariant-delete-property.js: Added.
+ (assert):
+ (blackbox):
+ (noInline.blackbox.polyvariant):
+ (doAlloc1):
+ (noInline.doAlloc1.doAlloc2):
+ (noInline.doAlloc2):
+ * stress/delete-property-dfg-inline.js: Added.
+ (assert):
+ (noInline.assert.assert_throws):
+ (noInline.assert_throws.blackbox):
+ (noInline.blackbox.testSingleStructure.doAlloc1):
+ (noInline.blackbox.testSingleStructure):
+ (noInline.testSingleStructure.testInlineSingleStructure.doDelete2):
+ (noInline.testSingleStructure.testInlineSingleStructure.doAlloc2):
+ (noInline.testSingleStructure.testInlineSingleStructure):
+ (noInline.testInlineSingleStructure.testExit.doDelete3):
+ (noInline.testInlineSingleStructure.testExit):
+ (noInline.testExit.testSingleStructureMiss.doAlloc4):
+ (noInline.testExit.testSingleStructureMiss):
+ (noInline.testSingleStructureMiss.testSingleStructureMissStrict.string_appeared_here.doAlloc5):
+ (noInline.testSingleStructureMiss.testSingleStructureMissStrict):
+ (noInline.testSingleStructureMissStrict.testSingleStructureMissNonConfigurable.doAlloc6):
+ (noInline.testSingleStructureMissStrict.testSingleStructureMissNonConfigurable):
+ (noInline.testSingleStructureMissNonConfigurable.testSingleStructureEmpty.doAlloc7):
+ (noInline.testSingleStructureMissNonConfigurable.testSingleStructureEmpty):
+ (noInline.testSingleStructureEmpty.testPolymorphic.doDelete8):
+ (noInline.testSingleStructureEmpty.testPolymorphic):
+ (noInline.testPolymorphic.testPolyvariant.doDelete9):
+ (noInline.testPolymorphic.testPolyvariant.polyvariant):
+ (noInline.testPolymorphic.testPolyvariant):
+ (noInline.testPolyvariant.testConstantFolding.doDelete10):
+ (noInline.testPolyvariant.testConstantFolding):
+ (noInline.testConstantFolding.testObjectSinking.doAlloc11):
+ (noInline.testConstantFolding.testObjectSinking):
+ (noInline.testObjectSinking.testProxy.doAlloc12):
+ (noInline.testObjectSinking.testProxy.noInline.doDelete12):
+ (noInline.testObjectSinking.testProxy):
+ (noInline.testProxy.testTypedArray.doDelete12):
+ (noInline.testProxy.testTypedArray):
+ (noInline.testTypedArray.testMissMixed.doDelete13):
+ (noInline.testTypedArray.testMissMixed):
+ (noInline.testMissMixed.testMissNonMixed.doDelete14):
+ (noInline.testMissMixed.testMissNonMixed):
+ (noInline.testMissNonMixed.testByVal.doDelete15):
+ (noInline.testMissNonMixed.testByVal):
+
2020-04-06 Saam Barati <[email protected]>
Implement 1GB of executable memory on arm64
Added: trunk/JSTests/microbenchmarks/delete-property-allocation-sinking.js (0 => 259583)
--- trunk/JSTests/microbenchmarks/delete-property-allocation-sinking.js (rev 0)
+++ trunk/JSTests/microbenchmarks/delete-property-allocation-sinking.js 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,26 @@
+//@ skip if $model == "Apple Watch Series 3"
+
+function assert(condition) {
+ if (!condition)
+ throw new Error("assertion failed")
+}
+noInline(assert)
+
+function blackbox(x) {
+ return x
+}
+noInline(blackbox)
+
+function doAlloc1() {
+ let obj = {}
+ obj.x = 5
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ assert(delete obj.x)
+ return obj.y
+}
+noInline(doAlloc1)
+
+for (let i = 0; i < 50000000; ++i) {
+ doAlloc1()
+}
Added: trunk/JSTests/microbenchmarks/polyvariant-delete-property.js (0 => 259583)
--- trunk/JSTests/microbenchmarks/polyvariant-delete-property.js (rev 0)
+++ trunk/JSTests/microbenchmarks/polyvariant-delete-property.js 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,41 @@
+//@ skip if $model == "Apple Watch Series 3"
+
+function assert(condition) {
+ if (!condition)
+ throw new Error("assertion failed")
+}
+
+function blackbox(x) {
+ return x
+}
+noInline(blackbox)
+
+function polyvariant(x) {
+ assert(delete x.x)
+}
+
+function doAlloc1() {
+ let obj = {}
+ obj.x = 5
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ polyvariant(obj)
+ return obj.y
+}
+noInline(doAlloc1)
+
+function doAlloc2() {
+ let obj = {}
+ obj.x = 5
+ obj.b = 9
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ polyvariant(obj)
+ return obj.y
+}
+noInline(doAlloc2)
+
+for (let i = 0; i < 10000000; ++i) {
+ doAlloc1()
+ doAlloc2()
+}
Added: trunk/JSTests/stress/delete-property-dfg-inline.js (0 => 259583)
--- trunk/JSTests/stress/delete-property-dfg-inline.js (rev 0)
+++ trunk/JSTests/stress/delete-property-dfg-inline.js 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,409 @@
+function assert(condition) {
+ if (!condition)
+ throw new Error("assertion failed")
+}
+noInline(assert)
+
+function assert_throws(f) {
+ try {
+ f()
+ } catch {
+ return
+ }
+ throw new Error("assertion failed")
+}
+noInline(assert_throws)
+
+function blackbox(x) {
+ return x
+}
+noInline(blackbox)
+
+function testSingleStructure() {
+ function doAlloc1() {
+ let obj = {}
+ obj.x = 5
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ assert(delete obj.x)
+ return obj.y
+ }
+ noInline(doAlloc1)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc1()
+ }
+}
+noInline(testSingleStructure)
+
+function testInlineSingleStructure() {
+ function doDelete2(o) {
+ assert(delete o.x)
+ }
+
+ function doAlloc2() {
+ let obj = {}
+ obj.x = 5
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ doDelete2(obj)
+ return obj.y
+ }
+ noInline(doAlloc2)
+
+ for (let i = 0; i < 50; ++i) {
+ doDelete2({ i, f: 4 })
+ doDelete2({ i, e: 4 })
+ doDelete2({ i, d: 4 })
+ doDelete2({ i, c: 4 })
+ doDelete2({ i, b: 4 })
+ doDelete2({ i, a: 4 })
+ doAlloc2()
+ }
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc2()
+ }
+}
+noInline(testInlineSingleStructure)
+
+function testExit() {
+ function doDelete3(obj) {
+ assert(delete obj.y)
+ }
+ noInline(doDelete3)
+
+ for (let i = 0; i < 100000; ++i) {
+ doDelete3({ i, a: 4 })
+ }
+
+ for (let i = 0; i < 50; ++i) {
+ doDelete3({ i, f: 4 })
+ doDelete3({ i, e: 4 })
+ doDelete3({ i, d: 4 })
+ doDelete3({ i, c: 4 })
+ doDelete3({ i, b: 4 })
+ doDelete3({ i, a: 4 })
+ }
+
+ for (let i = 0; i < 1000000; ++i) {
+ doDelete3({ i, a: 4 })
+ }
+}
+noInline(testExit)
+
+function testSingleStructureMiss() {
+ function doAlloc4() {
+ let obj = {}
+ obj.x = 5
+ obj.y = 7
+ obj.y = blackbox(obj.y)
+ assert(delete obj.z)
+ return obj.y
+ }
+ noInline(doAlloc4)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc4()
+ }
+}
+noInline(testSingleStructureMiss)
+
+function testSingleStructureMissStrict() {
+ "use strict"
+
+ function doAlloc5() {
+ let obj = {}
+ obj.y = 5
+ Object.defineProperty(obj, "x", {
+ configurable: false,
+ value: 41
+ })
+ obj.y = blackbox(obj.y)
+ assert_throws(() => delete obj.x)
+ return obj.y
+ }
+ noInline(doAlloc5)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc5()
+ }
+}
+noInline(testSingleStructureMissStrict)
+
+function testSingleStructureMissNonConfigurable() {
+ function doAlloc6() {
+ let obj = {}
+ obj.y = 5
+ Object.defineProperty(obj, "x", {
+ configurable: false,
+ value: 41
+ })
+ obj.y = blackbox(obj.y)
+ assert(!(delete obj.x))
+ return obj.y
+ }
+ noInline(doAlloc6)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc6()
+ }
+}
+noInline(testSingleStructureMissNonConfigurable)
+
+function testSingleStructureEmpty() {
+ function doAlloc7() {
+ let obj = { a: 1, b: 2}
+ obj[1] = 5
+ delete obj[1]
+ blackbox(() => obj.x = 5)()
+ assert(Object.keys(obj).length == 3)
+ assert(delete obj.a)
+ assert(delete obj.b)
+ assert(delete obj.x)
+ assert(Object.keys(obj).length == 0)
+ obj.x = 7
+ return obj
+ }
+ noInline(doAlloc7)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doAlloc7()
+ }
+}
+noInline(testSingleStructureEmpty)
+
+function testPolymorphic() {
+ function doDelete8(obj) {
+ assert(delete obj.y)
+ return obj
+ }
+ noInline(doDelete8)
+
+ for (let i = 0; i < 5; ++i) {
+ doDelete8({ i, a: 4 })
+ }
+
+ for (let i = 0; i < 100000; ++i) {
+ doDelete8({ i, f: 4 })
+ assert(doDelete8({ i, e: 4, y: 10 }).y === undefined)
+ doDelete8({ i, d: 4 })
+ doDelete8({ i, c: 4 })
+ doDelete8({ i, b: 4 })
+ assert(doDelete8({ i, a: 4, y: 10 }).y === undefined)
+ }
+
+ for (let i = 0; i < 1000000; ++i) {
+ doDelete8({ i, a: 4 })
+ }
+}
+noInline(testPolymorphic)
+
+function testPolyvariant() {
+ function doDelete9(obj) {
+ assert(delete obj.y)
+ return obj
+ }
+
+ function polyvariant(obj) {
+ return doDelete9(obj)
+ }
+ noInline(polyvariant)
+
+ for (let i = 0; i < 5; ++i) {
+ polyvariant({ i, a: 4 })
+ }
+
+ for (let i = 0; i < 100000; ++i) {
+ doDelete9({ i, f: 4 })
+ assert(doDelete9({ i, e: 4, y: 10 }).y === undefined)
+ doDelete9({ i, d: 4 })
+ doDelete9({ i, c: 4 })
+ doDelete9({ i, b: 4 })
+ assert(doDelete9({ i, a: 4, y: 10 }).y === undefined)
+ }
+
+ for (let i = 0; i < 1000000; ++i) {
+ polyvariant({ i, a: 4 })
+ }
+}
+noInline(testPolyvariant)
+
+function testConstantFolding() {
+ function doDelete10(obj) {
+ if ($vm.dfgTrue()) {
+ obj = { i: 0, a: 5, y: 20 }
+ }
+ assert(delete obj.y)
+ assert((delete obj.z) + 5 == 6)
+ return obj
+ }
+ noInline(doDelete10)
+
+ for (let i = 0; i < 1000000; ++i) {
+ assert(doDelete10({ i, a: 4, y: 10 }).y === undefined)
+ doDelete10({ i, f: 4 })
+ assert(doDelete10({ i, e: 4, y: 10 }).y === undefined)
+ doDelete10({ i, d: 4 })
+ doDelete10({ i, c: 4 })
+ doDelete10({ i, b: 4 })
+ }
+}
+noInline(testConstantFolding)
+
+function testObjectSinking() {
+ function doAlloc11(i) {
+ let obj = {}
+ obj.x = 1
+ obj.y = 2
+ if (i == 0)
+ obj.a = 3
+ else if (i == 1)
+ obj.b = 3
+ else if (i == 3)
+ obj.c = 3
+ delete obj.x
+ assert((delete obj.x) + 5 == 6)
+ return obj.y
+
+ }
+ noInline(doAlloc11)
+
+ for (let i = 0; i < 1000000; ++i) {
+ assert(doAlloc11(i % 3) == 2)
+ }
+ assert(doAlloc11(4) == 2)
+}
+noInline(testObjectSinking)
+
+function testProxy() {
+ function doAlloc12() {
+ let obj = {}
+ obj.x = 1
+ obj.count = 0
+
+ const handler = {
+ deleteProperty(target, prop) {
+ assert(prop == "z")
+ delete target.z
+ ++obj.count
+ }
+ }
+
+ return new Proxy(obj, handler)
+
+ }
+ noInline(doAlloc12)
+
+ function doDelete12(obj) {
+ delete obj.z
+ }
+ noInline(doDelete12)
+
+ let foo = doAlloc12()
+ for (let i = 0; i < 1000000; ++i) {
+ doDelete12(foo)
+ }
+ assert(foo.count = 1000000)
+}
+noInline(testProxy)
+
+function testTypedArray() {
+ function doDelete12(obj) {
+ obj.constructor = null
+ assert(delete obj.constructor)
+ }
+ noInline(doDelete12)
+
+ for (let i = 0; i < 1000000; ++i) {
+ doDelete12(new Uint8Array())
+ }
+
+ let foo = new Uint8Array(12);
+ transferArrayBuffer(foo.buffer)
+ doDelete12(foo)
+ assert_throws(() => delete foo[0])
+}
+noInline(testTypedArray)
+
+function testMissMixed() {
+ function doDelete13(obj) {
+ return delete obj.x
+ }
+ noInline(doDelete13)
+
+ for (let i = 0; i < 1000000; ++i) {
+ assert(doDelete13({ y: 4 }))
+ let foo = {}
+ Object.defineProperty(foo, "x", {
+ configurable: false,
+ value: 41
+ })
+ assert(!doDelete13(foo))
+ }
+}
+noInline(testMissMixed)
+
+function testMissNonMixed() {
+ function doDelete14(obj) {
+ return delete obj.x
+ }
+ noInline(doDelete14)
+
+ for (let i = 0; i < 1000000; ++i) {
+ let foo = {}
+ Object.defineProperty(foo, "x", {
+ configurable: false,
+ value: 41
+ })
+ assert(!doDelete14(foo))
+
+ foo = { y: 4 }
+ Object.defineProperty(foo, "x", {
+ configurable: false,
+ value: 40
+ })
+ assert(!doDelete14(foo))
+ }
+}
+noInline(testMissNonMixed)
+
+function testByVal() {
+ function doDelete15(obj) {
+ return delete obj["x"]
+ }
+ noInline(doDelete15)
+
+ for (let i = 0; i < 10000; ++i) {
+ assert(doDelete15({ y: 4 }))
+ let foo = {}
+ Object.defineProperty(foo, "x", {
+ configurable: false,
+ value: 41
+ })
+ assert(!doDelete15(foo))
+
+ foo = { x: 4 }
+ assert(doDelete15(foo))
+ assert(foo.x == undefined)
+ gc()
+ }
+}
+noInline(testByVal)
+
+testByVal()
+testMissMixed()
+testMissNonMixed()
+testTypedArray()
+testProxy()
+testSingleStructure()
+testInlineSingleStructure()
+testExit()
+testSingleStructureMiss()
+testSingleStructureMissStrict()
+testSingleStructureMissNonConfigurable()
+testSingleStructureEmpty()
+testPolymorphic()
+testPolyvariant()
+testConstantFolding()
+testObjectSinking()
Modified: trunk/Source/_javascript_Core/CMakeLists.txt (259582 => 259583)
--- trunk/Source/_javascript_Core/CMakeLists.txt 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/CMakeLists.txt 2020-04-06 18:48:04 UTC (rev 259583)
@@ -501,6 +501,8 @@
bytecode/CodeType.h
bytecode/DFGExitProfile.h
bytecode/DataFormat.h
+ bytecode/DeleteByIdVariant.h
+ bytecode/DeleteByStatus.h
bytecode/DirectEvalCodeCache.h
bytecode/ExecutableInfo.h
bytecode/ExecutableToCodeBlockEdge.h
Modified: trunk/Source/_javascript_Core/ChangeLog (259582 => 259583)
--- trunk/Source/_javascript_Core/ChangeLog 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1,3 +1,134 @@
+2020-04-06 Justin Michaud <[email protected]>
+
+ Allow deleteById to be cached in the DFG
+ https://bugs.webkit.org/show_bug.cgi?id=208664
+
+ Reviewed by Saam Barati.
+
+ When we see that the deleteById inline cache only saw one structure, we inline it into the DFG. This involves
+ creating a new node, FilterDeleteByStatus, and then turning these DeleteById nodes into a FilterDeleteByStatus,
+ CheckStructure, PutByOffset, then PutStructure (or just a CheckStructure in the case of a miss). The logic for
+ pessimising this optimization is the same as for PutById, giving inlined functions the opportunity to use only
+ the DFG profiling information, while everything else uses the DFG+Baseline information.
+
+ This also adds a MultiDeleteByOffset node, for the case when there are multiple structures seen by the delete. If
+ all of the cases are the same kind of miss, then we only emit a CheckStructure and constant.
+
+ Finally, if we see a delete by val with a single identifier, we inline that too.
+
+ This patch removes a dead code path from deleteProperty that checks if we need to nuke the object's butterfly.
+ This also fixes a bug where we were checking the neutering status of typed arrays for named properties when we should
+ only check for indexed properties. The behavior of this now matches for all tiers including when cached.
+
+ The benchmark shows a 2x improvement on polyvariant-delete-property, and a 50% improvement on delete-property-allocation-sinking.
+
+ * CMakeLists.txt:
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * Sources.txt:
+ * bytecode/AccessCase.cpp:
+ (JSC::AccessCase::createDelete):
+ (JSC::AccessCase::generateImpl):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::getICStatusMap):
+ * bytecode/DeleteByIdVariant.cpp: Added.
+ (JSC::DeleteByIdVariant::DeleteByIdVariant):
+ (JSC::DeleteByIdVariant::~DeleteByIdVariant):
+ (JSC::DeleteByIdVariant::operator=):
+ (JSC::DeleteByIdVariant::attemptToMerge):
+ (JSC::DeleteByIdVariant::writesStructures const):
+ (JSC::DeleteByIdVariant::visitAggregate):
+ (JSC::DeleteByIdVariant::markIfCheap):
+ (JSC::DeleteByIdVariant::dump const):
+ (JSC::DeleteByIdVariant::finalize):
+ (JSC::DeleteByIdVariant::dumpInContext const):
+ * bytecode/DeleteByIdVariant.h: Added.
+ (JSC::DeleteByIdVariant::oldStructure const):
+ (JSC::DeleteByIdVariant::newStructure const):
+ (JSC::DeleteByIdVariant::result const):
+ (JSC::DeleteByIdVariant::offset const):
+ (JSC::DeleteByIdVariant::isPropertyUnset const):
+ (JSC::DeleteByIdVariant::identifier const):
+ (JSC::DeleteByIdVariant::overlaps):
+ * bytecode/DeleteByStatus.cpp: Added.
+ (JSC::DeleteByStatus::appendVariant):
+ (JSC::DeleteByStatus::computeForBaseline):
+ (JSC::DeleteByStatus::DeleteByStatus):
+ (JSC::DeleteByStatus::computeForStubInfoWithoutExitSiteFeedback):
+ (JSC::DeleteByStatus::computeFor):
+ (JSC::DeleteByStatus::slowVersion const):
+ (JSC::DeleteByStatus::merge):
+ (JSC::DeleteByStatus::filter):
+ (JSC::DeleteByStatus::singleIdentifier const):
+ (JSC::DeleteByStatus::visitAggregate):
+ (JSC::DeleteByStatus::markIfCheap):
+ (JSC::DeleteByStatus::finalize):
+ (JSC::DeleteByStatus::dump const):
+ * bytecode/DeleteByStatus.h: Added.
+ * bytecode/ICStatusMap.h:
+ * bytecode/RecordedStatuses.cpp:
+ (JSC::RecordedStatuses::operator=):
+ (JSC::RecordedStatuses::addDeleteByStatus):
+ (JSC::RecordedStatuses::visitAggregate):
+ (JSC::RecordedStatuses::markIfCheap):
+ * bytecode/RecordedStatuses.h:
+ (JSC::RecordedStatuses::forEachVector):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::filterICStatus):
+ * dfg/DFGArgumentsEliminationPhase.cpp:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleDeleteById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGClobbersExitState.cpp:
+ (JSC::DFG::clobbersExitState):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ (JSC::DFG::ConstantFoldingPhase::emitDeleteByOffset):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ * dfg/DFGMayExit.cpp:
+ * dfg/DFGNode.cpp:
+ (JSC::DFG::MultiDeleteByOffsetData::writesStructures const):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasMultiDeleteByOffsetData):
+ (JSC::DFG::Node::multiDeleteByOffsetData):
+ (JSC::DFG::Node::hasDeleteByStatus):
+ (JSC::DFG::Node::deleteByStatus):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGObjectAllocationSinkingPhase.cpp:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
+ * dfg/DFGValidate.cpp:
+ * dfg/DFGVarargsForwardingPhase.cpp:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileMultiDeleteByOffset):
+ * runtime/JSGenericTypedArrayViewInlines.h:
+ (JSC::JSGenericTypedArrayView<Adaptor>::deleteProperty):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::deleteProperty):
+ * runtime/Structure.h:
+ * runtime/StructureInlines.h:
+ (JSC::Structure::mayHaveIndexingHeader const): Deleted.
+ (JSC::Structure::canCacheDeleteIC const): Deleted.
+
2020-04-06 Saam Barati <[email protected]>
Implement 1GB of executable memory on arm64
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (259582 => 259583)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1178,7 +1178,9 @@
70ECA6061AFDBEA200449739 /* JSTemplateObjectDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6011AFDBEA200449739 /* JSTemplateObjectDescriptor.h */; settings = {ATTRIBUTES = (Private, ); }; };
70ECA6091AFDBEA200449739 /* TemplateObjectDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6041AFDBEA200449739 /* TemplateObjectDescriptor.h */; settings = {ATTRIBUTES = (Private, ); }; };
72AAF7CE1D0D31B3005E60BE /* JSCustomGetterSetterFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 72AAF7CC1D0D318B005E60BE /* JSCustomGetterSetterFunction.h */; };
+ 7311FA32240DB1D3003D48DB /* DeleteByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 7311FA31240DB1D3003D48DB /* DeleteByIdVariant.h */; };
734B655523F5C10400A069D1 /* DeletePropertySlot.h in Headers */ = {isa = PBXBuildFile; fileRef = 734B655423F4A33100A069D1 /* DeletePropertySlot.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 73AD062923FF662600F53593 /* DeleteByStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 73AD062823FF662600F53593 /* DeleteByStatus.h */; };
73E3799422E0EF6500933565 /* B3ReduceLoopStrength.h in Headers */ = {isa = PBXBuildFile; fileRef = 73E3799322E0EF4F00933565 /* B3ReduceLoopStrength.h */; };
7593C898BE714A64BE93A6E7 /* WasmContextInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = A27958D7FA1142B0AC9E364D /* WasmContextInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
790081391E95A8EC0052D7CD /* WasmModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 790081371E95A8EC0052D7CD /* WasmModule.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3974,7 +3976,11 @@
70ECA6041AFDBEA200449739 /* TemplateObjectDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateObjectDescriptor.h; sourceTree = "<group>"; };
72AAF7CB1D0D318B005E60BE /* JSCustomGetterSetterFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCustomGetterSetterFunction.cpp; sourceTree = "<group>"; };
72AAF7CC1D0D318B005E60BE /* JSCustomGetterSetterFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCustomGetterSetterFunction.h; sourceTree = "<group>"; };
+ 7311FA31240DB1D3003D48DB /* DeleteByIdVariant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeleteByIdVariant.h; sourceTree = "<group>"; };
+ 7311FA33240DB249003D48DB /* DeleteByIdVariant.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DeleteByIdVariant.cpp; sourceTree = "<group>"; };
+ 73190D962400934900F891C9 /* DeleteByStatus.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DeleteByStatus.cpp; sourceTree = "<group>"; };
734B655423F4A33100A069D1 /* DeletePropertySlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeletePropertySlot.h; sourceTree = "<group>"; };
+ 73AD062823FF662600F53593 /* DeleteByStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeleteByStatus.h; sourceTree = "<group>"; };
73E3799322E0EF4F00933565 /* B3ReduceLoopStrength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3ReduceLoopStrength.h; path = b3/B3ReduceLoopStrength.h; sourceTree = "<group>"; };
73E3799522E0EF9100933565 /* B3ReduceLoopStrength.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3ReduceLoopStrength.cpp; path = b3/B3ReduceLoopStrength.cpp; sourceTree = "<group>"; };
77B25CB2C3094A92A38E1DB3 /* JSModuleLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleLoader.h; sourceTree = "<group>"; };
@@ -8243,6 +8249,10 @@
0FC712DD17CD8778008CC93C /* DeferredCompilationCallback.h */,
FE5068661AE25E280009DAB7 /* DeferredSourceDump.cpp */,
FE5068641AE246390009DAB7 /* DeferredSourceDump.h */,
+ 7311FA33240DB249003D48DB /* DeleteByIdVariant.cpp */,
+ 7311FA31240DB1D3003D48DB /* DeleteByIdVariant.h */,
+ 73190D962400934900F891C9 /* DeleteByStatus.cpp */,
+ 73AD062823FF662600F53593 /* DeleteByStatus.h */,
0FBC0AE41496C7C100D4FBDD /* DFGExitProfile.cpp */,
0FBC0AE51496C7C100D4FBDD /* DFGExitProfile.h */,
0F2EBBAA1DEDF94E00990369 /* DirectEvalCodeCache.cpp */,
@@ -9216,6 +9226,8 @@
FE5068651AE246390009DAB7 /* DeferredSourceDump.h in Headers */,
473DA4A4764C45FE871B0485 /* DefinePropertyAttributes.h in Headers */,
0FBB73BB1DEF8645002C009E /* DeleteAllCodeEffort.h in Headers */,
+ 7311FA32240DB1D3003D48DB /* DeleteByIdVariant.h in Headers */,
+ 73AD062923FF662600F53593 /* DeleteByStatus.h in Headers */,
734B655523F5C10400A069D1 /* DeletePropertySlot.h in Headers */,
0F96303C1D4192CD005609D9 /* DestructionMode.h in Headers */,
A77A423E17A0BBFD00A8DB81 /* DFGAbstractHeap.h in Headers */,
Modified: trunk/Source/_javascript_Core/Sources.txt (259582 => 259583)
--- trunk/Source/_javascript_Core/Sources.txt 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/Sources.txt 2020-04-06 18:48:04 UTC (rev 259583)
@@ -220,6 +220,8 @@
bytecode/DataFormat.cpp
bytecode/DeferredCompilationCallback.cpp
bytecode/DeferredSourceDump.cpp
+bytecode/DeleteByStatus.cpp
+bytecode/DeleteByIdVariant.cpp
bytecode/DirectEvalCodeCache.cpp
bytecode/EvalCodeBlock.cpp
bytecode/ExecutableToCodeBlockEdge.cpp
Modified: trunk/Source/_javascript_Core/bytecode/AccessCase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/bytecode/AccessCase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/bytecode/AccessCase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -129,10 +129,7 @@
VM& vm, JSCell* owner, CacheableIdentifier identifier, PropertyOffset offset, Structure* oldStructure, Structure* newStructure)
{
RELEASE_ASSERT(oldStructure == newStructure->previousID());
- // We do not cache this case so that we do not need to check the jscell, e.g. TypedArray cells require a check for neutering status.
- // See the Delete code below.
- if (!newStructure->canCacheDeleteIC())
- return nullptr;
+ ASSERT(!newStructure->outOfLineCapacity() || oldStructure->outOfLineCapacity());
return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Delete, identifier, offset, newStructure, { }, { }));
}
@@ -1950,18 +1947,9 @@
ScratchRegisterAllocator::PreservedState preservedState =
allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace);
- bool hasIndexingHeader = newStructure()->mayHaveIndexingHeader();
- // We do not cache this case yet so that we do not need to check the jscell.
- // See Structure::hasIndexingHeader and JSObject::deleteProperty.
- ASSERT(newStructure()->canCacheDeleteIC());
- // Clear the butterfly if we have no properties, since our put code expects this.
- bool shouldNukeStructureAndClearButterfly = !newStructure()->outOfLineCapacity() && structure()->outOfLineCapacity() && !hasIndexingHeader;
-
jit.moveValue(JSValue(), valueRegs);
- if (shouldNukeStructureAndClearButterfly) {
- jit.nukeStructureAndStoreButterfly(vm, valueRegs.payloadGPR(), baseGPR);
- } else if (isInlineOffset(m_offset)) {
+ if (isInlineOffset(m_offset)) {
jit.storeValue(
valueRegs,
CCallHelpers::Address(
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1500,6 +1500,8 @@
result.add(pair.first, ICStatus()).iterator->value.putStatus = pair.second.get();
for (auto& pair : dfgCommon->recordedStatuses.ins)
result.add(pair.first, ICStatus()).iterator->value.inStatus = pair.second.get();
+ for (auto& pair : dfgCommon->recordedStatuses.deletes)
+ result.add(pair.first, ICStatus()).iterator->value.deleteStatus = pair.second.get();
}
#endif
}
Added: trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.cpp (0 => 259583)
--- trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.cpp (rev 0)
+++ trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DeleteByIdVariant.h"
+
+#include "CacheableIdentifierInlines.h"
+#include "JSCInlines.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+
+DeleteByIdVariant::DeleteByIdVariant(CacheableIdentifier identifier, bool result,
+ Structure* oldStructure, Structure* newStructure, PropertyOffset offset)
+ : m_result(result)
+ , m_oldStructure(oldStructure)
+ , m_newStructure(newStructure)
+ , m_offset(offset)
+ , m_identifier(WTFMove(identifier))
+{
+ ASSERT(oldStructure);
+ if (m_offset == invalidOffset)
+ ASSERT(!newStructure);
+ else
+ ASSERT(newStructure);
+}
+
+DeleteByIdVariant::~DeleteByIdVariant() { }
+
+DeleteByIdVariant::DeleteByIdVariant(const DeleteByIdVariant& other)
+{
+ *this = other;
+}
+
+DeleteByIdVariant& DeleteByIdVariant::operator=(const DeleteByIdVariant& other)
+{
+ m_identifier = other.m_identifier;
+ m_result = other.m_result;
+ m_oldStructure = other.m_oldStructure;
+ m_newStructure = other.m_newStructure;
+ m_offset = other.m_offset;
+ return *this;
+}
+
+bool DeleteByIdVariant::attemptToMerge(const DeleteByIdVariant& other)
+{
+ if (!!m_identifier != !!other.m_identifier)
+ return false;
+
+ if (m_result != other.m_result)
+ return false;
+
+ if (m_identifier && (m_identifier != other.m_identifier))
+ return false;
+
+ if (m_offset != other.m_offset)
+ return false;
+
+ if (m_oldStructure != other.m_oldStructure)
+ return false;
+ ASSERT(m_newStructure == other.m_newStructure);
+
+ return true;
+}
+
+bool DeleteByIdVariant::writesStructures() const
+{
+ return !!newStructure();
+}
+
+void DeleteByIdVariant::visitAggregate(SlotVisitor& visitor)
+{
+ m_identifier.visitAggregate(visitor);
+}
+
+void DeleteByIdVariant::markIfCheap(SlotVisitor& visitor)
+{
+ if (m_oldStructure)
+ m_oldStructure->markIfCheap(visitor);
+ if (m_newStructure)
+ m_newStructure->markIfCheap(visitor);
+}
+
+void DeleteByIdVariant::dump(PrintStream& out) const
+{
+ dumpInContext(out, 0);
+}
+
+bool DeleteByIdVariant::finalize(VM& vm)
+{
+ if (!vm.heap.isMarked(m_oldStructure))
+ return false;
+ if (m_newStructure && !vm.heap.isMarked(m_newStructure))
+ return false;
+ return true;
+}
+
+void DeleteByIdVariant::dumpInContext(PrintStream& out, DumpContext*) const
+{
+ out.print("<");
+ out.print("id='", m_identifier, "', result=", m_result);
+ if (m_oldStructure)
+ out.print(", ", *m_oldStructure);
+ if (m_newStructure)
+ out.print(" -> ", *m_newStructure);
+ out.print(", offset = ", offset());
+ out.print(">");
+}
+
+} // namespace JSC
+
Added: trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.h (0 => 259583)
--- trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.h (rev 0)
+++ trunk/Source/_javascript_Core/bytecode/DeleteByIdVariant.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CacheableIdentifier.h"
+#include "CallLinkStatus.h"
+#include "ObjectPropertyConditionSet.h"
+#include "PropertyOffset.h"
+#include "StructureSet.h"
+#include <wtf/Box.h>
+
+namespace JSC {
+
+class CallLinkStatus;
+class DeleteByStatus;
+struct DumpContext;
+
+class DeleteByIdVariant {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ DeleteByIdVariant(
+ CacheableIdentifier, bool result,
+ Structure* oldStrucutre, Structure* newStructure, PropertyOffset);
+
+ ~DeleteByIdVariant();
+
+ DeleteByIdVariant(const DeleteByIdVariant&);
+ DeleteByIdVariant& operator=(const DeleteByIdVariant&);
+
+ Structure* oldStructure() const { return m_oldStructure; }
+ Structure* newStructure() const { return m_newStructure; }
+ bool result() const { return m_result; }
+ bool writesStructures() const;
+
+ PropertyOffset offset() const { return m_offset; }
+
+ bool isPropertyUnset() const { return offset() == invalidOffset; }
+
+ bool attemptToMerge(const DeleteByIdVariant& other);
+
+ void visitAggregate(SlotVisitor&);
+ void markIfCheap(SlotVisitor&);
+ bool finalize(VM&);
+
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+
+ CacheableIdentifier identifier() const { return m_identifier; }
+
+ bool overlaps(const DeleteByIdVariant& other)
+ {
+ if (!!m_identifier != !!other.m_identifier)
+ return true;
+ if (m_identifier) {
+ if (m_identifier != other.m_identifier)
+ return false;
+ }
+ return m_oldStructure == other.m_oldStructure;
+ }
+
+private:
+ friend class DeleteByStatus;
+
+ bool m_result;
+ Structure* m_oldStructure;
+ Structure* m_newStructure;
+ PropertyOffset m_offset;
+ CacheableIdentifier m_identifier;
+};
+
+} // namespace JSC
Added: trunk/Source/_javascript_Core/bytecode/DeleteByStatus.cpp (0 => 259583)
--- trunk/Source/_javascript_Core/bytecode/DeleteByStatus.cpp (rev 0)
+++ trunk/Source/_javascript_Core/bytecode/DeleteByStatus.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DeleteByStatus.h"
+
+#include "BytecodeStructs.h"
+#include "CacheableIdentifierInlines.h"
+#include "CodeBlock.h"
+#include "ICStatusUtils.h"
+#include "InterpreterInlines.h"
+#include "JSCInlines.h"
+#include "JSScope.h"
+#include "LLIntData.h"
+#include "LowLevelInterpreter.h"
+#include "ModuleNamespaceAccessCase.h"
+#include "PolymorphicAccess.h"
+#include "StructureStubInfo.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+namespace DOMJIT {
+class GetterSetter;
+}
+
+bool DeleteByStatus::appendVariant(const DeleteByIdVariant& variant)
+{
+ return appendICStatusVariant(m_variants, variant);
+}
+
+DeleteByStatus DeleteByStatus::computeForBaseline(CodeBlock* baselineBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit)
+{
+ ConcurrentJSLocker locker(baselineBlock->m_lock);
+
+ DeleteByStatus result;
+
+#if ENABLE(DFG_JIT)
+ result = computeForStubInfoWithoutExitSiteFeedback(
+ locker, baselineBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo);
+
+ if (didExit)
+ return result.slowVersion();
+#else
+ UNUSED_PARAM(map);
+ UNUSED_PARAM(didExit);
+ UNUSED_PARAM(bytecodeIndex);
+#endif
+
+ return result;
+}
+
+#if ENABLE(JIT)
+DeleteByStatus::DeleteByStatus(StubInfoSummary summary, StructureStubInfo& stubInfo)
+{
+ switch (summary) {
+ case StubInfoSummary::NoInformation:
+ m_state = NoInformation;
+ return;
+ case StubInfoSummary::Simple:
+ case StubInfoSummary::MakesCalls:
+ case StubInfoSummary::TakesSlowPathAndMakesCalls:
+ RELEASE_ASSERT_NOT_REACHED();
+ return;
+ case StubInfoSummary::TakesSlowPath:
+ m_state = stubInfo.tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath;
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+DeleteByStatus DeleteByStatus::computeForStubInfoWithoutExitSiteFeedback(
+ const ConcurrentJSLocker&, CodeBlock* block, StructureStubInfo* stubInfo)
+{
+ StubInfoSummary summary = StructureStubInfo::summary(block->vm(), stubInfo);
+ if (!isInlineable(summary))
+ return DeleteByStatus(summary, *stubInfo);
+
+ DeleteByStatus result;
+ result.m_state = Simple;
+ switch (stubInfo->cacheType()) {
+ case CacheType::Unset:
+ return DeleteByStatus(NoInformation);
+
+ case CacheType::Stub: {
+ PolymorphicAccess* list = stubInfo->u.stub;
+
+ for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
+ const AccessCase& access = list->at(listIndex);
+ ASSERT(!access.viaProxy());
+
+ Structure* structure = access.structure();
+ ASSERT(structure);
+
+ switch (access.type()) {
+ case AccessCase::DeleteMiss:
+ case AccessCase::DeleteNonConfigurable: {
+ DeleteByIdVariant variant(access.identifier(), access.type() == AccessCase::DeleteMiss ? true : false, structure, nullptr, invalidOffset);
+ if (!result.appendVariant(variant))
+ return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
+ break;
+ }
+ case AccessCase::Delete: {
+ PropertyOffset offset;
+ Structure* newStructure = Structure::removePropertyTransitionFromExistingStructureConcurrently(structure, access.identifier().uid(), offset);
+ if (!newStructure)
+ return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
+ ASSERT_UNUSED(offset, offset == access.offset());
+ DeleteByIdVariant variant(access.identifier(), true, structure, newStructure, access.offset());
+
+ if (!result.appendVariant(variant))
+ return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
+ }
+ }
+
+ return result;
+ }
+
+ default:
+ return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return DeleteByStatus();
+}
+
+DeleteByStatus DeleteByStatus::computeFor(
+ CodeBlock* baselineBlock, ICStatusMap& baselineMap,
+ ICStatusContextStack& contextStack, CodeOrigin codeOrigin)
+{
+ BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex();
+ ExitFlag didExit = hasBadCacheExitSite(baselineBlock, bytecodeIndex);
+
+ for (ICStatusContext* context : contextStack) {
+ ICStatus status = context->get(codeOrigin);
+
+ auto bless = [&] (const DeleteByStatus& result) -> DeleteByStatus {
+ if (!context->isInlined(codeOrigin)) {
+ DeleteByStatus baselineResult = computeForBaseline(
+ baselineBlock, baselineMap, bytecodeIndex, didExit);
+ baselineResult.merge(result);
+ return baselineResult;
+ }
+ if (didExit.isSet(ExitFromInlined))
+ return result.slowVersion();
+ return result;
+ };
+
+ if (status.stubInfo) {
+ DeleteByStatus result;
+ {
+ ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
+ result = computeForStubInfoWithoutExitSiteFeedback(
+ locker, context->optimizedCodeBlock, status.stubInfo);
+ }
+ if (result.isSet())
+ return bless(result);
+ }
+
+ if (status.deleteStatus)
+ return bless(*status.deleteStatus);
+ }
+
+ return computeForBaseline(baselineBlock, baselineMap, bytecodeIndex, didExit);
+}
+
+#endif // ENABLE(JIT)
+
+DeleteByStatus DeleteByStatus::slowVersion() const
+{
+ if (observedSlowPath())
+ return DeleteByStatus(ObservedTakesSlowPath);
+ return DeleteByStatus(LikelyTakesSlowPath);
+}
+
+void DeleteByStatus::merge(const DeleteByStatus& other)
+{
+ if (other.m_state == NoInformation)
+ return;
+
+ auto mergeSlow = [&] () {
+ if (observedSlowPath() || other.observedSlowPath())
+ *this = DeleteByStatus(ObservedTakesSlowPath);
+ else
+ *this = DeleteByStatus(LikelyTakesSlowPath);
+ };
+
+ switch (m_state) {
+ case NoInformation:
+ *this = other;
+ return;
+
+ case Simple:
+ if (m_state != other.m_state)
+ return mergeSlow();
+
+ for (auto& otherVariant : other.m_variants) {
+ if (!appendVariant(otherVariant))
+ return mergeSlow();
+ }
+ return;
+
+ case LikelyTakesSlowPath:
+ case ObservedTakesSlowPath:
+ return mergeSlow();
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+void DeleteByStatus::filter(const StructureSet& set)
+{
+ if (m_state != Simple)
+ return;
+ m_variants.removeAllMatching(
+ [&] (auto& variant) -> bool {
+ return !set.contains(variant.oldStructure());
+ });
+ if (m_variants.isEmpty())
+ m_state = NoInformation;
+}
+
+CacheableIdentifier DeleteByStatus::singleIdentifier() const
+{
+ if (m_variants.isEmpty())
+ return nullptr;
+
+ CacheableIdentifier result = m_variants.first().identifier();
+ if (!result)
+ return nullptr;
+ for (size_t i = 1; i < m_variants.size(); ++i) {
+ CacheableIdentifier identifier = m_variants[i].identifier();
+ if (!identifier)
+ return nullptr;
+ if (identifier != result)
+ return nullptr;
+ }
+ return result;
+}
+
+void DeleteByStatus::visitAggregate(SlotVisitor& visitor)
+{
+ for (DeleteByIdVariant& variant : m_variants)
+ variant.visitAggregate(visitor);
+}
+
+void DeleteByStatus::markIfCheap(SlotVisitor& visitor)
+{
+ for (DeleteByIdVariant& variant : m_variants)
+ variant.markIfCheap(visitor);
+}
+
+bool DeleteByStatus::finalize(VM& vm)
+{
+ for (auto& variant : m_variants) {
+ if (!variant.finalize(vm))
+ return false;
+ }
+ return true;
+}
+
+void DeleteByStatus::dump(PrintStream& out) const
+{
+ out.print("(");
+ switch (m_state) {
+ case NoInformation:
+ out.print("NoInformation");
+ break;
+ case Simple:
+ out.print("Simple");
+ break;
+ case LikelyTakesSlowPath:
+ out.print("LikelyTakesSlowPath");
+ break;
+ case ObservedTakesSlowPath:
+ out.print("ObservedTakesSlowPath");
+ break;
+ }
+ out.print(", ", listDump(m_variants), ")");
+}
+
+} // namespace JSC
Added: trunk/Source/_javascript_Core/bytecode/DeleteByStatus.h (0 => 259583)
--- trunk/Source/_javascript_Core/bytecode/DeleteByStatus.h (rev 0)
+++ trunk/Source/_javascript_Core/bytecode/DeleteByStatus.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CacheableIdentifier.h"
+#include "CallLinkStatus.h"
+#include "CodeOrigin.h"
+#include "ConcurrentJSLock.h"
+#include "DeleteByIdVariant.h"
+#include "ExitFlag.h"
+#include "ICStatusMap.h"
+#include "StubInfoSummary.h"
+
+namespace JSC {
+
+class AccessCase;
+class CodeBlock;
+class StructureStubInfo;
+
+class DeleteByStatus final {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ enum State : uint8_t {
+ // It's uncached so we have no information.
+ NoInformation,
+ // It's cached for a simple access.
+ Simple,
+ // It will likely take the slow path.
+ LikelyTakesSlowPath,
+ // It has been seen to take the slow path.
+ ObservedTakesSlowPath,
+ };
+
+ DeleteByStatus()
+ : m_state(NoInformation)
+ {
+ }
+
+ explicit DeleteByStatus(State state)
+ : m_state(state)
+ {
+ ASSERT(state != Simple);
+ }
+
+ static DeleteByStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& dfgContextStack, CodeOrigin);
+
+ State state() const { return m_state; }
+
+ bool isSet() const { return m_state != NoInformation; }
+ bool operator!() const { return !isSet(); }
+ bool observedSlowPath() const { return m_state == ObservedTakesSlowPath; }
+ bool isSimple() const { return m_state == Simple; }
+ const Vector<DeleteByIdVariant, 1>& variants() { return m_variants; }
+ CacheableIdentifier singleIdentifier() const;
+
+ DeleteByStatus slowVersion() const;
+
+ // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
+ void filter(const StructureSet&);
+
+ void visitAggregate(SlotVisitor&);
+ void markIfCheap(SlotVisitor&);
+ bool finalize(VM&);
+
+ bool appendVariant(const DeleteByIdVariant&);
+
+ void dump(PrintStream&) const;
+
+private:
+ explicit DeleteByStatus(StubInfoSummary, StructureStubInfo&);
+ void merge(const DeleteByStatus&);
+
+ static DeleteByStatus computeForBaseline(CodeBlock*, ICStatusMap&, BytecodeIndex, ExitFlag);
+#if ENABLE(JIT)
+ static DeleteByStatus computeForStubInfoWithoutExitSiteFeedback(
+ const ConcurrentJSLocker&, CodeBlock* profiledBlock, StructureStubInfo*);
+#endif
+
+ Vector<DeleteByIdVariant, 1> m_variants;
+ State m_state;
+};
+
+} // namespace JSC
Modified: trunk/Source/_javascript_Core/bytecode/ICStatusMap.h (259582 => 259583)
--- trunk/Source/_javascript_Core/bytecode/ICStatusMap.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/bytecode/ICStatusMap.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -37,6 +37,7 @@
class GetByStatus;
class InByIdStatus;
class PutByIdStatus;
+class DeleteByStatus;
class StructureStubInfo;
struct ByValInfo;
@@ -48,6 +49,7 @@
GetByStatus* getStatus { nullptr };
InByIdStatus* inStatus { nullptr };
PutByIdStatus* putStatus { nullptr };
+ DeleteByStatus* deleteStatus { nullptr };
};
typedef HashMap<CodeOrigin, ICStatus, CodeOriginApproximateHash> ICStatusMap;
Modified: trunk/Source/_javascript_Core/bytecode/RecordedStatuses.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/bytecode/RecordedStatuses.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/bytecode/RecordedStatuses.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -34,6 +34,7 @@
gets = WTFMove(other.gets);
puts = WTFMove(other.puts);
ins = WTFMove(other.ins);
+ deletes = WTFMove(other.deletes);
shrinkToFit();
return *this;
}
@@ -75,10 +76,20 @@
return result;
}
+DeleteByStatus* RecordedStatuses::addDeleteByStatus(const CodeOrigin& codeOrigin, const DeleteByStatus& status)
+{
+ auto statusPtr = makeUnique<DeleteByStatus>(status);
+ DeleteByStatus* result = statusPtr.get();
+ deletes.append(std::make_pair(codeOrigin, WTFMove(statusPtr)));
+ return result;
+}
+
void RecordedStatuses::visitAggregate(SlotVisitor& slotVisitor)
{
for (auto& pair : gets)
pair.second->visitAggregate(slotVisitor);
+ for (auto& pair : deletes)
+ pair.second->visitAggregate(slotVisitor);
}
void RecordedStatuses::markIfCheap(SlotVisitor& slotVisitor)
@@ -89,6 +100,8 @@
pair.second->markIfCheap(slotVisitor);
for (auto& pair : ins)
pair.second->markIfCheap(slotVisitor);
+ for (auto& pair : deletes)
+ pair.second->markIfCheap(slotVisitor);
}
void RecordedStatuses::finalizeWithoutDeleting(VM& vm)
Modified: trunk/Source/_javascript_Core/bytecode/RecordedStatuses.h (259582 => 259583)
--- trunk/Source/_javascript_Core/bytecode/RecordedStatuses.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/bytecode/RecordedStatuses.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -26,6 +26,7 @@
#pragma once
#include "CallLinkStatus.h"
+#include "DeleteByStatus.h"
#include "GetByStatus.h"
#include "InByIdStatus.h"
#include "PutByIdStatus.h"
@@ -47,6 +48,7 @@
GetByStatus* addGetByStatus(const CodeOrigin&, const GetByStatus&);
PutByIdStatus* addPutByIdStatus(const CodeOrigin&, const PutByIdStatus&);
InByIdStatus* addInByIdStatus(const CodeOrigin&, const InByIdStatus&);
+ DeleteByStatus* addDeleteByStatus(const CodeOrigin&, const DeleteByStatus&);
void visitAggregate(SlotVisitor&);
void markIfCheap(SlotVisitor&);
@@ -63,6 +65,7 @@
func(gets);
func(puts);
func(ins);
+ func(deletes);
}
Vector<std::pair<CodeOrigin, std::unique_ptr<CallLinkStatus>>> calls;
@@ -69,6 +72,7 @@
Vector<std::pair<CodeOrigin, std::unique_ptr<GetByStatus>>> gets;
Vector<std::pair<CodeOrigin, std::unique_ptr<PutByIdStatus>>> puts;
Vector<std::pair<CodeOrigin, std::unique_ptr<InByIdStatus>>> ins;
+ Vector<std::pair<CodeOrigin, std::unique_ptr<DeleteByStatus>>> deletes;
};
} // namespace JSC
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -3710,6 +3710,41 @@
m_state.setIsValid(false);
break;
}
+
+ case MultiDeleteByOffset: {
+ RegisteredStructureSet newSet;
+ TransitionVector transitions;
+
+ // Ordinarily you have to be careful with calling setShouldTryConstantFolding()
+ // because of the effect on compile times, but this node is FTL-only.
+ m_state.setShouldTryConstantFolding(true);
+
+ AbstractValue base = forNode(node->child1());
+
+ if (node->multiDeleteByOffsetData().writesStructures())
+ didFoldClobberStructures();
+
+ for (unsigned i = node->multiDeleteByOffsetData().variants.size(); i--;) {
+ const DeleteByIdVariant& variant = node->multiDeleteByOffsetData().variants[i];
+ RegisteredStructureSet thisSet = *m_graph.addStructureSet(variant.oldStructure());
+ thisSet.filter(base);
+ if (thisSet.isEmpty())
+ continue;
+
+ if (variant.newStructure()) {
+ RegisteredStructure newStructure = m_graph.registerStructure(variant.newStructure());
+ transitions.append(
+ Transition(m_graph.registerStructure(variant.oldStructure()), newStructure));
+ newSet.add(newStructure);
+ } else
+ newSet.merge(thisSet);
+ }
+
+ observeTransitions(clobberLimit, transitions);
+ if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction)
+ m_state.setIsValid(false);
+ break;
+ }
case GetExecutable: {
JSValue value = forNode(node->child1()).value();
@@ -4108,6 +4143,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
case ClearCatchLocals:
break;
@@ -4289,6 +4325,13 @@
break;
}
+ case FilterDeleteByStatus: {
+ AbstractValue& value = forNode(node->child1());
+ if (value.m_structure.isFinite())
+ node->deleteByStatus()->filter(value.m_structure.toStructureSet());
+ break;
+ }
+
default:
RELEASE_ASSERT_NOT_REACHED();
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -407,6 +407,7 @@
case FilterPutByIdStatus:
case FilterCallLinkStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
break;
case CheckArrayOrEmpty:
@@ -1266,7 +1267,8 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterCallLinkStatus:
- case FilterInByIdStatus: {
+ case FilterInByIdStatus:
+ case FilterDeleteByStatus: {
if (!isEliminatedAllocation(node->child1().node()))
break;
node->remove(m_graph);
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -48,6 +48,7 @@
#include "DFGClobbersExitState.h"
#include "DFGGraph.h"
#include "DFGJITCode.h"
+#include "DeleteByStatus.h"
#include "FunctionCodeBlock.h"
#include "GetByStatus.h"
#include "GetterSetter.h"
@@ -249,6 +250,9 @@
void handlePutById(
Node* base, CacheableIdentifier, unsigned identifierNumber, Node* value, const PutByIdStatus&,
bool isDirect, unsigned intructionSize);
+
+ void handleDeleteById(
+ VirtualRegister destination, Node* base, CacheableIdentifier, unsigned identifierNumber, DeleteByStatus);
// Either register a watchpoint or emit a check for this condition. Returns false if the
// condition no longer holds, and therefore no reasonable check can be emitted.
@@ -4614,6 +4618,104 @@
getter, numberOfParameters - 1, registerOffset, *variant.callLinkStatus(), prediction);
}
+void ByteCodeParser::handleDeleteById(
+ VirtualRegister destination, Node* base, CacheableIdentifier identifier,
+ unsigned identifierNumber, DeleteByStatus deleteByStatus)
+{
+ if (!deleteByStatus.isSimple() || !deleteByStatus.variants().size() || !Options::useAccessInlining()) {
+ set(destination,
+ addToGraph(DeleteById, OpInfo(identifier), base));
+ return;
+ }
+
+ if (deleteByStatus.variants().size() > 1) {
+ if (!m_graph.m_plan.isFTL()
+ || !Options::usePolymorphicAccessInlining()
+ || deleteByStatus.variants().size() > Options::maxPolymorphicAccessInliningListSize()) {
+ set(destination,
+ addToGraph(DeleteById, OpInfo(identifier), base));
+ return;
+ }
+
+ addToGraph(FilterDeleteByStatus, OpInfo(m_graph.m_plan.recordedStatuses().addDeleteByStatus(currentCodeOrigin(), deleteByStatus)), base);
+
+ bool hasHit = false;
+ bool hasMiss = false;
+ bool hasMissNonconfigurable = false;
+
+ for (const DeleteByIdVariant& variant : deleteByStatus.variants()) {
+ m_graph.registerStructure(variant.oldStructure());
+ if (variant.newStructure()) {
+ m_graph.registerStructure(variant.newStructure());
+ hasHit = true;
+ } else if (variant.result())
+ hasMiss = true;
+ else
+ hasMissNonconfigurable = true;
+ }
+
+ if (!hasHit) {
+ if ((hasMiss && !hasMissNonconfigurable) || (!hasMiss && hasMissNonconfigurable)) {
+ StructureSet baseSet;
+
+ for (const DeleteByIdVariant& variant : deleteByStatus.variants())
+ baseSet.add(variant.oldStructure());
+
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(baseSet)), base);
+ set(destination, jsConstant(jsBoolean(deleteByStatus.variants()[0].result())));
+ return;
+ }
+ }
+
+ MultiDeleteByOffsetData* data = ""
+ data->variants = deleteByStatus.variants();
+ data->identifierNumber = identifierNumber;
+ set(destination,
+ addToGraph(MultiDeleteByOffset, OpInfo(data), base));
+ return;
+ }
+
+ ASSERT(deleteByStatus.variants().size() == 1);
+ DeleteByIdVariant variant = deleteByStatus.variants()[0];
+
+ if (!variant.newStructure()) {
+ addToGraph(FilterDeleteByStatus, OpInfo(m_graph.m_plan.recordedStatuses().addDeleteByStatus(currentCodeOrigin(), deleteByStatus)), base);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
+ set(destination, jsConstant(jsBoolean(variant.result())));
+ return;
+ }
+
+ addToGraph(FilterDeleteByStatus, OpInfo(m_graph.m_plan.recordedStatuses().addDeleteByStatus(currentCodeOrigin(), deleteByStatus)), base);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
+ ASSERT(variant.oldStructure()->transitionWatchpointSetHasBeenInvalidated());
+ ASSERT(variant.newStructure());
+ ASSERT(isValidOffset(variant.offset()));
+
+ Node* propertyStorage;
+ Transition* transition = m_graph.m_transitions.add(
+ m_graph.registerStructure(variant.oldStructure()), m_graph.registerStructure(variant.newStructure()));
+
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = base;
+ else
+ propertyStorage = addToGraph(GetButterfly, base);
+
+ StorageAccessData* data = ""
+ data->offset = variant.offset();
+ data->identifierNumber = identifierNumber;
+
+ addToGraph(
+ PutByOffset,
+ OpInfo(data),
+ propertyStorage,
+ base,
+ jsConstant(JSValue()));
+
+ addToGraph(PutStructure, OpInfo(transition), base);
+ set(destination, jsConstant(jsBoolean(variant.result())));
+ return;
+}
+
void ByteCodeParser::emitPutById(
Node* base, CacheableIdentifier identifier, Node* value, const PutByIdStatus& putByIdStatus, bool isDirect)
{
@@ -5947,8 +6049,13 @@
auto bytecode = currentInstruction->as<OpDelById>();
Node* base = get(bytecode.m_base);
unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[bytecode.m_property];
+ DeleteByStatus deleteByStatus = DeleteByStatus::computeFor(
+ m_inlineStackTop->m_profiledBlock,
+ m_inlineStackTop->m_baselineMap, m_icContextStack,
+ currentCodeOrigin());
UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
- set(bytecode.m_dst, addToGraph(DeleteById, OpInfo(CacheableIdentifier::createFromIdentifierOwnedByCodeBlock(m_inlineStackTop->m_profiledBlock, uid)), base));
+ auto identifier = CacheableIdentifier::createFromIdentifierOwnedByCodeBlock(m_inlineStackTop->m_profiledBlock, uid);
+ handleDeleteById(bytecode.m_dst, base, identifier, identifierNumber, deleteByStatus);
NEXT_OPCODE(op_del_by_id);
}
@@ -5955,8 +6062,36 @@
case op_del_by_val: {
auto bytecode = currentInstruction->as<OpDelByVal>();
Node* base = get(bytecode.m_base);
- Node* key = get(bytecode.m_property);
- set(bytecode.m_dst, addToGraph(DeleteByVal, base, key));
+ Node* property = get(bytecode.m_property);
+ bool shouldCompileAsDeleteById = false;
+ DeleteByStatus deleteByStatus = DeleteByStatus::computeFor(
+ m_inlineStackTop->m_profiledBlock,
+ m_inlineStackTop->m_baselineMap, m_icContextStack,
+ currentCodeOrigin());
+
+ if (!m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIdent)
+ && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType)
+ && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell)) {
+
+ if (CacheableIdentifier identifier = deleteByStatus.singleIdentifier()) {
+ UniquedStringImpl* uid = identifier.uid();
+ unsigned identifierNumber = m_graph.identifiers().ensure(identifier.uid());
+ if (identifier.isCell()) {
+ FrozenValue* frozen = m_graph.freezeStrong(identifier.cell());
+ if (identifier.isSymbolCell())
+ addToGraph(CheckCell, OpInfo(frozen), property);
+ else
+ addToGraph(CheckIdent, OpInfo(uid), property);
+ } else
+ addToGraph(CheckIdent, OpInfo(uid), property);
+
+ handleDeleteById(bytecode.m_dst, base, identifier, identifierNumber, deleteByStatus);
+ shouldCompileAsDeleteById = true;
+ }
+ }
+
+ if (!shouldCompileAsDeleteById)
+ set(bytecode.m_dst, addToGraph(DeleteByVal, base, property));
NEXT_OPCODE(op_del_by_val);
}
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -467,6 +467,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
write(SideState);
return;
@@ -1278,6 +1279,17 @@
def(HeapLocation(NamedPropertyLoc, heap, node->child1()), LazyNode(node->child2().node()));
return;
}
+
+ case MultiDeleteByOffset: {
+ read(JSCell_structureID);
+ read(JSObject_butterfly);
+ AbstractHeap heap(NamedProperties, node->multiDeleteByOffsetData().identifierNumber);
+ write(heap);
+ if (node->multiDeleteByOffsetData().writesStructures())
+ write(JSCell_structureID);
+ def(HeapLocation(NamedPropertyLoc, heap, node->child1()), LazyNode(graph.freezeStrong(JSValue())));
+ return;
+ }
case PutByOffset: {
unsigned identifierNumber = node->storageAccessData().identifierNumber;
Modified: trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGClobbersExitState.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -86,6 +86,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
// These do clobber memory, but nothing that is observable. It may be nice to separate the
// heaps into those that are observable and those that aren't, but we don't do that right now.
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=148440
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -509,6 +509,36 @@
changed = true;
break;
}
+
+ case MultiDeleteByOffset: {
+ Edge baseEdge = node->child1();
+ Node* base = baseEdge.node();
+ MultiDeleteByOffsetData& data = ""
+
+ AbstractValue baseValue = m_state.forNode(base);
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+ for (unsigned i = 0; i < data.variants.size(); ++i) {
+ DeleteByIdVariant& variant = data.variants[i];
+
+ if (!baseValue.contains(m_graph.registerStructure(variant.oldStructure()))) {
+ data.variants[i--] = data.variants.last();
+ data.variants.removeLast();
+ changed = true;
+ continue;
+ }
+ }
+
+ if (data.variants.size() != 1)
+ break;
+
+ emitDeleteByOffset(
+ indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
+ changed = true;
+ break;
+ }
case MatchStructure: {
Edge baseEdge = node->child1();
@@ -1312,6 +1342,45 @@
childEdge);
}
}
+
+ void emitDeleteByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const DeleteByIdVariant& variant, unsigned identifierNumber)
+ {
+ NodeOrigin origin = node->origin;
+ DFG_ASSERT(m_graph, node, origin.exitOK);
+ addBaseCheck(indexInBlock, node, baseValue, m_graph.registerStructure(variant.oldStructure()));
+ node->child1().setUseKind(KnownCellUse);
+
+ if (!variant.newStructure()) {
+ m_graph.convertToConstant(node, jsBoolean(variant.result()));
+ node->origin = node->origin.withInvalidExit();
+ return;
+ }
+
+ Transition* transition = m_graph.m_transitions.add(
+ m_graph.registerStructure(variant.oldStructure()), m_graph.registerStructure(variant.newStructure()));
+
+ Edge propertyStorage;
+
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = node->child1();
+ else
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, node->child1()));
+
+ StorageAccessData& data = ""
+ data.offset = variant.offset();
+ data.identifierNumber = identifierNumber;
+
+ Node* clearValue = m_insertionSet.insertNode(indexInBlock, SpecNone, JSConstant, origin, OpInfo(m_graph.freezeStrong(JSValue())));
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, PutByOffset, origin, OpInfo(&data), propertyStorage, node->child1(), Edge(clearValue));
+ origin = origin.withInvalidExit();
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, PutStructure, origin, OpInfo(transition),
+ node->child1());
+ m_graph.convertToConstant(node, jsBoolean(variant.result()));
+ node->origin = origin;
+ }
void addBaseCheck(
unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const StructureSet& set)
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -186,6 +186,7 @@
case CheckVarargs:
case CheckTypeInfoFlags:
case MultiGetByOffset:
+ case MultiDeleteByOffset:
case ValueRep:
case DoubleRep:
case Int52Rep:
@@ -245,6 +246,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
case DateGetInt32OrNaN:
case DateGetTime:
case DataViewGetInt:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1808,6 +1808,11 @@
fixEdge<CellUse>(node->child1());
break;
}
+
+ case MultiDeleteByOffset: {
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
case MatchStructure: {
// FIXME: Introduce a variant of MatchStructure that doesn't do a cell check.
@@ -2579,6 +2584,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
case InvalidationPoint:
case CreateArgumentsButterfly:
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -304,6 +304,12 @@
for (unsigned i = 0; i < data.variants.size(); ++i)
out.print(comma, inContext(data.variants[i], context));
}
+ if (node->hasMultiDeleteByOffsetData()) {
+ MultiDeleteByOffsetData& data = ""
+ out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}");
+ for (unsigned i = 0; i < data.variants.size(); ++i)
+ out.print(comma, inContext(data.variants[i], context));
+ }
if (node->hasMatchStructureData()) {
for (MatchStructureVariant& variant : node->matchStructureData().variants)
out.print(comma, inContext(*variant.structure.get(), context), "=>", variant.result);
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1100,6 +1100,7 @@
Bag<SwitchData> m_switchData;
Bag<MultiGetByOffsetData> m_multiGetByOffsetData;
Bag<MultiPutByOffsetData> m_multiPutByOffsetData;
+ Bag<MultiDeleteByOffsetData> m_multiDeleteByOffsetData;
Bag<MatchStructureData> m_matchStructureData;
Bag<ObjectMaterializationData> m_objectMaterializationData;
Bag<CallVarargsData> m_callVarargsData;
Modified: trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGMayExit.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -109,6 +109,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
break;
case StrCat:
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -57,6 +57,15 @@
return false;
}
+bool MultiDeleteByOffsetData::writesStructures() const
+{
+ for (unsigned i = variants.size(); i--;) {
+ if (variants[i].writesStructures())
+ return true;
+ }
+ return false;
+}
+
void BranchTarget::dump(PrintStream& out) const
{
if (!block)
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -47,6 +47,7 @@
#include "DFGTransition.h"
#include "DFGUseKind.h"
#include "DFGVariableAccessData.h"
+#include "DeleteByIdVariant.h"
#include "GetByIdVariant.h"
#include "JSCJSValue.h"
#include "Operands.h"
@@ -92,6 +93,13 @@
bool reallocatesStorage() const;
};
+struct MultiDeleteByOffsetData {
+ unsigned identifierNumber;
+ Vector<DeleteByIdVariant, 2> variants;
+
+ bool writesStructures() const;
+};
+
struct MatchStructureVariant {
RegisteredStructure structure;
bool result;
@@ -2029,6 +2037,17 @@
ASSERT(hasMultiPutByOffsetData());
return *m_opInfo.as<MultiPutByOffsetData*>();
}
+
+ bool hasMultiDeleteByOffsetData()
+ {
+ return op() == MultiDeleteByOffset;
+ }
+
+ MultiDeleteByOffsetData& multiDeleteByOffsetData()
+ {
+ ASSERT(hasMultiDeleteByOffsetData());
+ return *m_opInfo.as<MultiDeleteByOffsetData*>();
+ }
bool hasMatchStructureData()
{
@@ -2991,6 +3010,17 @@
return m_opInfo.as<PutByIdStatus*>();
}
+ bool hasDeleteByStatus()
+ {
+ return op() == FilterDeleteByStatus;
+ }
+
+ DeleteByStatus* deleteByStatus()
+ {
+ ASSERT(hasDeleteByStatus());
+ return m_opInfo.as<DeleteByStatus*>();
+ }
+
void dumpChildren(PrintStream& out)
{
if (!child1())
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -252,6 +252,7 @@
macro(MultiGetByOffset, NodeResultJS | NodeMustGenerate) \
macro(PutByOffset, NodeMustGenerate) \
macro(MultiPutByOffset, NodeMustGenerate) \
+ macro(MultiDeleteByOffset, NodeMustGenerate | NodeResultJS) \
macro(GetArrayLength, NodeResultInt32) \
macro(GetVectorLength, NodeResultInt32) \
macro(GetTypedArrayByteOffset, NodeResultInt32) \
@@ -527,6 +528,7 @@
macro(FilterGetByStatus, NodeMustGenerate) \
macro(FilterInByIdStatus, NodeMustGenerate) \
macro(FilterPutByIdStatus, NodeMustGenerate) \
+ macro(FilterDeleteByStatus, NodeMustGenerate) \
/* Data view access */ \
macro(DataViewGetInt, NodeMustGenerate | NodeResultJS) /* The gets are must generate for now because they do bounds checks */ \
macro(DataViewGetFloat, NodeMustGenerate | NodeResultDouble) \
Modified: trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGObjectAllocationSinkingPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1220,6 +1220,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
break;
default:
@@ -2570,6 +2571,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
if (node->child1()->isPhantomAllocation())
node->removeWithoutChecks();
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -984,6 +984,7 @@
}
case DeleteByVal:
case DeleteById:
+ case MultiDeleteByOffset:
case LogicalNot:
case CompareLess:
case CompareLessEq:
@@ -1395,6 +1396,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
case ClearCatchLocals:
case DataViewSet:
case InvalidationPoint:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -361,6 +361,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
// We don't want these to be moved anywhere other than where we put them, since we want them
// to capture "profiling" at the point in control flow here the user put them.
return false;
@@ -608,6 +609,7 @@
case InvalidationPoint:
case NotifyWrite:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
case GetEnumerableLength:
case HasGenericProperty:
case HasStructureProperty:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -4190,6 +4190,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
m_interpreter.filterICStatus(node);
noResult(node);
break;
@@ -4208,6 +4209,7 @@
case ArithIMul:
case MultiGetByOffset:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
case CheckBadCell:
case BottomValue:
case PhantomNewObject:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -5255,6 +5255,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
m_interpreter.filterICStatus(node);
noResult(node);
break;
@@ -5269,6 +5270,7 @@
case ArithIMul:
case MultiGetByOffset:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
case FiatInt52:
case CheckBadCell:
case BottomValue:
Modified: trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGTypeCheckHoistingPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -287,6 +287,7 @@
case MovHint:
case MultiGetByOffset:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
// Don't count these uses.
break;
@@ -362,6 +363,7 @@
case MovHint:
case MultiGetByOffset:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
// Don't count these uses.
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -311,6 +311,12 @@
VALIDATE((node), !variant.oldStructureForTransition()->dfgShouldWatch());
}
break;
+ case MultiDeleteByOffset:
+ for (unsigned i = node->multiDeleteByOffsetData().variants.size(); i--;) {
+ const DeleteByIdVariant& variant = node->multiDeleteByOffsetData().variants[i];
+ VALIDATE((node), !variant.newStructure() || !variant.oldStructure()->dfgShouldWatch());
+ }
+ break;
case MaterializeNewObject:
for (RegisteredStructure structure : node->structureSet()) {
// This only supports structures that are JSFinalObject or JSArray.
Modified: trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -198,6 +198,7 @@
case FilterPutByIdStatus:
case FilterCallLinkStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
break;
case GetByOffset: {
@@ -399,6 +400,7 @@
case FilterPutByIdStatus:
case FilterCallLinkStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
if (node->child1().node() == candidate)
node->remove(m_graph);
break;
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -233,6 +233,7 @@
case ToThis:
case MultiGetByOffset:
case MultiPutByOffset:
+ case MultiDeleteByOffset:
case ToPrimitive:
case ToPropertyKey:
case Throw:
@@ -392,6 +393,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
case CreateThis:
case CreatePromise:
case CreateGenerator:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -1177,6 +1177,9 @@
case MultiPutByOffset:
compileMultiPutByOffset();
break;
+ case MultiDeleteByOffset:
+ compileMultiDeleteByOffset();
+ break;
case MatchStructure:
compileMatchStructure();
break;
@@ -1569,6 +1572,7 @@
case FilterGetByStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
+ case FilterDeleteByStatus:
compileFilterICStatus();
break;
case DateGetInt32OrNaN:
@@ -8130,6 +8134,106 @@
m_out.appendTo(continuation, lastNext);
}
+
+ void compileMultiDeleteByOffset()
+ {
+ LValue base = lowCell(m_node->child1());
+ MultiDeleteByOffsetData& data = ""
+
+ unsigned missConfigurable = 0;
+ unsigned missNonconfigurable = 0;
+
+ for (unsigned i = data.variants.size(); i--;) {
+ DeleteByIdVariant variant = data.variants[i];
+ if (!variant.newStructure()) {
+ if (variant.result())
+ ++missConfigurable;
+ else
+ ++missNonconfigurable;
+ }
+ }
+
+ unsigned uniqueCaseCount = data.variants.size();
+ if (missConfigurable)
+ uniqueCaseCount -= missConfigurable - 1;
+ if (missNonconfigurable)
+ uniqueCaseCount -= missNonconfigurable - 1;
+ int trueBlock = missConfigurable ? uniqueCaseCount - 1 : -1;
+ int falseBlock = missNonconfigurable ? uniqueCaseCount - 1 - !!missConfigurable : -1;
+
+ Vector<LBasicBlock, 2> blocks(uniqueCaseCount);
+ for (unsigned i = blocks.size(); i--;)
+ blocks[i] = m_out.newBlock();
+ LBasicBlock exit = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ Vector<SwitchCase, 2> cases;
+ RegisteredStructureSet baseSet;
+ for (unsigned i = data.variants.size(), block = 0; i--;) {
+ DeleteByIdVariant variant = data.variants[i];
+ RegisteredStructure structure = m_graph.registerStructure(variant.oldStructure());
+ baseSet.add(structure);
+ if (variant.newStructure())
+ cases.append(SwitchCase(weakStructureID(structure), blocks[block++], Weight(1)));
+ else
+ cases.append(SwitchCase(weakStructureID(structure), blocks[variant.result() ? trueBlock : falseBlock], Weight(1)));
+ }
+ bool structuresChecked = m_interpreter.forNode(m_node->child1()).m_structure.isSubsetOf(baseSet);
+ emitSwitchForMultiByOffset(base, structuresChecked, cases, exit);
+
+ LBasicBlock lastNext = m_out.m_nextBlock;
+
+ Vector<ValueFromBlock, 2> results;
+
+ for (unsigned i = data.variants.size(), block = 0; i--;) {
+ DeleteByIdVariant variant = data.variants[i];
+ if (!variant.newStructure())
+ continue;
+
+ m_out.appendTo(blocks[block], block + 1 < blocks.size() ? blocks[block + 1] : exit);
+
+ if (variant.newStructure()) {
+ LValue storage;
+
+ if (isInlineOffset(variant.offset()))
+ storage = base;
+ else
+ storage = m_out.loadPtr(base, m_heaps.JSObject_butterfly);
+
+ storeProperty(m_out.int64Zero, storage, data.identifierNumber, variant.offset());
+
+ ASSERT(variant.oldStructure()->indexingType() == variant.newStructure()->indexingType());
+ ASSERT(variant.oldStructure()->typeInfo().inlineTypeFlags() == variant.newStructure()->typeInfo().inlineTypeFlags());
+ ASSERT(variant.oldStructure()->typeInfo().type() == variant.newStructure()->typeInfo().type());
+ m_out.store32(
+ weakStructureID(m_graph.registerStructure(variant.newStructure())), base, m_heaps.JSCell_structureID);
+ }
+
+ results.append(m_out.anchor(variant.result() ? m_out.booleanTrue : m_out.booleanFalse));
+ m_out.jump(continuation);
+ ++block;
+ }
+
+ if (missNonconfigurable) {
+ m_out.appendTo(blocks[falseBlock]);
+ results.append(m_out.anchor(m_out.booleanFalse));
+ m_out.jump(continuation);
+ }
+
+ if (missConfigurable) {
+ m_out.appendTo(blocks[trueBlock], exit);
+ results.append(m_out.anchor(m_out.booleanTrue));
+ m_out.jump(continuation);
+ }
+
+ m_out.appendTo(exit, continuation);
+ if (!structuresChecked)
+ speculate(BadCache, noValue(), nullptr, m_out.booleanTrue);
+ m_out.unreachable();
+
+ m_out.appendTo(continuation, lastNext);
+ setBoolean(m_out.phi(Int32, results));
+ }
void compileMatchStructure()
{
Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h (259582 => 259583)
--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -447,11 +447,11 @@
auto scope = DECLARE_THROW_SCOPE(vm);
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
- if (thisObject->isNeutered())
- return typeError(globalObject, scope, true, typedArrayBufferHasBeenDetachedErrorMessage);
-
- if (parseIndex(propertyName))
+ if (parseIndex(propertyName)) {
+ if (thisObject->isNeutered())
+ return typeError(globalObject, scope, true, typedArrayBufferHasBeenDetachedErrorMessage);
return false;
+ }
return Base::deleteProperty(thisObject, globalObject, propertyName, slot);
}
Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (259582 => 259583)
--- trunk/Source/_javascript_Core/runtime/JSObject.cpp 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp 2020-04-06 18:48:04 UTC (rev 259583)
@@ -2011,12 +2011,7 @@
else {
structure = Structure::removePropertyTransition(vm, structure, propertyName, offset, &deferredWatchpointFire);
slot.setHit(offset);
- if (!structure->outOfLineCapacity() && thisObject->structure(vm)->outOfLineCapacity() && !structure->hasIndexingHeader(thisObject)) {
- ASSERT(thisObject->m_butterfly);
- thisObject->nukeStructureAndSetButterfly(vm, thisObject->structureID(), nullptr);
- offset = invalidOffset;
- ASSERT(structure->maxOffset() == invalidOffset);
- }
+ ASSERT(structure->outOfLineCapacity() || !thisObject->structure(vm)->outOfLineCapacity());
thisObject->setStructure(vm, structure);
}
Modified: trunk/Source/_javascript_Core/runtime/Structure.h (259582 => 259583)
--- trunk/Source/_javascript_Core/runtime/Structure.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/runtime/Structure.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -478,10 +478,7 @@
|| hijacksIndexingHeader();
}
- bool hasIndexingHeader(const JSCell*) const;
- bool mayHaveIndexingHeader() const;
- bool canCacheDeleteIC() const;
-
+ bool hasIndexingHeader(const JSCell*) const;
bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
PropertyOffset get(VM&, PropertyName);
Modified: trunk/Source/_javascript_Core/runtime/StructureInlines.h (259582 => 259583)
--- trunk/Source/_javascript_Core/runtime/StructureInlines.h 2020-04-06 18:19:52 UTC (rev 259582)
+++ trunk/Source/_javascript_Core/runtime/StructureInlines.h 2020-04-06 18:48:04 UTC (rev 259583)
@@ -228,22 +228,6 @@
return jsCast<const JSArrayBufferView*>(cell)->mode() == WastefulTypedArray;
}
-inline bool Structure::mayHaveIndexingHeader() const
-{
- if (hasIndexedProperties(indexingType()))
- return true;
-
- if (!isTypedView(typedArrayTypeForType(m_blob.type())))
- return false;
-
- return true;
-}
-
-inline bool Structure::canCacheDeleteIC() const
-{
- return !isTypedView(typedArrayTypeForType(m_blob.type()));
-}
-
inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject)
{
return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject;