diff --git a/hphp/doc/bytecode.specification b/hphp/doc/bytecode.specification index 3ae7d6851..2874fa40c 100644 --- a/hphp/doc/bytecode.specification +++ b/hphp/doc/bytecode.specification @@ -3619,8 +3619,10 @@ AKExists [C C] -> [C:Bool] CreateCl [F..F] -> [C] - Creates an instance of and pushes it on the stack. - must be a subclass of "Closure". + Creates an instance of and pushes it on the stack. The + class named by %2 must be a subclass of "Closure", and must be + defined at the point of the CreateCl opcode, or the behavior is + undefined. 14. Continuation creation and execution diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 27ed6ce6f..010ee0691 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -1202,11 +1202,11 @@ FreeSpill S0:ConstInt 16. Continuations & Closures -CreateCl S0:Int S1:Str S2:StkPtr S3:StkPtr +D:Obj = CreateCl S0:ConstCls S1:ConstInt S2:StkPtr S3:StkPtr - Creates an object of class S1 (which must be a subclass of Closure), - copies S0 number of parameters from S2 into the properties on the object, - saves the $this and Func* from ActRec S3 into the object, + Creates an object of class S0 (which must be a subclass of Closure), + copies S1 number of parameters from S2 into the properties on the + object. Saves the $this and Func* from ActRec S2 into the object, and returns the object. CreateCont diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 9620a9580..575164698 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -955,7 +955,9 @@ UnwindStatus Stack::unwindFrag(ActRec* fp, int offset, for (auto& idOff : eh->m_catches) { auto handler = func->unit()->at(idOff.second); FTRACE(1, "unwindFrag: catch candidate {}\n", handler); - Class* cls = func->unit()->lookupClass(idOff.first); + Class* cls = Unit::lookupClass( + func->unit()->lookupNamedEntityId(idOff.first) + ); if (cls && obj->instanceof(cls)) { pc = handler; FTRACE(1, "unwindFrag: entering catch at {}\n", pc); diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 341c29c84..d745f1ccf 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -320,6 +320,7 @@ CALL_OPCODE(FillContLocals) CALL_OPCODE(NewArray) CALL_OPCODE(NewTuple) CALL_OPCODE(NewObj) +CALL_OPCODE(CreateCl) CALL_OPCODE(PrintStr) CALL_OPCODE(PrintInt) CALL_OPCODE(PrintBool) @@ -3309,13 +3310,10 @@ static inline ALWAYS_INLINE Class* getKnownClass(Class** classCache, return cls; } -static Instance* -newInstanceHelperNoCtorCachedInstance(Class** classCache, - const StringData* clsName) { - Class* cls = getKnownClass(classCache, clsName); +Instance* createClHelper(Class* cls, int numArgs, ActRec* ar, TypedValue* sp) { Instance* newObj = newInstance(cls); newObj->incRefCount(); - return newObj; + return static_cast(newObj)->init(numArgs, ar, sp); } static Cell* @@ -3324,10 +3322,9 @@ newInstanceHelperNoCtorCached(CacheHandle cacheHandle, Cell* sp) { Cell* obj = sp - 1; // this is where the newly allocated object will go - Instance* newObj = newInstanceHelperNoCtorCachedInstance( - (Class**)handleToPtr(cacheHandle), - clsName - ); + Class* cls = getKnownClass((Class**)handleToPtr(cacheHandle), clsName); + Instance* newObj = newInstance(cls); + newObj->incRefCount(); // store obj into the stack obj->m_data.pobj = newObj; @@ -3379,43 +3376,6 @@ void CodeGenerator::cgNewObjNoCtorCached(IRInstruction* inst) { args); } -void CodeGenerator::cgCreateCl(IRInstruction* inst) { - SSATmp* dst = inst->getDst(); - SSATmp* numParams = inst->getSrc(0); - SSATmp* clsName = inst->getSrc(1); - SSATmp* sp = inst->getSrc(2); - SSATmp* fp = inst->getSrc(3); - - const StringData* classNameString = clsName->getValStr(); - CacheHandle ch = allocKnownClass(classNameString); - Class** classCache = static_cast(handleToPtr(ch)); - - Instance* (*helper)(Class** classCache, const StringData* clsName) = - newInstanceHelperNoCtorCachedInstance; - cgCallHelper( - m_as, - (TCA)helper, - rScratch, - kSyncPoint, - ArgGroup() - .immPtr(classCache) - .ssa(clsName) - ); - - cgCallHelper( - m_as, - Transl::Call(getMethodPtr(&c_Closure::init)), - dst->getReg(), - kSyncPoint, - ArgGroup() - .reg(rScratch) - .ssa(numParams) - .ssa(fp) - .ssa(sp) - ); -} - - void CodeGenerator::cgCall(IRInstruction* inst) { SSATmp* actRec = inst->getSrc(0); SSATmp* returnBcOffset = inst->getSrc(1); diff --git a/hphp/runtime/vm/translator/hopt/codegen.h b/hphp/runtime/vm/translator/hopt/codegen.h index 89f06e74c..c940da168 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.h +++ b/hphp/runtime/vm/translator/hopt/codegen.h @@ -463,6 +463,8 @@ ActRec* irNewInstanceHelper(Class* cls, Cell* sp, ActRec* prevAr); +Instance* createClHelper(Class*, int, ActRec*, TypedValue*); + void genCodeForTrace(Trace* trace, CodeGenerator::Asm& a, CodeGenerator::Asm& astubs, diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp index 01a7909f8..7351bdd8f 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -1295,17 +1295,27 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) { m_tb->genNewObjCached(numParams, className); } +/* + * 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 TargetCache. + */ void HhbcTranslator::emitCreateCl(int32_t numParams, int32_t funNameStrId) { - const StringData* clsName = lookupStringId(funNameStrId); - TRACE(3, "%u: CreateCl %d %s\n", m_bcOff, numParams, clsName->data()); - spillStack(); - SSATmp* closure = m_tb->gen( + auto const sp = spillStack(); + auto const cls = Unit::lookupUniqueClass(lookupStringId(funNameStrId)); + assert(cls && (cls->attrs() & AttrUnique)); + + auto const closure = m_tb->gen( CreateCl, - m_tb->genDefConst(numParams), - m_tb->genDefConst(clsName), - m_tb->getSp(), - m_tb->getFp() + cns(cls), + cns(numParams), + m_tb->getFp(), + sp ); + discard(numParams); push(closure); } diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index e0cba7da3..00baa0d23 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -242,7 +242,7 @@ O(ExitWhenSurprised, ND, NA, E) \ O(ExitOnVarEnv, ND, S(StkPtr), E) \ O(ReleaseVVOrExit, ND, S(StkPtr), N|E) \ O(RaiseError, ND, S(Str), E|N|Mem|Refs|T) \ -O(RaiseWarning, ND, S(Str), E|N|Mem|Refs|Er) \ +O(RaiseWarning, ND, S(Str), E|N|Mem|Refs|Er) \ O(CheckInit, ND, S(Gen), NF) \ O(CheckInitMem, ND, S(PtrToGen) C(Int), NF) \ O(Unbox, DUnbox(0), S(Gen), NF) \ @@ -301,8 +301,8 @@ O(NewObjCached, D(StkPtr), C(Int) \ S(StkPtr), E|Mem|N|Refs|PRc|Er) \ O(NewObjNoCtorCached, D(StkPtr), S(Str) \ S(StkPtr), E|Mem|N|Refs|PRc|Er) \ -O(CreateCl, D(Obj), C(Int) \ - S(Str) \ +O(CreateCl, D(Obj), C(Cls) \ + C(Int) \ S(StkPtr) \ S(StkPtr), Mem|N) \ O(NewArray, D(Arr), C(Int), E|Mem|N|PRc) \ diff --git a/hphp/runtime/vm/translator/hopt/nativecalls.cpp b/hphp/runtime/vm/translator/hopt/nativecalls.cpp index b82369190..ea934e537 100644 --- a/hphp/runtime/vm/translator/hopt/nativecalls.cpp +++ b/hphp/runtime/vm/translator/hopt/nativecalls.cpp @@ -101,6 +101,8 @@ static CallMap s_callMap({ {{SSA, 0}, {SSA, 1}, {TV, 2}, {SSA, 3}}}, {LdFuncCached, (TCA)FixedFuncCache::lookupUnknownFunc, DSSA, SSync, {{SSA, 0}}}, + {CreateCl, (TCA)createClHelper, DSSA, SSync, + {{SSA, 0}, {SSA, 1}, {SSA, 2}, {SSA, 3}}}, /* Switch helpers */ {LdSwitchDblIndex, (TCA)switchDoubleHelper, DSSA, SSync, diff --git a/hphp/runtime/vm/translator/translator-x64.cpp b/hphp/runtime/vm/translator/translator-x64.cpp index f5ca28557..3432c198c 100644 --- a/hphp/runtime/vm/translator/translator-x64.cpp +++ b/hphp/runtime/vm/translator/translator-x64.cpp @@ -9348,7 +9348,7 @@ TranslatorX64::translateCreateCl(const Tracelet& t, TargetCache::CacheHandle classCh = TargetCache::allocKnownClass(clsName); clsCache.alloc(); - a. lea_reg64_disp_reg64(rVmTl, classCh, r(clsCache)); + a. lea (rVmTl[classCh], r(clsCache)); EMIT_RCALL(a, i, newInstanceHelperNoCtorCached, R(clsCache), diff --git a/hphp/runtime/vm/unit.h b/hphp/runtime/vm/unit.h index 6c958222a..6830f198a 100644 --- a/hphp/runtime/vm/unit.h +++ b/hphp/runtime/vm/unit.h @@ -490,6 +490,23 @@ struct Unit { bool failIsFatal = true); void defTypedef(Id id); + /* + * Find the Class* for a defined class corresponding to the name + * `clsName'. + * + * Returns: nullptr if the class of the given name is not yet + * defined in this request. + */ + static Class *lookupClass(const StringData *clsName) { + return lookupClass(GetNamedEntity(clsName)); + } + + /* + * Find the Class* for a defined class with name mapped to the + * supplied NamedEntity. + * + * Returns: nullptr if the class is not yet defined in this request. + */ static Class *lookupClass(const NamedEntity *ne) { Class* cls; if (LIKELY((cls = ne->getCachedClass()) != nullptr)) { @@ -498,13 +515,14 @@ struct Unit { return nullptr; } - static Class *lookupClass(const StringData *clsName) { - return lookupClass(GetNamedEntity(clsName)); - } - /* - * Look up a unique class even if it hasn't been loaded in this - * request yet. + * Same as lookupClass, except if it's not defined *and* is unique, + * return the Class* anyway. + * + * The point of this is that when jitting code before a unique class + * is defined, we can often still burn the Class* into the TC, since + * it will be defined by the time the code that needs the Class* + * runs (via autoload or whatnot). */ static Class *lookupUniqueClass(const NamedEntity *ne) { Class* cls = ne->clsList(); @@ -540,10 +558,6 @@ struct Unit { static bool classExists(const StringData* name, bool autoload, Attr typeAttrs); - Class *lookupClass(Id id) const { - return lookupClass(lookupNamedEntityId(id)); - } - const PreConst* lookupPreConstId(Id id) const { assert(id < Id(m_preConsts.size())); return &m_preConsts[id];