LLInt should be able to cache prototype loads for values in GetById
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 May 2016 23:49:57 +0000 (23:49 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 May 2016 23:49:57 +0000 (23:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158032

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch adds prototype value caching to the LLInt for op_get_by_id.
Two previously unused words in the op_get_by_id bytecode have been
repurposed to hold extra information for the cache. The first is a
counter that records the number of get_by_ids that hit a cacheable value
on a prototype. When the counter is decremented from one to zero we
attempt to cache the prototype load, which will be discussed further
below. The second word is used to hold the prototype object when we have
started caching.

When the counter is decremented to zero we first attempt to generate and
watch the property conditions needed to ensure the validity of prototype
load. If the watchpoints are successfully created and installed we
replace the op_get_by_id opcode with the new op_get_by_id_proto_load
opcode, which tells the LLInt to use the cache prototype object for the
load rather than the base value.

Prior to this patch there was not LLInt specific data onCodeBlocks.
Since the CodeBlock needs to own the Watchpoints for the cache, a weak
map from each base structure to a bag of Watchpoints created for that
structure by some op_get_by_id has been added to the CodeBlock. During
GC, if we find that the a structure in the map has not been marked we
free the associated bag on the CodeBlock.

* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printGetByIdOp):
(JSC::CodeBlock::printGetByIdCacheStatus):
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::finalizeLLIntInlineCaches):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::llintGetByIdWatchpointMap):
(JSC::clearLLIntGetByIdCache):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFromLLInt):
* bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp: Added.
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint):
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::install):
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal):
* bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h: Added.
* bytecode/ObjectPropertyConditionSet.cpp:
(JSC::ObjectPropertyConditionSet::isValidAndWatchable):
* bytecode/ObjectPropertyConditionSet.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitGetById):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::setupGetByIdPrototypeCache):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/Options.h:
* tests/stress/llint-get-by-id-cache-prototype-load-from-dictionary.js: Added.
(test):

Source/WTF:

Add move constructors/initializers to Bags.

* wtf/Bag.h:
(WTF::Bag::Bag):
(WTF::Bag::operator=):

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

24 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
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/bytecode/ObjectPropertyConditionSet.cpp
Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h
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/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/tests/stress/llint-get-by-id-cache-prototype-load-from-dictionary.js [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/Bag.h

index 429b2a0..13108dc 100644 (file)
@@ -202,6 +202,7 @@ set(JavaScriptCore_SOURCES
     bytecode/InlineCallFrame.cpp
     bytecode/InlineCallFrameSet.cpp
     bytecode/JumpTable.cpp
+    bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
     bytecode/LazyOperandValueProfile.cpp
     bytecode/MethodOfGettingAValueProfile.cpp
     bytecode/ObjectPropertyCondition.cpp
index 58c92bd..e276e85 100644 (file)
@@ -1,5 +1,77 @@
 2016-05-24  Keith Miller  <keith_miller@apple.com>
 
+        LLInt should be able to cache prototype loads for values in GetById
+        https://bugs.webkit.org/show_bug.cgi?id=158032
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds prototype value caching to the LLInt for op_get_by_id.
+        Two previously unused words in the op_get_by_id bytecode have been
+        repurposed to hold extra information for the cache. The first is a
+        counter that records the number of get_by_ids that hit a cacheable value
+        on a prototype. When the counter is decremented from one to zero we
+        attempt to cache the prototype load, which will be discussed further
+        below. The second word is used to hold the prototype object when we have
+        started caching.
+
+        When the counter is decremented to zero we first attempt to generate and
+        watch the property conditions needed to ensure the validity of prototype
+        load. If the watchpoints are successfully created and installed we
+        replace the op_get_by_id opcode with the new op_get_by_id_proto_load
+        opcode, which tells the LLInt to use the cache prototype object for the
+        load rather than the base value.
+
+        Prior to this patch there was not LLInt specific data onCodeBlocks.
+        Since the CodeBlock needs to own the Watchpoints for the cache, a weak
+        map from each base structure to a bag of Watchpoints created for that
+        structure by some op_get_by_id has been added to the CodeBlock. During
+        GC, if we find that the a structure in the map has not been marked we
+        free the associated bag on the CodeBlock.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::printGetByIdOp):
+        (JSC::CodeBlock::printGetByIdCacheStatus):
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::finalizeLLIntInlineCaches):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::llintGetByIdWatchpointMap):
+        (JSC::clearLLIntGetByIdCache):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeFromLLInt):
+        * bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp: Added.
+        (JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::LLIntPrototypeLoadAdaptiveStructureWatchpoint):
+        (JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::install):
+        (JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal):
+        * bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h: Added.
+        * bytecode/ObjectPropertyConditionSet.cpp:
+        (JSC::ObjectPropertyConditionSet::isValidAndWatchable):
+        * bytecode/ObjectPropertyConditionSet.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitGetById):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileSlowCases):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::setupGetByIdPrototypeCache):
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/Options.h:
+        * tests/stress/llint-get-by-id-cache-prototype-load-from-dictionary.js: Added.
+        (test):
+
+2016-05-24  Keith Miller  <keith_miller@apple.com>
+
         We should be able to use the sampling profiler with DRT/WTR.
         https://bugs.webkit.org/show_bug.cgi?id=158041
 
index 8c8925c..3aa8a72 100644 (file)
                5370B4F61BF26205005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5370B4F41BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h */; };
                53917E7B1B7906FA000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */; };
                53F6BF6D1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53FA2AE31CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */; };
                5D5D8AD10E0D0EBE00F9C692 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */; };
                5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 14D857740A4696C80032146C /* testapi.js */; };
                5DBB1525131D0BD70056AD36 /* minidom.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 1412110D0A48788700480255 /* minidom.js */; };
                53917E831B791CB8000EBD33 /* TypedArrayPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = TypedArrayPrototype.js; path = builtins/TypedArrayPrototype.js; sourceTree = SOURCE_ROOT; };
                53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArrayViewPrototype.cpp; sourceTree = "<group>"; };
                53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalFunctionAllocationProfile.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>"; };
                593D43CCA0BBE06D89C59707 /* MapDataInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapDataInlines.h; sourceTree = "<group>"; };
                5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libedit.dylib; path = /usr/lib/libedit.dylib; sourceTree = "<absolute>"; };
                5DAFD6CB146B686300FBEFB4 /* JSC.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = JSC.xcconfig; sourceTree = "<group>"; };
                                0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */,
                                0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */,
                                0F0FC45814BD15F100B81154 /* LLIntCallLinkInfo.h */,
+                               53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */,
+                               53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */,
                                0FB5467C14F5CFD3002C2989 /* MethodOfGettingAValueProfile.cpp */,
                                0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */,
                                14CA958C16AB50FA00938A06 /* ObjectAllocationProfile.h */,
                                0FF7168C15A3B235008F5DAA /* PropertyOffset.h in Headers */,
                                BC18C4550E16F5CD00B34460 /* PropertySlot.h in Headers */,
                                0FB7F39C15ED8E4600F167B2 /* PropertyStorage.h in Headers */,
+                               53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */,
                                BC18C4560E16F5CD00B34460 /* Protect.h in Headers */,
                                1474C33B16AA2D950062F01D /* PrototypeMap.h in Headers */,
                                0F5780A218FE1E98001E72D9 /* PureNaN.h in Headers */,
                                65C02850171795E200351E35 /* ARMv7Disassembler.cpp in Sources */,
                                65C0285C1717966800351E35 /* ARMv7DOpcode.cpp in Sources */,
                                0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */,
+                               53FA2AE31CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp in Sources */,
                                A7A8AF3417ADB5F3005AB174 /* ArrayBuffer.cpp in Sources */,
                                0FFC99D4184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp in Sources */,
                                A7A8AF3617ADB5F3005AB174 /* ArrayBufferView.cpp in Sources */,
index a80656e..21ffb38 100644 (file)
             { "name" : "op_is_object_or_null", "length" : 3 },
             { "name" : "op_is_function", "length" : 3 },
             { "name" : "op_in", "length" : 4 },
-            { "name" : "op_try_get_by_id", "length" : 4 },
+            { "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_with_this", "length" : 5 },
             { "name" : "op_get_by_val_with_this", "length" : 5 },
-            { "name" : "op_get_array_length", "length" : 9 },
+            { "name" : "op_try_get_by_id", "length" : 4 },
             { "name" : "op_put_by_id", "length" : 9 },
             { "name" : "op_put_by_id_with_this", "length" : 5 },
             { "name" : "op_del_by_id", "length" : 4 },
index fd1f44e..6a7f75e 100644 (file)
@@ -158,6 +158,7 @@ void computeUsesForBytecodeOffset(
     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_array_length:
     case op_typeof:
     case op_is_empty:
@@ -392,6 +393,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     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_with_this:
     case op_get_by_val_with_this:
     case op_get_array_length:
index 2e1af96..04e3619 100644 (file)
@@ -50,6 +50,7 @@
 #include "JSLexicalEnvironment.h"
 #include "JSModuleEnvironment.h"
 #include "LLIntEntrypoint.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
 #include "PCToCodeOriginMap.h"
@@ -345,6 +346,9 @@ void CodeBlock::printGetByIdOp(PrintStream& out, ExecState* exec, int location,
     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_array_length:
         op = "array_length";
         break;
@@ -405,6 +409,8 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
         out.printf(" llint(");
         dumpStructure(out, "struct", structure, ident);
         out.printf(")");
+        if (exec->interpreter()->getOpcodeID(instruction[0].u.opcode) == op_get_by_id_proto_load)
+            out.printf(" proto(%p)", instruction[6].u.pointer);
     }
 
 #if ENABLE(JIT)
@@ -1112,6 +1118,7 @@ void CodeBlock::dumpBytecode(
             break;
         }
         case op_get_by_id:
+        case op_get_by_id_proto_load:
         case op_get_array_length: {
             printGetByIdOp(out, exec, location, it);
             printGetByIdCacheStatus(out, exec, location, stubInfos);
@@ -2798,14 +2805,14 @@ void CodeBlock::finalizeLLIntInlineCaches()
     for (size_t size = propertyAccessInstructions.size(), i = 0; i < size; ++i) {
         Instruction* curInstruction = &instructions()[propertyAccessInstructions[i]];
         switch (interpreter->getOpcodeID(curInstruction[0].u.opcode)) {
-        case op_get_by_id: {
+        case op_get_by_id:
+        case op_get_by_id_proto_load: {
             StructureID oldStructureID = curInstruction[4].u.structureID;
             if (!oldStructureID || Heap::isMarked(m_vm->heap.structureIDTable().get(oldStructureID)))
                 break;
             if (Options::verboseOSR())
                 dataLogF("Clearing LLInt property access.\n");
-            curInstruction[4].u.structureID = 0;
-            curInstruction[5].u.operand = 0;
+            clearLLIntGetByIdCache(curInstruction);
             break;
         }
         case op_put_by_id: {
@@ -2879,6 +2886,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 cbb8cf5..664ecfe 100644 (file)
@@ -56,6 +56,7 @@
 #include "JSGlobalObject.h"
 #include "JumpTable.h"
 #include "LLIntCallLinkInfo.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
 #include "LazyOperandValueProfile.h"
 #include "ObjectAllocationProfile.h"
 #include "Options.h"
@@ -678,6 +679,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
@@ -1019,6 +1023,7 @@ private:
 
     RefCountedArray<LLIntCallLinkInfo> m_llintCallLinkInfos;
     SentinelLinkedList<LLIntCallLinkInfo, BasicRawSentinelNode<LLIntCallLinkInfo>> m_incomingLLIntCalls;
+    StructureWatchpointMap m_llintGetByIdWatchpointMap;
     RefPtr<JITCode> m_jitCode;
 #if ENABLE(JIT)
     std::unique_ptr<RegisterAtOffsetList> m_calleeSaveRegisters;
@@ -1309,6 +1314,14 @@ private:
 };
 #endif
 
+inline void clearLLIntGetByIdCache(Instruction* instruction)
+{
+    instruction[0].u.opcode = LLInt::getOpcode(op_get_by_id);
+    instruction[4].u.pointer = nullptr;
+    instruction[5].u.pointer = nullptr;
+    instruction[6].u.pointer = nullptr;
+}
+
 inline Register& ExecState::r(int index)
 {
     CodeBlock* codeBlock = this->codeBlock();
index c69514c..59e81c7 100644 (file)
@@ -75,8 +75,12 @@ GetByIdStatus GetByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
     VM& vm = *profiledBlock->vm();
     
     Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
-    
-    if (instruction[0].u.opcode == LLInt::getOpcode(op_get_array_length) || instruction[0].u.opcode == LLInt::getOpcode(op_try_get_by_id))
+
+    Opcode opcode = instruction[0].u.opcode;
+
+    // FIXME: We should not just bail if we see a try_get_by_id or a get_by_id_proto_load.\ e
+    // https://bugs.webkit.org/show_bug.cgi?id=158039
+    if (opcode == LLInt::getOpcode(op_get_array_length) || opcode == LLInt::getOpcode(op_try_get_by_id) || opcode == LLInt::getOpcode(op_get_by_id_proto_load))
         return GetByIdStatus(NoInformation, false);
 
     StructureID structureID = instruction[4].u.structureID;
diff --git a/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp b/Source/JavaScriptCore/bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
new file mode 100644 (file)
index 0000000..7ae2c0d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
+
+#include "CodeBlock.h"
+#include "Instruction.h"
+#include "StructureInlines.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& detail)
+{
+    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        install();
+        return;
+    }
+
+    StringPrintStream out;
+    out.print("ObjectToStringValue Adaptation of ", m_key, " failed: ", detail);
+
+    StringFireDetail stringDetail(out.toCString().data());
+
+    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..2615e10
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef LLIntPrototypeLoadAdaptiveStructureWatchpoint_h
+#define LLIntPrototypeLoadAdaptiveStructureWatchpoint_h
+
+#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
+
+#endif /* LLIntPrototypeLoadAdaptiveStructureWatchpoint_h */
index d570040..05723ad 100644 (file)
@@ -167,6 +167,18 @@ void ObjectPropertyConditionSet::dump(PrintStream& out) const
     dumpInContext(out, nullptr);
 }
 
+bool ObjectPropertyConditionSet::isValidAndWatchable() const
+{
+    if (!isValid())
+        return false;
+
+    for (ObjectPropertyCondition condition : m_data->vector) {
+        if (!condition.isWatchable())
+            return false;
+    }
+    return true;
+}
+
 namespace {
 
 bool verbose = false;
@@ -254,9 +266,11 @@ ObjectPropertyConditionSet generateConditions(
         // Since we're accessing a prototype repeatedly, it's a good bet that it should not be
         // treated as a dictionary.
         if (structure->isDictionary()) {
-            if (concurrency == MainThread)
+            if (concurrency == MainThread) {
+                if (verbose)
+                    dataLog("Flattening ", pointerDump(structure));
                 structure->flattenDictionaryStructure(vm, object);
-            else {
+            else {
                 if (verbose)
                     dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n");
                 return ObjectPropertyConditionSet::invalid();
index 76e8a9c..01ce1a8 100644 (file)
@@ -67,6 +67,8 @@ public:
     {
         return !m_data || !m_data->vector.isEmpty();
     }
+
+    bool isValidAndWatchable() const;
     
     bool isEmpty() const
     {
index b50151d..5803661 100644 (file)
@@ -2431,7 +2431,7 @@ RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, co
     instructions().append(0);
     instructions().append(0);
     instructions().append(0);
-    instructions().append(0);
+    instructions().append(Options::prototypeHitCountForLLIntCaching());
     instructions().append(profile);
     return dst;
 }
index aa831c3..3c28122 100644 (file)
@@ -4081,6 +4081,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         }
 
         case op_get_by_id:
+        case op_get_by_id_proto_load:
         case op_get_array_length: {
             SpeculatedType prediction = getPrediction();
             
index 06669d4..062427c 100644 (file)
@@ -154,6 +154,7 @@ 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_with_this:
     case op_get_by_val_with_this:
     case op_get_array_length:
index adaa860..92aac99 100644 (file)
@@ -240,6 +240,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_eq_null)
         DEFINE_OP(op_try_get_by_id)
         case op_get_array_length:
+        case op_get_by_id_proto_load:
         DEFINE_OP(op_get_by_id)
         DEFINE_OP(op_get_by_id_with_this)
         DEFINE_OP(op_get_by_val)
@@ -422,6 +423,7 @@ 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:
         DEFINE_SLOWCASE_OP(op_get_by_id)
         DEFINE_SLOWCASE_OP(op_get_by_val)
         DEFINE_SLOWCASE_OP(op_instanceof)
index fa88374..a103588 100644 (file)
@@ -52,6 +52,7 @@
 #include "LLIntExceptions.h"
 #include "LowLevelInterpreter.h"
 #include "ObjectConstructor.h"
+#include "ObjectPropertyConditionSet.h"
 #include "ProtoCallFrame.h"
 #include "ShadowChicken.h"
 #include "StructureRareDataInlines.h"
@@ -580,6 +581,43 @@ LLINT_SLOW_PATH_DECL(slow_path_try_get_by_id)
     LLINT_RETURN(slot.getPureResult());
 }
 
+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() || structure->isDictionary())
+        return;
+
+    ObjectPropertyConditionSet 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);
+
+    ConcurrentJITLocker locker(codeBlock->m_lock);
+
+    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 is the prototype of some structure, s,
+    // watchpointed above. If any object with structure s were to change prototypes then the conditions
+    // for this cache would fail and this value will never be used again.
+    pc[6].u.pointer = slot.slotBase();
+}
+
+
 LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
 {
     LLINT_BEGIN();
@@ -594,37 +632,43 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
     
     if (!LLINT_ALWAYS_ACCESS_SLOW
         && baseValue.isCell()
-        && slot.isCacheable()
-        && slot.slotBase() == baseValue
         && slot.isCacheableValue()) {
-        
+
         JSCell* baseCell = baseValue.asCell();
         Structure* structure = baseCell->structure();
+        if (slot.slotBase() == baseValue) {
+            // Start out by clearing out the old cache.
+            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;
         
-        // Start out by clearing out the old cache.
-        pc[0].u.opcode = LLInt::getOpcode(op_get_by_id);
-        pc[4].u.pointer = nullptr; // old structure
-        pc[5].u.pointer = nullptr; // offset
-        
-        if (!structure->isUncacheableDictionary()
-            && !structure->typeInfo().prohibitsPropertyCaching()
-            && !structure->typeInfo().newImpurePropertyFiresWatchpoints()) {
-            vm.heap.writeBarrier(codeBlock);
-            
-            ConcurrentJITLocker locker(codeBlock->m_lock);
+            if (structure->propertyAccessesAreCacheable()) {
+                vm.heap.writeBarrier(codeBlock);
+                
+                ConcurrentJITLocker locker(codeBlock->m_lock);
 
-            pc[4].u.structureID = structure->id();
-            pc[5].u.operand = slot.cachedOffset();
-        }
-    }
+                pc[4].u.structureID = structure->id();
+                pc[5].u.operand = slot.cachedOffset();
+            }
+        } else if (UNLIKELY(pc[7].u.operand)) {
+            ASSERT(slot.slotBase() != baseValue);
 
-    if (!LLINT_ALWAYS_ACCESS_SLOW
+            if (!(--pc[7].u.operand))
+                setupGetByIdPrototypeCache(exec, vm, pc, baseCell, slot, ident);
+        }
+    } else if (!LLINT_ALWAYS_ACCESS_SLOW
         && isJSArray(baseValue)
         && ident == exec->propertyNames().length) {
         pc[0].u.opcode = LLInt::getOpcode(op_get_array_length);
         ArrayProfile* arrayProfile = codeBlock->getOrAddArrayProfile(pc - codeBlock->instructions().begin());
         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 2499542..da36305 100644 (file)
@@ -71,6 +71,7 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof_custom);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_try_get_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id_proto_load);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_del_by_id);
index 52e0b45..98eea4f 100644 (file)
@@ -1334,10 +1334,11 @@ end
 
 
 # We only do monomorphic get_by_id caching for now, and we do not modify the
-# opcode. We do, however, allow for the cache to change anytime if fails, since
-# ping-ponging is free. At best we get lucky and the get_by_id will continue
+# opcode for own properties. We also allow for the cache to change anytime it fails,
+# since ping-ponging is free. At best we get lucky and the get_by_id will continue
 # to take fast path on the new cache. At worst we take slow path, which is what
-# we would have been doing anyway.
+# we would have been doing anyway. For prototype properties, we will attempt to
+# convert opcode into a get_by_id_proto_load after a execution counter hits zero.
 
 _llint_op_get_by_id:
     traceExecution()
@@ -1358,6 +1359,27 @@ _llint_op_get_by_id:
     dispatch(9)
 
 
+
+_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(9)
+
+.opGetByIdProtoSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadi 8[PC], t0
index 46e5616..511ba74 100644 (file)
@@ -1232,6 +1232,26 @@ _llint_op_get_by_id:
     dispatch(9)
 
 
+_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(9)
+
+.opGetByIdProtoSlow:
+    callSlowPath(_llint_slow_path_get_by_id)
+    dispatch(9)
+
+
 _llint_op_get_array_length:
     traceExecution()
     loadisFromInstruction(2, t0)
index 9b4bb19..5d92d5d 100644 (file)
@@ -362,6 +362,8 @@ typedef const char* optionString;
     \
     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, dumpModuleRecord, false, Normal, nullptr) \
     v(bool, dumpModuleLoadingState, false, Normal, nullptr) \
     v(bool, exposeInternalModuleLoader, false, Normal, "expose the internal module loader object to the global space for debugging") \
diff --git a/Source/JavaScriptCore/tests/stress/llint-get-by-id-cache-prototype-load-from-dictionary.js b/Source/JavaScriptCore/tests/stress/llint-get-by-id-cache-prototype-load-from-dictionary.js
new file mode 100644 (file)
index 0000000..44a27e1
--- /dev/null
@@ -0,0 +1,19 @@
+
+expected = Object.prototype.toString;
+foo = {foo: 1, bar: 20};
+delete foo.bar;
+
+
+function test() {
+    let toString = foo.toString;
+    if (toString !== expected)
+        throw new Error();
+}
+
+for (i = 0; i < 10; i++)
+    test();
+
+foo.toString = 100;
+expected = 100;
+
+test();
index cf84282..ac9cf21 100644 (file)
@@ -1,3 +1,16 @@
+2016-05-24  Keith Miller  <keith_miller@apple.com>
+
+        LLInt should be able to cache prototype loads for values in GetById
+        https://bugs.webkit.org/show_bug.cgi?id=158032
+
+        Reviewed by Filip Pizlo.
+
+        Add move constructors/initializers to Bags.
+
+        * wtf/Bag.h:
+        (WTF::Bag::Bag):
+        (WTF::Bag::operator=):
+
 2016-05-24  Chris Dumez  <cdumez@apple.com>
 
         Use auto for some of our lambda function parameters
index db51132..52040c5 100644 (file)
@@ -48,9 +48,22 @@ private:
     
 public:
     Bag()
-        : m_head(nullptr)
     {
     }
+
+    Bag(Bag<T>&& other)
+    {
+        ASSERT(!m_head);
+        m_head = other.m_head;
+        other.m_head = nullptr;
+    }
+
+    Bag& operator=(Bag<T>&& other)
+    {
+        m_head = other.m_head;
+        other.m_head = nullptr;
+        return *this;
+    }
     
     ~Bag()
     {
@@ -121,7 +134,7 @@ public:
     bool isEmpty() const { return !m_head; }
     
 private:
-    Node* m_head;
+    Node* m_head { nullptr };
 };
 
 } // namespace WTF