Title: [259583] trunk
Revision
259583
Author
[email protected]
Date
2020-04-06 11:48:04 -0700 (Mon, 06 Apr 2020)

Log Message

Allow deleteById to be cached in the DFG
https://bugs.webkit.org/show_bug.cgi?id=208664

Reviewed by Saam Barati.

JSTests:

* 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):

Source/_javascript_Core:

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.

Modified Paths

Added Paths

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;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to