TCA emitCallToExit(CodeBlock& cb) { Asm 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), rax); a.cmpq(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()); a.jmp(TCA(enterTCExit)); // 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; }
TCA emitFreeLocalsHelpers(CodeBlock& cb, UniqueStubs& us) { Label doRelease; Label release; Label loopHead; auto const rData = rarg(0); // not live coming in, but used // for destructor calls auto const rIter = rarg(1); // live coming in auto const rFinished = rdx; auto const rType = ecx; int const tvSize = sizeof(TypedValue); Asm a { cb }; align(cb, Alignment::CacheLine, AlignContext::Dead); auto const start = a.frontier(); asm_label(a, release); a. loadq (rIter[TVOFF(m_data)], rData); a. cmpl (1, rData[FAST_REFCOUNT_OFFSET]); jccBlock<CC_L>(a, [&] { a. jz8 (doRelease); a. decl (rData[FAST_REFCOUNT_OFFSET]); }); a. ret (); asm_label(a, doRelease); a. push (rIter); a. push (rFinished); a. call (lookupDestructor(a, PhysReg(rType))); // Three quads between where %rsp is now and the saved RIP of the call into // the stub: two from the pushes above, and one for the saved RIP of the call // to `release' done below (e.g., in emitDecLocal). mcg->fixupMap().recordFixup(a.frontier(), makeIndirectFixup(3)); a. pop (rFinished); a. pop (rIter); a. ret (); auto emitDecLocal = [&]() { Label skipDecRef; // Zero-extend the type while loading so it can be used as an array index // to lookupDestructor() above. emitLoadTVType(a, rIter[TVOFF(m_type)], rType); emitCmpTVType(a, KindOfRefCountThreshold, rbyte(rType)); a. jle8 (skipDecRef); a. call (release); asm_label(a, skipDecRef); }; alignJmpTarget(cb); us.freeManyLocalsHelper = a.frontier(); a. lea (rvmfp()[-(jit::kNumFreeLocalsHelpers * sizeof(Cell))], rFinished); // Loop for the first few locals, but unroll the final kNumFreeLocalsHelpers. asm_label(a, loopHead); emitDecLocal(); a. addq (tvSize, rIter); a. cmpq (rIter, rFinished); a. jnz8 (loopHead); for (int i = 0; i < kNumFreeLocalsHelpers; ++i) { us.freeLocalsHelpers[kNumFreeLocalsHelpers - i - 1] = a.frontier(); emitDecLocal(); if (i != kNumFreeLocalsHelpers - 1) { a.addq (tvSize, rIter); } } a. ret (); // Keep me small! always_assert(Stats::enabled() || (a.frontier() - start <= 4 * kX64CacheLineSize)); return start; }