JITGetByIdGenerator::JITGetByIdGenerator(
    CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, const RegisterSet& usedRegisters,
    UniquedStringImpl* propertyName, JSValueRegs base, JSValueRegs value, AccessType accessType)
    : JITByIdGenerator(codeBlock, codeOrigin, callSite, accessType, usedRegisters, base, value)
    , m_isLengthAccess(propertyName == codeBlock->vm()->propertyNames->length.impl())
{
    RELEASE_ASSERT(base.payloadGPR() != value.tagGPR());
}
JITGetByIdGenerator::JITGetByIdGenerator(
    CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, const RegisterSet& usedRegisters,
    JSValueRegs base, JSValueRegs value, SpillRegistersMode spillMode)
    : JITByIdGenerator(
        codeBlock, codeOrigin, callSite, AccessType::Get, usedRegisters, base, value, spillMode)
{
    RELEASE_ASSERT(base.payloadGPR() != value.tagGPR());
}
JITByIdGenerator::JITByIdGenerator(
    CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, AccessType accessType,
    const RegisterSet& usedRegisters, JSValueRegs base, JSValueRegs value)
    : JITInlineCacheGenerator(codeBlock, codeOrigin, callSite, accessType)
    , m_base(base)
    , m_value(value)
{
    m_stubInfo->patch.usedRegisters = usedRegisters;
    
    m_stubInfo->patch.baseGPR = static_cast<int8_t>(base.payloadGPR());
    m_stubInfo->patch.valueGPR = static_cast<int8_t>(value.payloadGPR());
#if USE(JSVALUE32_64)
    m_stubInfo->patch.baseTagGPR = static_cast<int8_t>(base.tagGPR());
    m_stubInfo->patch.valueTagGPR = static_cast<int8_t>(value.tagGPR());
#endif
}
JITByIdGenerator::JITByIdGenerator(
    CodeBlock* codeBlock, CodeOrigin codeOrigin, const RegisterSet& usedRegisters,
    JSValueRegs base, JSValueRegs value, bool registersFlushed)
    : JITInlineCacheGenerator(codeBlock, codeOrigin)
    , m_base(base)
    , m_value(value)
{
    m_stubInfo->patch.registersFlushed = registersFlushed;
    m_stubInfo->patch.usedRegisters = usedRegisters;
    
    // This is a convenience - in cases where the only registers you're using are base/value,
    // it allows you to pass RegisterSet() as the usedRegisters argument.
    m_stubInfo->patch.usedRegisters.set(base);
    m_stubInfo->patch.usedRegisters.set(value);
    
    m_stubInfo->patch.baseGPR = static_cast<int8_t>(base.payloadGPR());
    m_stubInfo->patch.valueGPR = static_cast<int8_t>(value.payloadGPR());
#if USE(JSVALUE32_64)
    m_stubInfo->patch.valueTagGPR = static_cast<int8_t>(value.tagGPR());
#endif
}
JITByIdGenerator::JITByIdGenerator(
    CodeBlock* codeBlock, CodeOrigin codeOrigin, CallSiteIndex callSite, AccessType accessType,
    const RegisterSet& usedRegisters, JSValueRegs base, JSValueRegs value,
    SpillRegistersMode spillMode)
    : JITInlineCacheGenerator(codeBlock, codeOrigin, callSite, accessType)
    , m_base(base)
    , m_value(value)
{
    m_stubInfo->patch.spillMode = spillMode;
    m_stubInfo->patch.usedRegisters = usedRegisters;
    
    // This is a convenience - in cases where the only registers you're using are base/value,
    // it allows you to pass RegisterSet() as the usedRegisters argument.
    m_stubInfo->patch.usedRegisters.set(base);
    m_stubInfo->patch.usedRegisters.set(value);
    
    m_stubInfo->patch.baseGPR = static_cast<int8_t>(base.payloadGPR());
    m_stubInfo->patch.valueGPR = static_cast<int8_t>(value.payloadGPR());
#if USE(JSVALUE32_64)
    m_stubInfo->patch.valueTagGPR = static_cast<int8_t>(value.tagGPR());
#endif
}
AssemblyHelpers::JumpList AssemblyHelpers::branchIfNotType(
    JSValueRegs regs, GPRReg tempGPR, const InferredType::Descriptor& descriptor, TagRegistersMode mode)
{
    AssemblyHelpers::JumpList result;

    switch (descriptor.kind()) {
    case InferredType::Bottom:
        result.append(jump());
        break;

    case InferredType::Boolean:
        result.append(branchIfNotBoolean(regs, tempGPR));
        break;

    case InferredType::Other:
        result.append(branchIfNotOther(regs, tempGPR));
        break;

    case InferredType::Int32:
        result.append(branchIfNotInt32(regs, mode));
        break;

    case InferredType::Number:
        result.append(branchIfNotNumber(regs, tempGPR, mode));
        break;

    case InferredType::String:
        result.append(branchIfNotCell(regs, mode));
        result.append(branchIfNotString(regs.payloadGPR()));
        break;

    case InferredType::ObjectWithStructure:
        result.append(branchIfNotCell(regs, mode));
        result.append(
            branchStructure(
                NotEqual,
                Address(regs.payloadGPR(), JSCell::structureIDOffset()),
                descriptor.structure()));
        break;

    case InferredType::ObjectWithStructureOrOther: {
        Jump ok = branchIfOther(regs, tempGPR);
        result.append(branchIfNotCell(regs, mode));
        result.append(
            branchStructure(
                NotEqual,
                Address(regs.payloadGPR(), JSCell::structureIDOffset()),
                descriptor.structure()));
        ok.link(this);
        break;
    }

    case InferredType::Object:
        result.append(branchIfNotCell(regs, mode));
        result.append(branchIfNotObject(regs.payloadGPR()));
        break;

    case InferredType::ObjectOrOther: {
        Jump ok = branchIfOther(regs, tempGPR);
        result.append(branchIfNotCell(regs, mode));
        result.append(branchIfNotObject(regs.payloadGPR()));
        ok.link(this);
        break;
    }

    case InferredType::Top:
        break;
    }

    return result;
}
void GetterSetterAccessCase::emitDOMJITGetter(AccessGenerationState& state, const DOMJIT::GetterSetter* domJIT, GPRReg baseForGetGPR)
{
    CCallHelpers& jit = *state.jit;
    StructureStubInfo& stubInfo = *state.stubInfo;
    JSValueRegs valueRegs = state.valueRegs;
    GPRReg baseGPR = state.baseGPR;
    GPRReg scratchGPR = state.scratchGPR;

    // We construct the environment that can execute the DOMJIT::Snippet here.
    Ref<DOMJIT::CallDOMGetterSnippet> snippet = domJIT->compiler()();

    Vector<GPRReg> gpScratch;
    Vector<FPRReg> fpScratch;
    Vector<SnippetParams::Value> regs;

    ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
    allocator.lock(baseGPR);
#if USE(JSVALUE32_64)
    allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
#endif
    allocator.lock(valueRegs);
    allocator.lock(scratchGPR);

    GPRReg paramBaseGPR = InvalidGPRReg;
    GPRReg paramGlobalObjectGPR = InvalidGPRReg;
    JSValueRegs paramValueRegs = valueRegs;
    GPRReg remainingScratchGPR = InvalidGPRReg;

    // valueRegs and baseForGetGPR may be the same. For example, in Baseline JIT, we pass the same regT0 for baseGPR and valueRegs.
    // In FTL, there is no constraint that the baseForGetGPR interferes with the result. To make implementation simple in
    // Snippet, Snippet assumes that result registers always early interfere with input registers, in this case,
    // baseForGetGPR. So we move baseForGetGPR to the other register if baseForGetGPR == valueRegs.
    if (baseForGetGPR != valueRegs.payloadGPR()) {
        paramBaseGPR = baseForGetGPR;
        if (!snippet->requireGlobalObject)
            remainingScratchGPR = scratchGPR;
        else
            paramGlobalObjectGPR = scratchGPR;
    } else {
        jit.move(valueRegs.payloadGPR(), scratchGPR);
        paramBaseGPR = scratchGPR;
        if (snippet->requireGlobalObject)
            paramGlobalObjectGPR = allocator.allocateScratchGPR();
    }

    JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();

    regs.append(paramValueRegs);
    regs.append(paramBaseGPR);
    if (snippet->requireGlobalObject) {
        ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
        regs.append(SnippetParams::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
    }

    if (snippet->numGPScratchRegisters) {
        unsigned i = 0;
        if (remainingScratchGPR != InvalidGPRReg) {
            gpScratch.append(remainingScratchGPR);
            ++i;
        }
        for (; i < snippet->numGPScratchRegisters; ++i)
            gpScratch.append(allocator.allocateScratchGPR());
    }

    for (unsigned i = 0; i < snippet->numFPScratchRegisters; ++i)
        fpScratch.append(allocator.allocateScratchFPR());

    // Let's store the reused registers to the stack. After that, we can use allocated scratch registers.
    ScratchRegisterAllocator::PreservedState preservedState =
    allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);

    if (verbose) {
        dataLog("baseGPR = ", baseGPR, "\n");
        dataLog("valueRegs = ", valueRegs, "\n");
        dataLog("scratchGPR = ", scratchGPR, "\n");
        dataLog("paramBaseGPR = ", paramBaseGPR, "\n");
        if (paramGlobalObjectGPR != InvalidGPRReg)
            dataLog("paramGlobalObjectGPR = ", paramGlobalObjectGPR, "\n");
        dataLog("paramValueRegs = ", paramValueRegs, "\n");
        for (unsigned i = 0; i < snippet->numGPScratchRegisters; ++i)
            dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
    }

    if (snippet->requireGlobalObject)
        jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);

    // We just spill the registers used in Snippet here. For not spilled registers here explicitly,
    // they must be in the used register set passed by the callers (Baseline, DFG, and FTL) if they need to be kept.
    // Some registers can be locked, but not in the used register set. For example, the caller could make baseGPR
    // same to valueRegs, and not include it in the used registers since it will be changed.
    RegisterSet registersToSpillForCCall;
    for (auto& value : regs) {
        SnippetReg reg = value.reg();
        if (reg.isJSValueRegs())
            registersToSpillForCCall.set(reg.jsValueRegs());
        else if (reg.isGPR())
            registersToSpillForCCall.set(reg.gpr());
        else
            registersToSpillForCCall.set(reg.fpr());
    }
    for (GPRReg reg : gpScratch)
        registersToSpillForCCall.set(reg);
    for (FPRReg reg : fpScratch)
        registersToSpillForCCall.set(reg);
    registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());

    AccessCaseSnippetParams params(state.m_vm, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
    snippet->generator()->run(jit, params);
    allocator.restoreReusedRegistersByPopping(jit, preservedState);
    state.succeed();

    CCallHelpers::JumpList exceptions = params.emitSlowPathCalls(state, registersToSpillForCCall, jit);
    if (!exceptions.empty()) {
        exceptions.link(&jit);
        allocator.restoreReusedRegistersByPopping(jit, preservedState);
        state.emitExplicitExceptionHandler();
    }
}
void ScratchRegisterAllocator::lock(JSValueRegs regs)
{
    lock(regs.tagGPR());
    lock(regs.payloadGPR());
}
void CallFrameShuffler::emitLoad(CachedRecovery& location)
{
    if (!location.recovery().isInJSStack())
        return;

    if (verbose)
        dataLog("   * Loading ", location.recovery(), " into ");
    VirtualRegister reg { location.recovery().virtualRegister() };
    MacroAssembler::Address address { addressForOld(reg) };

    bool tryFPR { true };
    JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
    if (wantedJSValueRegs) {
        if (wantedJSValueRegs.payloadGPR() != InvalidGPRReg
            && !m_registers[wantedJSValueRegs.payloadGPR()]
            && !m_lockedRegisters.get(wantedJSValueRegs.payloadGPR()))
            tryFPR = false;
        if (wantedJSValueRegs.tagGPR() != InvalidGPRReg
            && !m_registers[wantedJSValueRegs.tagGPR()]
            && !m_lockedRegisters.get(wantedJSValueRegs.tagGPR()))
            tryFPR = false;
    }

    if (tryFPR && location.loadsIntoFPR()) {
        FPRReg resultFPR = location.wantedFPR();
        if (resultFPR == InvalidFPRReg || m_registers[resultFPR] || m_lockedRegisters.get(resultFPR))
            resultFPR = getFreeFPR();
        if (resultFPR != InvalidFPRReg) {
            m_jit.loadDouble(address, resultFPR);
            DataFormat dataFormat = DataFormatJS;
            if (location.recovery().dataFormat() == DataFormatDouble)
                dataFormat = DataFormatDouble;
            updateRecovery(location, 
                ValueRecovery::inFPR(resultFPR, dataFormat));
            if (verbose)
                dataLog(location.recovery(), "\n");
            if (reg == newAsOld(dangerFrontier()))
                updateDangerFrontier();
            return;
        }
    }

    if (location.loadsIntoGPR()) {
        GPRReg resultGPR { wantedJSValueRegs.payloadGPR() };
        if (resultGPR == InvalidGPRReg || m_registers[resultGPR] || m_lockedRegisters.get(resultGPR))
            resultGPR = getFreeGPR();
        ASSERT(resultGPR != InvalidGPRReg);
        m_jit.loadPtr(address.withOffset(PayloadOffset), resultGPR);
        updateRecovery(location, 
            ValueRecovery::inGPR(resultGPR, location.recovery().dataFormat()));
        if (verbose)
            dataLog(location.recovery(), "\n");
        if (reg == newAsOld(dangerFrontier()))
            updateDangerFrontier();
        return;
    }

    ASSERT(location.recovery().technique() == DisplacedInJSStack);
    GPRReg payloadGPR { wantedJSValueRegs.payloadGPR() };
    GPRReg tagGPR { wantedJSValueRegs.tagGPR() };
    if (payloadGPR == InvalidGPRReg || m_registers[payloadGPR] || m_lockedRegisters.get(payloadGPR))
        payloadGPR = getFreeGPR();
    m_lockedRegisters.set(payloadGPR);
    if (tagGPR == InvalidGPRReg || m_registers[tagGPR] || m_lockedRegisters.get(tagGPR))
        tagGPR = getFreeGPR();
    m_lockedRegisters.clear(payloadGPR);
    ASSERT(payloadGPR != InvalidGPRReg && tagGPR != InvalidGPRReg && tagGPR != payloadGPR);
    m_jit.loadPtr(address.withOffset(PayloadOffset), payloadGPR);
    m_jit.loadPtr(address.withOffset(TagOffset), tagGPR);
    updateRecovery(location, 
        ValueRecovery::inPair(tagGPR, payloadGPR));
    if (verbose)
        dataLog(location.recovery(), "\n");
    if (reg == newAsOld(dangerFrontier()))
        updateDangerFrontier();
}
void CallFrameShuffler::emitDisplace(CachedRecovery& location)
{
    ASSERT(location.recovery().isInRegisters());
    JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
    ASSERT(wantedJSValueRegs); // We don't support wanted FPRs on 32bit platforms

    GPRReg wantedTagGPR { wantedJSValueRegs.tagGPR() };
    GPRReg wantedPayloadGPR { wantedJSValueRegs.payloadGPR() };
    
    if (wantedTagGPR != InvalidGPRReg) {
        ASSERT(!m_lockedRegisters.get(wantedTagGPR));
        if (CachedRecovery* currentTag { m_registers[wantedTagGPR] }) {
            if (currentTag == &location) {
                if (verbose)
                    dataLog("   + ", wantedTagGPR, " is OK\n");
            } else {
                // This can never happen on 32bit platforms since we
                // have at most one wanted JSValueRegs, for the
                // callee, and no callee-save registers.
                RELEASE_ASSERT_NOT_REACHED();
            }
        }
    }

    if (wantedPayloadGPR != InvalidGPRReg) {
        ASSERT(!m_lockedRegisters.get(wantedPayloadGPR));
        if (CachedRecovery* currentPayload { m_registers[wantedPayloadGPR] }) {
            if (currentPayload == &location) {
                if (verbose)
                    dataLog("   + ", wantedPayloadGPR, " is OK\n");
            } else {
                // See above
                RELEASE_ASSERT_NOT_REACHED();
            }
        }
    }

    if (location.recovery().technique() == InPair
        || location.recovery().isInGPR()) {
        GPRReg payloadGPR;
        if (location.recovery().technique() == InPair)
            payloadGPR = location.recovery().payloadGPR();
        else
            payloadGPR = location.recovery().gpr();

        if (wantedPayloadGPR == InvalidGPRReg)
            wantedPayloadGPR = payloadGPR;

        if (payloadGPR != wantedPayloadGPR) {
            if (location.recovery().technique() == InPair
                && wantedPayloadGPR == location.recovery().tagGPR()) {
                if (verbose)
                    dataLog("   * Swapping ", payloadGPR, " and ", wantedPayloadGPR, "\n");
                m_jit.swap(payloadGPR, wantedPayloadGPR);
                updateRecovery(location, 
                    ValueRecovery::inPair(payloadGPR, wantedPayloadGPR));
            } else {
                if (verbose)
                    dataLog("   * Moving ", payloadGPR, " into ", wantedPayloadGPR, "\n");
                m_jit.move(payloadGPR, wantedPayloadGPR);
                if (location.recovery().technique() == InPair) {
                    updateRecovery(location,
                        ValueRecovery::inPair(location.recovery().tagGPR(),
                            wantedPayloadGPR));
                } else {
                    updateRecovery(location, 
                        ValueRecovery::inGPR(wantedPayloadGPR, location.recovery().dataFormat()));
                }
            }
        }

        if (wantedTagGPR == InvalidGPRReg)
            wantedTagGPR = getFreeGPR();
        switch (location.recovery().dataFormat()) {
        case DataFormatInt32:
            if (verbose)
                dataLog("   * Moving int32 tag into ", wantedTagGPR, "\n");
            m_jit.move(MacroAssembler::TrustedImm32(JSValue::Int32Tag),
                wantedTagGPR);
            break;
        case DataFormatCell:
            if (verbose)
                dataLog("   * Moving cell tag into ", wantedTagGPR, "\n");
            m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag),
                wantedTagGPR);
            break;
        case DataFormatBoolean:
            if (verbose)
                dataLog("   * Moving boolean tag into ", wantedTagGPR, "\n");
            m_jit.move(MacroAssembler::TrustedImm32(JSValue::BooleanTag),
                wantedTagGPR);
            break;
        case DataFormatJS:
            ASSERT(wantedTagGPR != location.recovery().payloadGPR());
            if (wantedTagGPR != location.recovery().tagGPR()) {
                if (verbose)
                    dataLog("   * Moving ", location.recovery().tagGPR(), " into ", wantedTagGPR, "\n");
                m_jit.move(location.recovery().tagGPR(), wantedTagGPR);
            }
            break;

        default:
            RELEASE_ASSERT_NOT_REACHED();
        }
    } else {
        ASSERT(location.recovery().isInFPR());
        if (wantedTagGPR == InvalidGPRReg) {
            ASSERT(wantedPayloadGPR != InvalidGPRReg);
            m_lockedRegisters.set(wantedPayloadGPR);
            wantedTagGPR = getFreeGPR();
            m_lockedRegisters.clear(wantedPayloadGPR);
        }
        if (wantedPayloadGPR == InvalidGPRReg) {
            m_lockedRegisters.set(wantedTagGPR);
            wantedPayloadGPR = getFreeGPR();
            m_lockedRegisters.clear(wantedTagGPR);
        }
        m_jit.boxDouble(location.recovery().fpr(), wantedTagGPR, wantedPayloadGPR);
    }
    updateRecovery(location, ValueRecovery::inPair(wantedTagGPR, wantedPayloadGPR));
}
void AccessCase::emitIntrinsicGetter(AccessGenerationState& state)
{
    CCallHelpers& jit = *state.jit;
    JSValueRegs valueRegs = state.valueRegs;
    GPRReg baseGPR = state.baseGPR;
    GPRReg valueGPR = valueRegs.payloadGPR();

    switch (intrinsic()) {
    case TypedArrayLengthIntrinsic: {
        jit.load32(MacroAssembler::Address(state.baseGPR, JSArrayBufferView::offsetOfLength()), valueGPR);
        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
        state.succeed();
        return;
    }

    case TypedArrayByteLengthIntrinsic: {
        TypedArrayType type = structure()->classInfo()->typedArrayStorageType;

        jit.load32(MacroAssembler::Address(state.baseGPR, JSArrayBufferView::offsetOfLength()), valueGPR);

        if (elementSize(type) > 1) {
            // We can use a bitshift here since we TypedArrays cannot have byteLength that overflows an int32.
            jit.lshift32(valueGPR, Imm32(logElementSize(type)), valueGPR);
        }

        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
        state.succeed();
        return;
    }

    case TypedArrayByteOffsetIntrinsic: {
        GPRReg scratchGPR = state.scratchGPR;

        CCallHelpers::Jump emptyByteOffset = jit.branch32(
            MacroAssembler::NotEqual,
            MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfMode()),
            TrustedImm32(WastefulTypedArray));

        jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
        jit.loadPtr(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfVector()), valueGPR);
        jit.loadPtr(MacroAssembler::Address(scratchGPR, Butterfly::offsetOfArrayBuffer()), scratchGPR);
        jit.loadPtr(MacroAssembler::Address(scratchGPR, ArrayBuffer::offsetOfData()), scratchGPR);
        jit.subPtr(scratchGPR, valueGPR);

        CCallHelpers::Jump done = jit.jump();
        
        emptyByteOffset.link(&jit);
        jit.move(TrustedImmPtr(0), valueGPR);
        
        done.link(&jit);
        
        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
        state.succeed();
        return;
    }

    default:
        break;
    }
    RELEASE_ASSERT_NOT_REACHED();
}