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).
Esse commit está contido em:
bsimmers
2013-04-02 16:48:15 -07:00
commit de Sara Golemon
commit b8ae94e039
10 arquivos alterados com 169 adições e 116 exclusões
+16 -9
Ver Arquivo
@@ -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<T> 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
+36 -20
Ver Arquivo
@@ -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<false>,
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);
+7 -2
Ver Arquivo
@@ -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));
}
+82 -35
Ver Arquivo
@@ -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,
@@ -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);
+3 -2
Ver Arquivo
@@ -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) \
@@ -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: \
@@ -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,
+2 -43
Ver Arquivo
@@ -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();
+3 -5
Ver Arquivo
@@ -9768,9 +9768,8 @@ TranslatorX64::setupActRecClsForStaticCall(const NormalizedInstruction &i,
}
}
template <bool warn>
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<false>(0, nullptr, nullptr);
checkClass<true>(0, nullptr, nullptr);
checkClass(0, nullptr, nullptr);
}
EMIT_CALL(astubs, TCA(safe ? checkClass<false> : checkClass<true>),
EMIT_CALL(astubs, TCA(checkClass),
IMM(ch), IMM(uintptr_t(cls->name())),
RPLUS(rVmSp, vstackOffset(ni, startOfActRec)));
recordReentrantStubCall(ni, true);