Unreviewed, rolling out r231316 and r231332.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 May 2018 21:16:05 +0000 (21:16 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 May 2018 21:16:05 +0000 (21:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185564

Appears to be a Speedometer2/MotionMark regression (Requested
by keith_miller on #webkit).

Reverted changesets:

"Remove the prototype caching for get_by_id in the LLInt"
https://bugs.webkit.org/show_bug.cgi?id=185226
https://trac.webkit.org/changeset/231316

"Unreviewed, fix 32-bit profile offset for change in bytecode"
https://trac.webkit.org/changeset/231332

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

20 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecode/BytecodeDumper.cpp
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h [new file with mode: 0644]
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/Options.h

index cfcda97..dd9a6f8 100644 (file)
@@ -454,6 +454,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     bytecode/InternalFunctionAllocationProfile.h
     bytecode/JumpTable.h
     bytecode/LLIntCallLinkInfo.h
+    bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h
     bytecode/LazyOperandValueProfile.h
     bytecode/ObjectAllocationProfile.h
     bytecode/ObjectPropertyCondition.h
index 97f3a60..9455e15 100644 (file)
@@ -1,3 +1,20 @@
+2018-05-11  Commit Queue  <commit-queue@webkit.org>
+
+        Unreviewed, rolling out r231316 and r231332.
+        https://bugs.webkit.org/show_bug.cgi?id=185564
+
+        Appears to be a Speedometer2/MotionMark regression (Requested
+        by keith_miller on #webkit).
+
+        Reverted changesets:
+
+        "Remove the prototype caching for get_by_id in the LLInt"
+        https://bugs.webkit.org/show_bug.cgi?id=185226
+        https://trac.webkit.org/changeset/231316
+
+        "Unreviewed, fix 32-bit profile offset for change in bytecode"
+        https://trac.webkit.org/changeset/231332
+
 2018-05-11  Michael Saboff  <msaboff@apple.com>
 
         [DFG] Compiler uses incorrect output register for NumberIsInteger operation
index f1d0538..f7f1f44 100644 (file)
                53F40E951D5A7AEF0099A1B6 /* WasmModuleParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */; };
                53F6BF6D1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53F8D2001E8387D400D21116 /* WasmBBQPlanInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F8D1FF1E8387D400D21116 /* WasmBBQPlanInlines.h */; };
+               53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53FD04D41D7AB291003287D3 /* WasmCallingConvention.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FD04D21D7AB187003287D3 /* WasmCallingConvention.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53FF7F991DBFCD9000A26CCC /* WasmValidate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FF7F981DBFCD9000A26CCC /* WasmValidate.h */; };
                5B70CFDE1DB69E6600EC23F9 /* JSAsyncFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B70CFD81DB69E5C00EC23F9 /* JSAsyncFunction.h */; };
                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmModuleParser.cpp; sourceTree = "<group>"; };
                53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalFunctionAllocationProfile.h; sourceTree = "<group>"; };
                53F8D1FF1E8387D400D21116 /* WasmBBQPlanInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmBBQPlanInlines.h; sourceTree = "<group>"; };
+               53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.h; sourceTree = "<group>"; };
+               53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp; sourceTree = "<group>"; };
                53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmCallingConvention.cpp; sourceTree = "<group>"; };
                53FD04D21D7AB187003287D3 /* WasmCallingConvention.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmCallingConvention.h; sourceTree = "<group>"; };
                53FF7F981DBFCD9000A26CCC /* WasmValidate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmValidate.h; sourceTree = "<group>"; };
                                865A30F0135007E100CDB49E /* JSCJSValueInlines.h */,
                                FE2B0B681FD0D2970075DA5F /* JSCPoison.cpp */,
                                FE2B0B701FD8C4630075DA5F /* JSCPoison.h */,
-                               FE7497E5209001B00003565B /* JSCPtrTag.h */,
                                72AAF7CB1D0D318B005E60BE /* JSCustomGetterSetterFunction.cpp */,
                                72AAF7CC1D0D318B005E60BE /* JSCustomGetterSetterFunction.h */,
                                0F2B66BD17B6B5AB00A7AE3F /* JSDataView.cpp */,
                                2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */,
                                862553CE16136AA5009F17D0 /* JSProxy.cpp */,
                                862553CF16136AA5009F17D0 /* JSProxy.h */,
+                               FE7497E5209001B00003565B /* JSCPtrTag.h */,
                                534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */,
                                534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */,
                                14874AE115EBDE4A002E3587 /* JSScope.cpp */,
                                0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */,
                                0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */,
                                0F0FC45814BD15F100B81154 /* LLIntCallLinkInfo.h */,
+                               53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */,
+                               53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */,
                                0FB5467C14F5CFD3002C2989 /* MethodOfGettingAValueProfile.cpp */,
                                0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */,
                                20ECB15EFC524624BC2F02D5 /* ModuleNamespaceAccessCase.cpp */,
                                A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
                                148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
                                FE2B0B731FD9EF700075DA5F /* JSCPoison.h in Headers */,
-                               FE7497E6209001B10003565B /* JSCPtrTag.h in Headers */,
                                A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
                                72AAF7CE1D0D31B3005E60BE /* JSCustomGetterSetterFunction.h in Headers */,
                                0F2B66EC17B6B5AB00A7AE3F /* JSDataView.h in Headers */,
                                7C008CDB187124BB00955C24 /* JSPromiseDeferred.h in Headers */,
                                7C184E1F17BEE22E007CB63A /* JSPromisePrototype.h in Headers */,
                                996B731F1BDA08EF00331B84 /* JSPromisePrototype.lut.h in Headers */,
+                               FE7497E6209001B10003565B /* JSCPtrTag.h in Headers */,
                                2A05ABD61961DF2400341750 /* JSPropertyNameEnumerator.h in Headers */,
                                862553D216136E1A009F17D0 /* JSProxy.h in Headers */,
                                A552C3801ADDB8FE00139726 /* JSRemoteInspector.h in Headers */,
                                0F4680CB14BBB17200BFE272 /* LLIntOfflineAsmConfig.h in Headers */,
                                FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */,
                                79CFC6F01C33B10000C768EA /* LLIntPCRanges.h in Headers */,
+                               53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */,
                                0F4680A514BA7F8D00BFE272 /* LLIntSlowPaths.h in Headers */,
                                0F0B839D14BCF46600885B4F /* LLIntThunks.h in Headers */,
                                0F75A061200D26180038E2CF /* LocalAllocator.h in Headers */,
index 6c40a94..29f3d76 100644 (file)
@@ -224,6 +224,7 @@ bytecode/InlineCallFrame.cpp
 bytecode/InlineCallFrameSet.cpp
 bytecode/IntrinsicGetterAccessCase.cpp
 bytecode/JumpTable.cpp
+bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
 bytecode/LazyOperandValueProfile.cpp
 bytecode/MethodOfGettingAValueProfile.cpp
 bytecode/ModuleNamespaceAccessCase.cpp
index 6e534e7..abe9a8a 100644 (file)
@@ -377,6 +377,12 @@ void BytecodeDumper<Block>::printGetByIdOp(PrintStream& out, int location, const
     case op_get_by_id:
         op = "get_by_id";
         break;
+    case op_get_by_id_proto_load:
+        op = "get_by_id_proto_load";
+        break;
+    case op_get_by_id_unset:
+        op = "get_by_id_unset";
+        break;
     case op_get_array_length:
         op = "array_length";
         break;
@@ -391,7 +397,7 @@ void BytecodeDumper<Block>::printGetByIdOp(PrintStream& out, int location, const
     int id0 = (++it)->u.operand;
     printLocationAndOp(out, location, it, op);
     out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), idName(id0, identifier(id0)).data());
-    it += 3; // Increment up to the value profiler.
+    it += 4; // Increment up to the value profiler.
 }
 
 static void dumpStructure(PrintStream& out, const char* name, Structure* structure, const Identifier& ident)
@@ -436,6 +442,8 @@ void BytecodeDumper<Block>::printGetByIdCacheStatus(PrintStream& out, int locati
         out.printf(" llint(");
         dumpStructure(out, "struct", structure, ident);
         out.printf(")");
+        if (Interpreter::getOpcodeID(instruction[0]) == op_get_by_id_proto_load)
+            out.printf(" proto(%p)", getPointer(instruction[6]));
     }
 
 #if ENABLE(JIT)
@@ -1030,6 +1038,8 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         break;
     }
     case op_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_array_length: {
         printGetByIdOp(out, location, it);
         printGetByIdCacheStatus(out, location, stubInfos);
index 0046c6e..5628df5 100644 (file)
             { "name" : "op_is_function", "length" : 3 },
             { "name" : "op_is_cell_with_type", "length" : 4 },
             { "name" : "op_in", "length" : 5 },
-            { "name" : "op_get_array_length", "length" : 8 },
-            { "name" : "op_get_by_id", "length" : 8  },
+            { "name" : "op_get_array_length", "length" : 9 },
+            { "name" : "op_get_by_id", "length" : 9  },
+            { "name" : "op_get_by_id_proto_load", "length" : 9 },
+            { "name" : "op_get_by_id_unset", "length" : 9 },
             { "name" : "op_get_by_id_with_this", "length" : 6 },
             { "name" : "op_get_by_val_with_this", "length" : 6 },
             { "name" : "op_get_by_id_direct", "length" : 7  },
index 15b952a..701d36c 100644 (file)
@@ -182,6 +182,8 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_to_primitive:
     case op_try_get_by_id:
     case op_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_by_id_direct:
     case op_get_array_length:
     case op_typeof:
@@ -432,6 +434,8 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_construct:
     case op_try_get_by_id:
     case op_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_by_id_direct:
     case op_get_by_id_with_this:
     case op_get_by_val_with_this:
index d111de0..8f1a4bd 100644 (file)
@@ -65,6 +65,7 @@
 #include "JSTemplateObjectDescriptor.h"
 #include "LLIntData.h"
 #include "LLIntEntrypoint.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
 #include "LowLevelInterpreter.h"
 #include "ModuleProgramCodeBlock.h"
 #include "ObjectAllocationProfileInlines.h"
@@ -1250,7 +1251,9 @@ void CodeBlock::finalizeLLIntInlineCaches()
     for (size_t size = propertyAccessInstructions.size(), i = 0; i < size; ++i) {
         Instruction* curInstruction = &instructions()[propertyAccessInstructions[i]];
         switch (Interpreter::getOpcodeID(curInstruction[0])) {
-        case op_get_by_id: {
+        case op_get_by_id:
+        case op_get_by_id_proto_load:
+        case op_get_by_id_unset: {
             StructureID oldStructureID = curInstruction[4].u.structureID;
             if (!oldStructureID || Heap::isMarked(vm.heap.structureIDTable().get(oldStructureID)))
                 break;
@@ -1344,6 +1347,12 @@ void CodeBlock::finalizeLLIntInlineCaches()
         }
     }
 
+    // We can't just remove all the sets when we clear the caches since we might have created a watchpoint set
+    // then cleared the cache without GCing in between.
+    m_llintGetByIdWatchpointMap.removeIf([](const StructureWatchpointMap::KeyValuePairType& pair) -> bool {
+        return !Heap::isMarked(pair.key);
+    });
+
     for (unsigned i = 0; i < m_llintCallLinkInfos.size(); ++i) {
         if (m_llintCallLinkInfos[i].isLinked() && !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) {
             if (Options::verboseOSR())
index 854f34f..97b8ab9 100644 (file)
@@ -54,6 +54,7 @@
 #include "JSGlobalObject.h"
 #include "JumpTable.h"
 #include "LLIntCallLinkInfo.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
 #include "LazyOperandValueProfile.h"
 #include "ModuleProgramExecutable.h"
 #include "ObjectAllocationProfile.h"
@@ -627,6 +628,9 @@ public:
         return m_llintExecuteCounter;
     }
 
+    typedef HashMap<Structure*, Bag<LLIntPrototypeLoadAdaptiveStructureWatchpoint>> StructureWatchpointMap;
+    StructureWatchpointMap& llintGetByIdWatchpointMap() { return m_llintGetByIdWatchpointMap; }
+
     // Functions for controlling when tiered compilation kicks in. This
     // controls both when the optimizing compiler is invoked and when OSR
     // entry happens. Two triggers exist: the loop trigger and the return
@@ -971,6 +975,7 @@ private:
 
     RefCountedArray<LLIntCallLinkInfo> m_llintCallLinkInfos;
     SentinelLinkedList<LLIntCallLinkInfo, BasicRawSentinelNode<LLIntCallLinkInfo>> m_incomingLLIntCalls;
+    StructureWatchpointMap m_llintGetByIdWatchpointMap;
     PoisonedRefPtr<CodeBlockPoison, JITCode> m_jitCode;
 #if ENABLE(JIT)
     std::unique_ptr<RegisterAtOffsetList> m_calleeSaveRegisters;
index 9fcde55..04a3767 100644 (file)
@@ -104,7 +104,11 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
     }
 
     case op_get_array_length:
-    case op_try_get_by_id: {
+    case op_try_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset: {
+        // FIXME: We should not just bail if we see a try_get_by_id or a get_by_id_proto_load.
+        // https://bugs.webkit.org/show_bug.cgi?id=158039
         return GetByIdStatus(NoInformation, false);
     }
 
diff --git a/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp b/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
new file mode 100644 (file)
index 0000000..d6cc22d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016-2017 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 "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
+
+#include "CodeBlock.h"
+#include "Instruction.h"
+#include "JSCInlines.h"
+
+namespace JSC {
+
+LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint(const ObjectPropertyCondition& key, Instruction* getByIdInstruction)
+    : m_key(key)
+    , m_getByIdInstruction(getByIdInstruction)
+{
+    RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
+    RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
+}
+
+void LLIntPrototypeLoadAdaptiveStructureWatchpoint::install()
+{
+    RELEASE_ASSERT(m_key.isWatchable());
+
+    m_key.object()->structure()->addTransitionWatchpoint(this);
+}
+
+void LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal(const FireDetail&)
+{
+    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        install();
+        return;
+    }
+
+    CodeBlock::clearLLIntGetByIdCache(m_getByIdInstruction);
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h b/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h
new file mode 100644 (file)
index 0000000..8a73c6c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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 "Instruction.h"
+#include "ObjectPropertyCondition.h"
+#include "Watchpoint.h"
+
+namespace JSC {
+
+class LLIntPrototypeLoadAdaptiveStructureWatchpoint : public Watchpoint {
+public:
+    LLIntPrototypeLoadAdaptiveStructureWatchpoint(const ObjectPropertyCondition&, Instruction*);
+
+    void install();
+
+protected:
+    void fireInternal(const FireDetail&) override;
+
+private:
+    ObjectPropertyCondition m_key;
+    Instruction* m_getByIdInstruction;
+};
+
+} // namespace JSC
index 8695ca0..6d6449b 100644 (file)
@@ -2703,6 +2703,7 @@ RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, co
     instructions().append(0);
     instructions().append(0);
     instructions().append(0);
+    instructions().append(Options::prototypeHitCountForLLIntCaching());
     instructions().append(profile);
     return dst;
 }
index 35e3522..4059022 100644 (file)
@@ -5153,6 +5153,8 @@ void ByteCodeParser::parseBlock(unsigned limit)
         case op_get_by_id_direct:
         case op_try_get_by_id:
         case op_get_by_id:
+        case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         case op_get_array_length: {
             SpeculatedType prediction = getPrediction();
             
index db84e96..5cee65b 100644 (file)
@@ -164,6 +164,8 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_put_by_val_direct:
     case op_try_get_by_id:
     case op_get_by_id:
+    case op_get_by_id_proto_load:
+    case op_get_by_id_unset:
     case op_get_by_id_with_this:
     case op_get_by_id_direct:
     case op_get_by_val_with_this:
index 1746693..1d3cc91 100644 (file)
@@ -342,6 +342,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_beloweq)
         DEFINE_OP(op_try_get_by_id)
         case op_get_array_length:
+        case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_id_with_this)
         DEFINE_OP(op_get_by_id_direct)
@@ -519,6 +521,8 @@ void JIT::privateCompileSlowCases()
         DEFINE_SLOWCASE_OP(op_eq)
         DEFINE_SLOWCASE_OP(op_try_get_by_id)
         case op_get_array_length:
+        case op_get_by_id_proto_load:
+        case op_get_by_id_unset:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_by_id_with_this)
         DEFINE_SLOWCASE_OP(op_get_by_id_direct)
index 817e6c9..88bcbc9 100644 (file)
@@ -641,6 +641,63 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id_direct)
     LLINT_RETURN_PROFILED(op_get_by_id_direct, result);
 }
 
+
+static void setupGetByIdPrototypeCache(ExecState* exec, VM& vm, Instruction* pc, JSCell* baseCell, PropertySlot& slot, const Identifier& ident)
+{
+    CodeBlock* codeBlock = exec->codeBlock();
+    Structure* structure = baseCell->structure();
+
+    if (structure->typeInfo().prohibitsPropertyCaching())
+        return;
+    
+    if (structure->needImpurePropertyWatchpoint())
+        return;
+
+    if (structure->isDictionary()) {
+        if (structure->hasBeenFlattenedBefore())
+            return;
+        structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseCell));
+    }
+
+    ObjectPropertyConditionSet conditions;
+    if (slot.isUnset())
+        conditions = generateConditionsForPropertyMiss(vm, codeBlock, exec, structure, ident.impl());
+    else
+        conditions = generateConditionsForPrototypePropertyHit(vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
+
+    if (!conditions.isValid())
+        return;
+
+    PropertyOffset offset = invalidOffset;
+    CodeBlock::StructureWatchpointMap& watchpointMap = codeBlock->llintGetByIdWatchpointMap();
+    auto result = watchpointMap.add(structure, Bag<LLIntPrototypeLoadAdaptiveStructureWatchpoint>());
+    for (ObjectPropertyCondition condition : conditions) {
+        if (!condition.isWatchable())
+            return;
+        if (condition.condition().kind() == PropertyCondition::Presence)
+            offset = condition.condition().offset();
+        result.iterator->value.add(condition, pc)->install();
+    }
+    ASSERT((offset == invalidOffset) == slot.isUnset());
+
+    ConcurrentJSLocker locker(codeBlock->m_lock);
+
+    if (slot.isUnset()) {
+        pc[0].u.opcode = LLInt::getOpcode(op_get_by_id_unset);
+        pc[4].u.structureID = structure->id();
+        return;
+    }
+    ASSERT(slot.isValue());
+
+    pc[0].u.opcode = LLInt::getOpcode(op_get_by_id_proto_load);
+    pc[4].u.structureID = structure->id();
+    pc[5].u.operand = offset;
+    // We know that this pointer will remain valid because it will be cleared by either a watchpoint fire or
+    // during GC when we clear the LLInt caches.
+    pc[6].u.pointer = slot.slotBase();
+}
+
+
 LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
 {
     LLINT_BEGIN();
@@ -661,7 +718,9 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
             StructureID oldStructureID = pc[4].u.structureID;
             if (oldStructureID) {
                 auto opcode = Interpreter::getOpcodeID(pc[0]);
-                if (opcode == op_get_by_id) {
+                if (opcode == op_get_by_id
+                    || opcode == op_get_by_id_unset
+                    || opcode == op_get_by_id_proto_load) {
                     Structure* a = vm.heap.structureIDTable().get(oldStructureID);
                     Structure* b = baseValue.asCell()->structure(vm);
 
@@ -680,6 +739,9 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
             pc[0].u.opcode = LLInt::getOpcode(op_get_by_id);
             pc[4].u.pointer = nullptr; // old structure
             pc[5].u.pointer = nullptr; // offset
+
+            // Prevent the prototype cache from ever happening.
+            pc[7].u.operand = 0;
         
             if (structure->propertyAccessesAreCacheable()
                 && !structure->needImpurePropertyWatchpoint()) {
@@ -690,6 +752,11 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
                 pc[4].u.structureID = structure->id();
                 pc[5].u.operand = slot.cachedOffset();
             }
+        } else if (UNLIKELY(pc[7].u.operand && (slot.isValue() || slot.isUnset()))) {
+            ASSERT(slot.slotBase() != baseValue);
+
+            if (!(--pc[7].u.operand))
+                setupGetByIdPrototypeCache(exec, vm, pc, baseCell, slot, ident);
         }
     } else if (!LLINT_ALWAYS_ACCESS_SLOW
         && isJSArray(baseValue)
@@ -698,6 +765,9 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
         ArrayProfile* arrayProfile = codeBlock->getOrAddArrayProfile(codeBlock->bytecodeOffset(pc));
         arrayProfile->observeStructure(baseValue.asCell()->structure());
         pc[4].u.arrayProfile = arrayProfile;
+
+        // Prevent the prototype cache from ever happening.
+        pc[7].u.operand = 0;
     }
 
     pc[OPCODE_LENGTH(op_get_by_id) - 1].u.profile->m_buckets[0] = JSValue::encode(result);
index 16d5ab8..d51b6d1 100644 (file)
@@ -1420,7 +1420,7 @@ _llint_op_get_by_id:
     loadi 4[PC], t2
     storei t0, TagOffset[cfr, t2, 8]
     storei t1, PayloadOffset[cfr, t2, 8]
-    valueProfile(t0, t1, 28, t2)
+    valueProfile(t0, t1, 32, t2)
     dispatch(constexpr op_get_by_id_length)
 
 .opGetByIdSlow:
@@ -1428,6 +1428,43 @@ _llint_op_get_by_id:
     dispatch(constexpr op_get_by_id_length)
 
 
+_llint_op_get_by_id_proto_load:
+    traceExecution()
+    loadi 8[PC], t0
+    loadi 16[PC], t1
+    loadConstantOrVariablePayload(t0, CellTag, t3, .opGetByIdProtoSlow)
+    loadi 20[PC], t2
+    bineq JSCell::m_structureID[t3], t1, .opGetByIdProtoSlow
+    loadpFromInstruction(6, t3)
+    loadPropertyAtVariableOffset(t2, t3, t0, t1)
+    loadi 4[PC], t2
+    storei t0, TagOffset[cfr, t2, 8]
+    storei t1, PayloadOffset[cfr, t2, 8]
+    valueProfile(t0, t1, 32, t2)
+    dispatch(constexpr op_get_by_id_proto_load_length)
+
+.opGetByIdProtoSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(constexpr op_get_by_id_proto_load_length)
+
+
+_llint_op_get_by_id_unset:
+    traceExecution()
+    loadi 8[PC], t0
+    loadi 16[PC], t1
+    loadConstantOrVariablePayload(t0, CellTag, t3, .opGetByIdUnsetSlow)
+    bineq JSCell::m_structureID[t3], t1, .opGetByIdUnsetSlow
+    loadi 4[PC], t2
+    storei UndefinedTag, TagOffset[cfr, t2, 8]
+    storei 0, PayloadOffset[cfr, t2, 8]
+    valueProfile(UndefinedTag, 0, 32, t2)
+    dispatch(constexpr op_get_by_id_unset_length)
+
+.opGetByIdUnsetSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(constexpr op_get_by_id_unset_length)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadi 8[PC], t0
@@ -1441,7 +1478,7 @@ _llint_op_get_array_length:
     loadp JSObject::m_butterfly[t3], t0
     loadi -sizeof IndexingHeader + IndexingHeader::u.lengths.publicLength[t0], t0
     bilt t0, 0, .opGetArrayLengthSlow
-    valueProfile(Int32Tag, t0, 28, t2)
+    valueProfile(Int32Tag, t0, 32, t2)
     storep t0, PayloadOffset[cfr, t1, 8]
     storep Int32Tag, TagOffset[cfr, t1, 8]
     dispatch(constexpr op_get_array_length_length)
index 7d2a98d..626989d 100644 (file)
@@ -1342,7 +1342,7 @@ _llint_op_get_by_id:
     loadisFromInstruction(1, t2)
     loadPropertyAtVariableOffset(t1, t3, t0)
     storeq t0, [cfr, t2, 8]
-    valueProfile(t0, constexpr (op_get_by_id_length - 1), t1)
+    valueProfile(t0, 8, t1)
     dispatch(constexpr op_get_by_id_length)
 
 .opGetByIdSlow:
@@ -1350,6 +1350,43 @@ _llint_op_get_by_id:
     dispatch(constexpr op_get_by_id_length)
 
 
+_llint_op_get_by_id_proto_load:
+    traceExecution()
+    loadisFromInstruction(2, t0)
+    loadConstantOrVariableCell(t0, t3, .opGetByIdProtoSlow)
+    loadi JSCell::m_structureID[t3], t1
+    loadisFromInstruction(4, t2)
+    bineq t2, t1, .opGetByIdProtoSlow
+    loadisFromInstruction(5, t1)
+    loadpFromInstruction(6, t3)
+    loadisFromInstruction(1, t2)
+    loadPropertyAtVariableOffset(t1, t3, t0)
+    storeq t0, [cfr, t2, 8]
+    valueProfile(t0, 8, t1)
+    dispatch(constexpr op_get_by_id_proto_load_length)
+
+.opGetByIdProtoSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(constexpr op_get_by_id_proto_load_length)
+
+
+_llint_op_get_by_id_unset:
+    traceExecution()
+    loadisFromInstruction(2, t0)
+    loadConstantOrVariableCell(t0, t3, .opGetByIdUnsetSlow)
+    loadi JSCell::m_structureID[t3], t1
+    loadisFromInstruction(4, t2)
+    bineq t2, t1, .opGetByIdUnsetSlow
+    loadisFromInstruction(1, t2)
+    storeq ValueUndefined, [cfr, t2, 8]
+    valueProfile(ValueUndefined, 8, t1)
+    dispatch(constexpr op_get_by_id_unset_length)
+
+.opGetByIdUnsetSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(constexpr op_get_by_id_unset_length)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadisFromInstruction(2, t0)
@@ -1364,7 +1401,7 @@ _llint_op_get_array_length:
     loadi -sizeof IndexingHeader + IndexingHeader::u.lengths.publicLength[t0], t0
     bilt t0, 0, .opGetArrayLengthSlow
     orq tagTypeNumber, t0
-    valueProfile(t0, constexpr (op_get_array_length_length - 1), t2)
+    valueProfile(t0, 8, t2)
     storeq t0, [cfr, t1, 8]
     dispatch(constexpr op_get_array_length_length)
 
index ee18e21..7c1409a 100644 (file)
@@ -457,6 +457,8 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     \
     v(bool, useICStats, false, Normal, nullptr) \
     \
+    v(unsigned, prototypeHitCountForLLIntCaching, 2, Normal, "Number of prototype property hits before caching a prototype in the LLInt. A count of 0 means never cache.") \
+    \
     v(bool, dumpCompiledRegExpPatterns, false, Normal, nullptr) \
     \
     v(bool, dumpModuleRecord, false, Normal, nullptr) \