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 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(); }