From b8ae94e03969aad037452bccddfe44f6767b579c Mon Sep 17 00:00:00 2001 From: bsimmers Date: Tue, 2 Apr 2013 16:48:15 -0700 Subject: [PATCH] Implement FPushCuf* in hhir This handles almost all the cases tx64 does. It takes a slow exit when a Class or Func that was around at translation time doesn't exist at runtime, which never happens in prod (as of right now). --- hphp/doc/ir.specification | 25 ++-- hphp/runtime/vm/translator/hopt/codegen.cpp | 56 ++++++--- hphp/runtime/vm/translator/hopt/codegen.h | 9 +- .../vm/translator/hopt/hhbctranslator.cpp | 117 ++++++++++++------ .../vm/translator/hopt/hhbctranslator.h | 3 + hphp/runtime/vm/translator/hopt/ir.h | 5 +- .../vm/translator/hopt/irtranslator.cpp | 14 +++ .../vm/translator/hopt/nativecalls.cpp | 3 + .../runtime/vm/translator/hopt/simplifier.cpp | 45 +------ hphp/runtime/vm/translator/translator-x64.cpp | 8 +- 10 files changed, 169 insertions(+), 116 deletions(-) diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 379915b29..76df56e8f 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -670,10 +670,10 @@ D:Cls = LdClsCached S0:ConstStr Loads the class named S0 via the target cache. Invokes autoload and may raise an error if the class is not defined. -D:Cls = LdCachedClass S0:ConstStr +D:Cls = LdClsCachedSafe S0:ConstStr [ -> L ] - Loads the class whose name is S0 out of the target cache. Returns - null and has no side effects if the class is not defined. + Loads the class whose name is S0 out of the target cache. If the class is not + defined, returns null and optionally branches to L. D:T = LdClsCns S0:ConstStr S1:ConstStr [ -> L ] @@ -696,10 +696,11 @@ D:FuncCtx = LdClsMethodFCache S0:ConstStr S1:ConstStr the MethodFCache if needed. In case the given method is not found, control is transferred to label L. -D:Ctx = GetCtxFwdCall S0:{Obj|Cls|Ctx} S1:Func +D:Ctx = GetCtxFwdCall S0:Ctx S1:Func - Loads the context (ActRec's m_this/m_cls slot) for a forwarding call - into D. S0 is the current context, and S1 is the method being called. + If S0 is an object and S1 is static, this opcode returns S0's + class. If S0 is an object and S1 is not static, this opcode increfs + S0 and returns it. If S0 is a Cctx, this opcode returns S0. LdClsMethodCache LdClsMethod @@ -753,10 +754,16 @@ D:Func = LdFunc S0:Str Loads the Func whose name is S0. Fatal if the named function is not defined, and the function autoloader fails to define it. -D:Func = LdFixedFunc S0:ConstStr +D:Func = LdFuncCached S0:ConstStr - Loads the Func whose name is S0. Fatal if the named function is - not defined, and the function autoloader fails to define it. + Loads the Func whose name is S0 from the target cache. Fatal if the + named function is not defined, and the function autoloader fails to + define it. + +D:Func = LdFuncCachedSafe S0:ConstStr [ -> L ] + + Loads the Func named S0 from the target cache. If the function is not + defined, returns null and optionally branches to L. D:Func = LdCurFuncPtr diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index aee409912..949298c1b 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -695,7 +695,7 @@ static void shuffleArgs(Asm& a, ArgGroup& args) { } } -void CodeGenerator::cgCallNative(IRInstruction* inst) { +void CodeGenerator::cgCallNative(Asm& a, IRInstruction* inst) { using namespace NativeCalls; Opcode opc = inst->getOpcode(); always_assert(CallMap::hasInfo(opc)); @@ -729,7 +729,7 @@ void CodeGenerator::cgCallNative(IRInstruction* inst) { addr = inst->getSrc(info.func.srcIdx)->getValTCA(); break; } - cgCallHelper(m_as, + cgCallHelper(a, addr, info.dest != DestType::None ? inst->getDst(0) : nullptr, info.sync, @@ -1983,13 +1983,12 @@ void CodeGenerator::cgUnbox(IRInstruction* inst) { }); } -void CodeGenerator::cgLdFixedFunc(IRInstruction* inst) { +void CodeGenerator::cgLdFuncCachedCommon(IRInstruction* inst) { SSATmp* dst = inst->getDst(); SSATmp* methodName = inst->getSrc(0); - using namespace TargetCache; const StringData* name = methodName->getValStr(); - CacheHandle ch = allocFixedFunction(name); + CacheHandle ch = TargetCache::allocFixedFunction(name); size_t funcCacheOff = ch + offsetof(FixedFuncCache, m_func); auto dstReg = dst->getReg(); @@ -2000,14 +1999,25 @@ void CodeGenerator::cgLdFixedFunc(IRInstruction* inst) { m_as. loadq (rVmTl[funcCacheOff], dstReg); m_as. testq (dstReg, dstReg); } +} + +void CodeGenerator::cgLdFuncCached(IRInstruction* inst) { + cgLdFuncCachedCommon(inst); // jz off to the helper call in astubs unlikelyIfBlock(CC_Z, [&] (Asm& a) { // this helper tries the autoload map, and fatals on failure - cgCallHelper(a, (TCA)FixedFuncCache::lookupUnknownFunc, - dstReg, kSyncPoint, ArgGroup().immPtr(name)); + cgCallNative(a, inst); }); } + +void CodeGenerator::cgLdFuncCachedSafe(IRInstruction* inst) { + cgLdFuncCachedCommon(inst); + if (Block* taken = inst->getTaken()) { + emitFwdJcc(m_as, CC_Z, taken); + } +} + void CodeGenerator::cgLdFunc(IRInstruction* inst) { SSATmp* dst = inst->getDst(); SSATmp* methodName = inst->getSrc(0); @@ -4349,18 +4359,11 @@ void CodeGenerator::cgLdClsPropAddr(IRInstruction* inst) { } } -void CodeGenerator::cgLdCachedClass(IRInstruction* inst) { - const StringData* classNameString = inst->getSrc(0)->getValStr(); - auto ch = TargetCache::allocKnownClass(classNameString); - m_as. loadq (rVmTl[ch], inst->getDst()->getReg()); -} - -void CodeGenerator::cgLdClsCached(IRInstruction* inst) { +TargetCache::CacheHandle CodeGenerator::cgLdClsCachedCommon( + IRInstruction* inst) { SSATmp* dst = inst->getDst(); - SSATmp* className = inst->getSrc(0); - // Note the redundancy with LdCachedClass above... - const StringData* classNameString = className->getValStr(); - auto ch = TargetCache::allocKnownClass(classNameString); + const StringData* className = inst->getSrc(0)->getValStr(); + auto ch = TargetCache::allocKnownClass(className); auto dstReg = dst->getReg(); if (dstReg == InvalidReg) { m_as. cmpq (0, rVmTl[ch]); @@ -4368,17 +4371,30 @@ void CodeGenerator::cgLdClsCached(IRInstruction* inst) { m_as. loadq (rVmTl[ch], dstReg); m_as. testq (dstReg, dstReg); } + + return ch; +} + +void CodeGenerator::cgLdClsCached(IRInstruction* inst) { + auto ch = cgLdClsCachedCommon(inst); unlikelyIfBlock(CC_E, [&] (Asm& a) { // Passing only two arguments to lookupKnownClass, since the // third is ignored in the checkOnly==false case. cgCallHelper(a, (TCA)TargetCache::lookupKnownClass, - dst, + inst->getDst(), kSyncPoint, - ArgGroup().addr(rVmTl, intptr_t(ch)).ssa(className)); + ArgGroup().addr(rVmTl, intptr_t(ch)).ssas(inst, 0)); }); } +void CodeGenerator::cgLdClsCachedSafe(IRInstruction* inst) { + cgLdClsCachedCommon(inst); + if (Block* taken = inst->getTaken()) { + emitFwdJcc(CC_Z, taken); + } +} + void CodeGenerator::cgLdCls(IRInstruction* inst) { SSATmp* dst = inst->getDst(); SSATmp* className = inst->getSrc(0); diff --git a/hphp/runtime/vm/translator/hopt/codegen.h b/hphp/runtime/vm/translator/hopt/codegen.h index cd5fd9ef5..370e1ff80 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.h +++ b/hphp/runtime/vm/translator/hopt/codegen.h @@ -115,7 +115,10 @@ private: #undef O // helper functions for code generation - void cgCallNative(IRInstruction* inst); + void cgCallNative(IRInstruction* inst) { + cgCallNative(m_as, inst); + } + void cgCallNative(Asm& a, IRInstruction* inst); void cgCallHelper(Asm&, TCA addr, SSATmp* dst, @@ -258,6 +261,8 @@ private: void cgIterNextCommon(IRInstruction* inst, bool isNextK); void cgIterInitCommon(IRInstruction* inst, bool isInitK); + void cgLdFuncCachedCommon(IRInstruction* inst); + TargetCache::CacheHandle cgLdClsCachedCommon(IRInstruction* inst); Address emitFwdJcc(ConditionCode cc, Block* target); Address emitFwdJcc(Asm& a, ConditionCode cc, Block* target); Address emitFwdJmp(Asm& as, Block* target); @@ -405,7 +410,7 @@ struct ArgGroup { return *this; } - ArgGroup& ssas(IRInstruction* inst, unsigned begin, unsigned count) { + ArgGroup& ssas(IRInstruction* inst, unsigned begin, unsigned count = 1) { for (SSATmp* s : inst->getSrcs().subpiece(begin, count)) { m_args.push_back(ArgDesc(s)); } diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp index 2ac9e5640..302cd299c 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -1194,6 +1194,56 @@ void HhbcTranslator::emitFPassV() { m_tb->genDecRef(tmp); } +void HhbcTranslator::emitFPushCufOp(VM::Op op, Class* cls, StringData* invName, + const Func* callee, int numArgs) { + const Func* curFunc = getCurFunc(); + const bool safe = op == OpFPushCufSafe; + const bool forward = op == OpFPushCufF; + + if (!callee) { + SSATmp* callable = topC(safe ? 1 : 0); + // The most common type for the callable in this case is Arr. We + // can't really do better than the interpreter here, so punt. + SPUNT(StringData::GetStaticString( + folly::format("FPushCuf-{}", + callable->getType().toString()).str()) + ->data()); + } + + SSATmp* ctx; + SSATmp* safeFlag = cns(true); // This is always true until the slow exits + // below are implemented + SSATmp* func = cns(callee); + if (cls) { + if (forward) { + ctx = m_tb->gen(LdCtx, m_tb->getFp(), cns(curFunc)); + ctx = m_tb->gen(GetCtxFwdCall, ctx, cns(callee)); + } else { + ctx = getClsMethodCtx(callee, cls); + } + if (!TargetCache::isPersistentHandle(cls->m_cachedOffset)) { + // The miss path is complicated and rare. Punt for now. + m_tb->gen(LdClsCachedSafe, getExitSlowTrace(), cns(cls->name())); + } + } else { + ctx = m_tb->genDefInitNull(); + if (!TargetCache::isPersistentHandle(callee->getCachedOffset())) { + // The miss path is complicated and rare. Punt for now. + func = m_tb->gen(LdFuncCachedSafe, getExitSlowTrace(), + cns(callee->name())); + } + } + + SSATmp* defaultVal = safe ? popC() : nullptr; + popDecRef(Type::Cell); // callable + if (safe) { + push(defaultVal); + push(safeFlag); + } + + emitFPushActRec(func, ctx, numArgs, invName); +} + void HhbcTranslator::emitNativeImpl() { TRACE(3, "%u: NativeImpl\n", m_bcOff); m_tb->genNativeImpl(); @@ -1260,10 +1310,10 @@ void HhbcTranslator::emitFPushFuncD(int32_t numParams, int32_t funcId) { const bool immutable = func->isNameBindingImmutable(getCurUnit()); if (!immutable) { - spillStack(); // LdFixedFunc can reenter + spillStack(); // LdFuncCached can reenter } SSATmp* ssaFunc = immutable ? m_tb->genDefConst(func) - : m_tb->gen(LdFixedFunc, m_tb->genDefConst(name)); + : m_tb->gen(LdFuncCached, m_tb->genDefConst(name)); emitFPushActRec(ssaFunc, m_tb->genDefInitNull(), numParams, @@ -1369,13 +1419,38 @@ void HhbcTranslator::emitFPushObjMethodD(int32_t numParams, } } +SSATmp* HhbcTranslator::getClsMethodCtx(const Func* callee, const Class* cls) { + bool mightNotBeStatic = false; + assert(callee); + if (!(callee->attrs() & AttrStatic) && + !(getCurFunc()->attrs() & AttrStatic) && + getCurClass() && + getCurClass()->classof(cls)) { + mightNotBeStatic = true; + } + + if (!mightNotBeStatic) { + // static function: ctx is just the Class*. LdCls will simplify to a + // DefConst or LdClsCached. + return m_tb->gen(LdCls, cns(cls->name()), cns(getCurClass())); + } else if (m_tb->isThisAvailable()) { + // might not be a static call and $this is available, so we know it's + // definitely not static + assert(getCurClass()); + return m_tb->genIncRef(m_tb->genLdThis(nullptr)); + } else { + // might be a non-static call. we have to inspect the func at runtime + PUNT(getClsMethodCtx-MightNotBeStatic); + } +} + void HhbcTranslator::emitFPushClsMethodD(int32_t numParams, int32_t methodNameStrId, int32_t clssNamedEntityPairId) { const StringData* methodName = lookupStringId(methodNameStrId); const NamedEntityPair& np = lookupNamedEntityPairId(clssNamedEntityPairId); - UNUSED const StringData* className = np.first; + const StringData* className = np.first; TRACE(3, "%u: FPushClsMethodD %s::%s %d\n", m_bcOff, className->data(), methodName->data(), numParams); const Class* baseClass = Unit::lookupUniqueClass(np.second); @@ -1385,35 +1460,8 @@ void HhbcTranslator::emitFPushClsMethodD(int32_t numParams, magicCall, /* staticLookup: */ true); - bool mightNotBeStatic = false; - if (func && - !(func->attrs() & AttrStatic) && - !(getCurFunc()->attrs() & AttrStatic) && - getCurClass() && - getCurClass()->classof(baseClass)) { - mightNotBeStatic = true; - } - if (func) { - SSATmp* objOrCls; - if (!mightNotBeStatic) { // definitely static - // static function: store base class into the m_cls/m_this slot - if (TargetCache::isPersistentHandle(baseClass->m_cachedOffset)) { - objOrCls = m_tb->genDefConst(baseClass); - } else { - objOrCls = m_tb->gen(LdClsCached, m_tb->genDefConst(className)); - } - } else if (m_tb->isThisAvailable()) { - // 'this' pointer is available, so use it. - assert(getCurClass()); - objOrCls = m_tb->genIncRef(m_tb->genLdThis(nullptr)); - } else { - // might be a non-static call - // generate code that tests at runtime whether to use - // this pointer or class - PUNT(FPushClsMethodD_MightNotBeStatic); - assert(0); - } + SSATmp* objOrCls = getClsMethodCtx(func, baseClass); emitFPushActRec(m_tb->genDefConst(func), objOrCls, numParams, @@ -1421,7 +1469,6 @@ void HhbcTranslator::emitFPushClsMethodD(int32_t numParams, } else { // lookup static method & class in the target cache Trace* exitTrace = getExitSlowTrace(); - const StringData* className = np.first; SSATmp* funcClassTmp = m_tb->genLdClsMethodCache(m_tb->genDefConst(className), m_tb->genDefConst(methodName), @@ -1954,7 +2001,7 @@ void HhbcTranslator::emitVerifyParamType(int32_t paramId) { Class::initInstanceBits(); bool haveBit = Class::haveInstanceBit(clsName); SSATmp* constraint = knownConstraint ? cns(knownConstraint) - : m_tb->gen(LdCachedClass, cns(clsName)); + : m_tb->gen(LdClsCachedSafe, cns(clsName)); locVal = m_tb->gen(Unbox, getExitTrace(), locVal); SSATmp* objClass = m_tb->gen(LdObjClass, locVal); if (haveBit || classIsUniqueNormalClass(knownConstraint)) { @@ -2016,14 +2063,14 @@ void HhbcTranslator::emitInstanceOfD(int classNameStrId) { * don't need to load it out of target cache because it must * already exist and be defined. * - * Otherwise, we only use LdCachedClass---instanceof with an + * Otherwise, we only use LdClsCachedSafe---instanceof with an * undefined class doesn't invoke autoload. */ SSATmp* checkClass = isUnique || (maybeCls && getCurClass() && getCurClass()->classof(maybeCls)) ? m_tb->genDefConst(maybeCls) - : m_tb->gen(LdCachedClass, ssaClassName); + : m_tb->gen(LdClsCachedSafe, ssaClassName); push( haveBit ? m_tb->gen(InstanceOfBitmask, diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index 990f13605..58a697146 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -213,11 +213,14 @@ struct HhbcTranslator { void emitFPassCOp(); void emitFPassR(); void emitFPassV(); + void emitFPushCufOp(VM::Op op, Class* cls, StringData* invName, + const Func* func, int numArgs); void emitFPushActRec(SSATmp* func, SSATmp* objOrClass, int32_t numArgs, const StringData* invName); void emitFPushFuncD(int32_t numParams, int32_t funcId); void emitFPushFunc(int32_t numParams); void emitFPushFunc(int32_t numParams, SSATmp* funcName); + SSATmp* getClsMethodCtx(const Func* callee, const Class* cls); void emitFPushClsMethodD(int32_t numParams, int32_t methodNameStrId, int32_t clssNamedEntityPairId); diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index f1b4a98d1..08e9b0385 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -264,7 +264,7 @@ O(LdCtx, D(Ctx), S(StkPtr) S(Func), C|Rm) \ O(LdCctx, D(Cctx), S(StkPtr), C|Rm) \ O(LdCls, D(Cls), S(Str) C(Cls), C|E|N|Refs|Er|Mem) \ O(LdClsCached, D(Cls), CStr, C|E|N|Refs|Er|Mem) \ -O(LdCachedClass, D(Cls), CStr, C) \ +O(LdClsCachedSafe, D(Cls), CStr, C) \ O(LdClsCtx, D(Cls), S(Ctx), C) \ O(LdClsCctx, D(Cls), S(Cctx), C) \ O(LdClsCns, DParam, CStr CStr, C) \ @@ -281,7 +281,8 @@ O(LdGblAddrDef, D(PtrToGen), S(Str), E|N|CRc) \ O(LdGblAddr, D(PtrToGen), S(Str), N ) \ O(LdObjClass, D(Cls), S(Obj), C) \ O(LdFunc, D(Func), S(Str), E|N|CRc|Er) \ -O(LdFixedFunc, D(Func), CStr, N|C|E|Er) \ +O(LdFuncCached, D(Func), CStr, N|C|E|Er) \ +O(LdFuncCachedSafe, D(Func), CStr, C) \ O(LdARFuncPtr, D(Func), S(StkPtr) C(Int), C) \ O(LdContLocalsPtr, D(PtrToCell), S(Obj), C) \ O(LdSSwitchDestFast, D(TCA), S(Gen), N) \ diff --git a/hphp/runtime/vm/translator/hopt/irtranslator.cpp b/hphp/runtime/vm/translator/hopt/irtranslator.cpp index 91179cd6c..b324a1539 100644 --- a/hphp/runtime/vm/translator/hopt/irtranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/irtranslator.cpp @@ -1075,6 +1075,16 @@ TranslatorX64::irTranslateFPassV(const Tracelet& t, HHIR_EMIT(FPassV); } +void +TranslatorX64::irTranslateFPushCufOp(const Tracelet& t, + const NormalizedInstruction& i) { + Class* cls = nullptr; + StringData* invName = nullptr; + bool forward = false; + const Func* func = findCuf(i, cls, invName, forward); + HHIR_EMIT(FPushCufOp, i.op(), cls, invName, func, i.imm[0].u_IVA); +} + void TranslatorX64::irTranslateFPassR(const Tracelet& t, const NormalizedInstruction& i) { @@ -1247,6 +1257,10 @@ TranslatorX64::irTranslateIterFree(const Tracelet& t, case OpFPassCW: \ case OpFPassCE: \ func(FPassCOp, t, i) \ + case OpFPushCuf: \ + case OpFPushCufF: \ + case OpFPushCufSafe: \ + func(FPushCufOp, t, i) \ case OpIssetL: \ case OpIsNullL: \ case OpIsStringL: \ diff --git a/hphp/runtime/vm/translator/hopt/nativecalls.cpp b/hphp/runtime/vm/translator/hopt/nativecalls.cpp index 91ecbb8cc..4ab67f23f 100644 --- a/hphp/runtime/vm/translator/hopt/nativecalls.cpp +++ b/hphp/runtime/vm/translator/hopt/nativecalls.cpp @@ -25,6 +25,7 @@ namespace HPHP { namespace VM { namespace JIT { namespace NativeCalls { using namespace HPHP::VM::Transl; +using namespace HPHP::VM::Transl::TargetCache; static const SyncOptions SNone = kNoSyncPoint; static const SyncOptions SSync = kSyncPoint; @@ -98,6 +99,8 @@ static CallMap s_callMap({ {{SSA, 0}, {SSA, 1}, {TV, 2}}}, {StaticLocInitCached, (TCA)staticLocInitCached, DSSA, SNone, {{SSA, 0}, {SSA, 1}, {TV, 2}, {SSA, 3}}}, + {LdFuncCached, (TCA)FixedFuncCache::lookupUnknownFunc, DSSA, SSync, + {{SSA, 0}}}, /* Switch helpers */ {LdSwitchDblIndex, (TCA)switchDoubleHelper, DSSA, SSync, diff --git a/hphp/runtime/vm/translator/hopt/simplifier.cpp b/hphp/runtime/vm/translator/hopt/simplifier.cpp index 66b48f090..614f37113 100644 --- a/hphp/runtime/vm/translator/hopt/simplifier.cpp +++ b/hphp/runtime/vm/translator/hopt/simplifier.cpp @@ -154,47 +154,6 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) { case SpillStack: return simplifySpillStack(inst); case Call: return simplifyCall(inst); - case Jmp_: - case JmpInstanceOf: - case JmpNInstanceOf: - case JmpInstanceOfBitmask: - case JmpNInstanceOfBitmask: - return nullptr; - - case LdObjClass: - case LdCachedClass: - case DecRefLoc: - case DecRefStack: - case GuardLoc: - case GuardStk: - case LdThis: - case LdLoc: - case LdMem: - case LdRef: - case LdStack: - case LdPropAddr: - case LdClsCns: - case LdObjMethod: - case RetVal: - case FreeActRec: - case LdClsMethodCache: - case LdClsMethodFCache: - case LdClsMethod: - case ExitTrace: - case ExitSlow: - case ExitGuardFailure: - case StMem: - case StMemNT: - case StLoc: - case DefFP: - case DefSP: - case LdFunc: - case LdFixedFunc: - case Box: - case DefLabel: - case Marker: - return nullptr; - default: unimplementedSimplify(inst->getOpcode()); return nullptr; @@ -346,8 +305,8 @@ SSATmp* Simplifier::simplifyLdCls(IRInstruction* inst) { if (clsName->isConst()) { const Class* cls = Unit::lookupClass(clsName->getValStr()); if (cls) { - if (RuntimeOption::RepoAuthoritative && (cls->attrs() & AttrUnique)) { - // the class is unique + if (TargetCache::isPersistentHandle(cls->m_cachedOffset)) { + // the class is always defined return m_tb->genDefConst(cls); } const Class* ctx = inst->getSrc(1)->getValClass(); diff --git a/hphp/runtime/vm/translator/translator-x64.cpp b/hphp/runtime/vm/translator/translator-x64.cpp index d69499ac7..879eda9c4 100644 --- a/hphp/runtime/vm/translator/translator-x64.cpp +++ b/hphp/runtime/vm/translator/translator-x64.cpp @@ -9768,9 +9768,8 @@ TranslatorX64::setupActRecClsForStaticCall(const NormalizedInstruction &i, } } -template int64_t checkClass(TargetCache::CacheHandle ch, StringData* clsName, - ActRec *ar) { + ActRec *ar) { VMRegAnchor _; AutoloadHandler::s_instance->invokeHandler(clsName->data()); if (*(Class**)TargetCache::handleToPtr(ch)) return true; @@ -9835,10 +9834,9 @@ TranslatorX64::translateFPushCufOp(const Tracelet& t, { UnlikelyIfBlock ifNull(CC_Z, a, astubs); if (false) { - checkClass(0, nullptr, nullptr); - checkClass(0, nullptr, nullptr); + checkClass(0, nullptr, nullptr); } - EMIT_CALL(astubs, TCA(safe ? checkClass : checkClass), + EMIT_CALL(astubs, TCA(checkClass), IMM(ch), IMM(uintptr_t(cls->name())), RPLUS(rVmSp, vstackOffset(ni, startOfActRec))); recordReentrantStubCall(ni, true);