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); }
void JSTailCall::emit(JITCode& jitCode, CCallHelpers& jit) { StackMaps::Record* record { nullptr }; for (unsigned i = jitCode.stackmaps.records.size(); i--;) { record = &jitCode.stackmaps.records[i]; if (record->patchpointID == m_stackmapID) break; } RELEASE_ASSERT(record->patchpointID == m_stackmapID); m_callLinkInfo = jit.codeBlock()->addCallLinkInfo(); CallFrameShuffleData shuffleData; // The callee was the first passed argument, and must be in a GPR because // we used the "anyregcc" calling convention auto calleeLocation = FTL::Location::forStackmaps(nullptr, record->locations[0]); GPRReg calleeGPR = calleeLocation.directGPR(); shuffleData.callee = ValueRecovery::inGPR(calleeGPR, DataFormatJS); // The tag type number was the second argument, if there was one auto tagTypeNumberLocation = FTL::Location::forStackmaps(&jitCode.stackmaps, record->locations[1]); if (tagTypeNumberLocation.isGPR() && !tagTypeNumberLocation.addend()) shuffleData.tagTypeNumber = tagTypeNumberLocation.directGPR(); shuffleData.args.grow(numArguments()); HashMap<Reg, Vector<std::pair<ValueRecovery*, int32_t>>> withAddend; size_t numAddends { 0 }; for (size_t i = 0; i < numArguments(); ++i) { shuffleData.args[i] = recoveryFor(m_arguments[i], *record, jitCode.stackmaps); if (FTL::Location addend = getRegisterWithAddend(m_arguments[i], *record, jitCode.stackmaps)) { withAddend.add( addend.reg(), Vector<std::pair<ValueRecovery*, int32_t>>()).iterator->value.append( std::make_pair(&shuffleData.args[i], addend.addend())); numAddends++; } } numAddends = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numAddends); shuffleData.numLocals = static_cast<int64_t>(jitCode.stackmaps.stackSizeForLocals()) / sizeof(void*) + numAddends; ASSERT(!numAddends == withAddend.isEmpty()); if (!withAddend.isEmpty()) { jit.subPtr(MacroAssembler::TrustedImm32(numAddends * sizeof(void*)), MacroAssembler::stackPointerRegister); VirtualRegister spillBase { 1 - static_cast<int>(shuffleData.numLocals) }; for (auto entry : withAddend) { for (auto pair : entry.value) { ASSERT(numAddends > 0); VirtualRegister spillSlot { spillBase + --numAddends }; ASSERT(entry.key.isGPR()); jit.addPtr(MacroAssembler::TrustedImm32(pair.second), entry.key.gpr()); jit.storePtr(entry.key.gpr(), CCallHelpers::addressFor(spillSlot)); jit.subPtr(MacroAssembler::TrustedImm32(pair.second), entry.key.gpr()); *pair.first = ValueRecovery::displacedInJSStack(spillSlot, pair.first->dataFormat()); } } ASSERT(numAddends < stackAlignmentRegisters()); } shuffleData.args.resize(numArguments()); for (size_t i = 0; i < numArguments(); ++i) shuffleData.args[i] = recoveryFor(m_arguments[i], *record, jitCode.stackmaps); shuffleData.setupCalleeSaveRegisters(jit.codeBlock()); CCallHelpers::Jump slowPath = jit.branchPtrWithPatch( CCallHelpers::NotEqual, calleeGPR, m_targetToCheck, CCallHelpers::TrustedImmPtr(0)); m_callLinkInfo->setFrameShuffleData(shuffleData); CallFrameShuffler(jit, shuffleData).prepareForTailCall(); m_fastCall = jit.nearTailCall(); slowPath.link(&jit); CallFrameShuffler slowPathShuffler(jit, shuffleData); slowPathShuffler.setCalleeJSValueRegs(JSValueRegs { GPRInfo::regT0 }); slowPathShuffler.prepareForSlowPath(); jit.move(CCallHelpers::TrustedImmPtr(m_callLinkInfo), GPRInfo::regT2); m_slowCall = jit.nearCall(); jit.abortWithReason(JITDidReturnFromTailCall); m_callLinkInfo->setUpCall(m_type, m_semanticeOrigin, calleeGPR); }