HOT_FUNC_VM int64_t decodeCufIterHelper(Iter* it, TypedValue func) { DECLARE_FRAME_POINTER(framePtr); ObjectData* obj = nullptr; HPHP::Class* cls = nullptr; StringData* invName = nullptr; auto ar = (ActRec*)framePtr->m_savedRbp; if (LIKELY(ar->m_func->isBuiltin())) { ar = g_vmContext->getOuterVMFrame(ar); } const Func* f = vm_decode_function(tvAsVariant(&func), ar, false, obj, cls, invName, false); if (UNLIKELY(!f)) return false; CufIter &cit = it->cuf(); cit.setFunc(f); if (obj) { cit.setCtx(obj); obj->incRefCount(); } else { cit.setCtx(cls); } cit.setName(invName); return true; }
TCA fcallHelper(ActRec* ar) { try { TCA tca = Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs(), ar); if (tca) { return tca; } return callAndResume(ar); } catch (...) { /* The return address is set to __fcallHelperThunk, which has no unwind information. Its "logically" part of the tc, but the c++ unwinder wont know that. So point our return address at the called function's return address (which will be in the tc). Note that the registers really are clean - we just came from callAndResume which cleaned them for us - so we just have to tell the unwinder that. */ DECLARE_FRAME_POINTER(framePtr); tl_regState = REGSTATE_CLEAN; framePtr->m_savedRip = ar->m_savedRip; throw; } }
void FixupMap::fixup(ExecutionContext* ec) const { if (RuntimeOption::EvalSimulateARM) { // Walking the C++ stack doesn't work in simulation mode. Fortunately, the // execution context has a stack of simulators, which we consult instead. fixupWorkSimulated(ec); } else { // Start looking for fixup entries at the current (C++) frame. This // will walk the frames upward until we find a TC frame. DECLARE_FRAME_POINTER(framePtr); fixupWork(ec, framePtr); } }
ActRec* callerFrameHelper() { DECLARE_FRAME_POINTER(frame); auto rbp = frame->m_sfp; while (true) { assertx(rbp && rbp != rbp->m_sfp && "Missing fixup for native call"); if (isVMFrame(rbp)) { return rbp; } rbp = rbp->m_sfp; } }
void functionEnterHelper(const ActRec* ar) { DECLARE_FRAME_POINTER(framePtr); uint64_t savedRip = ar->m_savedRip; uint64_t savedRbp = ar->m_savedRbp; if (LIKELY(EventHook::onFunctionEnter(ar, EventHook::NormalFunc))) return; /* We need to skip the function. FunctionEnter already cleaned up ar, and pushed the return value, so all we need to do is return to where ar would have returned to, with rbp set to ar's outer frame. */ framePtr->m_savedRip = savedRip; framePtr->m_savedRbp = savedRbp; sp = g_vmContext->m_stack.top(); }
TCA fcallHelper(ActRec* ar, void* sp) { try { TCA tca = mcg->getFuncPrologue((Func*)ar->m_func, ar->numArgs(), ar); if (tca) { return tca; } if (!ar->m_func->isClonedClosure()) { /* * If the func is a cloned closure, then the original * closure has already run the prologue, and the prologues * array is just being used as entry points for the * dv funclets. Dont run the prologue again. */ VMRegAnchor _(ar); uint64_t rip = ar->m_savedRip; if (g_context->doFCall(ar, g_context->m_pc)) { ar->m_savedRip = rip; return tx->uniqueStubs.resumeHelperRet; } // We've been asked to skip the function body // (fb_intercept). frame, stack and pc have // already been fixed - flag that with a negative // return address. return (TCA)-rip; } setupAfterPrologue(ar, sp); assert(ar == g_context->m_fp); return tx->uniqueStubs.resumeHelper; } catch (...) { /* The return address is set to __fcallHelperThunk, which has no unwind information. Its "logically" part of the tc, but the c++ unwinder wont know that. So point our return address at the called function's return address (which will be in the tc). Note that the registers really are clean - we cleaned them in the try above - so we just have to tell the unwinder that. */ DECLARE_FRAME_POINTER(framePtr); tl_regState = VMRegState::CLEAN; framePtr->m_savedRip = ar->m_savedRip; throw; } }
void syncVMRegsWork() { assertx(tl_regState != VMRegState::CLEAN); // Start looking for fixup entries at the current (C++) frame. This // will walk the frames upward until we find a TC frame. // In order to avoid tail call elimination optimization issues, grab the // parent frame pointer in order make sure this pointer is valid. The // fixupWork() looks for a TC frame, and we never call fixup() directly // from the TC, so skipping this frame isn't a problem. DECLARE_FRAME_POINTER(framePtr); auto fp = tl_regState >= VMRegState::GUARDED_THRESHOLD ? (ActRec*)tl_regState : framePtr->m_sfp; FixupMap::fixupWork(g_context.getNoCheck(), fp); tl_regState = VMRegState::CLEAN; Stats::inc(Stats::TC_Sync); }
void TranslatorX64::fCallArrayHelper(const Offset pcOff, const Offset pcNext) { DECLARE_FRAME_POINTER(framePtr); ActRec* fp = (ActRec*)framePtr->m_savedRbp; VMExecutionContext *ec = g_vmContext; ec->m_fp = fp; ec->m_stack.top() = sp; ec->m_pc = curUnit()->at(pcOff); PC pc = curUnit()->at(pcNext); tl_regState = REGSTATE_CLEAN; bool runFunc = ec->doFCallArray(pc); sp = ec->m_stack.top(); tl_regState = REGSTATE_DIRTY; if (!runFunc) return; ec->m_fp->m_savedRip = framePtr->m_savedRip; // smash our return and frame pointer chain framePtr->m_savedRip = (uint64_t)ec->m_fp->m_func->getFuncBody(); framePtr->m_savedRbp = (uint64_t)ec->m_fp; }