Ignore:
Timestamp:
Jul 30, 2009, 7:20:11 PM (16 years ago)
Author:
[email protected]
Message:

2009-07-23 Gavin Barraclough <[email protected]>

Reviewed by Oliver Hunt.

Make get_by_id/put_by_id/method_check/call defer optimization using a data flag rather than a code modification.
( https://bugs.webkit.org/show_bug.cgi?id=27635 )

This improves performance of ENABLE(ASSEMBLER_WX_EXCLUSIVE) builds by 2-2.5%, reducing the overhead to about 2.5%.
(No performance impact with ASSEMBLER_WX_EXCLUSIVE disabled).

  • bytecode/CodeBlock.cpp: (JSC::printStructureStubInfo):
    • Make StructureStubInfo store the type as an integer, rather than an OpcodeID.
  • bytecode/CodeBlock.h: (JSC::): (JSC::CallLinkInfo::seenOnce): (JSC::CallLinkInfo::setSeen): (JSC::MethodCallLinkInfo::seenOnce): (JSC::MethodCallLinkInfo::setSeen):
    • Change a pointer in CallLinkInfo/MethodCallLinkInfo to use a PtrAndFlags, use a flag to track when an op has been executed once.
  • bytecode/StructureStubInfo.cpp: (JSC::StructureStubInfo::deref):
    • Make StructureStubInfo store the type as an integer, rather than an OpcodeID.
  • bytecode/StructureStubInfo.h: (JSC::StructureStubInfo::StructureStubInfo): (JSC::StructureStubInfo::initGetByIdSelf): (JSC::StructureStubInfo::initGetByIdProto): (JSC::StructureStubInfo::initGetByIdChain): (JSC::StructureStubInfo::initGetByIdSelfList): (JSC::StructureStubInfo::initGetByIdProtoList): (JSC::StructureStubInfo::initPutByIdTransition): (JSC::StructureStubInfo::initPutByIdReplace): (JSC::StructureStubInfo::seenOnce): (JSC::StructureStubInfo::setSeen):
    • Make StructureStubInfo store the type as an integer, rather than an OpcodeID, add a flag to track when an op has been executed once.
  • bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitGetById): (JSC::BytecodeGenerator::emitPutById):
    • Make StructureStubInfo store the type as an integer, rather than an OpcodeID.
  • jit/JIT.cpp: (JSC::JIT::privateCompileCTIMachineTrampolines): (JSC::JIT::unlinkCall):
    • Remove the "don't lazy link" stage of calls.
  • jit/JIT.h: (JSC::JIT::compileCTIMachineTrampolines):
    • Remove the "don't lazy link" stage of calls.
  • jit/JITCall.cpp: (JSC::JIT::compileOpCallSlowCase):
    • Remove the "don't lazy link" stage of calls.
  • jit/JITStubs.cpp: (JSC::JITThunks::JITThunks): (JSC::JITThunks::tryCachePutByID): (JSC::JITThunks::tryCacheGetByID): (JSC::JITStubs::DEFINE_STUB_FUNCTION): (JSC::JITStubs::getPolymorphicAccessStructureListSlot):
    • Remove the "don't lazy link" stage of calls, and the "_second" stage of get_by_id/put_by_id/method_check.
  • jit/JITStubs.h: (JSC::JITThunks::ctiStringLengthTrampoline): (JSC::JITStubs::):
    • Remove the "don't lazy link" stage of calls, and the "_second" stage of get_by_id/put_by_id/method_check.
  • wtf/PtrAndFlags.h: (WTF::PtrAndFlags::PtrAndFlags): (WTF::PtrAndFlags::operator!): (WTF::PtrAndFlags::operator->):
    • Add ! and -> operators, add constuctor with pointer argument.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/jit/JITStubs.cpp

    r46610 r46618  
    584584JITThunks::JITThunks(JSGlobalData* globalData)
    585585{
    586     JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall, &m_ctiNativeCallThunk);
     586    JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallLink, &m_ctiVirtualCall, &m_ctiNativeCallThunk);
    587587
    588588#if PLATFORM_ARM_ARCH(7)
     
    607607#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
    608608
    609 NEVER_INLINE void JITThunks::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, ReturnAddressPtr returnAddress, JSValue baseValue, const PutPropertySlot& slot)
     609NEVER_INLINE void JITThunks::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, ReturnAddressPtr returnAddress, JSValue baseValue, const PutPropertySlot& slot, StructureStubInfo* stubInfo)
    610610{
    611611    // The interpreter checks for recursion here; I do not believe this can occur in CTI.
     
    633633        return;
    634634    }
    635 
    636     StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress);
    637635
    638636    // Cache hit: Specialize instruction and ref Structures.
     
    655653}
    656654
    657 NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, ReturnAddressPtr returnAddress, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot)
     655NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, ReturnAddressPtr returnAddress, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo* stubInfo)
    658656{
    659657    // FIXME: Write a test that proves we need to check for recursion here just
     
    693691        return;
    694692    }
    695 
    696     // In the interpreter the last structure is trapped here; in CTI we use the
    697     // *_second method to achieve a similar (but not quite the same) effect.
    698 
    699     StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress);
    700693
    701694    // Cache hit: Specialize instruction and ref Structures.
     
    727720    size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
    728721    if (!count) {
    729         stubInfo->opcodeID = op_get_by_id_generic;
     722        stubInfo->accessType = access_get_by_id_generic;
    730723        return;
    731724    }
     
    10291022    CallFrame* callFrame = stackFrame.callFrame;
    10301023    Identifier& ident = stackFrame.args[1].identifier();
    1031 
    10321024    PutPropertySlot slot;
    10331025    stackFrame.args[0].jsValue().put(callFrame, ident, stackFrame.args[2].jsValue(), slot);
    1034 
    1035     ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_put_by_id_second));
    1036 
    1037     CHECK_FOR_EXCEPTION_AT_END();
    1038 }
    1039 
    1040 DEFINE_STUB_FUNCTION(void, op_put_by_id_second)
    1041 {
    1042     STUB_INIT_STACK_FRAME(stackFrame);
    1043 
    1044     PutPropertySlot slot;
    1045     stackFrame.args[0].jsValue().put(stackFrame.callFrame, stackFrame.args[1].identifier(), stackFrame.args[2].jsValue(), slot);
    1046     JITThunks::tryCachePutByID(stackFrame.callFrame, stackFrame.callFrame->codeBlock(), STUB_RETURN_ADDRESS, stackFrame.args[0].jsValue(), slot);
     1026    CodeBlock* codeBlock = stackFrame.callFrame->codeBlock();
     1027    StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS);
     1028
     1029    if (!stubInfo->seenOnce())
     1030        stubInfo->setSeen();
     1031    else
     1032        JITThunks::tryCachePutByID(callFrame, codeBlock, STUB_RETURN_ADDRESS, stackFrame.args[0].jsValue(), slot, stubInfo);
     1033
    10471034    CHECK_FOR_EXCEPTION_AT_END();
    10481035}
     
    10761063}
    10771064
    1078 DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id)
     1065DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check)
    10791066{
    10801067    STUB_INIT_STACK_FRAME(stackFrame);
     
    10821069    CallFrame* callFrame = stackFrame.callFrame;
    10831070    Identifier& ident = stackFrame.args[1].identifier();
    1084 
    10851071    JSValue baseValue = stackFrame.args[0].jsValue();
    10861072    PropertySlot slot(baseValue);
    10871073    JSValue result = baseValue.get(callFrame, ident, slot);
    1088 
    1089     ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_second));
    1090 
    1091     CHECK_FOR_EXCEPTION_AT_END();
    1092     return JSValue::encode(result);
    1093 }
    1094 
    1095 DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check)
     1074    CHECK_FOR_EXCEPTION();
     1075    MethodCallLinkInfo& methodCallLinkInfo = callFrame->codeBlock()->getMethodCallLinkInfo(STUB_RETURN_ADDRESS);
     1076
     1077    if (!methodCallLinkInfo.seenOnce())
     1078        methodCallLinkInfo.setSeen();
     1079    else {
     1080        // If we successfully got something, then the base from which it is being accessed must
     1081        // be an object.  (Assertion to ensure asObject() call below is safe, which comes after
     1082        // an isCacheable() chceck.
     1083        ASSERT(!slot.isCacheable() || slot.slotBase().isObject());
     1084
     1085        // Check that:
     1086        //   * We're dealing with a JSCell,
     1087        //   * the property is cachable,
     1088        //   * it's not a dictionary
     1089        //   * there is a function cached.
     1090        Structure* structure;
     1091        JSCell* specific;
     1092        JSObject* slotBaseObject;
     1093        if (baseValue.isCell()
     1094            && slot.isCacheable()
     1095            && !(structure = asCell(baseValue)->structure())->isDictionary()
     1096            && (slotBaseObject = asObject(slot.slotBase()))->getPropertySpecificValue(callFrame, ident, specific)
     1097            && specific
     1098            ) {
     1099
     1100            JSFunction* callee = (JSFunction*)specific;
     1101
     1102            // Since we're accessing a prototype in a loop, it's a good bet that it
     1103            // should not be treated as a dictionary.
     1104            if (slotBaseObject->structure()->isDictionary())
     1105                slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure()));
     1106
     1107            // The result fetched should always be the callee!
     1108            ASSERT(result == JSValue(callee));
     1109
     1110            // Check to see if the function is on the object's prototype.  Patch up the code to optimize.
     1111            if (slot.slotBase() == structure->prototypeForLookup(callFrame))
     1112                JIT::patchMethodCallProto(callFrame->codeBlock(), methodCallLinkInfo, callee, structure, slotBaseObject);
     1113            // Check to see if the function is on the object itself.
     1114            // Since we generate the method-check to check both the structure and a prototype-structure (since this
     1115            // is the common case) we have a problem - we need to patch the prototype structure check to do something
     1116            // useful.  We could try to nop it out altogether, but that's a little messy, so lets do something simpler
     1117            // for now.  For now it performs a check on a special object on the global object only used for this
     1118            // purpose.  The object is in no way exposed, and as such the check will always pass.
     1119            else if (slot.slotBase() == baseValue)
     1120                JIT::patchMethodCallProto(callFrame->codeBlock(), methodCallLinkInfo, callee, structure, callFrame->scopeChain()->globalObject()->methodCallDummy());
     1121
     1122            // For now let any other case be cached as a normal get_by_id.
     1123        }
     1124
     1125        // Revert the get_by_id op back to being a regular get_by_id - allow it to cache like normal, if it needs to.
     1126        ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id));
     1127    }
     1128
     1129    return JSValue::encode(result);
     1130}
     1131
     1132DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id)
    10961133{
    10971134    STUB_INIT_STACK_FRAME(stackFrame);
     
    10991136    CallFrame* callFrame = stackFrame.callFrame;
    11001137    Identifier& ident = stackFrame.args[1].identifier();
    1101 
    11021138    JSValue baseValue = stackFrame.args[0].jsValue();
    11031139    PropertySlot slot(baseValue);
    11041140    JSValue result = baseValue.get(callFrame, ident, slot);
    1105 
    1106     ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_method_check_second));
    1107 
    1108     CHECK_FOR_EXCEPTION_AT_END();
    1109     return JSValue::encode(result);
    1110 }
    1111 
    1112 DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check_second)
    1113 {
    1114     STUB_INIT_STACK_FRAME(stackFrame);
    1115 
    1116     CallFrame* callFrame = stackFrame.callFrame;
    1117     Identifier& ident = stackFrame.args[1].identifier();
    1118 
    1119     JSValue baseValue = stackFrame.args[0].jsValue();
    1120     PropertySlot slot(baseValue);
    1121     JSValue result = baseValue.get(callFrame, ident, slot);
    1122 
    1123     CHECK_FOR_EXCEPTION();
    1124 
    1125     // If we successfully got something, then the base from which it is being accessed must
    1126     // be an object.  (Assertion to ensure asObject() call below is safe, which comes after
    1127     // an isCacheable() chceck.
    1128     ASSERT(!slot.isCacheable() || slot.slotBase().isObject());
    1129 
    1130     // Check that:
    1131     //   * We're dealing with a JSCell,
    1132     //   * the property is cachable,
    1133     //   * it's not a dictionary
    1134     //   * there is a function cached.
    1135     Structure* structure;
    1136     JSCell* specific;
    1137     JSObject* slotBaseObject;
    1138     if (baseValue.isCell()
    1139         && slot.isCacheable()
    1140         && !(structure = asCell(baseValue)->structure())->isDictionary()
    1141         && (slotBaseObject = asObject(slot.slotBase()))->getPropertySpecificValue(callFrame, ident, specific)
    1142         && specific
    1143         ) {
    1144 
    1145         JSFunction* callee = (JSFunction*)specific;
    1146 
    1147         // Since we're accessing a prototype in a loop, it's a good bet that it
    1148         // should not be treated as a dictionary.
    1149         if (slotBaseObject->structure()->isDictionary())
    1150             slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure()));
    1151 
    1152         // The result fetched should always be the callee!
    1153         ASSERT(result == JSValue(callee));
    1154         MethodCallLinkInfo& methodCallLinkInfo = callFrame->codeBlock()->getMethodCallLinkInfo(STUB_RETURN_ADDRESS);
    1155 
    1156         // Check to see if the function is on the object's prototype.  Patch up the code to optimize.
    1157         if (slot.slotBase() == structure->prototypeForLookup(callFrame))
    1158             JIT::patchMethodCallProto(callFrame->codeBlock(), methodCallLinkInfo, callee, structure, slotBaseObject);
    1159         // Check to see if the function is on the object itself.
    1160         // Since we generate the method-check to check both the structure and a prototype-structure (since this
    1161         // is the common case) we have a problem - we need to patch the prototype structure check to do something
    1162         // useful.  We could try to nop it out altogether, but that's a little messy, so lets do something simpler
    1163         // for now.  For now it performs a check on a special object on the global object only used for this
    1164         // purpose.  The object is in no way exposed, and as such the check will always pass.
    1165         else if (slot.slotBase() == baseValue)
    1166             JIT::patchMethodCallProto(callFrame->codeBlock(), methodCallLinkInfo, callee, structure, callFrame->scopeChain()->globalObject()->methodCallDummy());
    1167 
    1168         // For now let any other case be cached as a normal get_by_id.
    1169     }
    1170 
    1171     // Revert the get_by_id op back to being a regular get_by_id - allow it to cache like normal, if it needs to.
    1172     ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id));
    1173 
    1174     return JSValue::encode(result);
    1175 }
    1176 
    1177 DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_second)
    1178 {
    1179     STUB_INIT_STACK_FRAME(stackFrame);
    1180 
    1181     CallFrame* callFrame = stackFrame.callFrame;
    1182     Identifier& ident = stackFrame.args[1].identifier();
    1183 
    1184     JSValue baseValue = stackFrame.args[0].jsValue();
    1185     PropertySlot slot(baseValue);
    1186     JSValue result = baseValue.get(callFrame, ident, slot);
    1187 
    1188     JITThunks::tryCacheGetByID(callFrame, callFrame->codeBlock(), STUB_RETURN_ADDRESS, baseValue, ident, slot);
     1141    CodeBlock* codeBlock = callFrame->codeBlock();
     1142    StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS);
     1143
     1144    if (!stubInfo->seenOnce())
     1145        stubInfo->setSeen();
     1146    else
     1147        JITThunks::tryCacheGetByID(callFrame, codeBlock, STUB_RETURN_ADDRESS, baseValue, ident, slot, stubInfo);
    11891148
    11901149    CHECK_FOR_EXCEPTION_AT_END();
     
    12181177        int listIndex = 1;
    12191178
    1220         if (stubInfo->opcodeID == op_get_by_id_self) {
     1179        if (stubInfo->accessType == access_get_by_id_self) {
    12211180            ASSERT(!stubInfo->stubRoutine);
    12221181            polymorphicStructureList = new PolymorphicAccessStructureList(CodeLocationLabel(), stubInfo->u.getByIdSelf.baseObjectStructure);
     
    12421201    listIndex = 1;
    12431202
    1244     switch (stubInfo->opcodeID) {
    1245     case op_get_by_id_proto:
     1203    switch (stubInfo->accessType) {
     1204    case access_get_by_id_proto:
    12461205        prototypeStructureList = new PolymorphicAccessStructureList(stubInfo->stubRoutine, stubInfo->u.getByIdProto.baseObjectStructure, stubInfo->u.getByIdProto.prototypeStructure);
    12471206        stubInfo->stubRoutine = CodeLocationLabel();
    12481207        stubInfo->initGetByIdProtoList(prototypeStructureList, 2);
    12491208        break;
    1250     case op_get_by_id_chain:
     1209    case access_get_by_id_chain:
    12511210        prototypeStructureList = new PolymorphicAccessStructureList(stubInfo->stubRoutine, stubInfo->u.getByIdChain.baseObjectStructure, stubInfo->u.getByIdChain.chain);
    12521211        stubInfo->stubRoutine = CodeLocationLabel();
    12531212        stubInfo->initGetByIdProtoList(prototypeStructureList, 2);
    12541213        break;
    1255     case op_get_by_id_proto_list:
     1214    case access_get_by_id_proto_list:
    12561215        prototypeStructureList = stubInfo->u.getByIdProtoList.structureList;
    12571216        listIndex = stubInfo->u.getByIdProtoList.listSize;
     
    15201479
    15211480#if ENABLE(JIT_OPTIMIZE_CALL)
    1522 DEFINE_STUB_FUNCTION(void*, vm_dontLazyLinkCall)
     1481DEFINE_STUB_FUNCTION(void*, vm_lazyLinkCall)
    15231482{
    15241483    STUB_INIT_STACK_FRAME(stackFrame);
     
    15261485    JSGlobalData* globalData = stackFrame.globalData;
    15271486    JSFunction* callee = asFunction(stackFrame.args[0].jsValue());
    1528 
    1529     ctiPatchNearCallByReturnAddress(stackFrame.callFrame->callerFrame()->codeBlock(), stackFrame.args[1].returnAddress(), globalData->jitStubs.ctiVirtualCallLink());
    1530 
    1531     return callee->body()->generatedJITCode().addressForCall().executableAddress();
    1532 }
    1533 
    1534 DEFINE_STUB_FUNCTION(void*, vm_lazyLinkCall)
    1535 {
    1536     STUB_INIT_STACK_FRAME(stackFrame);
    1537 
    1538     JSFunction* callee = asFunction(stackFrame.args[0].jsValue());
    15391487    JITCode& jitCode = callee->body()->generatedJITCode();
    1540    
    1541     CodeBlock* codeBlock = 0;
    1542     if (!callee->isHostFunction())
    1543         codeBlock = &callee->body()->bytecode(callee->scope().node());
    1544     else
    1545         codeBlock = &callee->body()->generatedBytecode();
    1546 
    15471488    CallLinkInfo* callLinkInfo = &stackFrame.callFrame->callerFrame()->codeBlock()->getCallLinkInfo(stackFrame.args[1].returnAddress());
    1548     JIT::linkCall(callee, stackFrame.callFrame->callerFrame()->codeBlock(), codeBlock, jitCode, callLinkInfo, stackFrame.args[2].int32(), stackFrame.globalData);
     1489
     1490    if (!callLinkInfo->seenOnce())
     1491        callLinkInfo->setSeen();
     1492    else {
     1493
     1494        CodeBlock* codeBlock = 0;
     1495        if (!callee->isHostFunction())
     1496            codeBlock = &callee->body()->bytecode(callee->scope().node());
     1497        else
     1498            codeBlock = &callee->body()->generatedBytecode();
     1499        JIT::linkCall(callee, stackFrame.callFrame->callerFrame()->codeBlock(), codeBlock, jitCode, callLinkInfo, stackFrame.args[2].int32(), globalData);
     1500
     1501    }
    15491502
    15501503    return jitCode.addressForCall().executableAddress();
Note: See TracChangeset for help on using the changeset viewer.