/* * The CreateCl opcode is specified as not being allowed before the * class it creates exists, and closure classes are always unique. * * This means even if we're not in RepoAuthoritative mode, as long as * this code is reachable it will always use the same closure Class*, * so we can just burn it into the TC without using RDS. */ void emitCreateCl(HTS& env, int32_t numParams, const StringData* clsName) { auto const cls = Unit::lookupClassOrUniqueClass(clsName); auto const invokeFunc = cls->lookupMethod(s_uuinvoke.get()); auto const clonedFunc = invokeFunc->cloneAndSetClass( const_cast<Class*>(curClass(env)) ); assert(cls && (cls->attrs() & AttrUnique)); auto const closure = allocObjFast(env, cls); gen(env, IncRef, closure); auto const ctx = [&]{ if (!curClass(env)) return cns(env, nullptr); auto const ldctx = gen(env, LdCtx, fp(env)); if (invokeFunc->attrs() & AttrStatic) { return gen(env, ConvClsToCctx, gen(env, LdClsCtx, ldctx)); } gen(env, IncRefCtx, ldctx); return ldctx; }(); gen(env, StClosureCtx, closure, ctx); gen(env, StClosureFunc, FuncData(clonedFunc), closure); SSATmp* args[numParams]; for (int32_t i = 0; i < numParams; ++i) { args[numParams - i - 1] = popF(env); } int32_t propId = 0; for (; propId < numParams; ++propId) { gen( env, StClosureArg, PropByteOffset(cls->declPropOffset(propId)), closure, args[propId] ); } // Closure static variables are per instance, and need to start // uninitialized. After numParams use vars, the remaining instance // properties hold any static locals. assert(cls->numDeclProperties() == clonedFunc->numStaticLocals() + numParams); for (int32_t numDeclProperties = cls->numDeclProperties(); propId < numDeclProperties; ++propId) { gen( env, StClosureArg, PropByteOffset(cls->declPropOffset(propId)), closure, cns(env, Type::Uninit) ); } push(env, closure); }
/* * Attempts to begin inlining, and returns whether or not it successed. * * When doing gen-time inlining, we set up a series of IR instructions * that looks like this: * * fp0 = DefFP * sp = DefSP<offset> * * // ... normal stuff happens ... * * // FPI region: * SpillFrame sp, ... * // ... probably some StStks due to argument expressions * fp2 = DefInlineFP<func,retBC,retSP,off> sp * * // ... callee body ... * * InlineReturn fp2 * * In DCE we attempt to remove the InlineReturn and DefInlineFP instructions if * they aren't needed. */ bool beginInlining(IRGS& env, unsigned numParams, const Func* target, Offset returnBcOffset) { auto const& fpiStack = env.irb->fpiStack(); assertx(!fpiStack.empty() && "Inlining does not support calls with the FPush* in a different Tracelet"); assertx(returnBcOffset >= 0 && "returnBcOffset before beginning of caller"); assertx(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() && "returnBcOffset past end of caller"); FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data()); SSATmp** params = (SSATmp**)alloca(sizeof(SSATmp*) * numParams); for (unsigned i = 0; i < numParams; ++i) { params[numParams - i - 1] = popF(env); } auto const prevSP = fpiStack.front().returnSP; auto const prevSPOff = fpiStack.front().returnSPOff; spillStack(env); auto const calleeSP = sp(env); always_assert_flog( prevSP == calleeSP, "FPI stack pointer and callee stack pointer didn't match in beginInlining" ); auto const& info = fpiStack.front(); always_assert(!isFPushCuf(info.fpushOpc) && !info.interp); auto ctx = [&] { if (info.ctx || isFPushFunc(info.fpushOpc)) { return info.ctx; } constexpr int32_t adjust = offsetof(ActRec, m_r) - offsetof(ActRec, m_this); IRSPOffset ctxOff{invSPOff(env) - info.returnSPOff - adjust}; return gen(env, LdStk, TCtx, IRSPOffsetData{ctxOff}, sp(env)); }(); DefInlineFPData data; data.target = target; data.retBCOff = returnBcOffset; data.fromFPushCtor = isFPushCtor(info.fpushOpc); data.ctx = ctx; data.retSPOff = prevSPOff; data.spOffset = offsetFromIRSP(env, BCSPOffset{0}); // Push state and update the marker before emitting any instructions so // they're all given markers in the callee. auto const key = SrcKey { target, target->getEntryForNumArgs(numParams), false }; env.bcStateStack.emplace_back(key); env.inlineLevel++; updateMarker(env); auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, fp(env)); for (unsigned i = 0; i < numParams; ++i) { stLocRaw(env, i, calleeFP, params[i]); } const bool hasVariadicArg = target->hasVariadicCaptureParam(); for (unsigned i = numParams; i < target->numLocals() - hasVariadicArg; ++i) { /* * Here we need to be generating hopefully-dead stores to initialize * non-parameter locals to KindOfUninit in case we have to leave the trace. */ stLocRaw(env, i, calleeFP, cns(env, TUninit)); } if (hasVariadicArg) { auto argNum = target->numLocals() - 1; always_assert(numParams <= argNum); stLocRaw(env, argNum, calleeFP, cns(env, staticEmptyArray())); } return true; }
/* * Attempts to begin inlining, and returns whether or not it successed. * * When doing gen-time inlining, we set up a series of IR instructions * that looks like this: * * fp0 = DefFP * sp = DefSP<offset> * * // ... normal stuff happens ... * * // FPI region: * SpillFrame sp, ... * // ... probably some StStks due to argument expressions * fp2 = DefInlineFP<func,retBC,retSP,off> sp * * // ... callee body ... * * InlineReturn fp2 * * In DCE we attempt to remove the InlineReturn and DefInlineFP instructions if * they aren't needed. */ bool beginInlining(IRGS& env, unsigned numParams, const Func* target, Offset returnBcOffset) { assertx(!env.fpiStack.empty() && "Inlining does not support calls with the FPush* in a different Tracelet"); assertx(returnBcOffset >= 0 && "returnBcOffset before beginning of caller"); assertx(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() && "returnBcOffset past end of caller"); FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data()); SSATmp* params[numParams]; for (unsigned i = 0; i < numParams; ++i) { params[numParams - i - 1] = popF(env); } auto const prevSP = env.fpiStack.top().returnSP; auto const prevSPOff = env.fpiStack.top().returnSPOff; spillStack(env); auto const calleeSP = sp(env); always_assert_flog( prevSP == calleeSP, "FPI stack pointer and callee stack pointer didn't match in beginInlining" ); // This can only happen if the code is unreachable, in which case // the FPush* can punt if it gets a TBottom. if (env.fpiStack.top().spillFrame == nullptr) return false; auto const sframe = env.fpiStack.top().spillFrame; DefInlineFPData data; data.target = target; data.retBCOff = returnBcOffset; data.fromFPushCtor = sframe->extra<ActRecInfo>()->fromFPushCtor; data.ctx = sframe->src(2); data.retSPOff = prevSPOff; data.spOffset = offsetFromIRSP(env, BCSPOffset{0}); // Push state and update the marker before emitting any instructions so // they're all given markers in the callee. auto const key = SrcKey { target, target->getEntryForNumArgs(numParams), false }; env.bcStateStack.emplace_back(key); env.inlineLevel++; updateMarker(env); auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, fp(env)); for (unsigned i = 0; i < numParams; ++i) { stLocRaw(env, i, calleeFP, params[i]); } const bool hasVariadicArg = target->hasVariadicCaptureParam(); for (unsigned i = numParams; i < target->numLocals() - hasVariadicArg; ++i) { /* * Here we need to be generating hopefully-dead stores to initialize * non-parameter locals to KindOfUninit in case we have to leave the trace. */ stLocRaw(env, i, calleeFP, cns(env, TUninit)); } if (hasVariadicArg) { auto argNum = target->numLocals() - 1; always_assert(numParams <= argNum); stLocRaw(env, argNum, calleeFP, cns(env, staticEmptyArray())); } env.fpiActiveStack.push(std::make_pair(env.fpiStack.top().returnSP, env.fpiStack.top().returnSPOff)); env.fpiStack.pop(); return true; }
/* * When doing gen-time inlining, we set up a series of IR instructions * that looks like this: * * fp0 = DefFP * sp0 = DefSP<offset> * * // ... normal stuff happens ... * // sp_pre = some SpillStack, or maybe the DefSP * * // FPI region: * sp1 = SpillStack sp_pre, ... * sp2 = SpillFrame sp1, ... * // ... possibly more spillstacks due to argument expressions * sp3 = SpillStack sp2, -argCount * fp2 = DefInlineFP<func,retBC,retSP,off> sp2 sp1 * sp4 = ReDefSP<spOffset,spansCall> sp1 fp2 * * // ... callee body ... * * = InlineReturn fp2 * * [ sp5 = ResetSP<spOffset> fp0 ] * * The rest of the code then depends on sp5, and not any of the StkPtr * tree going through the callee body. The sp5 tmp has the same view * of the stack as sp1 did, which represents what the stack looks like * before the return address is pushed but after the activation record * is popped. * * In DCE we attempt to remove the SpillFrame, InlineReturn, and * DefInlineFP instructions if they aren't needed. * * ReDefSP takes sp1, the stack pointer from before the inlined frame. * This SSATmp may be used for determining stack types in the * simplifier, or stack values if the inlined body doesn't contain a * call---these instructions both take an extradata `spansCall' which * is true iff a Call occured anywhere between the the definition of * its first argument and itself. */ void beginInlining(HTS& env, unsigned numParams, const Func* target, Offset returnBcOffset) { assert(!env.fpiStack.empty() && "Inlining does not support calls with the FPush* in a different Tracelet"); assert(returnBcOffset >= 0 && "returnBcOffset before beginning of caller"); assert(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() && "returnBcOffset past end of caller"); FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data()); SSATmp* params[numParams]; for (unsigned i = 0; i < numParams; ++i) { params[numParams - i - 1] = popF(env); } auto const prevSP = env.fpiStack.top().returnSP; auto const prevSPOff = env.fpiStack.top().returnSPOff; spillStack(env); auto const calleeSP = sp(env); always_assert_flog( env.fpiStack.top().spillFrame != nullptr, "Couldn't find SpillFrame for inlined call on sp {}." " Was the FPush instruction interpreted?\n{}", *calleeSP->inst(), env.irb->unit() ); auto const sframe = env.fpiStack.top().spillFrame; DefInlineFPData data; data.target = target; data.retBCOff = returnBcOffset; data.fromFPushCtor = sframe->extra<ActRecInfo>()->isFromFPushCtor(); data.ctx = sframe->src(2); data.retSPOff = prevSPOff; data.spOffset = offsetFromSP(env, 0); // Push state and update the marker before emitting any instructions so // they're all given markers in the callee. auto const key = SrcKey { target, target->getEntryForNumArgs(numParams), false }; env.bcStateStack.emplace_back(key); updateMarker(env); auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, prevSP, fp(env)); gen(env, ReDefSP, StackOffset{target->numLocals()}, fp(env)); for (unsigned i = 0; i < numParams; ++i) { stLocRaw(env, i, calleeFP, params[i]); } for (unsigned i = numParams; i < target->numLocals(); ++i) { /* * Here we need to be generating hopefully-dead stores to initialize * non-parameter locals to KindOfUninit in case we have to leave the trace. */ stLocRaw(env, i, calleeFP, cns(env, Type::Uninit)); } env.fpiActiveStack.push(std::make_pair(env.fpiStack.top().returnSP, env.fpiStack.top().returnSPOff)); env.fpiStack.pop(); }