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 emitServiceReqWork(CodeBlock& cb, TCA start, bool persist, SRFlags flags, ServiceRequest req, const ServiceReqArgVec& argv) { assert(start); const bool align = flags & SRFlags::Align; Asm as { cb }; /* * Remember previous state of the code cache. */ boost::optional<CodeCursor> maybeCc = boost::none; if (start != as.frontier()) { maybeCc = boost::in_place<CodeCursor>(boost::ref(as), start); } /* max space for moving to align, saving VM regs plus emitting args */ static const int kVMRegSpace = 0x14, kMovSize = 0xa, kNumServiceRegs = sizeof(serviceReqArgRegs) / sizeof(PhysReg), kMaxStubSpace = kJmpTargetAlign - 1 + kVMRegSpace + kNumServiceRegs * kMovSize; if (align) { moveToAlign(cb); } TCA retval = as.frontier(); TRACE(3, "Emit Service Req @%p %s(", start, serviceReqName(req)); /* * Move args into appropriate regs. Eager VMReg save may bash flags, * so set the CondCode arguments first. */ for (int i = 0; i < argv.size(); ++i) { assert(i < kNumServiceReqArgRegs); auto reg = serviceReqArgRegs[i]; const auto& argInfo = argv[i]; switch(argv[i].m_kind) { case ServiceReqArgInfo::Immediate: { TRACE(3, "%" PRIx64 ", ", argInfo.m_imm); as. emitImmReg(argInfo.m_imm, reg); } break; case ServiceReqArgInfo::CondCode: { // Already set before VM reg save. DEBUG_ONLY TCA start = as.frontier(); as. setcc(argInfo.m_cc, rbyte(reg)); assert(start - as.frontier() <= kMovSize); TRACE(3, "cc(%x), ", argInfo.m_cc); } break; default: not_reached(); } } emitEagerVMRegSave(as, RegSaveFlags::SaveFP); if (persist) { as. emitImmReg(0, Transl::reg::rAsm); } else { as. emitImmReg((uint64_t)start, Transl::reg::rAsm); } TRACE(3, ")\n"); as. emitImmReg(req, Transl::reg::rdi); /* * 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. */ if (flags & SRFlags::JmpInsteadOfRet) { as. pop(Transl::reg::rax); as. jmp(Transl::reg::rax); } else { as. ret(); } // TODO(2796856): we should record an OpServiceRequest pseudo-bytecode here. translator_not_reached(as); if (!persist) { /* * Recycled stubs need to be uniformly sized. Make space for the * maximal possible service requests. */ assert(as.frontier() - start <= kMaxStubSpace); as.emitNop(start + kMaxStubSpace - as.frontier()); assert(as.frontier() - start == kMaxStubSpace); } return retval; }