CodeOrigin CallFrame::codeOrigin() { if (!codeBlock()) return CodeOrigin(0); #if ENABLE(DFG_JIT) if (callSiteBitsAreCodeOriginIndex()) { CallSiteIndex index = callSiteIndex(); ASSERT(codeBlock()->canGetCodeOrigin(index)); return codeBlock()->codeOrigin(index); } #endif return CodeOrigin(callSiteBitsAsBytecodeOffset()); }
CodeOrigin CallFrame::codeOrigin() { if (!codeBlock()) return CodeOrigin(0); #if ENABLE(DFG_JIT) if (hasLocationAsCodeOriginIndex()) { unsigned index = locationAsCodeOriginIndex(); ASSERT(codeBlock()->canGetCodeOrigin(index)); return codeBlock()->codeOrigin(index); } #endif return CodeOrigin(locationAsBytecodeOffset()); }
CallLinkStatus CallLinkStatus::computeFor( CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map) { ConcurrentJITLocker locker(profiledBlock->m_lock); UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(map); #if ENABLE(DFG_JIT) if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache)) || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCacheWatchpoint)) || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable))) return takesSlowPath(); CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex)); if (!callLinkInfo) return computeFromLLInt(locker, profiledBlock, bytecodeIndex); CallLinkStatus result = computeFor(locker, *callLinkInfo); if (!result) return computeFromLLInt(locker, profiledBlock, bytecodeIndex); if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadFunction))) result.makeClosureCall(); return result; #else return CallLinkStatus(); #endif }
void JIT::compileCallEvalSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter) { CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); info->setUpCall(CallLinkInfo::Call, CodeOrigin(m_bytecodeOffset), regT0); linkSlowCase(iter); int registerOffset = -instruction[4].u.operand; addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); loadPtr(Address(stackPointerRegister, sizeof(Register) * JSStack::Callee - sizeof(CallerFrameAndPC)), regT0); loadPtr(Address(stackPointerRegister, sizeof(Register) * JSStack::Callee - sizeof(CallerFrameAndPC)), regT1); move(TrustedImmPtr(info), regT2); emitLoad(JSStack::Callee, regT1, regT0); MacroAssemblerCodeRef virtualThunk = virtualThunkFor(m_vm, *info); info->setSlowStub(createJITStubRoutine(virtualThunk, *m_vm, nullptr, true)); emitNakedCall(virtualThunk.code()); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(instruction); }
PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); #if ENABLE(DFG_JIT) if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex) || hasExitSite(locker, profiledBlock, bytecodeIndex)) return PutByIdStatus(TakesSlowPath); StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)); PutByIdStatus result = computeForStubInfo( locker, profiledBlock, stubInfo, uid, CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex)); if (!result) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); return result; #else // ENABLE(JIT) UNUSED_PARAM(map); return PutByIdStatus(NoInformation); #endif // ENABLE(JIT) }
void JITCompiler::compile() { setStartOfCode(); compileEntry(); m_speculative = std::make_unique<SpeculativeJIT>(*this); // Plant a check that sufficient space is available in the JSStack. addPtr(TrustedImm32(virtualRegisterForLocal(m_graph.requiredRegisterCountForExecutionAndExit() - 1).offset() * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1); addPtr(TrustedImm32(m_graph.stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); compileSetupRegistersForEntry(); compileEntryExecutionFlag(); compileBody(); setEndOfMainPath(); // === Footer code generation === // // Generate the stack overflow handling; if the stack check in the entry head fails, // we need to call out to a helper function to throw the StackOverflowError. stackOverflow.link(this); emitStoreCodeOrigin(CodeOrigin(0)); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); m_speculative->callOperationWithCallFrameRollbackOnException(operationThrowStackOverflowError, m_codeBlock); // Generate slow path code. m_speculative->runSlowPathGenerators(m_pcToCodeOriginMapBuilder); m_pcToCodeOriginMapBuilder.appendItem(label(), PCToCodeOriginMapBuilder::defaultCodeOrigin()); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. m_speculative->createOSREntries(); setEndOfCode(); auto linkBuffer = std::make_unique<LinkBuffer>(*m_vm, *this, m_codeBlock, JITCompilationCanFail); if (linkBuffer->didFailToAllocate()) { m_graph.m_plan.finalizer = std::make_unique<FailedFinalizer>(m_graph.m_plan); return; } link(*linkBuffer); m_speculative->linkOSREntries(*linkBuffer); m_jitCode->shrinkToFit(); codeBlock()->shrinkToFit(CodeBlock::LateShrink); disassemble(*linkBuffer); m_graph.m_plan.finalizer = std::make_unique<JITFinalizer>( m_graph.m_plan, m_jitCode.release(), WTFMove(linkBuffer)); }
void* prepareOSREntry( ExecState* exec, CodeBlock* dfgCodeBlock, CodeBlock* entryCodeBlock, unsigned bytecodeIndex, unsigned streamIndex) { VM& vm = exec->vm(); CodeBlock* baseline = dfgCodeBlock->baselineVersion(); DFG::JITCode* dfgCode = dfgCodeBlock->jitCode()->dfg(); ForOSREntryJITCode* entryCode = entryCodeBlock->jitCode()->ftlForOSREntry(); if (Options::verboseOSR()) { dataLog( "FTL OSR from ", *dfgCodeBlock, " to ", *entryCodeBlock, " at bc#", bytecodeIndex, ".\n"); } if (bytecodeIndex != entryCode->bytecodeIndex()) { if (Options::verboseOSR()) dataLog(" OSR failed because we don't have an entrypoint for bc#", bytecodeIndex, "; ours is for bc#", entryCode->bytecodeIndex()); return 0; } Operands<JSValue> values; dfgCode->reconstruct( exec, dfgCodeBlock, CodeOrigin(bytecodeIndex), streamIndex, values); if (Options::verboseOSR()) dataLog(" Values at entry: ", values, "\n"); for (int argument = values.numberOfArguments(); argument--;) { RELEASE_ASSERT( exec->r(virtualRegisterForArgument(argument).offset()).jsValue() == values.argument(argument)); } RELEASE_ASSERT( static_cast<int>(values.numberOfLocals()) == baseline->m_numCalleeRegisters); EncodedJSValue* scratch = static_cast<EncodedJSValue*>( entryCode->entryBuffer()->dataBuffer()); for (int local = values.numberOfLocals(); local--;) scratch[local] = JSValue::encode(values.local(local)); int stackFrameSize = entryCode->common.requiredRegisterCountForExecutionAndExit(); if (!vm.interpreter->stack().grow(&exec->registers()[virtualRegisterForLocal(stackFrameSize).offset()])) { if (Options::verboseOSR()) dataLog(" OSR failed because stack growth failed.\n"); return 0; } exec->setCodeBlock(entryCodeBlock); void* result = entryCode->addressForCall().executableAddress(); if (Options::verboseOSR()) dataLog(" Entry will succeed, going to address", RawPointer(result), "\n"); return result; }
void handle(unsigned nodeIndex, Node* node) { if (m_originThatHadFire.isSet() && m_originThatHadFire != node->origin.forExit) { insertInvalidationCheck(nodeIndex, node); m_originThatHadFire = CodeOrigin(); } if (writesOverlap(m_graph, node, Watchpoint_fire)) m_originThatHadFire = node->origin.forExit; }
void JIT::compileCallEvalSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter) { CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); info->setUpCall(CallLinkInfo::Call, CodeOrigin(m_bytecodeOffset), regT0); linkSlowCase(iter); int registerOffset = -instruction[4].u.operand; addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); load64(Address(stackPointerRegister, sizeof(Register) * CallFrameSlot::callee - sizeof(CallerFrameAndPC)), regT0); emitDumbVirtualCall(info); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(instruction); }
void JIT::compileCallEvalSlowCase(const Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter) { linkAllSlowCases(iter); auto bytecode = instruction->as<OpCallEval>(); CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); info->setUpCall(CallLinkInfo::Call, CodeOrigin(m_bytecodeOffset), regT0); int registerOffset = -bytecode.m_argv; int callee = bytecode.m_callee.offset(); addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); emitLoad(callee, regT1, regT0); emitDumbVirtualCall(*vm(), info); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(bytecode); }
CallLinkStatus CallLinkStatus::computeFor( CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map) { ConcurrentJITLocker locker(profiledBlock->m_lock); UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(map); #if ENABLE(DFG_JIT) ExitSiteData exitSiteData = computeExitSiteData(locker, profiledBlock, bytecodeIndex); if (exitSiteData.m_takesSlowPath) return takesSlowPath(); CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex)); if (!callLinkInfo) return computeFromLLInt(locker, profiledBlock, bytecodeIndex); return computeFor(locker, *callLinkInfo, exitSiteData); #else return CallLinkStatus(); #endif }
GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); GetByIdStatus result; #if ENABLE(DFG_JIT) result = computeForStubInfo( locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)), uid); if (!result.takesSlowPath() && (hasExitSite(locker, profiledBlock, bytecodeIndex) || profiledBlock->likelyToTakeSlowCase(bytecodeIndex))) return GetByIdStatus(TakesSlowPath, true); #else UNUSED_PARAM(map); #endif if (!result) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); return result; }
GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); GetByIdStatus result; #if ENABLE(DFG_JIT) result = computeForStubInfoWithoutExitSiteFeedback( locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)), uid, CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex)); if (!result.takesSlowPath() && hasExitSite(locker, profiledBlock, bytecodeIndex)) return GetByIdStatus(result.makesCalls() ? MakesCalls : TakesSlowPath, true); #else UNUSED_PARAM(map); #endif if (!result) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); return result; }
bool run() { RELEASE_ASSERT(m_graph.m_plan.mode == FTLForOSREntryMode); RELEASE_ASSERT(m_graph.m_form == ThreadedCPS); unsigned bytecodeIndex = m_graph.m_plan.osrEntryBytecodeIndex; RELEASE_ASSERT(bytecodeIndex); RELEASE_ASSERT(bytecodeIndex != UINT_MAX); // Needed by createPreHeader(). m_graph.ensureDominators(); CodeBlock* baseline = m_graph.m_profiledBlock; BasicBlock* target = 0; for (unsigned blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; unsigned nodeIndex = 0; Node* firstNode = block->at(0); while (firstNode->isSemanticallySkippable()) firstNode = block->at(++nodeIndex); if (firstNode->op() == LoopHint && firstNode->origin.semantic == CodeOrigin(bytecodeIndex)) { target = block; break; } } if (!target) { // This is a terrible outcome. It shouldn't often happen but it might // happen and so we should defend against it. If it happens, then this // compilation is a failure. return false; } BlockInsertionSet insertionSet(m_graph); // We say that the execution count of the entry block is 1, because we know for sure // that this must be the case. Under our definition of executionCount, "1" means "once // per invocation". We could have said NaN here, since that would ask any clients of // executionCount to use best judgement - but that seems unnecessary since we know for // sure what the executionCount should be in this case. BasicBlock* newRoot = insertionSet.insert(0, 1); // We'd really like to use an unset origin, but ThreadedCPS won't allow that. NodeOrigin origin = NodeOrigin(CodeOrigin(0), CodeOrigin(0), false); Vector<Node*> locals(baseline->m_numCalleeLocals); for (int local = 0; local < baseline->m_numCalleeLocals; ++local) { Node* previousHead = target->variablesAtHead.local(local); if (!previousHead) continue; VariableAccessData* variable = previousHead->variableAccessData(); locals[local] = newRoot->appendNode( m_graph, variable->prediction(), ExtractOSREntryLocal, origin, OpInfo(variable->local().offset())); newRoot->appendNode( m_graph, SpecNone, MovHint, origin, OpInfo(variable->local().offset()), Edge(locals[local])); } // Now use the origin of the target, since it's not OK to exit, and we will probably hoist // type checks to here. origin = target->at(0)->origin; for (int argument = 0; argument < baseline->numParameters(); ++argument) { Node* oldNode = target->variablesAtHead.argument(argument); if (!oldNode) { // Just for sanity, always have a SetArgument even if it's not needed. oldNode = m_graph.m_arguments[argument]; } Node* node = newRoot->appendNode( m_graph, SpecNone, SetArgument, origin, OpInfo(oldNode->variableAccessData())); m_graph.m_arguments[argument] = node; } for (int local = 0; local < baseline->m_numCalleeLocals; ++local) { Node* previousHead = target->variablesAtHead.local(local); if (!previousHead) continue; VariableAccessData* variable = previousHead->variableAccessData(); Node* node = locals[local]; newRoot->appendNode( m_graph, SpecNone, SetLocal, origin, OpInfo(variable), Edge(node)); } newRoot->appendNode( m_graph, SpecNone, Jump, origin, OpInfo(createPreHeader(m_graph, insertionSet, target))); insertionSet.execute(); m_graph.resetReachability(); m_graph.killUnreachableBlocks(); return true; }
void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex) { CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); int callee = instruction[2].u.operand; /* Caller always: - Updates callFrameRegister to callee callFrame. - Initializes ArgumentCount; CallerFrame; Callee. For a JS call: - Callee initializes ReturnPC; CodeBlock. - Callee restores callFrameRegister before return. For a non-JS call: - Caller initializes ReturnPC; CodeBlock. - Caller restores callFrameRegister after return. */ if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs) compileSetupVarargsFrame(instruction, info); else { int argCount = instruction[3].u.operand; int registerOffset = -instruction[4].u.operand; if (opcodeID == op_call && shouldEmitProfiling()) { emitLoad(registerOffset + CallFrame::argumentOffsetIncludingThis(0), regT0, regT1); Jump done = branch32(NotEqual, regT0, TrustedImm32(JSValue::CellTag)); loadPtr(Address(regT1, JSCell::structureIDOffset()), regT1); storePtr(regT1, instruction[OPCODE_LENGTH(op_call) - 2].u.arrayProfile->addressOfLastSeenStructureID()); done.link(this); } addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); store32(TrustedImm32(argCount), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); } // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized. uint32_t locationBits = CallFrame::Location::encodeAsBytecodeInstruction(instruction); store32(TrustedImm32(locationBits), tagFor(JSStack::ArgumentCount, callFrameRegister)); emitLoad(callee, regT1, regT0); // regT1, regT0 holds callee. store32(regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); store32(regT1, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + TagOffset - sizeof(CallerFrameAndPC))); if (opcodeID == op_call_eval) { compileCallEval(instruction); return; } addSlowCase(branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag))); DataLabelPtr addressOfLinkedFunctionCheck; Jump slowCase = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0)); addSlowCase(slowCase); ASSERT(m_callCompilationInfo.size() == callLinkInfoIndex); info->setUpCall(CallLinkInfo::callTypeFor(opcodeID), CodeOrigin(m_bytecodeOffset), regT0); m_callCompilationInfo.append(CallCompilationInfo()); m_callCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck; m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info; checkStackPointerAlignment(); m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(instruction); }
void JITCompiler::compileFunction() { 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. addPtr(TrustedImm32(virtualRegisterForLocal(m_graph.requiredRegisterCountForExecutionAndExit() - 1).offset() * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1); // Move the stack pointer down to accommodate locals addPtr(TrustedImm32(m_graph.stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); // === Function body code generation === m_speculative = adoptPtr(new SpeculativeJIT(*this)); compileBody(); setEndOfMainPath(); // === Function footer code generation === // // Generate code to perform the stack overflow handling (if the stack check in // the function header fails), and generate the entry point with arity check. // // Generate the stack overflow handling; if the stack check in the function head fails, // we need to call out to a helper function to throw the StackOverflowError. stackOverflow.link(this); emitStoreCodeOrigin(CodeOrigin(0)); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); m_speculative->callOperationWithCallFrameRollbackOnException(operationThrowStackOverflowError, m_codeBlock); // 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). m_arityCheck = label(); compileEntry(); load32(AssemblyHelpers::payloadFor((VirtualRegister)JSStack::ArgumentCount), GPRInfo::regT1); branch32(AboveOrEqual, GPRInfo::regT1, TrustedImm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this); emitStoreCodeOrigin(CodeOrigin(0)); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); m_speculative->callOperationWithCallFrameRollbackOnException(m_codeBlock->m_isConstructor ? operationConstructArityCheck : operationCallArityCheck, GPRInfo::regT0); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(maxFrameExtentForSlowPathCall), stackPointerRegister); branchTest32(Zero, GPRInfo::regT0).linkTo(fromArityCheck, this); emitStoreCodeOrigin(CodeOrigin(0)); move(TrustedImmPtr(m_vm->arityCheckFailReturnThunks->returnPCsFor(*m_vm, m_codeBlock->numParameters())), GPRInfo::regT5); loadPtr(BaseIndex(GPRInfo::regT5, GPRInfo::regT0, timesPtr()), GPRInfo::regT5); m_callArityFixup = call(); jump(fromArityCheck); // Generate slow path code. m_speculative->runSlowPathGenerators(); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. m_speculative->createOSREntries(); setEndOfCode(); }
void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit) { ASSERT(jit.baselineCodeBlock()->jitType() == JITCode::BaselineJIT); jit.storePtr(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), AssemblyHelpers::addressFor((VirtualRegister)JSStack::CodeBlock)); CodeOrigin codeOrigin; for (codeOrigin = exit.m_codeOrigin; codeOrigin.inlineCallFrame; codeOrigin = codeOrigin.inlineCallFrame->caller) { InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(codeOrigin); CodeBlock* baselineCodeBlockForCaller = jit.baselineCodeBlockFor(inlineCallFrame->caller); void* jumpTarget = nullptr; void* trueReturnPC = nullptr; unsigned callBytecodeIndex = inlineCallFrame->caller.bytecodeIndex; switch (inlineCallFrame->kind) { case InlineCallFrame::Call: case InlineCallFrame::Construct: case InlineCallFrame::CallVarargs: case InlineCallFrame::ConstructVarargs: { CallLinkInfo* callLinkInfo = baselineCodeBlockForCaller->getCallLinkInfoForBytecodeIndex(callBytecodeIndex); RELEASE_ASSERT(callLinkInfo); jumpTarget = callLinkInfo->callReturnLocation().executableAddress(); break; } case InlineCallFrame::GetterCall: case InlineCallFrame::SetterCall: { StructureStubInfo* stubInfo = baselineCodeBlockForCaller->findStubInfo(CodeOrigin(callBytecodeIndex)); RELEASE_ASSERT(stubInfo); switch (inlineCallFrame->kind) { case InlineCallFrame::GetterCall: jumpTarget = jit.vm()->getCTIStub(baselineGetterReturnThunkGenerator).code().executableAddress(); break; case InlineCallFrame::SetterCall: jumpTarget = jit.vm()->getCTIStub(baselineSetterReturnThunkGenerator).code().executableAddress(); break; default: RELEASE_ASSERT_NOT_REACHED(); break; } trueReturnPC = stubInfo->callReturnLocation.labelAtOffset( stubInfo->patch.deltaCallToDone).executableAddress(); break; } } GPRReg callerFrameGPR; if (inlineCallFrame->caller.inlineCallFrame) { jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); callerFrameGPR = GPRInfo::regT3; } else callerFrameGPR = GPRInfo::callFrameRegister; jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget), AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset())); if (trueReturnPC) jit.storePtr(AssemblyHelpers::TrustedImmPtr(trueReturnPC), AssemblyHelpers::addressFor(inlineCallFrame->stackOffset + virtualRegisterForArgument(inlineCallFrame->arguments.size()).offset())); jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::CodeBlock))); if (!inlineCallFrame->isVarargs()) jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); #if USE(JSVALUE64) jit.store64(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset())); uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(codeOrigin.bytecodeIndex); jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); if (!inlineCallFrame->isClosureCall) jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()))), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); #else // USE(JSVALUE64) // so this is the 32-bit part jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset())); Instruction* instruction = baselineCodeBlock->instructions().begin() + codeOrigin.bytecodeIndex; uint32_t locationBits = CallFrame::Location::encodeAsBytecodeInstruction(instruction); jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount))); jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); if (!inlineCallFrame->isClosureCall) jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee))); #endif // USE(JSVALUE64) // ending the #else part, so directly above is the 32-bit part } #if USE(JSVALUE64) uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(codeOrigin.bytecodeIndex); #else Instruction* instruction = jit.baselineCodeBlock()->instructions().begin() + codeOrigin.bytecodeIndex; uint32_t locationBits = CallFrame::Location::encodeAsBytecodeInstruction(instruction); #endif jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(JSStack::ArgumentCount))); }
void JIT::compileOpCall(const Instruction* instruction, unsigned callLinkInfoIndex) { OpcodeID opcodeID = Op::opcodeID; auto bytecode = instruction->as<Op>(); int callee = bytecode.m_callee.offset(); /* Caller always: - Updates callFrameRegister to callee callFrame. - Initializes ArgumentCount; CallerFrame; Callee. For a JS call: - Callee initializes ReturnPC; CodeBlock. - Callee restores callFrameRegister before return. For a non-JS call: - Caller initializes ReturnPC; CodeBlock. - Caller restores callFrameRegister after return. */ CallLinkInfo* info = nullptr; if (opcodeID != op_call_eval) info = m_codeBlock->addCallLinkInfo(); compileSetupFrame(bytecode, info); // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized. uint32_t locationBits = CallSiteIndex(instruction).bits(); store32(TrustedImm32(locationBits), tagFor(CallFrameSlot::argumentCount)); emitLoad(callee, regT1, regT0); // regT1, regT0 holds callee. store32(regT0, Address(stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); store32(regT1, Address(stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) + TagOffset - sizeof(CallerFrameAndPC))); if (compileCallEval(bytecode)) return; if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs) emitRestoreCalleeSaves(); addSlowCase(branchIfNotCell(regT1)); DataLabelPtr addressOfLinkedFunctionCheck; Jump slowCase = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(nullptr)); addSlowCase(slowCase); ASSERT(m_callCompilationInfo.size() == callLinkInfoIndex); info->setUpCall(CallLinkInfo::callTypeFor(opcodeID), CodeOrigin(m_bytecodeOffset), regT0); m_callCompilationInfo.append(CallCompilationInfo()); m_callCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck; m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info; checkStackPointerAlignment(); if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments) { prepareForTailCallSlow(); m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall(); return; } m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(bytecode); }
void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex) { int callee = instruction[2].u.operand; /* Caller always: - Updates callFrameRegister to callee callFrame. - Initializes ArgumentCount; CallerFrame; Callee. For a JS call: - Caller initializes ScopeChain. - Callee initializes ReturnPC; CodeBlock. - Callee restores callFrameRegister before return. For a non-JS call: - Caller initializes ScopeChain; ReturnPC; CodeBlock. - Caller restores callFrameRegister after return. */ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct), call_and_construct_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_varargs), call_and_call_varargs_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct_varargs), call_and_construct_varargs_opcodes_must_be_same_length); if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs) compileLoadVarargs(instruction); else { int argCount = instruction[3].u.operand; int registerOffset = -instruction[4].u.operand; if (opcodeID == op_call && shouldEmitProfiling()) { emitGetVirtualRegister(registerOffset + CallFrame::argumentOffsetIncludingThis(0), regT0); Jump done = emitJumpIfNotJSCell(regT0); load32(Address(regT0, JSCell::structureIDOffset()), regT0); store32(regT0, instruction[OPCODE_LENGTH(op_call) - 2].u.arrayProfile->addressOfLastSeenStructureID()); done.link(this); } addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); store32(TrustedImm32(argCount), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); } // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized. uint32_t bytecodeOffset = instruction - m_codeBlock->instructions().begin(); uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(bytecodeOffset); store32(TrustedImm32(locationBits), Address(callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + TagOffset)); emitGetVirtualRegister(callee, regT0); // regT0 holds callee. store64(regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC))); if (opcodeID == op_call_eval) { compileCallEval(instruction); return; } DataLabelPtr addressOfLinkedFunctionCheck; Jump slowCase = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0)); addSlowCase(slowCase); ASSERT(m_callCompilationInfo.size() == callLinkInfoIndex); CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); info->callType = CallLinkInfo::callTypeFor(opcodeID); info->codeOrigin = CodeOrigin(m_bytecodeOffset); info->calleeGPR = regT0; m_callCompilationInfo.append(CallCompilationInfo()); m_callCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck; m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info; loadPtr(Address(regT0, OBJECT_OFFSETOF(JSFunction, m_scope)), regT2); store64(regT2, Address(MacroAssembler::stackPointerRegister, JSStack::ScopeChain * sizeof(Register) - sizeof(CallerFrameAndPC))); m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(instruction); }
GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); #if ENABLE(JIT) StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)); if (!stubInfo || !stubInfo->seen) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); if (stubInfo->resetByGC) return GetByIdStatus(TakesSlowPath, true); PolymorphicAccessStructureList* list; int listSize; switch (stubInfo->accessType) { case access_get_by_id_self_list: list = stubInfo->u.getByIdSelfList.structureList; listSize = stubInfo->u.getByIdSelfList.listSize; break; case access_get_by_id_proto_list: list = stubInfo->u.getByIdProtoList.structureList; listSize = stubInfo->u.getByIdProtoList.listSize; break; default: list = 0; listSize = 0; break; } for (int i = 0; i < listSize; ++i) { if (!list->list[i].isDirect) return GetByIdStatus(MakesCalls, true); } // Next check if it takes slow case, in which case we want to be kind of careful. if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)) return GetByIdStatus(TakesSlowPath, true); // Finally figure out if we can derive an access strategy. GetByIdStatus result; result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only. switch (stubInfo->accessType) { case access_unset: return computeFromLLInt(profiledBlock, bytecodeIndex, uid); case access_get_by_id_self: { Structure* structure = stubInfo->u.getByIdSelf.baseObjectStructure.get(); if (structure->takesSlowPathInDFGForImpureProperty()) return GetByIdStatus(TakesSlowPath, true); unsigned attributesIgnored; JSCell* specificValue; result.m_offset = structure->getConcurrently( *profiledBlock->vm(), uid, attributesIgnored, specificValue); if (structure->isDictionary()) specificValue = 0; if (isValidOffset(result.m_offset)) { result.m_structureSet.add(structure); result.m_specificValue = JSValue(specificValue); } if (isValidOffset(result.m_offset)) ASSERT(result.m_structureSet.size()); break; } case access_get_by_id_self_list: { for (int i = 0; i < listSize; ++i) { ASSERT(list->list[i].isDirect); Structure* structure = list->list[i].base.get(); if (structure->takesSlowPathInDFGForImpureProperty()) return GetByIdStatus(TakesSlowPath, true); if (result.m_structureSet.contains(structure)) continue; unsigned attributesIgnored; JSCell* specificValue; PropertyOffset myOffset = structure->getConcurrently( *profiledBlock->vm(), uid, attributesIgnored, specificValue); if (structure->isDictionary()) specificValue = 0; if (!isValidOffset(myOffset)) { result.m_offset = invalidOffset; break; } if (!i) { result.m_offset = myOffset; result.m_specificValue = JSValue(specificValue); } else if (result.m_offset != myOffset) { result.m_offset = invalidOffset; break; } else if (result.m_specificValue != JSValue(specificValue)) result.m_specificValue = JSValue(); result.m_structureSet.add(structure); } if (isValidOffset(result.m_offset)) ASSERT(result.m_structureSet.size()); break; } case access_get_by_id_proto: { if (!stubInfo->u.getByIdProto.isDirect) return GetByIdStatus(MakesCalls, true); result.m_chain = adoptRef(new IntendedStructureChain( profiledBlock, stubInfo->u.getByIdProto.baseObjectStructure.get(), stubInfo->u.getByIdProto.prototypeStructure.get())); computeForChain(result, profiledBlock, uid); break; } case access_get_by_id_chain: { if (!stubInfo->u.getByIdChain.isDirect) return GetByIdStatus(MakesCalls, true); result.m_chain = adoptRef(new IntendedStructureChain( profiledBlock, stubInfo->u.getByIdChain.baseObjectStructure.get(), stubInfo->u.getByIdChain.chain.get(), stubInfo->u.getByIdChain.count)); computeForChain(result, profiledBlock, uid); break; } default: ASSERT(!isValidOffset(result.m_offset)); break; } if (!isValidOffset(result.m_offset)) { result.m_state = TakesSlowPath; result.m_structureSet.clear(); result.m_chain.clear(); result.m_specificValue = JSValue(); } else result.m_state = Simple; return result; #else // ENABLE(JIT) UNUSED_PARAM(map); return GetByIdStatus(NoInformation, false); #endif // ENABLE(JIT) }
void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex) { int callee = instruction[2].u.operand; /* Caller always: - Updates callFrameRegister to callee callFrame. - Initializes ArgumentCount; CallerFrame; Callee. For a JS call: - Callee initializes ReturnPC; CodeBlock. - Callee restores callFrameRegister before return. For a non-JS call: - Caller initializes ReturnPC; CodeBlock. - Caller restores callFrameRegister after return. */ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct), call_and_construct_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_varargs), call_and_call_varargs_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct_varargs), call_and_construct_varargs_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call), call_and_tail_call_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call_varargs), call_and_tail_call_varargs_opcodes_must_be_same_length); COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call_forward_arguments), call_and_tail_call_forward_arguments_opcodes_must_be_same_length); CallLinkInfo* info = nullptr; if (opcodeID != op_call_eval) info = m_codeBlock->addCallLinkInfo(); if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments) compileSetupVarargsFrame(opcodeID, instruction, info); else { int argCount = instruction[3].u.operand; int registerOffset = -instruction[4].u.operand; if (opcodeID == op_call && shouldEmitProfiling()) { emitGetVirtualRegister(registerOffset + CallFrame::argumentOffsetIncludingThis(0), regT0); Jump done = emitJumpIfNotJSCell(regT0); load32(Address(regT0, JSCell::structureIDOffset()), regT0); store32(regT0, instruction[OPCODE_LENGTH(op_call) - 2].u.arrayProfile->addressOfLastSeenStructureID()); done.link(this); } addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister); store32(TrustedImm32(argCount), Address(stackPointerRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); } // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized. uint32_t bytecodeOffset = instruction - m_codeBlock->instructions().begin(); uint32_t locationBits = CallSiteIndex(bytecodeOffset).bits(); store32(TrustedImm32(locationBits), Address(callFrameRegister, CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + TagOffset)); emitGetVirtualRegister(callee, regT0); // regT0 holds callee. store64(regT0, Address(stackPointerRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC))); if (opcodeID == op_call_eval) { compileCallEval(instruction); return; } DataLabelPtr addressOfLinkedFunctionCheck; Jump slowCase = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0)); addSlowCase(slowCase); ASSERT(m_callCompilationInfo.size() == callLinkInfoIndex); info->setUpCall(CallLinkInfo::callTypeFor(opcodeID), CodeOrigin(m_bytecodeOffset), regT0); m_callCompilationInfo.append(CallCompilationInfo()); m_callCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck; m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info; if (opcodeID == op_tail_call) { CallFrameShuffleData shuffleData; shuffleData.tagTypeNumber = GPRInfo::tagTypeNumberRegister; shuffleData.numLocals = instruction[4].u.operand - sizeof(CallerFrameAndPC) / sizeof(Register); shuffleData.args.resize(instruction[3].u.operand); for (int i = 0; i < instruction[3].u.operand; ++i) { shuffleData.args[i] = ValueRecovery::displacedInJSStack( virtualRegisterForArgument(i) - instruction[4].u.operand, DataFormatJS); } shuffleData.callee = ValueRecovery::inGPR(regT0, DataFormatJS); shuffleData.setupCalleeSaveRegisters(m_codeBlock); info->setFrameShuffleData(shuffleData); CallFrameShuffler(*this, shuffleData).prepareForTailCall(); m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall(); return; } if (opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments) { emitRestoreCalleeSaves(); prepareForTailCallSlow(); m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall(); return; } m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(); addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); sampleCodeBlock(m_codeBlock); emitPutCallResult(instruction); }
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 handleBlockForTryCatch(BasicBlock* block, InsertionSet& insertionSet) { HandlerInfo* currentExceptionHandler = nullptr; FastBitVector liveAtCatchHead; liveAtCatchHead.resize(m_graph.block(0)->variablesAtTail.numberOfLocals()); HandlerInfo* cachedHandlerResult; CodeOrigin cachedCodeOrigin; auto catchHandler = [&] (CodeOrigin origin) -> HandlerInfo* { ASSERT(origin); if (origin == cachedCodeOrigin) return cachedHandlerResult; unsigned bytecodeIndexToCheck = origin.bytecodeIndex; cachedCodeOrigin = origin; while (1) { InlineCallFrame* inlineCallFrame = origin.inlineCallFrame; CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame); if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeIndexToCheck)) { liveAtCatchHead.clearAll(); unsigned catchBytecodeIndex = handler->target; m_graph.forAllLocalsLiveInBytecode(CodeOrigin(catchBytecodeIndex, inlineCallFrame), [&] (VirtualRegister operand) { liveAtCatchHead[operand.toLocal()] = true; }); cachedHandlerResult = handler; break; } if (!inlineCallFrame) { cachedHandlerResult = nullptr; break; } bytecodeIndexToCheck = inlineCallFrame->directCaller.bytecodeIndex; origin = inlineCallFrame->directCaller; } return cachedHandlerResult; }; Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr); HashSet<InlineCallFrame*> seenInlineCallFrames; auto flushEverything = [&] (NodeOrigin origin, unsigned index) { RELEASE_ASSERT(currentExceptionHandler); auto flush = [&] (VirtualRegister operand, bool alwaysInsert) { if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument() || alwaysInsert) { ASSERT(isValidFlushLocation(block, index, operand)); VariableAccessData* accessData = currentBlockAccessData.operand(operand); if (!accessData) accessData = newVariableAccessData(operand); currentBlockAccessData.operand(operand) = accessData; insertionSet.insertNode(index, SpecNone, Flush, origin, OpInfo(accessData)); } }; for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++) flush(virtualRegisterForLocal(local), false); for (InlineCallFrame* inlineCallFrame : seenInlineCallFrames) flush(VirtualRegister(inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()), true); flush(VirtualRegister(CallFrame::thisArgumentOffset()), true); seenInlineCallFrames.clear(); }; for (unsigned nodeIndex = 0; nodeIndex < block->size(); nodeIndex++) { Node* node = block->at(nodeIndex); { HandlerInfo* newHandler = catchHandler(node->origin.semantic); if (newHandler != currentExceptionHandler && currentExceptionHandler) flushEverything(node->origin, nodeIndex); currentExceptionHandler = newHandler; } if (currentExceptionHandler && (node->op() == SetLocal || node->op() == SetArgument)) { InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame; if (inlineCallFrame) seenInlineCallFrames.add(inlineCallFrame); VirtualRegister operand = node->local(); int stackOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument() || (operand.offset() == stackOffset + CallFrame::thisArgumentOffset())) { ASSERT(isValidFlushLocation(block, nodeIndex, operand)); VariableAccessData* variableAccessData = currentBlockAccessData.operand(operand); if (!variableAccessData) variableAccessData = newVariableAccessData(operand); insertionSet.insertNode(nodeIndex, SpecNone, Flush, node->origin, OpInfo(variableAccessData)); } } if (node->accessesStack(m_graph)) currentBlockAccessData.operand(node->local()) = node->variableAccessData(); } if (currentExceptionHandler) { NodeOrigin origin = block->at(block->size() - 1)->origin; flushEverything(origin, block->size()); } }
void Disassembler::append(Vector<Disassembler::DumpedOp>& result, StringPrintStream& out, CodeOrigin& previousOrigin) { result.append(DumpedOp(previousOrigin, out.toCString())); previousOrigin = CodeOrigin(); out.reset(); }
SUPPRESS_ASAN void* prepareOSREntry( ExecState* exec, CodeBlock* dfgCodeBlock, CodeBlock* entryCodeBlock, unsigned bytecodeIndex, unsigned streamIndex) { VM& vm = exec->vm(); CodeBlock* baseline = dfgCodeBlock->baselineVersion(); ExecutableBase* executable = dfgCodeBlock->ownerExecutable(); DFG::JITCode* dfgCode = dfgCodeBlock->jitCode()->dfg(); ForOSREntryJITCode* entryCode = entryCodeBlock->jitCode()->ftlForOSREntry(); if (Options::verboseOSR()) { dataLog( "FTL OSR from ", *dfgCodeBlock, " to ", *entryCodeBlock, " at bc#", bytecodeIndex, ".\n"); } if (bytecodeIndex) jsCast<ScriptExecutable*>(executable)->setDidTryToEnterInLoop(true); if (bytecodeIndex != entryCode->bytecodeIndex()) { if (Options::verboseOSR()) dataLog(" OSR failed because we don't have an entrypoint for bc#", bytecodeIndex, "; ours is for bc#", entryCode->bytecodeIndex(), "\n"); return 0; } Operands<JSValue> values; dfgCode->reconstruct( exec, dfgCodeBlock, CodeOrigin(bytecodeIndex), streamIndex, values); if (Options::verboseOSR()) dataLog(" Values at entry: ", values, "\n"); for (int argument = values.numberOfArguments(); argument--;) { JSValue valueOnStack = exec->r(virtualRegisterForArgument(argument).offset()).asanUnsafeJSValue(); JSValue reconstructedValue = values.argument(argument); if (valueOnStack == reconstructedValue || !argument) continue; dataLog("Mismatch between reconstructed values and the the value on the stack for argument arg", argument, " for ", *entryCodeBlock, " at bc#", bytecodeIndex, ":\n"); dataLog(" Value on stack: ", valueOnStack, "\n"); dataLog(" Reconstructed value: ", reconstructedValue, "\n"); RELEASE_ASSERT_NOT_REACHED(); } RELEASE_ASSERT( static_cast<int>(values.numberOfLocals()) == baseline->m_numCalleeRegisters); EncodedJSValue* scratch = static_cast<EncodedJSValue*>( entryCode->entryBuffer()->dataBuffer()); for (int local = values.numberOfLocals(); local--;) scratch[local] = JSValue::encode(values.local(local)); int stackFrameSize = entryCode->common.requiredRegisterCountForExecutionAndExit(); if (!vm.interpreter->stack().ensureCapacityFor(&exec->registers()[virtualRegisterForLocal(stackFrameSize - 1).offset()])) { if (Options::verboseOSR()) dataLog(" OSR failed because stack growth failed.\n"); return 0; } exec->setCodeBlock(entryCodeBlock); void* result = entryCode->addressForCall( vm, executable, ArityCheckNotRequired, RegisterPreservationNotRequired).executableAddress(); if (Options::verboseOSR()) dataLog(" Entry will succeed, going to address", RawPointer(result), "\n"); return result; }
void JITCompiler::compileFunction() { 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(Above, AbsoluteAddress(m_vm->interpreter->stack().addressOfEnd()), GPRInfo::regT1); // Return here after stack check. Label fromStackCheck = label(); // === Function body code generation === m_speculative = adoptPtr(new SpeculativeJIT(*this)); compileBody(); 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*)); emitStoreCodeOrigin(CodeOrigin(0)); m_callStackCheck = call(); 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). m_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*)); emitStoreCodeOrigin(CodeOrigin(0)); m_callArityCheck = call(); branchTest32(Zero, GPRInfo::regT0).linkTo(fromArityCheck, this); emitStoreCodeOrigin(CodeOrigin(0)); m_callArityFixup = call(); jump(fromArityCheck); // Generate slow path code. m_speculative->runSlowPathGenerators(); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. m_speculative->createOSREntries(); setEndOfCode(); }
char* JIT_OPERATION triggerOSREntryNow( ExecState* exec, int32_t bytecodeIndex, int32_t streamIndex) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); DeferGC deferGC(vm->heap); CodeBlock* codeBlock = exec->codeBlock(); if (codeBlock->jitType() != JITCode::DFGJIT) { dataLog("Unexpected code block in DFG->FTL tier-up: ", *codeBlock, "\n"); RELEASE_ASSERT_NOT_REACHED(); } JITCode* jitCode = codeBlock->jitCode()->dfg(); if (Options::verboseOSR()) { dataLog( *codeBlock, ": Entered triggerOSREntryNow with executeCounter = ", jitCode->tierUpCounter, "\n"); } // - If we don't have an FTL code block, then try to compile one. // - If we do have an FTL code block, then try to enter for a while. // - If we couldn't enter for a while, then trigger OSR entry. triggerFTLReplacementCompile(vm, codeBlock, jitCode); if (!codeBlock->hasOptimizedReplacement()) return 0; if (jitCode->osrEntryRetry < Options::ftlOSREntryRetryThreshold()) { jitCode->osrEntryRetry++; return 0; } // It's time to try to compile code for OSR entry. Worklist::State worklistState; if (Worklist* worklist = existingGlobalFTLWorklistOrNull()) { worklistState = worklist->completeAllReadyPlansForVM( *vm, CompilationKey(codeBlock->baselineVersion(), FTLForOSREntryMode)); } else worklistState = Worklist::NotKnown; if (worklistState == Worklist::Compiling) return 0; if (CodeBlock* entryBlock = jitCode->osrEntryBlock.get()) { void* address = FTL::prepareOSREntry( exec, codeBlock, entryBlock, bytecodeIndex, streamIndex); if (address) return static_cast<char*>(address); FTL::ForOSREntryJITCode* entryCode = entryBlock->jitCode()->ftlForOSREntry(); entryCode->countEntryFailure(); if (entryCode->entryFailureCount() < Options::ftlOSREntryFailureCountForReoptimization()) return 0; // OSR entry failed. Oh no! This implies that we need to retry. We retry // without exponential backoff and we only do this for the entry code block. jitCode->osrEntryBlock.clear(); jitCode->osrEntryRetry = 0; return 0; } if (worklistState == Worklist::Compiled) { // This means that compilation failed and we already set the thresholds. if (Options::verboseOSR()) dataLog("Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement.\n"); return 0; } // We aren't compiling and haven't compiled anything for OSR entry. So, try to compile // something. Operands<JSValue> mustHandleValues; jitCode->reconstruct( exec, codeBlock, CodeOrigin(bytecodeIndex), streamIndex, mustHandleValues); RefPtr<CodeBlock> replacementCodeBlock = codeBlock->newReplacement(); CompilationResult forEntryResult = compile( *vm, replacementCodeBlock.get(), codeBlock, FTLForOSREntryMode, bytecodeIndex, mustHandleValues, ToFTLForOSREntryDeferredCompilationCallback::create(codeBlock)); if (forEntryResult != CompilationSuccessful) { ASSERT(forEntryResult == CompilationDeferred || replacementCodeBlock->hasOneRef()); return 0; } // It's possible that the for-entry compile already succeeded. In that case OSR // entry will succeed unless we ran out of stack. It's not clear what we should do. // We signal to try again after a while if that happens. void* address = FTL::prepareOSREntry( exec, codeBlock, jitCode->osrEntryBlock.get(), bytecodeIndex, streamIndex); return static_cast<char*>(address); }
void JITCompiler::compileFunction() { 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. addPtr(TrustedImm32(virtualRegisterForLocal(m_graph.requiredRegisterCountForExecutionAndExit() - 1).offset() * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1); // Move the stack pointer down to accommodate locals addPtr(TrustedImm32(m_graph.stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); compileSetupRegistersForEntry(); compileEntryExecutionFlag(); // === Function body code generation === m_speculative = std::make_unique<SpeculativeJIT>(*this); compileBody(); setEndOfMainPath(); // === Function footer code generation === // // Generate code to perform the stack overflow handling (if the stack check in // the function header fails), and generate the entry point with arity check. // // Generate the stack overflow handling; if the stack check in the function head fails, // we need to call out to a helper function to throw the StackOverflowError. stackOverflow.link(this); emitStoreCodeOrigin(CodeOrigin(0)); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); m_speculative->callOperationWithCallFrameRollbackOnException(operationThrowStackOverflowError, m_codeBlock); // 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). m_arityCheck = label(); compileEntry(); load32(AssemblyHelpers::payloadFor((VirtualRegister)JSStack::ArgumentCount), GPRInfo::regT1); branch32(AboveOrEqual, GPRInfo::regT1, TrustedImm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this); emitStoreCodeOrigin(CodeOrigin(0)); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); m_speculative->callOperationWithCallFrameRollbackOnException(m_codeBlock->m_isConstructor ? operationConstructArityCheck : operationCallArityCheck, GPRInfo::regT0); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(maxFrameExtentForSlowPathCall), stackPointerRegister); branchTest32(Zero, GPRInfo::returnValueGPR).linkTo(fromArityCheck, this); emitStoreCodeOrigin(CodeOrigin(0)); move(GPRInfo::returnValueGPR, GPRInfo::argumentGPR0); m_callArityFixup = call(); jump(fromArityCheck); // Generate slow path code. m_speculative->runSlowPathGenerators(m_pcToCodeOriginMapBuilder); m_pcToCodeOriginMapBuilder.appendItem(label(), PCToCodeOriginMapBuilder::defaultCodeOrigin()); compileExceptionHandlers(); linkOSRExits(); // Create OSR entry trampolines if necessary. m_speculative->createOSREntries(); setEndOfCode(); // === Link === auto linkBuffer = std::make_unique<LinkBuffer>(*m_vm, *this, m_codeBlock, JITCompilationCanFail); if (linkBuffer->didFailToAllocate()) { m_graph.m_plan.finalizer = std::make_unique<FailedFinalizer>(m_graph.m_plan); return; } link(*linkBuffer); m_speculative->linkOSREntries(*linkBuffer); m_jitCode->shrinkToFit(); codeBlock()->shrinkToFit(CodeBlock::LateShrink); linkBuffer->link(m_callArityFixup, FunctionPtr((m_vm->getCTIStub(arityFixupGenerator)).code().executableAddress())); disassemble(*linkBuffer); MacroAssemblerCodePtr withArityCheck = linkBuffer->locationOf(m_arityCheck); m_graph.m_plan.finalizer = std::make_unique<JITFinalizer>( m_graph.m_plan, m_jitCode.release(), WTFMove(linkBuffer), withArityCheck); }
Vector<Disassembler::DumpedOp> Disassembler::createDumpList(LinkBuffer& linkBuffer) { StringPrintStream out; Vector<DumpedOp> result; CodeOrigin previousOrigin = CodeOrigin(); dumpHeader(out, linkBuffer); append(result, out, previousOrigin); m_graph.m_dominators.computeIfNecessary(m_graph); m_graph.m_naturalLoops.computeIfNecessary(m_graph); const char* prefix = " "; const char* disassemblyPrefix = " "; Node* lastNode = 0; MacroAssembler::Label previousLabel = m_startOfCode; for (size_t blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; dumpDisassembly(out, disassemblyPrefix, linkBuffer, previousLabel, m_labelForBlockIndex[blockIndex], lastNode); append(result, out, previousOrigin); m_graph.dumpBlockHeader(out, prefix, block, Graph::DumpLivePhisOnly, &m_dumpContext); append(result, out, previousOrigin); Node* lastNodeForDisassembly = block->at(0); for (size_t i = 0; i < block->size(); ++i) { MacroAssembler::Label currentLabel; HashMap<Node*, MacroAssembler::Label>::iterator iter = m_labelForNode.find(block->at(i)); if (iter != m_labelForNode.end()) currentLabel = iter->value; else { // Dump the last instruction by using the first label of the next block // as the end point. This case is hit either during peephole compare // optimizations (the Branch won't have its own label) or if we have a // forced OSR exit. if (blockIndex + 1 < m_graph.numBlocks()) currentLabel = m_labelForBlockIndex[blockIndex + 1]; else currentLabel = m_endOfMainPath; } dumpDisassembly(out, disassemblyPrefix, linkBuffer, previousLabel, currentLabel, lastNodeForDisassembly); append(result, out, previousOrigin); previousOrigin = block->at(i)->origin.semantic; if (m_graph.dumpCodeOrigin(out, prefix, lastNode, block->at(i), &m_dumpContext)) { append(result, out, previousOrigin); previousOrigin = block->at(i)->origin.semantic; } m_graph.dump(out, prefix, block->at(i), &m_dumpContext); lastNode = block->at(i); lastNodeForDisassembly = block->at(i); } } dumpDisassembly(out, disassemblyPrefix, linkBuffer, previousLabel, m_endOfMainPath, lastNode); append(result, out, previousOrigin); out.print(prefix, "(End Of Main Path)\n"); append(result, out, previousOrigin); dumpDisassembly(out, disassemblyPrefix, linkBuffer, previousLabel, m_endOfCode, 0); append(result, out, previousOrigin); m_dumpContext.dump(out, prefix); append(result, out, previousOrigin); return result; }
bool run() { RELEASE_ASSERT(m_graph.m_plan.mode == FTLForOSREntryMode); RELEASE_ASSERT(m_graph.m_form == ThreadedCPS); unsigned bytecodeIndex = m_graph.m_plan.osrEntryBytecodeIndex; RELEASE_ASSERT(bytecodeIndex); RELEASE_ASSERT(bytecodeIndex != UINT_MAX); // Needed by createPreHeader(). m_graph.m_dominators.computeIfNecessary(m_graph); CodeBlock* baseline = m_graph.m_profiledBlock; BasicBlock* target = 0; for (unsigned blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; unsigned nodeIndex = 0; Node* firstNode = block->at(0); while (firstNode->isSemanticallySkippable()) firstNode = block->at(++nodeIndex); if (firstNode->op() == LoopHint && firstNode->origin.semantic == CodeOrigin(bytecodeIndex)) { target = block; break; } } if (!target) { // This is a terrible outcome. It shouldn't often happen but it might // happen and so we should defend against it. If it happens, then this // compilation is a failure. return false; } BlockInsertionSet insertionSet(m_graph); BasicBlock* newRoot = insertionSet.insert(0, QNaN); NodeOrigin origin = target->at(0)->origin; Vector<Node*> locals(baseline->m_numCalleeRegisters); for (int local = 0; local < baseline->m_numCalleeRegisters; ++local) { Node* previousHead = target->variablesAtHead.local(local); if (!previousHead) continue; VariableAccessData* variable = previousHead->variableAccessData(); locals[local] = newRoot->appendNode( m_graph, variable->prediction(), ExtractOSREntryLocal, origin, OpInfo(variable->local().offset())); newRoot->appendNode( m_graph, SpecNone, MovHint, origin, OpInfo(variable->local().offset()), Edge(locals[local])); } for (int argument = 0; argument < baseline->numParameters(); ++argument) { Node* oldNode = target->variablesAtHead.argument(argument); if (!oldNode) { // Just for sanity, always have a SetArgument even if it's not needed. oldNode = m_graph.m_arguments[argument]; } Node* node = newRoot->appendNode( m_graph, SpecNone, SetArgument, origin, OpInfo(oldNode->variableAccessData())); m_graph.m_arguments[argument] = node; } for (int local = 0; local < baseline->m_numCalleeRegisters; ++local) { Node* previousHead = target->variablesAtHead.local(local); if (!previousHead) continue; VariableAccessData* variable = previousHead->variableAccessData(); Node* node = locals[local]; newRoot->appendNode( m_graph, SpecNone, SetLocal, origin, OpInfo(variable), Edge(node)); } newRoot->appendNode( m_graph, SpecNone, Jump, origin, OpInfo(createPreHeader(m_graph, insertionSet, target))); insertionSet.execute(); m_graph.resetReachability(); m_graph.killUnreachableBlocks(); return true; }