void CodeGenerator::cgInterpOneCommon(IRInstruction* inst) { auto fpReg = x2a(curOpd(inst->src(0)).reg()); auto spReg = x2a(curOpd(inst->src(1)).reg()); auto pcOff = inst->extra<InterpOneData>()->bcOff; auto opc = *(curFunc()->unit()->at(pcOff)); auto* interpOneHelper = interpOneEntryPoints[opc]; // This means push x30 (the link register) first, then x29. This mimics the // x64 stack frame: return address higher in memory than saved FP. m_as. Push (x30, x29); // TODO(2966997): this really should be saving caller-save registers and // basically doing everything else that cgCallHelper does. This only works // now because no caller-saved registers are live. m_as. Mov (rHostCallReg, reinterpret_cast<uint64_t>(interpOneHelper)); m_as. Mov (argReg(0), fpReg); m_as. Mov (argReg(1), spReg); m_as. Mov (argReg(2), pcOff); // Note that sync points for HostCalls have to be recorded at the *start* of // the instruction. recordHostCallSyncPoint(m_as, m_as.frontier()); m_as. HostCall(3); m_as. Pop (x29, x30); }
void CodeGenerator::cgInterpOneCommon(IRInstruction* inst) { auto fpReg = x2a(m_regs[inst->src(0)].reg()); auto spReg = x2a(m_regs[inst->src(1)].reg()); auto pcOff = inst->extra<InterpOneData>()->bcOff; auto opc = *(curFunc()->unit()->at(pcOff)); auto* interpOneHelper = interpOneEntryPoints[opc]; m_as. Push (x29, x30); // TODO(2966997): this really should be saving caller-save registers and // basically doing everything else that cgCallHelper does. This only works // now because no caller-saved registers are live. m_as. Mov (rHostCallReg, reinterpret_cast<uint64_t>(interpOneHelper)); m_as. Mov (argReg(0), fpReg); m_as. Mov (argReg(1), spReg); m_as. Mov (argReg(2), pcOff); m_as. HostCall(3); m_as. Pop (x30, x29); }
// Lower svcreq{} by making copies to abi registers explicit, saving // vm regs, and returning to the VM. svcreq{} is guaranteed to be // at the end of a block, so we can just keep appending to the same block. void lower_svcreq(Vunit& unit, Vlabel b, Vinstr& inst) { auto svcreq = inst.svcreq_; // copy it auto origin = inst.origin; auto& argv = unit.tuples[svcreq.extraArgs]; unit.blocks[b].code.pop_back(); // delete the svcreq instruction Vout v(unit, b, origin); RegSet arg_regs; VregList arg_dests; for (int i = 0, n = argv.size(); i < n; ++i) { PhysReg d{serviceReqArgReg(i)}; arg_dests.push_back(d); arg_regs |= d; } v << copyargs{svcreq.extraArgs, v.makeTuple(arg_dests)}; // Save VM regs PhysReg vmfp{rVmFp}, vmsp{rVmSp}, sp{vixl::sp}, rdsp{rVmTl}; v << store{vmfp, rdsp[rds::kVmfpOff]}; v << store{vmsp, rdsp[rds::kVmspOff]}; if (svcreq.stub_block) { always_assert(false && "use rip-rel addr to get ephemeral stub addr"); } else { v << ldimmq{0, PhysReg{arm::rAsm}}; // because persist flag } v << ldimmq{svcreq.req, PhysReg{argReg(0)}}; arg_regs |= arm::rAsm | argReg(0); // Weird hand-shaking with enterTC: reverse-call a service routine. // In the case of some special stubs (m_callToExit, m_retHelper), we // have already unbalanced the return stack by doing a ret to // something other than enterTCHelper. In that case // SRJmpInsteadOfRet indicates to fake the return. v << load{sp[0], PhysReg{rLinkReg}}; v << lea{sp[16], sp}; // fake postindexing arg_regs |= rLinkReg; // arm ret{} implicitly uses LR v << ret{arg_regs}; }