void reclaimTranslation(TransLoc loc) { BlockingLeaseHolder writer(Translator::WriteLease()); ITRACE(1, "Reclaiming translation M[{}, {}] C[{}, {}] F[{}, {}]\n", loc.mainStart(), loc.mainEnd(), loc.coldStart(), loc.coldEnd(), loc.frozenStart(), loc.frozenEnd()); Trace::Indent _i; auto& cache = mcg->code(); cache.blockFor(loc.mainStart()).free(loc.mainStart(), loc.mainSize()); cache.blockFor(loc.coldStart()).free(loc.coldStart(), loc.coldSize()); if (loc.coldStart() != loc.frozenStart()) { cache.blockFor(loc.frozenStart()).free(loc.frozenStart(), loc.frozenSize()); } for (auto it = s_smashedBranches.begin(); it != s_smashedBranches.end();) { auto br = it++; if (loc.contains(br->first)) { ITRACE(1, "Erasing smashed branch @ {} from SrcRec addr={}\n", br->first, (void*)br->second); br->second->removeIncomingBranch(br->first); s_smashedBranches.erase(br); } } // Erase meta-data about these regions of the TC { ITRACE(1, "Clearing translation meta-data\n"); Trace::Indent _i; clearTCMaps(loc.mainStart(), loc.mainEnd()); clearTCMaps(loc.coldCodeStart(), loc.coldEnd()); clearTCMaps(loc.frozenCodeStart(), loc.frozenEnd()); } if (debug) { // Ensure no one calls into the function ITRACE(1, "Overwriting function\n"); auto clearBlock = [] (CodeBlock& cb) { X64Assembler a {cb}; while (cb.available() >= 2) a.ud2(); if (cb.available() > 0) a.int3(); always_assert(!cb.available()); }; CodeBlock main, cold, frozen; main.init(loc.mainStart(), loc.mainSize(), "Dead Main"); cold.init(loc.coldStart(), loc.coldSize(), "Dead Cold"); frozen.init(loc.frozenStart(), loc.frozenSize(), "Dead Frozen"); clearBlock(main); clearBlock(cold); clearBlock(frozen); } }
/* * Satisfy an alignment constraint. Bridge the gap with int3's. */ void moveToAlign(CodeBlock& cb, const size_t align /* =kJmpTargetAlign */) { X64Assembler a { cb }; assertx(folly::isPowTwo(align)); size_t leftInBlock = align - ((align - 1) & uintptr_t(cb.frontier())); if (leftInBlock == align) return; if (leftInBlock > 2) { a.ud2(); leftInBlock -= 2; } if (leftInBlock > 0) { a.emitInt3s(leftInBlock); } }
void operator()() { auto codeLock = mcg->lockCode(); for (auto& e : entries) { CodeBlock cb; cb.init(e.first, e.second - e.first, "relocated"); X64Assembler a { cb }; while (a.canEmit(2)) { a.ud2(); } if (a.canEmit(1)) a.int3(); } okToRelocate = true; }
/* * Satisfy an alignment constraint. Bridge the gap with int3's. */ void moveToAlign(CodeBlock& cb, const size_t align /* =kJmpTargetAlign */) { // TODO(2967396) implement properly, move function if (arch() == Arch::ARM) return; using namespace HPHP::Util; X64Assembler a { cb }; assert(isPowerOfTwo(align)); size_t leftInBlock = align - ((align - 1) & uintptr_t(cb.frontier())); if (leftInBlock == align) return; if (leftInBlock > 2) { a.ud2(); leftInBlock -= 2; } if (leftInBlock > 0) { a.emitInt3s(leftInBlock); } }
void operator()() { LeaseHolder writer(Translator::WriteLease()); if (!writer) { Treadmill::enqueue(std::move(*this)); return; } for (auto& e : entries) { CodeBlock cb; cb.init(e.first, e.second - e.first, "relocated"); X64Assembler a { cb }; while (a.canEmit(2)) { a.ud2(); } if (a.canEmit(1)) a.int3(); } okToRelocate = true; }
/* * Satisfy an alignment constraint. Bridge the gap with int3's. */ void moveToAlign(CodeBlock& cb, const size_t align /* =kJmpTargetAlign */) { switch (arch()) { case Arch::X64: { X64Assembler a { cb }; assert(folly::isPowTwo(align)); size_t leftInBlock = align - ((align - 1) & uintptr_t(cb.frontier())); if (leftInBlock == align) return; if (leftInBlock > 2) { a.ud2(); leftInBlock -= 2; } break; } case Arch::ARM: // TODO(2967396) implement properly, move function break; } }
TCA emitCallToExit(CodeBlock& cb) { X64Assembler a { cb }; // Emit a byte of padding. This is a kind of hacky way to avoid // hitting an assert in recordGdbStub when we call it with stub - 1 // as the start address. a.emitNop(1); auto const start = a.frontier(); if (RuntimeOption::EvalHHIRGenerateAsserts) { Label ok; a.emitImmReg(uintptr_t(enterTCExit), reg::rax); a.cmpq(reg::rax, *rsp()); a.je8 (ok); a.ud2(); asm_label(a, ok); } // Emulate a ret to enterTCExit without actually doing one to avoid // unbalancing the return stack buffer. The call from enterTCHelper() that // got us into the TC was popped off the RSB by the ret that got us to this // stub. a.addq(8, rsp()); if (a.jmpDeltaFits(TCA(enterTCExit))) { a.jmp(TCA(enterTCExit)); } else { // can't do a near jmp and a rip-relative load/jmp would require threading // through extra state to allocate a literal. use an indirect jump through // a register a.emitImmReg(uintptr_t(enterTCExit), reg::rax); a.jmp(reg::rax); } // On a backtrace, gdb tries to locate the calling frame at address // returnRIP-1. However, for the first VM frame, there is no code at // returnRIP-1, since the AR was set up manually. For this frame, // record the tracelet address as starting from this callToExit-1, // so gdb does not barf. return start; }