bool JITCompiler::compile(JITCode& entry) { setStartOfCode(); compileEntry(); SpeculativeJIT speculative(*this); compileBody(speculative); setEndOfMainPath(); // Generate slow path code. speculative.runSlowPathGenerators(); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. speculative.createOSREntries(); setEndOfCode(); LinkBuffer linkBuffer(*m_globalData, this, m_codeBlock, JITCompilationCanFail); if (linkBuffer.didFailToAllocate()) return false; link(linkBuffer); speculative.linkOSREntries(linkBuffer); if (m_disassembler) m_disassembler->dump(linkBuffer); entry = JITCode( linkBuffer.finalizeCodeWithoutDisassembly(), JITCode::DFGJIT); return true; }
void JITCompiler::compile(JITCode& entry) { compileEntry(); SpeculativeJIT speculative(*this); compileBody(speculative); LinkBuffer linkBuffer(*m_globalData, this); link(linkBuffer); speculative.linkOSREntries(linkBuffer); entry = JITCode(linkBuffer.finalizeCode(), JITCode::DFGJIT); }
bool JITCompiler::compile(JITCode& entry) { SamplingRegion samplingRegion("DFG Backend"); setStartOfCode(); compileEntry(); SpeculativeJIT speculative(*this); compileBody(speculative); setEndOfMainPath(); // Generate slow path code. speculative.runSlowPathGenerators(); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. speculative.createOSREntries(); setEndOfCode(); LinkBuffer linkBuffer(*m_vm, this, m_codeBlock, JITCompilationCanFail); if (linkBuffer.didFailToAllocate()) return false; link(linkBuffer); speculative.linkOSREntries(linkBuffer); if (shouldShowDisassembly()) m_disassembler->dump(linkBuffer); if (m_graph.m_compilation) m_disassembler->reportToProfiler(m_graph.m_compilation.get(), linkBuffer); entry = JITCode( linkBuffer.finalizeCodeWithoutDisassembly(), JITCode::DFGJIT); return true; }
void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { // === Stage 1 - Function header code generation === // // This code currently matches the old JIT. In the function header we need to // pop the return address (since we do not allow any recursion on the machine // stack), and perform a fast register file check. // This is the main entry point, without performing an arity check. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56292 // We'll need to convert the remaining cti_ style calls (specifically the register file // check) which will be dependent on stack layout. (We'd need to account for this in // both normal return code and when jumping to an exception handler). preserveReturnAddressAfterCall(GPRInfo::regT2); emitPutToCallFrameHeader(GPRInfo::regT2, RegisterFile::ReturnPC); // If we needed to perform an arity check we will already have moved the return address, // so enter after this. Label fromArityCheck(this); // Setup a pointer to the codeblock in the CallFrameHeader. emitPutImmediateToCallFrameHeader(m_codeBlock, RegisterFile::CodeBlock); // Plant a check that sufficient space is available in the RegisterFile. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56291 addPtr(Imm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump registerFileCheck = branchPtr(Below, AbsoluteAddress(m_globalData->interpreter->registerFile().addressOfEnd()), GPRInfo::regT1); // Return here after register file check. Label fromRegisterFileCheck = label(); // === Stage 2 - Function body code generation === // // We generate the speculative code path, followed by the non-speculative // code for the function. Next we need to link the two together, making // bail-outs from the speculative path jump to the corresponding point on // the non-speculative one (and generating any code necessary to juggle // register values around, rebox values, and ensure spilled, to match the // non-speculative path's requirements). #if DFG_JIT_BREAK_ON_EVERY_FUNCTION // Handy debug tool! breakpoint(); #endif // First generate the speculative path. Label speculativePathBegin = label(); SpeculativeJIT speculative(*this); #if !DFG_DEBUG_LOCAL_DISBALE_SPECULATIVE bool compiledSpeculative = speculative.compile(); #else bool compiledSpeculative = false; #endif // Next, generate the non-speculative path. We pass this a SpeculationCheckIndexIterator // to allow it to check which nodes in the graph may bail out, and may need to reenter the // non-speculative path. if (compiledSpeculative) { SpeculationCheckIndexIterator checkIterator(speculative.speculationChecks()); NonSpeculativeJIT nonSpeculative(*this); nonSpeculative.compile(checkIterator); // Link the bail-outs from the speculative path to the corresponding entry points into the non-speculative one. linkSpeculationChecks(speculative, nonSpeculative); } else { // If compilation through the SpeculativeJIT failed, throw away the code we generated. m_calls.clear(); rewindToLabel(speculativePathBegin); SpeculationCheckVector noChecks; SpeculationCheckIndexIterator checkIterator(noChecks); NonSpeculativeJIT nonSpeculative(*this); nonSpeculative.compile(checkIterator); } // === Stage 3 - Function footer code generation === // // Generate code to lookup and jump to exception handlers, to perform the slow // register file check (if the fast one in the function header fails), and // generate the entry point with arity check. // Iterate over the m_calls vector, checking for exception checks, // and linking them to here. unsigned exceptionCheckCount = 0; for (unsigned i = 0; i < m_calls.size(); ++i) { Jump& exceptionCheck = m_calls[i].m_exceptionCheck; if (exceptionCheck.isSet()) { exceptionCheck.link(this); ++exceptionCheckCount; } } // If any exception checks were linked, generate code to lookup a handler. if (exceptionCheckCount) { // lookupExceptionHandler is passed two arguments, exec (the CallFrame*), and // an identifier for the operation that threw the exception, which we can use // to look up handler information. The identifier we use is the return address // of the call out from JIT code that threw the exception; this is still // available on the stack, just below the stack pointer! move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); peek(GPRInfo::argumentGPR1, -1); m_calls.append(CallRecord(call(), lookupExceptionHandler)); // lookupExceptionHandler leaves the handler CallFrame* in the returnValueGPR, // and the address of the handler in returnValueGPR2. jump(GPRInfo::returnValueGPR2); } // Generate the register file check; if the fast check in the function head fails, // we need to call out to a helper function to check whether more space is available. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). registerFileCheck.link(this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callRegisterFileCheck = call(); jump(fromRegisterFileCheck); // The fast entry point into a function does not check the correct number of arguments // have been passed to the call (we only use the fast entry point where we can statically // determine the correct number of arguments have been passed, or have already checked). // In cases where an arity check is necessary, we enter here. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). Label arityCheck = label(); preserveReturnAddressAfterCall(GPRInfo::regT2); emitPutToCallFrameHeader(GPRInfo::regT2, RegisterFile::ReturnPC); branch32(Equal, GPRInfo::regT1, Imm32(m_codeBlock->m_numParameters)).linkTo(fromArityCheck, this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callArityCheck = call(); move(GPRInfo::regT0, GPRInfo::callFrameRegister); jump(fromArityCheck); // === Stage 4 - Link === // // Link the code, populate data in CodeBlock data structures. LinkBuffer linkBuffer(*m_globalData, this, m_globalData->executableAllocator); #if DFG_DEBUG_VERBOSE fprintf(stderr, "JIT code start at %p\n", linkBuffer.debugAddress()); #endif // Link all calls out from the JIT code to their respective functions. for (unsigned i = 0; i < m_calls.size(); ++i) linkBuffer.link(m_calls[i].m_call, m_calls[i].m_function); if (m_codeBlock->needsCallReturnIndices()) { m_codeBlock->callReturnIndexVector().reserveCapacity(exceptionCheckCount); for (unsigned i = 0; i < m_calls.size(); ++i) { if (m_calls[i].m_exceptionCheck.isSet()) { unsigned returnAddressOffset = linkBuffer.returnAddressOffset(m_calls[i].m_call); unsigned exceptionInfo = m_calls[i].m_exceptionInfo; m_codeBlock->callReturnIndexVector().append(CallReturnOffsetToBytecodeOffset(returnAddressOffset, exceptionInfo)); } } } // FIXME: switch the register file check & arity check over to DFGOpertaion style calls, not JIT stubs. linkBuffer.link(callRegisterFileCheck, cti_register_file_check); linkBuffer.link(callArityCheck, m_codeBlock->m_isConstructor ? cti_op_construct_arityCheck : cti_op_call_arityCheck); entryWithArityCheck = linkBuffer.locationOf(arityCheck); entry = linkBuffer.finalizeCode(); }
bool JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { SamplingRegion samplingRegion("DFG Backend"); setStartOfCode(); compileEntry(); // === Function header code generation === // This is the main entry point, without performing an arity check. // If we needed to perform an arity check we will already have moved the return address, // so enter after this. Label fromArityCheck(this); // Plant a check that sufficient space is available in the JSStack. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56291 addPtr(TrustedImm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump stackCheck = branchPtr(Below, AbsoluteAddress(m_vm->interpreter->stack().addressOfEnd()), GPRInfo::regT1); // Return here after stack check. Label fromStackCheck = label(); // === Function body code generation === SpeculativeJIT speculative(*this); compileBody(speculative); setEndOfMainPath(); // === Function footer code generation === // // Generate code to perform the slow stack check (if the fast one in // the function header fails), and generate the entry point with arity check. // // Generate the stack check; if the fast check in the function head fails, // we need to call out to a helper function to check whether more space is available. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). stackCheck.link(this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); CallBeginToken token; beginCall(CodeOrigin(0), token); Call callStackCheck = call(); notifyCall(callStackCheck, CodeOrigin(0), token); jump(fromStackCheck); // The fast entry point into a function does not check the correct number of arguments // have been passed to the call (we only use the fast entry point where we can statically // determine the correct number of arguments have been passed, or have already checked). // In cases where an arity check is necessary, we enter here. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). Label arityCheck = label(); compileEntry(); load32(AssemblyHelpers::payloadFor((VirtualRegister)JSStack::ArgumentCount), GPRInfo::regT1); branch32(AboveOrEqual, GPRInfo::regT1, TrustedImm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); beginCall(CodeOrigin(0), token); Call callArityCheck = call(); notifyCall(callArityCheck, CodeOrigin(0), token); move(GPRInfo::regT0, GPRInfo::callFrameRegister); jump(fromArityCheck); // Generate slow path code. speculative.runSlowPathGenerators(); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. speculative.createOSREntries(); setEndOfCode(); // === Link === LinkBuffer linkBuffer(*m_vm, this, m_codeBlock, JITCompilationCanFail); if (linkBuffer.didFailToAllocate()) return false; link(linkBuffer); speculative.linkOSREntries(linkBuffer); // FIXME: switch the stack check & arity check over to DFGOpertaion style calls, not JIT stubs. linkBuffer.link(callStackCheck, cti_stack_check); linkBuffer.link(callArityCheck, m_codeBlock->m_isConstructor ? cti_op_construct_arityCheck : cti_op_call_arityCheck); if (shouldShowDisassembly()) m_disassembler->dump(linkBuffer); if (m_graph.m_compilation) m_disassembler->reportToProfiler(m_graph.m_compilation.get(), linkBuffer); entryWithArityCheck = linkBuffer.locationOf(arityCheck); entry = JITCode( linkBuffer.finalizeCodeWithoutDisassembly(), JITCode::DFGJIT); return true; }
void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { compileEntry(); // === Function header code generation === // This is the main entry point, without performing an arity check. // If we needed to perform an arity check we will already have moved the return address, // so enter after this. Label fromArityCheck(this); // Plant a check that sufficient space is available in the RegisterFile. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56291 addPtr(Imm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump registerFileCheck = branchPtr(Below, AbsoluteAddress(m_globalData->interpreter->registerFile().addressOfEnd()), GPRInfo::regT1); // Return here after register file check. Label fromRegisterFileCheck = label(); // === Function body code generation === SpeculativeJIT speculative(*this); compileBody(speculative); // === Function footer code generation === // // Generate code to perform the slow register file check (if the fast one in // the function header fails), and generate the entry point with arity check. // // Generate the register file check; if the fast check in the function head fails, // we need to call out to a helper function to check whether more space is available. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). registerFileCheck.link(this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callRegisterFileCheck = call(); jump(fromRegisterFileCheck); // The fast entry point into a function does not check the correct number of arguments // have been passed to the call (we only use the fast entry point where we can statically // determine the correct number of arguments have been passed, or have already checked). // In cases where an arity check is necessary, we enter here. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). Label arityCheck = label(); compileEntry(); load32(Address(GPRInfo::callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))), GPRInfo::regT1); branch32(Equal, GPRInfo::regT1, Imm32(m_codeBlock->m_numParameters)).linkTo(fromArityCheck, this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callArityCheck = call(); move(GPRInfo::regT0, GPRInfo::callFrameRegister); jump(fromArityCheck); // === Link === LinkBuffer linkBuffer(*m_globalData, this); link(linkBuffer); speculative.linkOSREntries(linkBuffer); // FIXME: switch the register file check & arity check over to DFGOpertaion style calls, not JIT stubs. linkBuffer.link(callRegisterFileCheck, cti_register_file_check); linkBuffer.link(callArityCheck, m_codeBlock->m_isConstructor ? cti_op_construct_arityCheck : cti_op_call_arityCheck); entryWithArityCheck = linkBuffer.locationOf(arityCheck); entry = JITCode(linkBuffer.finalizeCode(), JITCode::DFGJIT); }