// Return the set of physical registers implicitly accessed (used or defined) // TODO: t4779515: replace this, and other switches, with logic using // attributes, instead of hardcoded opcode names. void getEffects(const Abi& abi, const Vinstr& i, RegSet& uses, RegSet& defs) { uses = defs = RegSet(); switch (i.op) { case Vinstr::mccall: case Vinstr::call: case Vinstr::callm: case Vinstr::callr: defs = abi.all() - abi.calleeSaved; break; case Vinstr::callstub: defs = i.callstub_.kills; break; case Vinstr::bindcall: case Vinstr::contenter: defs = abi.all(); break; case Vinstr::cqo: uses = RegSet(rax); defs = RegSet(rdx); break; case Vinstr::idiv: uses = defs = RegSet(rax).add(rdx); break; case Vinstr::shlq: case Vinstr::sarq: uses = RegSet(rcx); break; default: break; } }
static RegSet range(Register start, Register end) { uint32_t bits = ~0; bits <<= start->encoding(); bits <<= 31 - end->encoding(); bits >>= 31 - end->encoding(); return RegSet(bits); }
RegSet RegAlloc::getRegsLike(RegInfo::State state) const { RegSet retval; FOR_EACH_REG(r) { if (r->m_state == state) { retval |= RegSet(r->m_pReg); } } return retval; }
TCA emitCallToExit(CodeBlock& cb, DataBlock& data, const UniqueStubs& us) { ppc64_asm::Assembler a { cb }; auto const start = a.frontier(); if (RuntimeOption::EvalHHIRGenerateAsserts) { vwrap(cb, data, [&] (Vout& v) { // Not doing it directly as rret(0) == rarg(0) on ppc64 Vreg ret_addr = v.makeReg(); // exittc address pushed on calltc/resumetc. v << copy{rsp(), ret_addr}; // We need to spill the return registers around the assert call. v << push{rret(0)}; v << push{rret(1)}; v << copy{ret_addr, rarg(0)}; v << call{TCA(assert_tc_saved_rip), RegSet(rarg(0))}; v << pop{rret(1)}; v << pop{rret(0)}; }); } // Discard the exittc address pushed on calltc/resumetc for balancing the // stack next. a.addi(rsp(), rsp(), 8); // Reinitialize r1 for the external code found after enterTCExit's stubret a.addi(rsfp(), rsp(), 8); // r31 should have the same value as caller's r1. Loading it soon on stubret. // (this corrupts the backchain, but it's not relevant as this frame will be // destroyed soon) a.std(rsfp(), rsp()[8]); // Emulate a ret to enterTCExit without actually doing one to avoid // unbalancing the return stack buffer. a.branchAuto(TCA(mcg->ustubs().enterTCExit)); return start; }
TCA emitCallToExit(CodeBlock& cb, DataBlock& data, const UniqueStubs& /*us*/) { ppc64_asm::Assembler a { cb }; auto const start = a.frontier(); if (RuntimeOption::EvalHHIRGenerateAsserts) { vwrap(cb, data, [&] (Vout& v) { // Not doing it directly as rret(0) == rarg(0) on ppc64 Vreg ret_addr = v.makeReg(); // exittc address pushed on calltc/resumetc. v << copy{rsp(), ret_addr}; // We need to spill the return registers around the assert call. v << push{rret(0)}; v << push{rret(1)}; v << copy{ret_addr, rarg(0)}; v << call{TCA(assert_tc_saved_rip), RegSet(rarg(0))}; v << pop{rret(1)}; v << pop{rret(0)}; }); } // Discard the exittc address pushed on calltc/resumetc for balancing the // stack next. a.addi(rsp(), rsp(), 8); // Reinitialize r1 for the external code found after enterTCExit's stubret a.addi(rsfp(), rsp(), 8); // Restore the rvmfp when leaving the VM, which must be the same of rsfp. a.mr(rvmfp(), rsfp()); // Emulate a ret to enterTCExit without actually doing one to avoid // unbalancing the return stack buffer. a.branchAuto(TCA(tc::ustubs().enterTCExit)); return start; }
static RegSet of(Register r1) { return RegSet(r1); }
void RegAlloc::reconcile(RegAlloc& branch) { RegSet unconsideredRegs; TRACE(1, "Beginning reconcile with a branch\n"); FOR_EACH_REG (r) { if (r->m_state == RegInfo::FREE || r->m_state == RegInfo::SCRATCH) { unconsideredRegs |= RegSet(r->m_pReg); continue; } const ContToRegMap::iterator it = branch.m_contToRegMap.find(r->m_cont); const bool inSameReg = it != branch.m_contToRegMap.end() && it->second == r->m_pReg; if (inSameReg) { if (r->m_state == branch.getInfo(r->m_pReg)->m_state) { // Done. Same state, same content, same register. We're cool. continue; } } /* * If we got past the above, we now have one of two situations. * * A) The branch's register is mapped to the same location, but * the states are different between the branch and the main * line. We skipped FREE and SCRATCH above, so: * * - If the branch version is DIRTY, that means the mainline * is CLEAN, so we need to spill now or it will never be * spilt. * * - If the branch version is CLEAN, that means the main line * thinks the register is DIRTY---nothing wrong will happen * but we could spill unnecessarily if the branch was * taken. * * B) The branch's register is mapped to a possibly different * location (or completely unmapped). * * - We still need to clean the register if it is DIRTY, to * evict whatever it holds. * * - We then need to fill it with what is actually supposed * to be there. If it is also DIRTY in the main line * that's ok: we'll just spill more than we needed to if we * took the branch. */ // In both situation A and B we need to spill a DIRTY branch reg. if (branch.regIsDirty(r->m_pReg)) { branch.cleanReg(r->m_pReg); } // In situation B (i.e. !inSameReg), we also need to fill the reg // with the main line's expected content. if (!inSameReg) { /* * The register allocator has an invariant that only one * register can be mapped to a given RegContent at a time. So * if we're going to steal this content from another register * via fillByMov, we need to unmap the RegContent from it first. * If the register was dirty, we have to spill (note that it's * important to do this before freeing r->m_pReg so spill() * can't allocate it for an immediate register). * * XXX: the above comment is out of date (spill can't allocate * anymore). * * If the other register was dirty, we know we'll need to clean * it, because the location is obviously in a different * register. (The only case we don't have to spill a dirty * branch register is when it is the same state and location in * the main line.) * * Note: we could do better in the case where two register just * have swapped locations (this can happen via shuffleRegisters * when making a call). */ PhysReg oldReg = InvalidReg; if (it != branch.m_contToRegMap.end()) { oldReg = it->second; if (branch.regIsDirty(oldReg)) { branch.cleanReg(oldReg); } branch.freeRegInfo(branch.physRegToInfo(oldReg)); } branch.freeRegInfo(branch.physRegToInfo(r->m_pReg)); branch.assignRegInfo(branch.physRegToInfo(r->m_pReg), r->m_cont, RegInfo::CLEAN, r->m_type); branch.verify(); if (r->m_cont.m_kind == RegContent::Int || r->m_cont.m_loc.isLiteral()) { ASSERT(r->m_state == RegInfo::CLEAN); branch.m_spf->loadImm(r->m_cont.m_int, r->m_pReg); } else if (oldReg != InvalidReg) { branch.m_spf->fillByMov(oldReg, r->m_pReg); } else { branch.m_spf->fill(r->m_cont.m_loc, r->m_pReg); } } } FOR_EACH_REG_IN_SET (r, unconsideredRegs) { if (branch.regIsDirty(r->m_pReg)) { branch.cleanReg(r->m_pReg); } } TRACE(1, "Done with branch reconcile\n"); }
TCA emitFunctionEnterHelper(CodeBlock& cb, UniqueStubs& us) { alignJmpTarget(cb); auto const start = vwrap(cb, [&] (Vout& v) { auto const ar = v.makeReg(); v << copy{rvmfp(), ar}; // Fully set up the call frame for the stub. We can't skip this like we do // in other stubs because we need the return IP for this frame in the %rbp // chain, in order to find the proper fixup for the VMRegAnchor in the // intercept handler. v << stublogue{true}; v << copy{rsp(), rvmfp()}; // When we call the event hook, it might tell us to skip the callee // (because of fb_intercept). If that happens, we need to return to the // caller, but the handler will have already popped the callee's frame. // So, we need to save these values for later. v << pushm{ar[AROFF(m_savedRip)]}; v << pushm{ar[AROFF(m_sfp)]}; v << copy2{ar, v.cns(EventHook::NormalFunc), rarg(0), rarg(1)}; bool (*hook)(const ActRec*, int) = &EventHook::onFunctionCall; v << call{TCA(hook)}; }); us.functionEnterHelperReturn = vwrap2(cb, [&] (Vout& v, Vout& vcold) { auto const sf = v.makeReg(); v << testb{rret(), rret(), sf}; unlikelyIfThen(v, vcold, CC_Z, sf, [&] (Vout& v) { auto const saved_rip = v.makeReg(); // The event hook has already cleaned up the stack and popped the // callee's frame, so we're ready to continue from the original call // site. We just need to grab the fp/rip of the original frame that we // saved earlier, and sync rvmsp(). v << pop{rvmfp()}; v << pop{saved_rip}; // Drop our call frame; the stublogue{} instruction guarantees that this // is exactly 16 bytes. v << lea{rsp()[16], rsp()}; // Sync vmsp and return to the caller. This unbalances the return stack // buffer, but if we're intercepting, we probably don't care. v << load{rvmtl()[rds::kVmspOff], rvmsp()}; v << jmpr{saved_rip}; }); // Skip past the stuff we saved for the intercept case. v << lea{rsp()[16], rsp()}; // Restore rvmfp() and return to the callee's func prologue. v << stubret{RegSet(), true}; }); return start; }