Changeset 37670 in webkit for trunk/JavaScriptCore/VM/CTI.cpp
- Timestamp:
- Oct 17, 2008, 7:51:52 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/VM/CTI.cpp
r37653 r37670 96 96 "pushl %esi" "\n" 97 97 "pushl %edi" "\n" 98 "subl $0x24, %esp" "\n" 98 "pushl %ebx" "\n" 99 "subl $0x20, %esp" "\n" 99 100 "movl $512, %esi" "\n" 100 101 "movl 0x38(%esp), %edi" "\n" // Ox38 = 0x0E * 4, 0x0E = CTI_ARGS_callFrame (see assertion above) 101 102 "call *0x30(%esp)" "\n" // Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code (see assertion above) 102 "addl $0x24, %esp" "\n" 103 "addl $0x20, %esp" "\n" 104 "popl %ebx" "\n" 103 105 "popl %edi" "\n" 104 106 "popl %esi" "\n" … … 119 121 "call " SYMBOL_STRING(_ZN3JSC7Machine12cti_vm_throwEPvz) "\n" 120 122 #endif 121 "addl $0x24, %esp" "\n" 123 "addl $0x20, %esp" "\n" 124 "popl %ebx" "\n" 122 125 "popl %edi" "\n" 123 126 "popl %esi" "\n" … … 134 137 push esi; 135 138 push edi; 136 sub esp, 0x24; 139 push ebx; 140 sub esp, 0x20; 137 141 mov esi, 512; 138 142 mov ecx, esp; 139 143 mov edi, [esp + 0x38]; 140 144 call [esp + 0x30]; // Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code (see assertion above) 141 add esp, 0x24; 145 add esp, 0x20; 146 pop ebx; 142 147 pop edi; 143 148 pop esi; … … 151 156 mov ecx, esp; 152 157 call JSC::Machine::cti_vm_throw; 153 add esp, 0x24; 158 add esp, 0x20; 159 pop ebx; 154 160 pop edi; 155 161 pop esi; … … 304 310 #endif 305 311 306 ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, X86::RegisterID r) 307 { 308 m_jit.emitRestoreArgumentReference(); 312 ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitNakedCall(unsigned opcodeIndex, X86::RegisterID r) 313 { 309 314 X86Assembler::JmpSrc call = m_jit.emitCall(r); 310 315 m_calls.append(CallRecord(call, opcodeIndex)); 311 316 317 return call; 318 } 319 320 ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitNakedCall(unsigned opcodeIndex, void(*function)()) 321 { 322 X86Assembler::JmpSrc call = m_jit.emitCall(); 323 m_calls.append(CallRecord(call, reinterpret_cast<CTIHelper_v>(function), opcodeIndex)); 312 324 return call; 313 325 } … … 512 524 #endif 513 525 526 static void unreachable() 527 { 528 ASSERT_NOT_REACHED(); 529 exit(1); 530 } 531 514 532 void CTI::compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount) 515 533 { … … 519 537 m_jit.movl_rm(X86::ecx, RegisterFile::Callee * static_cast<int>(sizeof(Register)), X86::edx); 520 538 521 m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::e cx); // newScopeChain539 m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::ebx); // newScopeChain 522 540 m_jit.movl_i32m(argCount, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register)), X86::edx); 523 541 m_jit.movl_rm(X86::edi, RegisterFile::CallerFrame * static_cast<int>(sizeof(Register)), X86::edx); 524 m_jit.movl_rm(X86::ecx, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register)), X86::edx); 525 } 526 527 void CTI::compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type) 528 { 529 int dst = instruction[i + 1].u.operand; 530 int callee = instruction[i + 2].u.operand; 531 int firstArg = instruction[i + 4].u.operand; 532 int argCount = instruction[i + 5].u.operand; 533 int registerOffset = instruction[i + 6].u.operand; 534 535 if (type == OpCallEval) 536 emitGetPutArg(instruction[i + 3].u.operand, 16, X86::ecx); 537 538 if (type == OpConstruct) { 539 emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 20); 540 emitPutArgConstant(argCount, 16); 541 emitPutArgConstant(registerOffset, 12); 542 emitPutArgConstant(firstArg, 8); 543 emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx); 544 } else { 545 emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 12); 546 emitPutArgConstant(argCount, 8); 547 emitPutArgConstant(registerOffset, 4); 548 549 int thisVal = instruction[i + 3].u.operand; 542 m_jit.movl_rm(X86::ebx, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register)), X86::edx); 543 } 544 545 void CTI::compileOpCallSetupArgs(Instruction* instruction, bool isConstruct, bool isEval) 546 { 547 int firstArg = instruction[4].u.operand; 548 int argCount = instruction[5].u.operand; 549 int registerOffset = instruction[6].u.operand; 550 551 emitPutArg(X86::ecx, 0); 552 emitPutArgConstant(registerOffset, 4); 553 emitPutArgConstant(argCount, 8); 554 emitPutArgConstant(reinterpret_cast<unsigned>(instruction), 12); 555 if (isConstruct) { 556 emitGetPutArg(instruction[3].u.operand, 16, X86::eax); 557 emitPutArgConstant(firstArg, 20); 558 } else if (isEval) 559 emitGetPutArg(instruction[3].u.operand, 16, X86::eax); 560 } 561 562 void CTI::compileOpCall(Instruction* instruction, unsigned i, unsigned structureIDInstructionIndex, CompileOpCallType type) 563 { 564 int dst = instruction[1].u.operand; 565 int callee = instruction[2].u.operand; 566 int firstArg = instruction[4].u.operand; 567 int argCount = instruction[5].u.operand; 568 int registerOffset = instruction[6].u.operand; 569 570 // Setup this value as the first argument (does not apply to constructors) 571 if (type != OpConstruct) { 572 int thisVal = instruction[3].u.operand; 550 573 if (thisVal == missingThisObjectMarker()) { 551 574 // FIXME: should this be loaded dynamically off m_callFrame? 552 575 m_jit.movl_i32m(reinterpret_cast<unsigned>(m_callFrame->globalThisValue()), firstArg * sizeof(Register), X86::edi); 553 576 } else { 554 emitGetArg(thisVal, X86::e cx);555 emitPutResult(firstArg , X86::ecx);577 emitGetArg(thisVal, X86::eax); 578 emitPutResult(firstArg); 556 579 } 557 580 } 558 581 582 // Handle eval 559 583 X86Assembler::JmpSrc wasEval; 560 584 if (type == OpCallEval) { 561 emitGetPutArg(callee, 0, X86::ecx); 585 emitGetArg(callee, X86::ecx); 586 compileOpCallSetupArgs(instruction, false, true); 587 562 588 emitCTICall(i, Machine::cti_op_call_eval); 563 564 589 m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::eax); 565 590 wasEval = m_jit.emitUnlinkedJne(); 566 567 // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below). 591 } 592 593 // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee. 594 // This deliberately leaves the callee in ecx, used when setting up the stack frame below 595 emitGetArg(callee, X86::ecx); 596 m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::ecx); 597 X86Assembler::JmpDst addressOfLinkedFunctionCheck = m_jit.label(); 598 m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i)); 599 ASSERT(X86Assembler::getDifferenceBetweenLabels(addressOfLinkedFunctionCheck, m_jit.label()) == repatchOffsetOpCallCall); 600 m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathBegin = addressOfLinkedFunctionCheck; 601 602 // The following is the fast case, only used whan a callee can be linked. 603 604 emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax); 605 m_jit.cmpl_i32m(0, X86::eax); 606 X86Assembler::JmpSrc noProfile = m_jit.emitUnlinkedJe(); 607 emitPutArg(X86::ecx, 0); 608 emitCTICall(i, Machine::cti_op_call_profiler); 609 emitGetArg(callee, X86::ecx); 610 m_jit.link(noProfile, m_jit.label()); 611 612 // In the case of OpConstruct, call oout to a cti_ function to create the new object. 613 if (type == OpConstruct) { 614 emitPutArg(X86::ecx, 0); 615 emitGetPutArg(instruction[3].u.operand, 4, X86::eax); 616 emitCTICall(i, Machine::cti_op_construct_JSConstructFast); 617 emitPutResult(instruction[4].u.operand); 568 618 emitGetArg(callee, X86::ecx); 569 } else {570 // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).571 emitGetPutArg(callee, 0, X86::ecx);572 619 } 573 620 574 // Fast check for JS function. 575 m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx); 576 X86Assembler::JmpSrc isNotObject = m_jit.emitUnlinkedJne(); 577 m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx); 578 X86Assembler::JmpSrc isJSFunction = m_jit.emitUnlinkedJe(); 579 m_jit.link(isNotObject, m_jit.label()); 580 581 // This handles host functions 582 emitCTICall(i, ((type == OpConstruct) ? Machine::cti_op_construct_NotJSConstruct : Machine::cti_op_call_NotJSFunction)); 583 584 X86Assembler::JmpSrc wasNotJSFunction = m_jit.emitUnlinkedJmp(); 585 m_jit.link(isJSFunction, m_jit.label()); 586 587 // This handles JSFunctions 588 emitCTICall(i, (type == OpConstruct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction); 589 590 compileOpCallInitializeCallFrame(callee, argCount); 591 592 // load ctiCode from the new codeBlock. 593 m_jit.movl_mr(OBJECT_OFFSET(CodeBlock, ctiCode), X86::eax, X86::eax); 594 595 // Put the new value of 'callFrame' into edi and onto the stack, too. 596 m_jit.movl_rr(X86::edx, X86::edi); 597 598 // Check the ctiCode has been generated - if not, this is handled in a slow case. 599 m_jit.testl_rr(X86::eax, X86::eax); 600 m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i)); 601 emitCall(i, X86::eax); 621 // Fast version of stack frame initialization, directly relative to edi. 622 // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee 623 m_jit.movl_i32m(reinterpret_cast<unsigned>(nullJSValue), (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register)), X86::edi); 624 m_jit.movl_rm(X86::ecx, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register)), X86::edi); 625 m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::edx); // newScopeChain 626 m_jit.movl_i32m(argCount, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register)), X86::edi); 627 m_jit.movl_rm(X86::edi, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register)), X86::edi); 628 m_jit.movl_rm(X86::edx, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register)), X86::edi); 629 m_jit.addl_i32r(registerOffset * sizeof(Register), X86::edi); 630 631 // Call to the callee 632 m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathOther = emitNakedCall(i, unreachable); 602 633 603 X86Assembler::JmpDst end = m_jit.label();604 m_jit.link(wasNotJSFunction, end);605 634 if (type == OpCallEval) 606 m_jit.link(wasEval, end);635 m_jit.link(wasEval, m_jit.label()); 607 636 608 637 // Put the return value in dst. In the interpreter, op_ret does this. … … 1215 1244 } 1216 1245 case op_call: { 1217 compileOpCall(instruction , i);1246 compileOpCall(instruction + i, i, structureIDInstructionIndex++); 1218 1247 i += 7; 1219 1248 break; … … 1322 1351 } 1323 1352 case op_construct: { 1324 compileOpCall(instruction , i, OpConstruct);1353 compileOpCall(instruction + i, i, structureIDInstructionIndex++, OpConstruct); 1325 1354 i += 7; 1326 1355 break; … … 1755 1784 } 1756 1785 case op_call_eval: { 1757 compileOpCall(instruction , i, OpCallEval);1786 compileOpCall(instruction + i, i, structureIDInstructionIndex++, OpCallEval); 1758 1787 i += 7; 1759 1788 break; … … 1762 1791 emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx); 1763 1792 emitCTICall(i, Machine::cti_op_throw); 1764 m_jit.addl_i8r(0x24, X86::esp); 1793 m_jit.addl_i8r(0x20, X86::esp); 1794 m_jit.popl_r(X86::ebx); 1765 1795 m_jit.popl_r(X86::edi); 1766 1796 m_jit.popl_r(X86::esi); … … 2113 2143 for (Vector<SlowCaseEntry>::iterator iter = m_slowCases.begin(); iter != m_slowCases.end(); ++iter) { 2114 2144 unsigned i = iter->to; 2115 switch ( m_machine->getOpcodeID(instruction[i].u.opcode)) {2145 switch (OpcodeID opcodeID = m_machine->getOpcodeID(instruction[i].u.opcode)) { 2116 2146 case op_convert_this: { 2117 2147 m_jit.link(iter->from, m_jit.label()); … … 2578 2608 case op_call_eval: 2579 2609 case op_construct: { 2610 int dst = instruction[i + 1].u.operand; 2611 int callee = instruction[i + 2].u.operand; 2612 int argCount = instruction[i + 5].u.operand; 2613 2580 2614 m_jit.link(iter->from, m_jit.label()); 2581 2615 2582 // We jump to this slow case if the ctiCode for the codeBlock has not yet been generated; compile it now. 2616 // The arguments have been set up on the hot path for op_call_eval 2617 if (opcodeID != op_call_eval) 2618 compileOpCallSetupArgs(instruction + i, (opcodeID == op_construct), false); 2619 2620 // Fast check for JS function. 2621 m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx); 2622 X86Assembler::JmpSrc callLinkFailNotObject = m_jit.emitUnlinkedJne(); 2623 m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx); 2624 X86Assembler::JmpSrc callLinkFailNotJSFunction = m_jit.emitUnlinkedJne(); 2625 2626 // This handles JSFunctions 2627 emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax); 2628 m_jit.cmpl_i32m(0, X86::eax); 2629 X86Assembler::JmpSrc noProfile1 = m_jit.emitUnlinkedJe(); 2630 emitCTICall(i, Machine::cti_op_call_profiler); 2631 m_jit.link(noProfile1, m_jit.label()); 2632 2633 emitCTICall(i, (opcodeID == op_construct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction); 2634 // initialize the new call frame (pointed to by edx, after the last call), then set edi to point to it. 2635 compileOpCallInitializeCallFrame(callee, argCount); 2636 m_jit.movl_rr(X86::edx, X86::edi); 2637 2638 // Try to link & repatch this call. 2639 m_structureStubCompilationInfo[structureIDInstructionIndex].callReturnLocation = 2640 emitCTICall(i, Machine::cti_vm_lazyLinkCall); 2641 emitNakedCall(i, X86::eax); 2642 X86Assembler::JmpSrc storeResultForFirstRun = m_jit.emitUnlinkedJmp(); 2643 2644 // This is the address for the cold path *after* the first run (which tries to link the call). 2645 m_structureStubCompilationInfo[structureIDInstructionIndex].coldPathOther = m_jit.label(); 2646 2647 // The arguments have been set up on the hot path for op_call_eval 2648 if (opcodeID != op_call_eval) 2649 compileOpCallSetupArgs(instruction + i, (opcodeID == op_construct), false); 2650 2651 // Check for JSFunctions. 2652 m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx); 2653 X86Assembler::JmpSrc isNotObject = m_jit.emitUnlinkedJne(); 2654 m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx); 2655 X86Assembler::JmpSrc isJSFunction = m_jit.emitUnlinkedJe(); 2656 2657 // This handles host functions 2658 X86Assembler::JmpDst notJSFunctionlabel = m_jit.label(); 2659 m_jit.link(isNotObject, notJSFunctionlabel); 2660 m_jit.link(callLinkFailNotObject, notJSFunctionlabel); 2661 m_jit.link(callLinkFailNotJSFunction, notJSFunctionlabel); 2662 emitCTICall(i, ((opcodeID == op_construct) ? Machine::cti_op_construct_NotJSConstruct : Machine::cti_op_call_NotJSFunction)); 2663 X86Assembler::JmpSrc wasNotJSFunction = m_jit.emitUnlinkedJmp(); 2664 2665 // Next, handle JSFunctions... 2666 m_jit.link(isJSFunction, m_jit.label()); 2667 2668 // Profiler check 2669 emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax); 2670 m_jit.cmpl_i32m(0, X86::eax); 2671 X86Assembler::JmpSrc noProfile = m_jit.emitUnlinkedJe(); 2672 emitCTICall(i, Machine::cti_op_call_profiler); 2673 m_jit.link(noProfile, m_jit.label()); 2674 2675 emitCTICall(i, (opcodeID == op_construct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction); 2676 // initialize the new call frame (pointed to by edx, after the last call). 2677 compileOpCallInitializeCallFrame(callee, argCount); 2678 m_jit.movl_rr(X86::edx, X86::edi); 2679 2680 // load ctiCode from the new codeBlock. 2681 m_jit.movl_mr(OBJECT_OFFSET(CodeBlock, ctiCode), X86::eax, X86::eax); 2682 2683 // Move the new callframe into edi. 2684 m_jit.movl_rr(X86::edx, X86::edi); 2685 2686 // Check the ctiCode has been generated (if not compile it now), and make the call. 2687 m_jit.testl_rr(X86::eax, X86::eax); 2688 X86Assembler::JmpSrc hasCode = m_jit.emitUnlinkedJne(); 2583 2689 emitCTICall(i, Machine::cti_vm_compile); 2584 emitCall(i, X86::eax);2585 2586 // Instead of checking for 0 we could initialize the CodeBlock::ctiCode to point to a trampoline that would trigger the translation.2690 m_jit.link(hasCode, m_jit.label()); 2691 2692 emitNakedCall(i, X86::eax); 2587 2693 2588 2694 // Put the return value in dst. In the interpreter, op_ret does this. 2589 emitPutResult(instruction[i + 1].u.operand); 2695 X86Assembler::JmpDst storeResult = m_jit.label(); 2696 m_jit.link(wasNotJSFunction, storeResult); 2697 m_jit.link(storeResultForFirstRun, storeResult); 2698 emitPutResult(dst); 2699 2700 ++structureIDInstructionIndex; 2590 2701 i += 7; 2591 2702 break; … … 2623 2734 X86Assembler::JmpDst afterRegisterFileCheck; 2624 2735 if (m_codeBlock->codeType == FunctionCode) { 2736 // In the case of a fast linked call, we do not set this up in the caller. 2737 m_jit.movl_i32m(reinterpret_cast<unsigned>(m_codeBlock), RegisterFile::CodeBlock * static_cast<int>(sizeof(Register)), X86::edi); 2738 2625 2739 emitGetCTIParam(CTI_ARGS_registerFile, X86::eax); 2626 2740 m_jit.leal_mr(m_codeBlock->numCalleeRegisters * sizeof(Register), X86::edi, X86::edx); … … 2691 2805 info.callReturnLocation = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].callReturnLocation); 2692 2806 info.hotPathBegin = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].hotPathBegin); 2807 info.hotPathOther = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].hotPathOther); 2808 info.coldPathOther = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].coldPathOther); 2693 2809 } 2694 2810 … … 2971 3087 2972 3088 ctiRepatchCallByReturnAddress(returnAddress, code); 3089 } 3090 3091 void CTI::unlinkCall(StructureStubInfo* structureStubInfo) 3092 { 3093 // When the JSFunction is deleted the pointer embedded in the instruction stream will no longer be valid 3094 // (and, if a new JSFunction happened to be constructed at the same location, we could get a false positive 3095 // match). Reset the check so it no longer matches. 3096 reinterpret_cast<void**>(structureStubInfo->hotPathBegin)[-1] = JSImmediate::impossibleValue(); 3097 } 3098 3099 void CTI::linkCall(CodeBlock* callerCodeBlock, JSFunction* callee, CodeBlock* calleeCodeBlock, void* ctiCode, void* returnAddress, int callerArgCount) 3100 { 3101 StructureStubInfo& stubInfo = callerCodeBlock->getStubInfo(returnAddress); 3102 3103 // Currently we only link calls with the exact number of arguments. 3104 if (callerArgCount == calleeCodeBlock->numParameters) { 3105 ASSERT(!stubInfo.linkInfoPtr); 3106 3107 calleeCodeBlock->addCaller(&stubInfo); 3108 3109 reinterpret_cast<void**>(stubInfo.hotPathBegin)[-1] = callee; 3110 ctiRepatchCallByReturnAddress(stubInfo.hotPathOther, ctiCode); 3111 } 3112 3113 // repatch the instruction that jumps out to the cold path, so that we only try to link once. 3114 void* repatchCheck = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(stubInfo.hotPathBegin) + repatchOffsetOpCallCall); 3115 ctiRepatchCallByReturnAddress(repatchCheck, stubInfo.coldPathOther); 2973 3116 } 2974 3117
Note:
See TracChangeset
for help on using the changeset viewer.