RegionDescPtr selectCalleeRegion(const SrcKey& sk, const Func* callee, const irgen::IRGS& irgs, InliningDecider& inl, int32_t maxBCInstrs) { auto const op = sk.pc(); auto const numArgs = getImm(op, 0).u_IVA; auto const& fpi = irgs.irb->fs().fpiStack(); assertx(!fpi.empty()); auto const ctx = fpi.back().ctxType; std::vector<Type> argTypes; for (int i = numArgs - 1; i >= 0; --i) { // DataTypeGeneric is used because we're just passing the locals into the // callee. It's up to the callee to constrain further if needed. auto type = irgen::publicTopType(irgs, BCSPRelOffset{i}); // If we don't have sufficient type information to inline the region return // early if (!(type <= TCell) && !(type <= TBoxedCell) && !(type <= TCls)) { return nullptr; } argTypes.push_back(type); } const auto mode = RuntimeOption::EvalInlineRegionMode; if (mode == "tracelet" || mode == "both") { auto region = selectCalleeTracelet( callee, numArgs, ctx, argTypes, maxBCInstrs ); auto const maxCost = RuntimeOption::EvalHHIRInliningMaxVasmCost; if (region && inl.shouldInline(sk, callee, *region, maxCost)) return region; if (mode == "tracelet") return nullptr; } if (profData()) { auto region = selectCalleeCFG(callee, numArgs, ctx, argTypes, maxBCInstrs); auto const maxCost = RuntimeOption::EvalHHIRInliningMaxVasmCost; if (region && inl.shouldInline(sk, callee, *region, maxCost)) return region; } return nullptr; }
void addDbgGuardImpl(SrcKey sk, SrcRec* sr) { TCA realCode = sr->getTopTranslation(); if (!realCode) return; // No translations, nothing to do. auto& cb = mcg->code.main(); auto const dbgGuard = vwrap(cb, [&] (Vout& v) { if (!sk.resumed()) { auto const off = sr->nonResumedSPOff(); v << lea{rvmfp()[-cellsToBytes(off.offset)], rvmsp()}; } auto const tinfo = v.makeReg(); auto const attached = v.makeReg(); auto const sf = v.makeReg(); auto const done = v.makeBlock(); constexpr size_t dbgOff = offsetof(ThreadInfo, m_reqInjectionData) + RequestInjectionData::debuggerReadOnlyOffset(); v << ldimmq{reinterpret_cast<uintptr_t>(sk.pc()), rarg(0)}; emitTLSLoad(v, tls_datum(ThreadInfo::s_threadInfo), tinfo); v << loadb{tinfo[dbgOff], attached}; v << testbi{static_cast<int8_t>(0xffu), attached, sf}; v << jcci{CC_NZ, sf, done, mcg->ustubs().interpHelper}; v = done; v << fallthru{}; }, CodeKind::Helper); // Emit a jump to the actual code. auto const dbgBranchGuardSrc = emitSmashableJmp(cb, realCode); // Add the guard to the SrcRec. sr->addDebuggerGuard(dbgGuard, dbgBranchGuardSrc); }
/* * Check that we don't have any missing or extra arguments. */ bool checkNumArgs(SrcKey callSK, const Func* callee, Annotations& annotations) { assertx(callSK.op() == Op::FCall); assertx(callee); auto refuse = [&] (const char* why) { return traceRefusal(callSK, callee, why, annotations); }; auto pc = callSK.pc(); auto const fca = getImm(pc, 0).u_FCA; auto const numParams = callee->numParams(); if (fca.numArgs > numParams) { return refuse("callee called with too many arguments"); } if (fca.hasUnpack) { return refuse("callee called with variadic arguments"); } if (fca.numRets != 1) { return refuse("callee with multiple returns"); } // It's okay if we passed fewer arguments than there are parameters as long // as the gap can be filled in by DV funclets. for (auto i = fca.numArgs; i < numParams; ++i) { auto const& param = callee->params()[i]; if (!param.hasDefaultValue() && (i < numParams - 1 || !callee->hasVariadicCaptureParam())) { return refuse("callee called with too few arguments"); } } return true; }