Exemple #1
0
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);
}
Exemple #2
0
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);
}