We should have a way of profiling when a get_by_id is pure and to emit a PureGetById...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 30 Oct 2016 01:38:22 +0000 (01:38 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 30 Oct 2016 01:38:22 +0000 (01:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163305

Reviewed by Keith Miller.

JSTests:

* microbenchmarks/pure-get-by-id-cse-2.js: Added.
(foo):
* microbenchmarks/pure-get-by-id-cse.js: Added.
(foo):
* stress/pure-get-by-id-cse-correctness.js: Added.
(assert):
(foo):
* stress/pure-get-by-id-on-non-object.js: Added.
(assert):
(foo):

Source/JavaScriptCore:

This creates a new GetById node in the DFG called PureGetById. We emit a
PureGetById when we profile that a get_by_id in the baseline never does
side effects. PureGetById speculates on the fact that it won't do side
effects. If it realizes that it must perform side effects, it will perform
the side effect, but also invalidate the CodeBlock that compiled it,
which will cause us to exit once we return back to the compiled code.
This allows us to have stricter clobberize rules for PureGetById which
model how getOwnPropertySlot(VMInquiry) behaves. This means that PureGetByIds
can be CSEd with each other, and that other things are more likely to
be CSEd around a PureGetById. To profile if a get_by_id does side
effects, I've added an extra bit into StructureStubInfo that we write
to when performing a get_by_id slow path call. If we notice that we
never do effects, inside the ByteCodeParser, we will emit a PureGetById
instead of a GetById.

To justify the performance benefit of this patch, I ran the suite of
benchmarks with useAccessInlining=false. This meant that we don't compile
any (Multi)GetByOffset/(Multi)PutByOffset. This was just to try to see if
this patch is worth anything at all in a world where we emit many
PureGetByIds. Running the benchmarks under this mode showed a 3.5% octane
improvement and a 15% kraken improvement. However, when running benchmarks
with useAccessInlining=true (the default JSC state), this patch is neutral.
I think the main reason for this is that the DFG is good at knowing when to
emit (Multi)GetByOffset, and most benchmarks that would benefit from
PureGetById are already emitting (Multi)GetByOffset. However, PureGetById can be
profitable when we encounter code that is too polymorphic for (Multi)GetByOffset.
It's reasonable to expect that JS code in the wild will fall into this use
case even though it might not be represented in some of the major JS benchmarks.
I wrote some microbenchmarks to demonstrate the benefits of PureGetById CSE,
and they were 30% (eliminating a few PureGetById from an add expression)
to 10x faster (eliminating a PureGetById in a loop).

* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::StructureStubInfo):
(JSC::StructureStubInfo::reset):
* bytecode/StructureStubInfo.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::canBecomeGetArrayLength):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(JSC::DFG::Node::convertToMultiGetByOffset):
(JSC::DFG::Node::hasIdentifier):
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileTryGetById):
(JSC::DFG::SpeculativeJIT::compilePureGetById):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileGetById):
(JSC::FTL::DFG::LowerDFGToB3::getById):
* jit/JITOperations.cpp:
(JSC::pureGetByIdCommon):
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_try_get_by_id):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_try_get_by_id):
* jit/Repatch.cpp:
(JSC::appropriateOptimizingGetByIdFunction):
(JSC::appropriateGenericGetByIdFunction):
(JSC::tryCacheGetByID):
* jit/Repatch.h:
* profiler/ProfilerJettisonReason.cpp:
(WTF::printInternal):
* profiler/ProfilerJettisonReason.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208117 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/pure-get-by-id-cse-2.js [new file with mode: 0644]
JSTests/microbenchmarks/pure-get-by-id-cse.js [new file with mode: 0644]
JSTests/stress/pure-get-by-id-cse-correctness.js [new file with mode: 0644]
JSTests/stress/pure-get-by-id-on-non-object.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArrayMode.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/jit/Repatch.h
Source/JavaScriptCore/profiler/ProfilerJettisonReason.cpp
Source/JavaScriptCore/profiler/ProfilerJettisonReason.h

index 3f8b1c1..28a51fb 100644 (file)
@@ -1,3 +1,21 @@
+2016-10-29  Saam Barati  <sbarati@apple.com>
+
+        We should have a way of profiling when a get_by_id is pure and to emit a PureGetById in the DFG/FTL
+        https://bugs.webkit.org/show_bug.cgi?id=163305
+
+        Reviewed by Keith Miller.
+
+        * microbenchmarks/pure-get-by-id-cse-2.js: Added.
+        (foo):
+        * microbenchmarks/pure-get-by-id-cse.js: Added.
+        (foo):
+        * stress/pure-get-by-id-cse-correctness.js: Added.
+        (assert):
+        (foo):
+        * stress/pure-get-by-id-on-non-object.js: Added.
+        (assert):
+        (foo):
+
 2016-10-28  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Skip JSTests/microbenchmarks/dense-set.js on memory limited devices
diff --git a/JSTests/microbenchmarks/pure-get-by-id-cse-2.js b/JSTests/microbenchmarks/pure-get-by-id-cse-2.js
new file mode 100644 (file)
index 0000000..bd84978
--- /dev/null
@@ -0,0 +1,30 @@
+function foo(o, c) {
+    if (o.f) {
+        let sum = 0;
+        for (let i = 0; i < c; i++)
+            sum += o.f;
+        return sum;
+    }
+}
+noInline(foo);
+
+let start = Date.now();
+let objects = [];
+const objectCount = 20;
+for (let i = 0; i < objectCount; i++) {
+    let obj = {};
+    for (let j = 0; j < i * 2; j++) {
+        obj["j" + j] = j;
+    }
+    obj.f = 20;
+    objects.push(obj);
+}
+
+for (let i = 0; i < 1000000; i++) {
+    let obj = objects[i % objects.length];
+    foo(obj, 150);
+}
+
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
diff --git a/JSTests/microbenchmarks/pure-get-by-id-cse.js b/JSTests/microbenchmarks/pure-get-by-id-cse.js
new file mode 100644 (file)
index 0000000..f6f84d8
--- /dev/null
@@ -0,0 +1,26 @@
+function foo(o) {
+    if (o.f)
+        return o.f + o.f + o.f + o.f; 
+}
+noInline(foo);
+
+let start = Date.now();
+let objects = [];
+const objectCount = 20;
+for (let i = 0; i < objectCount; i++) {
+    let obj = {};
+    for (let j = 0; j < i * 2; j++) {
+        obj["j" + j] = j;
+    }
+    obj.f = 20;
+    objects.push(obj);
+}
+
+for (let i = 0; i < 10000000; i++) {
+    let obj = objects[i % objects.length];
+    foo(obj);
+}
+
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
diff --git a/JSTests/stress/pure-get-by-id-cse-correctness.js b/JSTests/stress/pure-get-by-id-cse-correctness.js
new file mode 100644 (file)
index 0000000..1559ba3
--- /dev/null
@@ -0,0 +1,39 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion")
+}
+noInline(assert);
+
+function foo(o1, o2) {
+    let a = o1.f1;
+    let b = o2.f2;
+    return a + o1.f1 + b;
+}
+noInline(foo);
+
+let objs = [];
+const count = 80;
+for (let i = 0; i < count; i++) {
+    let o = {};
+    for (let j = 0; j < i; ++j) {
+        o[j + "J"] = j;
+    }
+    o.f1 = 20;
+    o.f2 = 40;
+    objs.push(o);
+}
+
+for (let i = 0; i < 1000; i++) {
+    let o1 = objs[i % objs.length];
+    let o2 = objs[(i + 1) % objs.length];
+    assert(foo(o1, o2) === 80);
+}
+
+let o = objs[count - 1];
+let numCalls = 0;
+Object.defineProperty(o, "f1", {
+    get() { ++numCalls; return 25; }
+});
+
+assert(foo(o, objs[count - 2]) === 90);
+assert(numCalls === 2);
diff --git a/JSTests/stress/pure-get-by-id-on-non-object.js b/JSTests/stress/pure-get-by-id-on-non-object.js
new file mode 100644 (file)
index 0000000..aa8dad3
--- /dev/null
@@ -0,0 +1,32 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.")
+}
+
+function foo(o) {
+    assert(o.length === o.length);
+    return o.length;
+}
+noInline(foo);
+
+let items = [];
+const numItems = 30;
+for (let i = 0; i < numItems; i++) {
+    let o = {};
+    for (let j = 0; j < i; j++) {
+        o[j + "j"] = j;
+    }
+    o.length = 2;
+    items.push(o);
+} 
+
+items.push("st");
+
+for (let i = 0; i < 10000; i++)
+    assert(foo(items[i % items.length]) === 2);
+
+Number.prototype.length = 2;
+items.push(42);
+
+for (let i = 0; i < 100000; i++)
+    assert(foo(items[i % items.length]) === 2);
index edc03e2..958d908 100644 (file)
@@ -1,3 +1,102 @@
+2016-10-29  Saam Barati  <sbarati@apple.com>
+
+        We should have a way of profiling when a get_by_id is pure and to emit a PureGetById in the DFG/FTL
+        https://bugs.webkit.org/show_bug.cgi?id=163305
+
+        Reviewed by Keith Miller.
+
+        This creates a new GetById node in the DFG called PureGetById. We emit a
+        PureGetById when we profile that a get_by_id in the baseline never does
+        side effects. PureGetById speculates on the fact that it won't do side
+        effects. If it realizes that it must perform side effects, it will perform
+        the side effect, but also invalidate the CodeBlock that compiled it,
+        which will cause us to exit once we return back to the compiled code.
+        This allows us to have stricter clobberize rules for PureGetById which
+        model how getOwnPropertySlot(VMInquiry) behaves. This means that PureGetByIds
+        can be CSEd with each other, and that other things are more likely to
+        be CSEd around a PureGetById. To profile if a get_by_id does side
+        effects, I've added an extra bit into StructureStubInfo that we write
+        to when performing a get_by_id slow path call. If we notice that we
+        never do effects, inside the ByteCodeParser, we will emit a PureGetById
+        instead of a GetById.
+
+        To justify the performance benefit of this patch, I ran the suite of
+        benchmarks with useAccessInlining=false. This meant that we don't compile
+        any (Multi)GetByOffset/(Multi)PutByOffset. This was just to try to see if
+        this patch is worth anything at all in a world where we emit many
+        PureGetByIds. Running the benchmarks under this mode showed a 3.5% octane
+        improvement and a 15% kraken improvement. However, when running benchmarks
+        with useAccessInlining=true (the default JSC state), this patch is neutral.
+        I think the main reason for this is that the DFG is good at knowing when to
+        emit (Multi)GetByOffset, and most benchmarks that would benefit from
+        PureGetById are already emitting (Multi)GetByOffset. However, PureGetById can be
+        profitable when we encounter code that is too polymorphic for (Multi)GetByOffset.
+        It's reasonable to expect that JS code in the wild will fall into this use
+        case even though it might not be represented in some of the major JS benchmarks.
+        I wrote some microbenchmarks to demonstrate the benefits of PureGetById CSE,
+        and they were 30% (eliminating a few PureGetById from an add expression)
+        to 10x faster (eliminating a PureGetById in a loop).
+
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::StructureStubInfo):
+        (JSC::StructureStubInfo::reset):
+        * bytecode/StructureStubInfo.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::canBecomeGetArrayLength):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToGetByOffset):
+        (JSC::DFG::Node::convertToMultiGetByOffset):
+        (JSC::DFG::Node::hasIdentifier):
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileTryGetById):
+        (JSC::DFG::SpeculativeJIT::compilePureGetById):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileGetById):
+        (JSC::FTL::DFG::LowerDFGToB3::getById):
+        * jit/JITOperations.cpp:
+        (JSC::pureGetByIdCommon):
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_try_get_by_id):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_try_get_by_id):
+        * jit/Repatch.cpp:
+        (JSC::appropriateOptimizingGetByIdFunction):
+        (JSC::appropriateGenericGetByIdFunction):
+        (JSC::tryCacheGetByID):
+        * jit/Repatch.h:
+        * profiler/ProfilerJettisonReason.cpp:
+        (WTF::printInternal):
+        * profiler/ProfilerJettisonReason.h:
+
 2016-10-28  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Breakpoints not working in scripts with unicode characters
index 38e5cef..ba75947 100644 (file)
@@ -48,6 +48,7 @@ StructureStubInfo::StructureStubInfo(AccessType accessType)
     , resetByGC(false)
     , tookSlowPath(false)
     , everConsidered(false)
+    , didSideEffects(false)
 {
 }
 
@@ -216,7 +217,10 @@ void StructureStubInfo::reset(CodeBlock* codeBlock)
     }
 
     switch (accessType) {
-    case AccessType::GetPure:
+    case AccessType::TryGet:
+        resetGetByID(codeBlock, *this, GetByIDKind::Try);
+        break;
+    case AccessType::PureGet:
         resetGetByID(codeBlock, *this, GetByIDKind::Pure);
         break;
     case AccessType::Get:
index c225c05..a117349 100644 (file)
@@ -46,7 +46,8 @@ class PolymorphicAccess;
 
 enum class AccessType : int8_t {
     Get,
-    GetPure,
+    TryGet,
+    PureGet,
     Put,
     In
 };
@@ -205,6 +206,7 @@ public:
     bool resetByGC : 1;
     bool tookSlowPath : 1;
     bool everConsidered : 1;
+    bool didSideEffects : 1;
 };
 
 inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo)
index 7afc860..1d1f308 100644 (file)
@@ -2119,6 +2119,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).makeHeapTop();
         break;
 
+    case PureGetById:
     case GetById:
     case GetByIdFlush: {
         if (!node->prediction()) {
@@ -2150,7 +2151,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
         }
 
-        clobberWorld(node->origin.semantic, clobberLimit);
+        if (node->op() == PureGetById)
+            clobberStructures(clobberLimit);
+        else
+            clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).makeHeapTop();
         break;
     }
index 56a2a65..2c3c0ed 100644 (file)
@@ -152,10 +152,11 @@ ArrayMode ArrayMode::fromObserved(const ConcurrentJITLocker& locker, ArrayProfil
 
 static bool canBecomeGetArrayLength(Graph& graph, Node* node)
 {
-    if (node->op() != GetById)
-        return false;
-    auto uid = graph.identifiers()[node->identifierNumber()];
-    return uid == graph.m_vm.propertyNames->length.impl();
+    if (node->op() == GetById || node->op() == PureGetById) {
+        auto uid = graph.identifiers()[node->identifierNumber()];
+        return uid == graph.m_vm.propertyNames->length.impl();
+    }
+    return false;
 }
 
 ArrayMode ArrayMode::refine(
index b2b3403..35dc078 100644 (file)
@@ -3307,10 +3307,19 @@ void ByteCodeParser::handleGetById(
     }
     
     NodeType getById;
-    if (type == AccessType::Get)
+    switch (type) {
+    case AccessType::Get:
         getById = getByIdStatus.makesCalls() ? GetByIdFlush : GetById;
-    else
+        break;
+    case AccessType::TryGet:
         getById = TryGetById;
+        break;
+    case AccessType::PureGet:
+        getById = PureGetById;
+        break;
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
 
     // Special path for custom accessors since custom's offset does not have any meanings.
     // So, this is completely different from Simple one. But we have a chance to optimize it when we use DOMJIT.
@@ -4198,7 +4207,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
 
             Node* base = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* property = get(VirtualRegister(currentInstruction[3].u.operand));
-            bool compiledAsGetById = false;
+            bool compileAsGetById = false;
+            bool compileAsPureGetById = false;
             GetByIdStatus getByIdStatus;
             unsigned identifierNumber = 0;
             {
@@ -4207,7 +4217,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 // FIXME: When the bytecode is not compiled in the baseline JIT, byValInfo becomes null.
                 // At that time, there is no information.
                 if (byValInfo && byValInfo->stubInfo && !byValInfo->tookSlowPath && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIdent) && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell)) {
-                    compiledAsGetById = true;
+                    compileAsGetById = true;
+                    compileAsPureGetById = !byValInfo->stubInfo->didSideEffects;
                     identifierNumber = m_graph.identifiers().ensure(byValInfo->cachedId.impl());
                     UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
 
@@ -4225,9 +4236,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 }
             }
 
-            if (compiledAsGetById)
-                handleGetById(currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus, AccessType::Get, OPCODE_LENGTH(op_get_by_val));
-            else {
+            if (compileAsGetById) {
+                AccessType type = compileAsPureGetById ? AccessType::PureGet : AccessType::Get;
+                handleGetById(currentInstruction[1].u.operand, prediction, base, identifierNumber, getByIdStatus, type, OPCODE_LENGTH(op_get_by_val));
+            } else {
                 ArrayMode arrayMode = getArrayMode(currentInstruction[4].u.arrayProfile, Array::Read);
                 Node* getByVal = addToGraph(GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction), base, property);
                 m_exitOK = false; // GetByVal must be treated as if it clobbers exit state, since FixupPhase may make it generic.
@@ -4363,7 +4375,18 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
                 m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
                 currentCodeOrigin(), uid);
-            AccessType type = op_try_get_by_id == opcodeID ? AccessType::GetPure : AccessType::Get;
+            AccessType type;
+            if (opcodeID == op_try_get_by_id) 
+                type = AccessType::TryGet;
+            else {
+                ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
+                unsigned bytecodeIndex = currentCodeOrigin().bytecodeIndex;
+                StructureStubInfo* info = m_inlineStackTop->m_stubInfos.get(CodeOrigin(bytecodeIndex));
+                if (info && info->everConsidered && !info->didSideEffects)
+                    type = AccessType::PureGet;
+                else
+                    type = AccessType::Get;
+            }
 
             unsigned opcodeLength = opcodeID == op_try_get_by_id ? OPCODE_LENGTH(op_try_get_by_id) : OPCODE_LENGTH(op_get_by_id);
 
index 6657da8..5a3294d 100644 (file)
@@ -495,6 +495,35 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(IsFunctionLoc, MiscFields, node->child1()), LazyNode(node));
         return;
         
+    case PureGetById: {
+        // We model what is allowed inside a getOwnPropertySlot(VMInquiry) here.
+        // Some getOwnPropertySlot implementations will lazily inject properties, which
+        // may change the object's structure.
+
+        read(JSCell_structureID);
+        read(JSCell_typeInfoFlags);
+        read(JSCell_typeInfoType);
+        read(JSCell_indexingType);
+        read(JSObject_butterfly);
+        read(MiscFields);
+
+        AbstractHeap propertyNameHeap(NamedProperties, node->identifierNumber());
+        read(propertyNameHeap);
+
+        write(JSCell_structureID);
+        write(JSCell_typeInfoFlags);
+
+        write(Watchpoint_fire);
+        write(MiscFields);
+
+        // This can happen if lazily adding fields to an object happens in getOwnPropertySlot
+        // and we need to allocate out of line storage.
+        write(JSObject_butterfly);
+
+        def(HeapLocation(NamedPropertyLoc, propertyNameHeap, node->child1()), LazyNode(node));
+        return;
+    }
+
     case GetById:
     case GetByIdFlush:
     case GetByIdWithThis:
index d9bc7e7..38b6402 100644 (file)
@@ -448,6 +448,7 @@ private:
                 break;
             }
         
+            case PureGetById:
             case GetById:
             case GetByIdFlush: {
                 Edge childEdge = node->child1();
@@ -459,6 +460,9 @@ private:
                 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.
 
+                if (!Options::useAccessInlining())
+                    break;
+
                 if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered()
                     || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell)))
                     break;
@@ -514,6 +518,9 @@ private:
                 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.
 
+                if (!Options::useAccessInlining())
+                    break;
+
                 if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered())
                     break;
                 
index 077c715..cb48e5c 100644 (file)
@@ -264,6 +264,7 @@ bool doesGC(Graph& graph, Node* node)
     case ResolveScope:
         return false;
 
+    case PureGetById: // We are modeling getOwnPropertySlot here, which may GC because it is allowed to allocate things.
     case CreateActivation:
     case CreateDirectArguments:
     case CreateScopedArguments:
index c59d3d9..78fe0e9 100644 (file)
@@ -1167,6 +1167,7 @@ private:
             break;
         }
 
+        case PureGetById:
         case GetById:
         case GetByIdFlush: {
             // FIXME: This should be done in the ByteCodeParser based on reading the
index dee02c7..a67b293 100644 (file)
@@ -532,7 +532,7 @@ public:
     
     void convertToGetByOffset(StorageAccessData& data, Edge storage, Edge base)
     {
-        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
+        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == PureGetById || m_op == MultiGetByOffset);
         m_opInfo = &data;
         children.setChild1(storage);
         children.setChild2(base);
@@ -542,7 +542,7 @@ public:
     
     void convertToMultiGetByOffset(MultiGetByOffsetData* data)
     {
-        ASSERT(m_op == GetById || m_op == GetByIdFlush);
+        ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == PureGetById);
         m_opInfo = data;
         child1().setUseKind(CellUse);
         m_op = MultiGetByOffset;
@@ -918,6 +918,7 @@ public:
         switch (op()) {
         case TryGetById:
         case GetById:
+        case PureGetById:
         case GetByIdFlush:
         case GetByIdWithThis:
         case PutById:
@@ -1428,6 +1429,7 @@ public:
         case ArithCeil:
         case ArithTrunc:
         case GetDirectPname:
+        case PureGetById:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
index f6070f8..cd24a44 100644 (file)
@@ -185,6 +185,7 @@ namespace JSC { namespace DFG {
     macro(PutByVal, NodeMustGenerate | NodeHasVarArgs) \
     macro(PutByValAlias, NodeMustGenerate | NodeHasVarArgs) \
     macro(TryGetById, NodeResultJS) \
+    macro(PureGetById, NodeResultJS | NodeMustGenerate) \
     macro(GetById, NodeResultJS | NodeMustGenerate) \
     macro(GetByIdFlush, NodeResultJS | NodeMustGenerate) \
     macro(GetByIdWithThis, NodeResultJS | NodeMustGenerate) \
index 1d6fc5c..397e999 100644 (file)
@@ -682,6 +682,7 @@ private:
         case RegExpTest:
         case StringReplace:
         case StringReplaceRegExp:
+        case PureGetById:
         case GetById:
         case GetByIdFlush:
         case GetByIdWithThis:
index 9c6495d..0a2ecd8 100644 (file)
@@ -199,6 +199,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case TryGetById:
     case DeleteById:
     case DeleteByVal:
+    case PureGetById:
     case GetById:
     case GetByIdWithThis:
     case GetByValWithThis:
index 58c119d..7b57b18 100644 (file)
@@ -1003,7 +1003,7 @@ void SpeculativeJIT::compileTryGetById(Node* node)
 
         base.use();
 
-        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), NeedToSpill, AccessType::GetPure);
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), NeedToSpill, AccessType::TryGet);
 
         jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
         break;
@@ -1020,7 +1020,7 @@ void SpeculativeJIT::compileTryGetById(Node* node)
 
         JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
 
-        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, NeedToSpill, AccessType::GetPure);
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, NeedToSpill, AccessType::TryGet);
 
         jsValueResult(resultRegs, node, DataFormatJS, UseChildrenCalledExplicitly);
         break;
@@ -1032,6 +1032,42 @@ void SpeculativeJIT::compileTryGetById(Node* node)
     } 
 }
 
+void SpeculativeJIT::compilePureGetById(Node* node)
+{
+    ASSERT(node->op() == PureGetById);
+
+    switch (node->child1().useKind()) {
+    case CellUse: {
+        SpeculateCellOperand base(this, node->child1());
+        JSValueRegsTemporary result(this, Reuse, base);
+
+        JSValueRegs baseRegs = JSValueRegs::payloadOnly(base.gpr());
+        JSValueRegs resultRegs = result.regs();
+
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), JITCompiler::Jump(), NeedToSpill, AccessType::PureGet);
+
+        jsValueResult(resultRegs, node);
+        break;
+    }
+    case UntypedUse: {
+        JSValueOperand base(this, node->child1());
+        JSValueRegsTemporary result(this, Reuse, base);
+
+        JSValueRegs baseRegs = base.jsValueRegs();
+        JSValueRegs resultRegs = result.regs();
+    
+        JITCompiler::Jump notCell = m_jit.branchIfNotCell(baseRegs);
+    
+        cachedGetById(node->origin.semantic, baseRegs, resultRegs, node->identifierNumber(), notCell, NeedToSpill, AccessType::PureGet);
+    
+        jsValueResult(resultRegs, node);
+        break;
+    }
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+}
+
 void SpeculativeJIT::compileIn(Node* node)
 {
     SpeculateCellOperand base(this, node->child2());
index a6fcfb4..56538a2 100644 (file)
@@ -721,6 +721,7 @@ public:
     void compileDeleteById(Node*);
     void compileDeleteByVal(Node*);
     void compileTryGetById(Node*);
+    void compilePureGetById(Node*);
     void compileIn(Node*);
     
     void nonSpeculativeNonPeepholeCompareNullOrUndefined(Edge operand);
index 7624fe7..8f72b1e 100644 (file)
@@ -210,6 +210,8 @@ void SpeculativeJIT::cachedGetById(
     J_JITOperation_ESsiJI getByIdFunction;
     if (type == AccessType::Get)
         getByIdFunction = operationGetByIdOptimize;
+    else if (type == AccessType::PureGet)
+        getByIdFunction = operationPureGetByIdOptimize;
     else
         getByIdFunction = operationTryGetByIdOptimize;
 
@@ -4269,6 +4271,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case PureGetById: {
+        compilePureGetById(node);
+        break;
+    }
+
     case GetByIdWithThis: {
         JSValueOperand base(this, node->child1());
         JSValueRegs baseRegs = base.jsValueRegs();
index aaec893..f89c1e6 100644 (file)
@@ -179,8 +179,11 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg
         slowCases.append(slowPathTarget);
     slowCases.append(gen.slowPathJump());
     
+    auto slowPathFunction = type == AccessType::Get ? operationGetByIdOptimize :
+        type == AccessType::PureGet ? operationPureGetByIdOptimize : operationTryGetByIdOptimize;
+
     auto slowPath = slowPathCall(
-        slowCases, this, type == AccessType::Get ? operationGetByIdOptimize : operationTryGetByIdOptimize,
+        slowCases, this, slowPathFunction,
         spillMode, ExceptionCheckRequirement::CheckNeeded,
         resultGPR, gen.stubInfo(), baseGPR, identifierUID(identifierNumber));
     
@@ -4235,6 +4238,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case PureGetById: {
+        compilePureGetById(node);
+        break;
+    }
+
     case GetByIdFlush: {
         if (!node->prediction()) {
             terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
index de1b288..3af1cb7 100644 (file)
@@ -176,6 +176,7 @@ inline CapabilityLevel canCompile(Node* node)
     case NewArrayWithSize:
     case TryGetById:
     case GetById:
+    case PureGetById:
     case GetByIdFlush:
     case GetByIdWithThis:
     case ToThis:
index a8f9edb..3f09dcf 100644 (file)
@@ -623,7 +623,10 @@ private:
             compilePutStructure();
             break;
         case TryGetById:
-            compileGetById(AccessType::GetPure);
+            compileGetById(AccessType::TryGet);
+            break;
+        case PureGetById:
+            compileGetById(AccessType::PureGet);
             break;
         case GetById:
         case GetByIdFlush:
@@ -2788,7 +2791,7 @@ private:
     
     void compileGetById(AccessType type)
     {
-        ASSERT(type == AccessType::Get || type == AccessType::GetPure);
+        ASSERT(type == AccessType::Get || type == AccessType::TryGet || type == AccessType::PureGet);
         switch (m_node->child1().useKind()) {
         case CellUse: {
             setJSValue(getById(lowCell(m_node->child1()), type));
@@ -2815,6 +2818,8 @@ private:
             J_JITOperation_EJI getByIdFunction;
             if (type == AccessType::Get)
                 getByIdFunction = operationGetByIdGeneric;
+            else if (type == AccessType::PureGet)
+                getByIdFunction = operationPureGetByIdGeneric;
             else
                 getByIdFunction = operationTryGetByIdGeneric;
 
@@ -8838,6 +8843,8 @@ private:
                         J_JITOperation_ESsiJI optimizationFunction;
                         if (type == AccessType::Get)
                             optimizationFunction = operationGetByIdOptimize;
+                        else if (type == AccessType::PureGet)
+                            optimizationFunction = operationPureGetByIdOptimize;
                         else
                             optimizationFunction = operationTryGetByIdOptimize;
 
index 744e905..1eabbc4 100644 (file)
 
 namespace JSC {
 
+ALWAYS_INLINE static EncodedJSValue pureGetByIdCommon(VM& vm, ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid, const std::function<void (const PropertySlot&, const Identifier&)>& function = [] (const PropertySlot&, const Identifier&) { })
+{
+    Identifier ident = Identifier::fromUid(&vm, uid);
+    JSValue baseValue = JSValue::decode(base);
+
+    ASSERT(JITCode::isOptimizingJIT(exec->codeBlock()->jitType()));
+
+    PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+    return JSValue::encode(baseValue.getPropertySlot(exec, ident, slot, [&] (bool, PropertySlot&) -> JSValue {
+        bool willDoSideEffects = !(slot.isValue() || slot.isUnset()) || slot.isTaintedByOpaqueObject();
+        if (UNLIKELY(willDoSideEffects)) {
+            {
+                CodeOrigin codeOrigin = exec->codeOrigin();
+                CodeBlock* currentBaseline = baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, exec->codeBlock()->alternative());
+                CodeOrigin originBytecodeIndex = CodeOrigin(codeOrigin.bytecodeIndex); // Since we're searching in the baseline, we just care about bytecode index.
+                ConcurrentJITLocker locker(currentBaseline->m_lock);
+                if (StructureStubInfo* stub = currentBaseline->findStubInfo(originBytecodeIndex))
+                    stub->didSideEffects = true;
+            }
+
+            exec->codeBlock()->jettison(Profiler::JettisonDueToPureGetByIdEffects);
+            return baseValue.get(exec, uid);
+        }
+
+        function(slot, ident);
+        return slot.isValue() ? slot.getValue(exec, ident) : jsUndefined();
+    }));
+}
+
 extern "C" {
 
 #if COMPILER(MSVC)
@@ -165,6 +194,38 @@ int32_t JIT_OPERATION operationConstructArityCheck(ExecState* exec)
     return missingArgCount;
 }
 
+EncodedJSValue JIT_OPERATION operationPureGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    return pureGetByIdCommon(*vm, exec, base, uid);
+}
+
+EncodedJSValue JIT_OPERATION operationPureGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    stubInfo->tookSlowPath = true;
+
+    return pureGetByIdCommon(*vm, exec, base, uid);
+}
+
+EncodedJSValue JIT_OPERATION operationPureGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    return pureGetByIdCommon(*vm, exec, base, uid, 
+        [&] (const PropertySlot& slot, const Identifier& ident) {
+            ASSERT((slot.isValue() || slot.isUnset()) && !slot.isTaintedByOpaqueObject());
+            JSValue baseValue = JSValue::decode(base);
+            if (stubInfo->considerCaching(baseValue.structureOrNull()))
+                repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Pure);
+        });
+}
+
 EncodedJSValue JIT_OPERATION operationTryGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
 {
     VM* vm = &exec->vm();
@@ -179,7 +240,6 @@ EncodedJSValue JIT_OPERATION operationTryGetById(ExecState* exec, StructureStubI
     return JSValue::encode(slot.getPureResult());
 }
 
-
 EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
 {
     VM* vm = &exec->vm();
@@ -207,7 +267,7 @@ EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, Struct
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     if (stubInfo->considerCaching(baseValue.structureOrNull()) && !slot.isTaintedByOpaqueObject() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset()))
-        repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Pure);
+        repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Try);
 
     return JSValue::encode(slot.getPureResult());
 }
@@ -226,7 +286,10 @@ EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo
     Identifier ident = Identifier::fromUid(vm, uid);
     
     LOG_IC((ICEvent::OperationGetById, baseValue.classInfoOrNull(), ident));
-    return JSValue::encode(baseValue.get(exec, ident, slot));
+    JSValue result = baseValue.get(exec, ident, slot);
+    bool willDoSideEffects = !(slot.isValue() || slot.isUnset()) || slot.isTaintedByOpaqueObject();
+    stubInfo->didSideEffects |= willDoSideEffects;
+    return JSValue::encode(result);
 }
 
 EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
@@ -255,6 +318,9 @@ EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, Structure
     LOG_IC((ICEvent::OperationGetByIdOptimize, baseValue.classInfoOrNull(), ident));
 
     return JSValue::encode(baseValue.getPropertySlot(exec, ident, [&] (bool found, PropertySlot& slot) -> JSValue {
+        bool willDoSideEffects = !(slot.isValue() || slot.isUnset()) || slot.isTaintedByOpaqueObject();
+        stubInfo->didSideEffects |= willDoSideEffects;
+
         if (stubInfo->considerCaching(baseValue.structureOrNull()))
             repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Normal);
         return found ? slot.getValue(exec, ident) : jsUndefined();
index db5ba89..8f6c697 100644 (file)
@@ -330,6 +330,9 @@ int32_t JIT_OPERATION operationConstructArityCheck(ExecState*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationTryGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationPureGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationPureGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationPureGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetById(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState*, StructureStubInfo*, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
index d7424f1..e39d16c 100644 (file)
@@ -579,7 +579,7 @@ void JIT::emit_op_try_get_by_id(Instruction* currentInstruction)
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(m_bytecodeOffset), RegisterSet::stubUnavailableRegisters(),
-        ident->impl(), JSValueRegs(regT0), JSValueRegs(regT0), AccessType::GetPure);
+        ident->impl(), JSValueRegs(regT0), JSValueRegs(regT0), AccessType::TryGet);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
index 5f14e90..bc34b30 100644 (file)
@@ -594,7 +594,7 @@ void JIT::emit_op_try_get_by_id(Instruction* currentInstruction)
 
     JITGetByIdGenerator gen(
         m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
-        ident->impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::GetPure);
+        ident->impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::TryGet);
     gen.generateFastPath(*this);
     addSlowCase(gen.slowPathJump());
     m_getByIds.append(gen);
index 90691f2..b633617 100644 (file)
@@ -134,16 +134,34 @@ static bool forceICFailure(ExecState*)
 
 inline J_JITOperation_ESsiJI appropriateOptimizingGetByIdFunction(GetByIDKind kind)
 {
-    if (kind == GetByIDKind::Normal)
+    switch (kind) {
+    case GetByIDKind::Normal:
         return operationGetByIdOptimize;
-    return operationTryGetByIdOptimize;
+    case GetByIDKind::Try:
+        return operationTryGetByIdOptimize;
+    case GetByIDKind::Pure:
+        return operationPureGetByIdOptimize;
+    default:
+        break;
+    }
+    ASSERT_NOT_REACHED();
+    return operationGetByIdOptimize;
 }
 
 inline J_JITOperation_ESsiJI appropriateGenericGetByIdFunction(GetByIDKind kind)
 {
-    if (kind == GetByIDKind::Normal)
+    switch (kind) {
+    case GetByIDKind::Normal:
         return operationGetById;
-    return operationTryGetById;
+    case GetByIDKind::Try:
+        return operationTryGetById;
+    case GetByIDKind::Pure:
+        return operationPureGetById;
+    default:
+        break;
+    }
+    ASSERT_NOT_REACHED();
+    return operationGetById;
 }
 
 static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
@@ -271,6 +289,16 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
                 type = AccessCase::Load;
             else if (slot.isUnset())
                 type = AccessCase::Miss;
+            else
+                RELEASE_ASSERT_NOT_REACHED();
+
+            newCase = AccessCase::tryGet(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
+        } else if (kind == GetByIDKind::Try) {
+            AccessCase::AccessType type;
+            if (slot.isCacheableValue())
+                type = AccessCase::Load;
+            else if (slot.isUnset())
+                type = AccessCase::Miss;
             else if (slot.isCacheableGetter())
                 type = AccessCase::GetGetter;
             else
index e176768..e858744 100644 (file)
@@ -36,6 +36,7 @@ namespace JSC {
 
 enum class GetByIDKind {
     Normal,
+    Try,
     Pure
 };
 
index 3751fed..5c93533 100644 (file)
@@ -56,6 +56,9 @@ void printInternal(PrintStream& out, JettisonReason reason)
     case JettisonDueToOSRExit:
         out.print("OSRExit");
         return;
+    case JettisonDueToPureGetByIdEffects:
+        out.print("PureGetByIdEffects");
+        return;
     case JettisonDueToProfiledWatchpoint:
         out.print("ProfiledWatchpoint");
         return;
index 745964f..2fe9313 100644 (file)
@@ -35,6 +35,7 @@ enum JettisonReason {
     JettisonDueToBaselineLoopReoptimizationTrigger,
     JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail,
     JettisonDueToOSRExit,
+    JettisonDueToPureGetByIdEffects,
     JettisonDueToProfiledWatchpoint,
     JettisonDueToUnprofiledWatchpoint,
     JettisonDueToOldAge