2008-11-19 Gavin Barraclough <barraclough@apple.com>
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2008 05:04:19 +0000 (05:04 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2008 05:04:19 +0000 (05:04 +0000)
        Reviewed by Darin Adler.

        Add support for (really) polymorphic caching of prototype accesses.

        If a cached prototype access misses, cti_op_get_by_id_proto_list is called.
        When this occurs the Structure pointers from the instruction stream are copied
        off into a new ProtoStubInfo object.  A second prototype access trampoline is
        generated, and chained onto the first.  Subsequent missed call to
        cti_op_get_by_id_proto_list_append, which append futher new trampolines, up to
        PROTOTYPE_LIST_CACHE_SIZE (currently 4).  If any of the misses result in an
        access other than to a direct prototype property, list formation is halted (or
        for the initial miss, does not take place at all).

        Separate fail case functions are provided for each access since this contributes
        to the performance progression (enables better processor branch prediction).

        Overall this is a near 5% progression on v8, with around 10% wins on richards
        and deltablue.

        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::dump):
        (JSC::CodeBlock::derefStructures):
        * bytecode/Instruction.h:
        (JSC::ProtoStructureList::ProtoStubInfo::set):
        (JSC::ProtoStructureList::ProtoStructureList):
        (JSC::Instruction::Instruction):
        (JSC::Instruction::):
        * bytecode/Opcode.h:
        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::privateExecute):
        (JSC::Interpreter::tryCTICacheGetByID):
        (JSC::Interpreter::cti_op_put_by_id_fail):
        (JSC::Interpreter::cti_op_get_by_id_self_fail):
        (JSC::Interpreter::cti_op_get_by_id_proto_list):
        (JSC::Interpreter::cti_op_get_by_id_proto_list_append):
        (JSC::Interpreter::cti_op_get_by_id_proto_list_full):
        (JSC::Interpreter::cti_op_get_by_id_proto_fail):
        (JSC::Interpreter::cti_op_get_by_id_chain_fail):
        (JSC::Interpreter::cti_op_get_by_id_array_fail):
        (JSC::Interpreter::cti_op_get_by_id_string_fail):
        * interpreter/Interpreter.h:
        * jit/JIT.cpp:
        (JSC::JIT::privateCompileMainPass):
        (JSC::JIT::privateCompileGetByIdSelf):
        (JSC::JIT::privateCompileGetByIdProto):
        (JSC::JIT::privateCompileGetByIdProtoList):
        (JSC::JIT::privateCompileGetByIdChain):
        (JSC::JIT::privateCompileCTIMachineTrampolines):
        (JSC::JIT::privateCompilePatchGetArrayLength):
        * jit/JIT.h:
        (JSC::JIT::compileGetByIdProtoList):

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

JavaScriptCore/ChangeLog
JavaScriptCore/bytecode/CodeBlock.cpp
JavaScriptCore/bytecode/Instruction.h
JavaScriptCore/bytecode/Opcode.h
JavaScriptCore/interpreter/Interpreter.cpp
JavaScriptCore/interpreter/Interpreter.h
JavaScriptCore/jit/JIT.cpp
JavaScriptCore/jit/JIT.h

index fc59bd0..1dc4074 100644 (file)
@@ -1,3 +1,57 @@
+2008-11-19  Gavin Barraclough  <barraclough@apple.com>
+
+        Reviewed by Darin Adler.
+
+        Add support for (really) polymorphic caching of prototype accesses.
+        
+        If a cached prototype access misses, cti_op_get_by_id_proto_list is called.
+        When this occurs the Structure pointers from the instruction stream are copied
+        off into a new ProtoStubInfo object.  A second prototype access trampoline is
+        generated, and chained onto the first.  Subsequent missed call to
+        cti_op_get_by_id_proto_list_append, which append futher new trampolines, up to
+        PROTOTYPE_LIST_CACHE_SIZE (currently 4).  If any of the misses result in an
+        access other than to a direct prototype property, list formation is halted (or
+        for the initial miss, does not take place at all).
+
+        Separate fail case functions are provided for each access since this contributes
+        to the performance progression (enables better processor branch prediction).
+
+        Overall this is a near 5% progression on v8, with around 10% wins on richards
+        and deltablue.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dump):
+        (JSC::CodeBlock::derefStructures):
+        * bytecode/Instruction.h:
+        (JSC::ProtoStructureList::ProtoStubInfo::set):
+        (JSC::ProtoStructureList::ProtoStructureList):
+        (JSC::Instruction::Instruction):
+        (JSC::Instruction::):
+        * bytecode/Opcode.h:
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::privateExecute):
+        (JSC::Interpreter::tryCTICacheGetByID):
+        (JSC::Interpreter::cti_op_put_by_id_fail):
+        (JSC::Interpreter::cti_op_get_by_id_self_fail):
+        (JSC::Interpreter::cti_op_get_by_id_proto_list):
+        (JSC::Interpreter::cti_op_get_by_id_proto_list_append):
+        (JSC::Interpreter::cti_op_get_by_id_proto_list_full):
+        (JSC::Interpreter::cti_op_get_by_id_proto_fail):
+        (JSC::Interpreter::cti_op_get_by_id_chain_fail):
+        (JSC::Interpreter::cti_op_get_by_id_array_fail):
+        (JSC::Interpreter::cti_op_get_by_id_string_fail):
+        * interpreter/Interpreter.h:
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        (JSC::JIT::privateCompileGetByIdSelf):
+        (JSC::JIT::privateCompileGetByIdProto):
+        (JSC::JIT::privateCompileGetByIdProtoList):
+        (JSC::JIT::privateCompileGetByIdChain):
+        (JSC::JIT::privateCompileCTIMachineTrampolines):
+        (JSC::JIT::privateCompilePatchGetArrayLength):
+        * jit/JIT.h:
+        (JSC::JIT::compileGetByIdProtoList):
+
 2008-11-20  Sam Weinig  <sam@webkit.org>
 
         Try and fix the tiger build.
index c7d329d..e4e2fab 100644 (file)
@@ -646,6 +646,10 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printGetByIdOp(location, it, identifiers, "get_by_id_proto");
             break;
         }
+        case op_get_by_id_proto_list: {
+            printGetByIdOp(location, it, identifiers, "op_get_by_id_proto_list");
+            break;
+        }
         case op_get_by_id_chain: {
             printGetByIdOp(location, it, identifiers, "get_by_id_chain");
             break;
@@ -1019,7 +1023,21 @@ void CodeBlock::derefStructures(Instruction* vPC) const
             vPC[4].u.structure->deref();
         return;
     }
-    
+    if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto_list)) {
+        PrototypeStructureList* prototypeStructures = vPC[4].u.prototypeStructure;
+        int count = vPC[5].u.operand;
+        for (int i = 0; i < count; ++i) {
+            PrototypeStructureList::ProtoStubInfo& info = prototypeStructures->list[i];
+            ASSERT(info.base);
+            ASSERT(info.proto);
+            ASSERT(info.stubRoutine);
+            info.base->deref();
+            info.proto->deref();
+            WTF::fastFreeExecutable(info.stubRoutine);
+        }
+        return;
+    }
+
     // These instructions don't ref their Structures.
     ASSERT(vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id) || vPC[0].u.opcode == interpreter->getOpcode(op_put_by_id) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == interpreter->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == interpreter->getOpcode(op_get_array_length) || vPC[0].u.opcode == interpreter->getOpcode(op_get_string_length));
 }
index 6a66406..cb29d91 100644 (file)
 #include "ResultType.h"
 #include <wtf/VectorTraits.h>
 
+#define PROTOTYPE_LIST_CACHE_SIZE 4
+
 namespace JSC {
 
     class JSCell;
     class Structure;
     class StructureChain;
 
+    // Structure used by op_get_by_id_proto_list instruction to hold data off the main opcode stream.
+    struct PrototypeStructureList {
+        struct ProtoStubInfo {
+            Structure* base;
+            Structure* proto;
+            int cachedOffset;
+            void* stubRoutine;
+            
+            void set(Structure* _base, Structure* _proto, int _cachedOffset, void* _stubRoutine)
+            {
+                base = _base;
+                proto = _proto;
+                cachedOffset = _cachedOffset;
+                stubRoutine = _stubRoutine;
+            }
+        } list[PROTOTYPE_LIST_CACHE_SIZE];
+        
+        PrototypeStructureList(Structure* firstBase, Structure* firstProto, int cachedOffset, void* stubRoutine)
+        {
+            list[0].set(firstBase, firstProto, cachedOffset, stubRoutine);
+        }
+    };
+
     struct Instruction {
         Instruction(Opcode opcode) { u.opcode = opcode; }
         Instruction(int operand)
@@ -52,6 +77,7 @@ namespace JSC {
         Instruction(Structure* structure) { u.structure = structure; }
         Instruction(StructureChain* structureChain) { u.structureChain = structureChain; }
         Instruction(JSCell* jsCell) { u.jsCell = jsCell; }
+        Instruction(PrototypeStructureList* prototypeStructure) { u.prototypeStructure = prototypeStructure; }
 
         union {
             Opcode opcode;
@@ -60,6 +86,7 @@ namespace JSC {
             StructureChain* structureChain;
             JSCell* jsCell;
             ResultType::Type resultType;
+            PrototypeStructureList* prototypeStructure;
         } u;
     };
 
index 81bcf9b..0d78845 100644 (file)
@@ -102,6 +102,7 @@ namespace JSC {
         macro(op_get_by_id) \
         macro(op_get_by_id_self) \
         macro(op_get_by_id_proto) \
+        macro(op_get_by_id_proto_list) \
         macro(op_get_by_id_chain) \
         macro(op_get_by_id_generic) \
         macro(op_get_array_length) \
index c65c4e3..b01ff4d 100644 (file)
@@ -2586,6 +2586,13 @@ JSValue* Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerF
         uncacheGetByID(callFrame->codeBlock(), vPC);
         NEXT_INSTRUCTION();
     }
+    DEFINE_OPCODE(op_get_by_id_proto_list) {
+        // Polymorphic prototype access caching currently only supported when JITting.
+        ASSERT_NOT_REACHED();
+        // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)!
+        vPC += 8;
+        NEXT_INSTRUCTION();
+    }
     DEFINE_OPCODE(op_get_by_id_chain) {
         /* op_get_by_id_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n)
 
@@ -4523,9 +4530,6 @@ void Interpreter::cti_op_put_by_id_fail(CTI_ARGS)
     PutPropertySlot slot;
     ARG_src1->put(callFrame, ident, ARG_src3, slot);
 
-    // should probably uncachePutByID() ... this would mean doing a vPC lookup - might be worth just bleeding this until the end.
-    ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_put_by_id_generic));
-
     CHECK_FOR_EXCEPTION_AT_END();
 }
 
@@ -4578,7 +4582,7 @@ JSValue* Interpreter::cti_op_get_by_id_generic(CTI_ARGS)
     return result;
 }
 
-JSValue* Interpreter::cti_op_get_by_id_fail(CTI_ARGS)
+JSValue* Interpreter::cti_op_get_by_id_self_fail(CTI_ARGS)
 {
     CTI_STACK_HACK();
 
@@ -4589,8 +4593,129 @@ JSValue* Interpreter::cti_op_get_by_id_fail(CTI_ARGS)
     PropertySlot slot(baseValue);
     JSValue* result = baseValue->get(callFrame, ident, slot);
 
-    // should probably uncacheGetByID() ... this would mean doing a vPC lookup - might be worth just bleeding this until the end.
-    ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_generic));
+    CHECK_FOR_EXCEPTION_AT_END();
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_proto_list(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    CallFrame* callFrame = ARG_callFrame;
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(callFrame, *ARG_id2, slot);
+
+    CHECK_FOR_EXCEPTION();
+
+    if (baseValue->isObject()
+        && slot.isCacheable()
+        && !asCell(baseValue)->structure()->isDictionary()
+        && slot.slotBase() == asCell(baseValue)->structure()->prototypeForLookup(callFrame)) {
+
+        JSCell* baseCell = asCell(baseValue);
+        Structure* structure = baseCell->structure();
+        CodeBlock* codeBlock = callFrame->codeBlock();
+        unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
+        Instruction* vPC = codeBlock->instructions.begin() + vPCIndex;
+
+        ASSERT(slot.slotBase()->isObject());
+
+        JSObject* slotBaseObject = asObject(slot.slotBase());
+
+        // Heavy access to a prototype is a good indication that it's not being
+        // used as a dictionary.
+        if (slotBaseObject->structure()->isDictionary()) {
+            RefPtr<Structure> transition = Structure::fromDictionaryTransition(slotBaseObject->structure());
+            slotBaseObject->setStructure(transition.release());
+            asObject(baseValue)->structure()->setCachedPrototypeChain(0);
+        }
+
+        StructureStubInfo* stubInfo = &codeBlock->getStubInfo(CTI_RETURN_ADDRESS);
+
+        PrototypeStructureList* prototypeStructureList;
+        int listIndex = 1;
+
+        if (vPC[0].u.opcode == ARG_globalData->interpreter->getOpcode(op_get_by_id_proto)) {
+            prototypeStructureList = new PrototypeStructureList(vPC[4].u.structure, vPC[5].u.structure, vPC[6].u.operand, stubInfo->stubRoutine);
+            stubInfo->stubRoutine = 0;
+
+            vPC[0] = ARG_globalData->interpreter->getOpcode(op_get_by_id_proto_list);
+            vPC[4] = prototypeStructureList;
+            vPC[5] = 2;
+        } else {
+            prototypeStructureList = vPC[4].u.prototypeStructure;
+            listIndex = vPC[5].u.operand;
+
+            vPC[5] = listIndex + 1;
+        }
+
+        JIT::compileGetByIdProtoList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, slotBaseObject->structure(), slot.cachedOffset());
+
+        if (listIndex == (PROTOTYPE_LIST_CACHE_SIZE - 1))
+            ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full));
+    } else {
+        ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail));
+    }
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_proto_list_full(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
+
+    CHECK_FOR_EXCEPTION_AT_END();
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_proto_fail(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
+
+    CHECK_FOR_EXCEPTION_AT_END();
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_chain_fail(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
+
+    CHECK_FOR_EXCEPTION_AT_END();
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_array_fail(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
+
+    CHECK_FOR_EXCEPTION_AT_END();
+    return result;
+}
+
+JSValue* Interpreter::cti_op_get_by_id_string_fail(CTI_ARGS)
+{
+    CTI_STACK_HACK();
+
+    JSValue* baseValue = ARG_src1;
+    PropertySlot slot(baseValue);
+    JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
 
     CHECK_FOR_EXCEPTION_AT_END();
     return result;
@@ -4641,11 +4766,10 @@ JSValue* Interpreter::cti_op_del_by_id(CTI_ARGS)
     CTI_STACK_HACK();
 
     CallFrame* callFrame = ARG_callFrame;
-    Identifier& ident = *ARG_id2;
     
     JSObject* baseObj = ARG_src1->toObject(callFrame);
 
-    JSValue* result = jsBoolean(baseObj->deleteProperty(callFrame, ident));
+    JSValue* result = jsBoolean(baseObj->deleteProperty(callFrame, *ARG_id2));
     CHECK_FOR_EXCEPTION_AT_END();
     return result;
 }
@@ -5890,9 +6014,8 @@ void Interpreter::cti_op_put_getter(CTI_ARGS)
 
     ASSERT(ARG_src1->isObject());
     JSObject* baseObj = asObject(ARG_src1);
-    Identifier& ident = *ARG_id2;
     ASSERT(ARG_src3->isObject());
-    baseObj->defineGetter(callFrame, ident, asObject(ARG_src3));
+    baseObj->defineGetter(callFrame, *ARG_id2, asObject(ARG_src3));
 }
 
 void Interpreter::cti_op_put_setter(CTI_ARGS)
@@ -5903,9 +6026,8 @@ void Interpreter::cti_op_put_setter(CTI_ARGS)
 
     ASSERT(ARG_src1->isObject());
     JSObject* baseObj = asObject(ARG_src1);
-    Identifier& ident = *ARG_id2;
     ASSERT(ARG_src3->isObject());
-    baseObj->defineSetter(callFrame, ident, asObject(ARG_src3));
+    baseObj->defineSetter(callFrame, *ARG_id2, asObject(ARG_src3));
 }
 
 JSObject* Interpreter::cti_op_new_error(CTI_ARGS)
index 841ceae..99d5dbe 100644 (file)
@@ -187,7 +187,13 @@ namespace JSC {
         static JSValue* SFX_CALL cti_op_get_by_id(CTI_ARGS);
         static JSValue* SFX_CALL cti_op_get_by_id_second(CTI_ARGS);
         static JSValue* SFX_CALL cti_op_get_by_id_generic(CTI_ARGS);
-        static JSValue* SFX_CALL cti_op_get_by_id_fail(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_self_fail(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_proto_list(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_proto_list_full(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_proto_fail(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_chain_fail(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_array_fail(CTI_ARGS);
+        static JSValue* SFX_CALL cti_op_get_by_id_string_fail(CTI_ARGS);
         static JSValue* SFX_CALL cti_op_del_by_id(CTI_ARGS);
         static JSValue* SFX_CALL cti_op_instanceof(CTI_ARGS);
         static JSValue* SFX_CALL cti_op_mul(CTI_ARGS);
index a60270d..bf61886 100644 (file)
@@ -530,6 +530,12 @@ ALWAYS_INLINE JmpSrc JIT::emitCTICall(Instruction* vPC, unsigned bytecodeIndex,
     return call;
 }
 
+JmpSrc JIT::checkStructure(RegisterID reg, Structure* structure)
+{
+    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), reg);
+    return __ jne();
+}
+
 ALWAYS_INLINE void JIT::emitJumpSlowCaseIfNotJSCell(RegisterID reg, unsigned bytecodeIndex)
 {
     __ testl_i32r(JSImmediate::TagMask, reg);
@@ -2351,6 +2357,7 @@ void JIT::privateCompileMainPass()
         case op_get_by_id_chain:
         case op_get_by_id_generic:
         case op_get_by_id_proto:
+        case op_get_by_id_proto_list:
         case op_get_by_id_self:
         case op_get_string_length:
         case op_put_by_id_generic:
@@ -3116,8 +3123,7 @@ void JIT::privateCompileGetByIdSelf(Structure* structure, size_t cachedOffset, v
     // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
     JmpSrc failureCases1 = __ jne();
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
-    JmpSrc failureCases2 = __ jne();
+    JmpSrc failureCases2 = checkStructure(X86::eax, structure);
 
     // Checks out okay! - getDirectOffset
     __ movl_mr(FIELD_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
@@ -3127,8 +3133,8 @@ void JIT::privateCompileGetByIdSelf(Structure* structure, size_t cachedOffset, v
     void* code = __ executableCopy();
     ASSERT(code);
 
-    X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
+    X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail));
+    X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail));
     
     m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
     
@@ -3141,23 +3147,25 @@ void JIT::privateCompileGetByIdProto(Structure* structure, Structure* prototypeS
     StructureStubInfo& info = m_codeBlock->getStubInfo(returnAddress);
 
     // We don't want to repatch more than once - in future go to cti_op_put_by_id_generic.
-    ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-
+#if USE(CTI_REPATCH_PIC)
+    ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list));
+#else
+    ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail));
+#endif
     // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
     // referencing the prototype object - let's speculatively load it's table nice and early!)
     JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
     PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
     __ movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
 
-    // check eax is an object of the right Structure.
+    // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
     JmpSrc failureCases1 = __ jne();
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
-    JmpSrc failureCases2 = __ jne();
+    JmpSrc failureCases2 = checkStructure(X86::eax, structure);
 
     // Check the prototype object's Structure had not changed.
     Structure** prototypeStructureAddress = &(protoObject->m_structure);
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructure), static_cast<void*>(prototypeStructureAddress));
+    __ cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructure), prototypeStructureAddress);
     JmpSrc failureCases3 = __ jne();
 
     // Checks out okay! - getDirectOffset
@@ -3175,14 +3183,13 @@ void JIT::privateCompileGetByIdProto(Structure* structure, Structure* prototypeS
     X86Assembler::link(code, failureCases3, slowCaseBegin);
 
     // On success return back to the hot patch code, at a point it will perform the store to dest for us.
-    intptr_t successDest = (intptr_t)(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
+    intptr_t successDest = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
     X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
 
     // Track the stub we have created so that it will be deleted later.
-    m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
+    info.stubRoutine = code;
 
-    // Finally repatch the jump to sow case back in the hot path to jump here instead.
-    // FIXME: should revert this repatching, on failure.
+    // Finally repatch the jump to slow case back in the hot path to jump here instead.
     intptr_t jmpLocation = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
     X86Assembler::repatchBranchOffset(jmpLocation, code);
 #else
@@ -3192,15 +3199,14 @@ void JIT::privateCompileGetByIdProto(Structure* structure, Structure* prototypeS
     PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
     __ movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
 
-    // check eax is an object of the right Structure.
+    // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
     JmpSrc failureCases1 = __ jne();
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
-    JmpSrc failureCases2 = __ jne();
+    JmpSrc failureCases2 = checkStructure(X86::eax, structure);
 
     // Check the prototype object's Structure had not changed.
     Structure** prototypeStructureAddress = &(protoObject->m_structure);
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructure), static_cast<void*>(prototypeStructureAddress));
+    __ cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructure), prototypeStructureAddress);
     JmpSrc failureCases3 = __ jne();
 
     // Checks out okay! - getDirectOffset
@@ -3211,9 +3217,15 @@ void JIT::privateCompileGetByIdProto(Structure* structure, Structure* prototypeS
     void* code = __ executableCopy();
     ASSERT(code);
 
-    X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
+#if USE(CTI_REPATCH_PIC)
+    X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list));
+    X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list));
+    X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list));
+#else
+    X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail));
+    X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail));
+    X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail));
+#endif
 
     m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
 
@@ -3221,6 +3233,53 @@ void JIT::privateCompileGetByIdProto(Structure* structure, Structure* prototypeS
 #endif
 }
 
+#if USE(CTI_REPATCH_PIC)
+void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PrototypeStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
+{
+    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
+    // referencing the prototype object - let's speculatively load it's table nice and early!)
+    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
+    PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
+    __ movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
+
+    // Check eax is an object of the right Structure.
+    __ testl_i32r(JSImmediate::TagMask, X86::eax);
+    JmpSrc failureCases1 = __ jne();
+    JmpSrc failureCases2 = checkStructure(X86::eax, structure);
+
+    // Check the prototype object's Structure had not changed.
+    Structure** prototypeStructureAddress = &(protoObject->m_structure);
+    __ cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructure), static_cast<void*>(prototypeStructureAddress));
+    JmpSrc failureCases3 = __ jne();
+
+    // Checks out okay! - getDirectOffset
+    __ movl_mr(cachedOffset * sizeof(JSValue*), X86::edx, X86::eax);
+
+    JmpSrc success = __ jmp();
+
+    void* code = __ executableCopy();
+    ASSERT(code);
+
+    // Use the repatch information to link the failure cases back to the original slow case routine.
+    void* lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
+    X86Assembler::link(code, failureCases1, lastProtoBegin);
+    X86Assembler::link(code, failureCases2, lastProtoBegin);
+    X86Assembler::link(code, failureCases3, lastProtoBegin);
+
+    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
+    intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
+    X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
+
+    structure->ref();
+    prototypeStructure->ref();
+    prototypeStructures->list[currentIndex].set(structure, prototypeStructure, cachedOffset, code);
+
+    // Finally repatch the jump to slow case back in the hot path to jump here instead.
+    intptr_t jmpLocation = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
+    X86Assembler::repatchBranchOffset(jmpLocation, code);
+}
+#endif
+
 void JIT::privateCompileGetByIdChain(Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, void* returnAddress, CallFrame* callFrame)
 {
     ASSERT(count);
@@ -3229,9 +3288,7 @@ void JIT::privateCompileGetByIdChain(Structure* structure, StructureChain* chain
 
     // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
-    bucketsOfFail.append(__ jne());
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
-    bucketsOfFail.append(__ jne());
+    bucketsOfFail.append(checkStructure(X86::eax, structure));
 
     Structure* currStructure = structure;
     RefPtr<Structure>* chainEntries = chain->head();
@@ -3258,7 +3315,7 @@ void JIT::privateCompileGetByIdChain(Structure* structure, StructureChain* chain
     ASSERT(code);
 
     for (unsigned i = 0; i < bucketsOfFail.size(); ++i)
-        X86Assembler::link(code, bucketsOfFail[i], reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
+        X86Assembler::link(code, bucketsOfFail[i], reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_chain_fail));
 
     m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
 
@@ -3267,11 +3324,10 @@ void JIT::privateCompileGetByIdChain(Structure* structure, StructureChain* chain
 
 void JIT::privateCompilePutByIdReplace(Structure* structure, size_t cachedOffset, void* returnAddress)
 {
-    // check eax is an object of the right Structure.
+    // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
     JmpSrc failureCases1 = __ jne();
-    __ cmpl_i32m(reinterpret_cast<uint32_t>(structure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
-    JmpSrc failureCases2 = __ jne();
+    JmpSrc failureCases2 = checkStructure(X86::eax, structure);
 
     // checks out okay! - putDirectOffset
     __ movl_mr(FIELD_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
@@ -3307,7 +3363,7 @@ static inline bool transitionWillNeedStorageRealloc(Structure* oldStructure, Str
 void JIT::privateCompilePutByIdTransition(Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, void* returnAddress)
 {
     Vector<JmpSrc, 16> failureCases;
-    // check eax is an object of the right Structure.
+    // Check eax is an object of the right Structure.
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
     failureCases.append(__ jne());
     __ cmpl_i32m(reinterpret_cast<uint32_t>(oldStructure), FIELD_OFFSET(JSCell, m_structure), X86::eax);
@@ -3427,8 +3483,10 @@ void JIT::privateCompileCTIMachineTrampolines()
     __ movl_mr(FIELD_OFFSET(JSArray, m_storage), X86::eax, X86::eax);
     __ movl_mr(FIELD_OFFSET(ArrayStorage, m_length), X86::eax, X86::eax);
 
+    __ cmpl_i32r(JSImmediate::maxImmediateInt, X86::eax);
+    JmpSrc array_failureCases3 = __ ja();
+
     __ addl_rr(X86::eax, X86::eax);
-    JmpSrc array_failureCases3 = __ jo();
     __ addl_i8r(1, X86::eax);
     
     __ ret();
@@ -3447,8 +3505,10 @@ void JIT::privateCompileCTIMachineTrampolines()
     __ movl_mr(FIELD_OFFSET(JSString, m_value) + FIELD_OFFSET(UString, m_rep), X86::eax, X86::eax);
     __ movl_mr(FIELD_OFFSET(UString::Rep, len), X86::eax, X86::eax);
 
+    __ cmpl_i32r(JSImmediate::maxImmediateInt, X86::eax);
+    JmpSrc string_failureCases3 = __ ja();
+
     __ addl_rr(X86::eax, X86::eax);
-    JmpSrc string_failureCases3 = __ jo();
     __ addl_i8r(1, X86::eax);
     
     __ ret();
@@ -3582,12 +3642,12 @@ void JIT::privateCompileCTIMachineTrampolines()
     void* code = __ executableCopy();
     ASSERT(code);
 
-    X86Assembler::link(code, array_failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, array_failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, array_failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, string_failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, string_failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
-    X86Assembler::link(code, string_failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
+    X86Assembler::link(code, array_failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_array_fail));
+    X86Assembler::link(code, array_failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_array_fail));
+    X86Assembler::link(code, array_failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_array_fail));
+    X86Assembler::link(code, string_failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_string_fail));
+    X86Assembler::link(code, string_failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_string_fail));
+    X86Assembler::link(code, string_failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_string_fail));
     X86Assembler::link(code, callArityCheck1, reinterpret_cast<void*>(Interpreter::cti_op_call_arityCheck));
     X86Assembler::link(code, callArityCheck2, reinterpret_cast<void*>(Interpreter::cti_op_call_arityCheck));
     X86Assembler::link(code, callArityCheck3, reinterpret_cast<void*>(Interpreter::cti_op_call_arityCheck));
@@ -3640,7 +3700,7 @@ void JIT::privateCompilePatchGetArrayLength(void* returnAddress)
     StructureStubInfo& info = m_codeBlock->getStubInfo(returnAddress);
 
     // We don't want to repatch more than once - in future go to cti_op_put_by_id_generic.
-    ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_fail));
+    ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_array_fail));
 
     // Check eax is an array
     __ testl_i32r(JSImmediate::TagMask, X86::eax);
@@ -3670,14 +3730,13 @@ void JIT::privateCompilePatchGetArrayLength(void* returnAddress)
     X86Assembler::link(code, failureCases3, slowCaseBegin);
 
     // On success return back to the hot patch code, at a point it will perform the store to dest for us.
-    intptr_t successDest = (intptr_t)(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
+    intptr_t successDest = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
     X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
 
     // Track the stub we have created so that it will be deleted later.
     m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
 
     // Finally repatch the jump to sow case back in the hot path to jump here instead.
-    // FIXME: should revert this repatching, on failure.
     intptr_t jmpLocation = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
     X86Assembler::repatchBranchOffset(jmpLocation, code);
 }
index 19e50fc..8f3e649 100644 (file)
@@ -109,6 +109,8 @@ namespace JSC {
     struct CallLinkInfo;
     struct Instruction;
     struct OperandTypes;
+    struct PrototypeStructureList;
+    struct StructureStubInfo;
 
     typedef JSValue* (SFX_CALL *CTIHelper_j)(CTI_ARGS);
     typedef JSObject* (SFX_CALL *CTIHelper_o)(CTI_ARGS);
@@ -317,6 +319,14 @@ namespace JSC {
             jit.privateCompileGetByIdProto(structure, prototypeStructure, cachedOffset, returnAddress, callFrame);
         }
 
+#if USE(CTI_REPATCH_PIC)
+        static void compileGetByIdProtoList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PrototypeStructureList* prototypeStructureList, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset)
+        {
+            JIT jit(globalData, codeBlock);
+            jit.privateCompileGetByIdProtoList(stubInfo, prototypeStructureList, currentIndex, structure, prototypeStructure, cachedOffset, callFrame);
+        }
+#endif
+
         static void compileGetByIdChain(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, void* returnAddress)
         {
             JIT jit(globalData, codeBlock);
@@ -368,6 +378,9 @@ namespace JSC {
         void privateCompile();
         void privateCompileGetByIdSelf(Structure*, size_t cachedOffset, void* returnAddress);
         void privateCompileGetByIdProto(Structure*, Structure* prototypeStructure, size_t cachedOffset, void* returnAddress, CallFrame* callFrame);
+#if USE(CTI_REPATCH_PIC)
+        void privateCompileGetByIdProtoList(StructureStubInfo*, PrototypeStructureList*, int, Structure*, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame);
+#endif
         void privateCompileGetByIdChain(Structure*, StructureChain*, size_t count, size_t cachedOffset, void* returnAddress, CallFrame* callFrame);
         void privateCompilePutByIdReplace(Structure*, size_t cachedOffset, void* returnAddress);
         void privateCompilePutByIdTransition(Structure*, Structure*, size_t cachedOffset, StructureChain*, void* returnAddress);
@@ -414,6 +427,8 @@ namespace JSC {
         void emitJumpSlowCaseIfNotImmNum(RegisterID, unsigned bytecodeIndex);
         void emitJumpSlowCaseIfNotImmNums(RegisterID, RegisterID, unsigned bytecodeIndex);
 
+        JmpSrc checkStructure(RegisterID reg, Structure* structure);
+
         void emitFastArithDeTagImmediate(RegisterID);
         JmpSrc emitFastArithDeTagImmediateJumpIfZero(RegisterID);
         void emitFastArithReTagImmediate(RegisterID);