Inline object constructors
This should automatically handle the case when we had a generated 86pinit constructor.
Esse commit está contido em:
@@ -665,6 +665,11 @@ D:Cls = LdClsCctx S0:Cctx
|
||||
Loads into D the class representing the current context. Extracts
|
||||
the class from the S0, which is a context class.
|
||||
|
||||
D:Func = LdClsCtor S0:Cls
|
||||
|
||||
Loads into D the constructor of class S0. If the constructor cannot
|
||||
be called from the current context, raise an error.
|
||||
|
||||
D:Home = LdHome S0:StkPtr S1:ConstInt
|
||||
|
||||
Logically packages the location of a pointer into the VM stack and
|
||||
@@ -821,19 +826,9 @@ D:StkPtr = NewObj S0:ConstInt S1:Cls S2:StkPtr S3:FramePtr
|
||||
S2 is the current stack pointer
|
||||
S3 is the frame pointer of the current ActRec
|
||||
|
||||
D:StkPtr = NewObjCached S0:ConstInt S1:Str S2:StkPtr S3:FramePtr
|
||||
D:Obj = AllocObj S1:Cls
|
||||
|
||||
Allocate a new object with class name S1, using a target cache
|
||||
lookup. S1 is the string name of the class, other parameters are
|
||||
the same as NewObj.
|
||||
|
||||
D:StkPtr = NewObjNoCtorCached S0:Str S1:StkPtr
|
||||
|
||||
Allocate a new object, which does not require a constructor call,
|
||||
with class name S0, using a target cache lookup. Because no
|
||||
constructor will be called this opcode does not allocate an ActRec.
|
||||
Operand S0 is the name of the class, S1 is the current stack
|
||||
pointer.
|
||||
Allocate a new object of class S1.
|
||||
|
||||
NewArray
|
||||
NewTuple
|
||||
@@ -843,8 +838,8 @@ NewTuple
|
||||
|
||||
D:StkPtr = SpillFrame<numArgs,invName> S0:StkPtr
|
||||
S1:FramePtr
|
||||
S1:Func
|
||||
S2:{Ctx|Class|Null}
|
||||
S2:Func
|
||||
S3:{Ctx|Class|Null}
|
||||
|
||||
Operands:
|
||||
|
||||
|
||||
@@ -143,13 +143,14 @@ void annotate(NormalizedInstruction* i) {
|
||||
case OpFPushObjMethodD:
|
||||
case OpFPushClsMethodD:
|
||||
case OpFPushClsMethodF:
|
||||
case OpFPushCtorD:
|
||||
case OpFPushFuncD: {
|
||||
// When we push predictable action records, we can use a simpler
|
||||
// translation for their corresponding FCall.
|
||||
const StringData* className = nullptr;
|
||||
const StringData* funcName = nullptr;
|
||||
if (i->op() == OpFPushFuncD) {
|
||||
funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
|
||||
funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
|
||||
} else if (i->op() == OpFPushObjMethodD) {
|
||||
if (i->inputs[0]->valueType() != KindOfObject) break;
|
||||
const Class* cls = i->inputs[0]->rtt.valueClass();
|
||||
@@ -166,10 +167,15 @@ void annotate(NormalizedInstruction* i) {
|
||||
if (!cls) break;
|
||||
funcName = i->inputs[1]->rtt.valueString();
|
||||
className = cls->name();
|
||||
} else {
|
||||
assert(i->op() == OpFPushClsMethodD);
|
||||
} else if (i->op() == OpFPushClsMethodD) {
|
||||
funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
|
||||
className = curUnit()->lookupLitstrId(i->imm[2].u_SA);
|
||||
} else {
|
||||
assert(i->op() == OpFPushCtorD);
|
||||
className = curUnit()->lookupLitstrId(i->imm[1].u_SA);
|
||||
const Class* cls = Unit::lookupUniqueClass(className);
|
||||
if (!cls) break;
|
||||
funcName = cls->getCtor()->name();
|
||||
}
|
||||
assert(funcName->isStatic());
|
||||
recordActRecPush(*i, curUnit(), funcName, className,
|
||||
|
||||
@@ -351,6 +351,7 @@ CALL_OPCODE(FillContLocals)
|
||||
CALL_OPCODE(NewArray)
|
||||
CALL_OPCODE(NewTuple)
|
||||
CALL_OPCODE(NewObj)
|
||||
CALL_OPCODE(LdClsCtor);
|
||||
CALL_OPCODE(CreateCl)
|
||||
CALL_OPCODE(PrintStr)
|
||||
CALL_OPCODE(PrintInt)
|
||||
@@ -3228,6 +3229,17 @@ void CodeGenerator::cgSpillFrame(IRInstruction* inst) {
|
||||
spOffset);
|
||||
}
|
||||
|
||||
const Func* loadClassCtor(Class* cls) {
|
||||
const Func* f = cls->getCtor();
|
||||
if (UNLIKELY(!(f->attrs() & AttrPublic))) {
|
||||
VMRegAnchor _;
|
||||
UNUSED MethodLookup::LookupResult res =
|
||||
g_vmContext->lookupCtorMethod(f, cls, true /*raise*/);
|
||||
assert(res == MethodLookup::MethodFoundWithThis);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
HOT_FUNC_VM ActRec*
|
||||
irNewInstanceHelper(Class* cls,
|
||||
int numArgs,
|
||||
@@ -3243,28 +3255,6 @@ irNewInstanceHelper(Class* cls,
|
||||
return ar;
|
||||
}
|
||||
|
||||
HOT_FUNC_VM static ActRec*
|
||||
irNewInstanceHelperCached(CacheHandle cacheHandle,
|
||||
const StringData* clsName,
|
||||
int numArgs,
|
||||
Cell* sp,
|
||||
ActRec* prevAr) {
|
||||
Cell* obj = sp - 1; // this is where the newly allocated object will go
|
||||
ActRec* ar = (ActRec*)(uintptr_t(obj) - sizeof(ActRec));
|
||||
|
||||
Instance* newObj = newInstanceHelperCached(
|
||||
static_cast<Class**>(handleToPtr(cacheHandle)),
|
||||
clsName,
|
||||
numArgs,
|
||||
ar,
|
||||
prevAr
|
||||
);
|
||||
// store obj into the stack
|
||||
obj->m_data.pobj = newObj;
|
||||
obj->m_type = KindOfObject;
|
||||
return ar;
|
||||
}
|
||||
|
||||
static inline ALWAYS_INLINE Class* getKnownClass(Class** classCache,
|
||||
const StringData* clsName) {
|
||||
Class* cls = *classCache;
|
||||
@@ -3283,59 +3273,13 @@ Instance* createClHelper(Class* cls, int numArgs, ActRec* ar, TypedValue* sp) {
|
||||
return static_cast<c_Closure*>(newObj)->init(numArgs, ar, sp);
|
||||
}
|
||||
|
||||
static Cell*
|
||||
newInstanceHelperNoCtorCached(CacheHandle cacheHandle,
|
||||
const StringData* clsName,
|
||||
Cell* sp) {
|
||||
Cell* obj = sp - 1; // this is where the newly allocated object will go
|
||||
void CodeGenerator::cgAllocObj(IRInstruction* inst) {
|
||||
SSATmp* dst = inst->getDst();
|
||||
SSATmp* cls = inst->getSrc(0);
|
||||
|
||||
Class* cls = getKnownClass((Class**)handleToPtr(cacheHandle), clsName);
|
||||
Instance* newObj = newInstance(cls);
|
||||
newObj->incRefCount();
|
||||
|
||||
// store obj into the stack
|
||||
obj->m_data.pobj = newObj;
|
||||
obj->m_type = KindOfObject;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void CodeGenerator::cgNewObjCached(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);
|
||||
|
||||
assert(clsName->isString());
|
||||
const StringData* classNameString = clsName->getValStr();
|
||||
CacheHandle ch = allocKnownClass(classNameString);
|
||||
|
||||
ActRec* (*helper)(CacheHandle, const StringData*, int, Cell*, ActRec*)
|
||||
= irNewInstanceHelperCached;
|
||||
Instance* (*helper)(Class*) = newInstance;
|
||||
ArgGroup args;
|
||||
args.imm(ch)
|
||||
.ssa(clsName)
|
||||
.ssa(numParams)
|
||||
.ssa(sp)
|
||||
.ssa(fp);
|
||||
cgCallHelper(m_as, (TCA)helper, dst, kSyncPoint, args);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgNewObjNoCtorCached(IRInstruction* inst) {
|
||||
SSATmp* dst = inst->getDst();
|
||||
SSATmp* clsName = inst->getSrc(0);
|
||||
SSATmp* sp = inst->getSrc(1);
|
||||
|
||||
assert(clsName->isString());
|
||||
const StringData* classNameString = clsName->getValStr();
|
||||
CacheHandle ch = allocKnownClass(classNameString);
|
||||
|
||||
Cell* (*helper)(CacheHandle, const StringData*, Cell*) =
|
||||
newInstanceHelperNoCtorCached;
|
||||
ArgGroup args;
|
||||
args.imm(ch)
|
||||
.ssa(clsName)
|
||||
.ssa(sp);
|
||||
args.ssa(cls);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)helper,
|
||||
dst,
|
||||
|
||||
@@ -487,6 +487,8 @@ private:
|
||||
std::vector<ArgDesc> m_args;
|
||||
};
|
||||
|
||||
const Func* loadClassCtor(Class* cls);
|
||||
|
||||
ActRec* irNewInstanceHelper(Class* cls,
|
||||
int numArgs,
|
||||
Cell* sp,
|
||||
|
||||
@@ -1499,27 +1499,43 @@ void HhbcTranslator::emitFPushCtor(int32_t numParams) {
|
||||
void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
|
||||
const StringData* className = lookupStringId(classNameStrId);
|
||||
exceptionBarrier();
|
||||
m_fpiStack.emplace(nullptr, 0);
|
||||
|
||||
// If constructor is the generated 86ctor, no need to call it.
|
||||
if (RuntimeOption::RepoAuthoritative &&
|
||||
numParams == 0) {
|
||||
const Class* cls = Unit::lookupUniqueClass(className);
|
||||
if (cls &&
|
||||
(cls->attrs() & AttrUnique) &&
|
||||
Func::isSpecial(cls->getCtor()->name())) {
|
||||
// This optimization is only safe if the FCall is in the same
|
||||
// tracelet. Luckily that is always the case: since this
|
||||
// optimization only applies when numParams==0, there will be
|
||||
// nothing between the FPushCtorD and the FCall.
|
||||
gen(NewObjNoCtorCached, cns(className), m_tb->getSp());
|
||||
push(m_tb->genDefNull());
|
||||
return;
|
||||
const Class* cls = Unit::lookupUniqueClass(className);
|
||||
bool uniqueCls = classIsUnique(cls);
|
||||
bool persistentCls = classIsPersistent(cls);
|
||||
|
||||
const Func* func = uniqueCls ? cls->getCtor() : nullptr;
|
||||
if (func && !(func->attrs() & AttrPublic)) {
|
||||
Class* ctx = arGetContextClass(curFrame());
|
||||
if (!ctx) {
|
||||
func = nullptr;
|
||||
} else if (ctx != cls) {
|
||||
if ((func->attrs() & AttrPrivate) ||
|
||||
!(ctx->classof(cls) || cls->classof(ctx))) {
|
||||
func = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
gen(
|
||||
NewObjCached, cns(numParams), cns(className), m_tb->getSp(), m_tb->getFp()
|
||||
);
|
||||
|
||||
SSATmp* clss = nullptr;
|
||||
if (persistentCls) {
|
||||
clss = cns(cls);
|
||||
} else {
|
||||
clss = gen(LdClsCached, cns(className));
|
||||
}
|
||||
SSATmp* obj = gen(IncRef, gen(AllocObj, clss));
|
||||
push(obj);
|
||||
|
||||
SSATmp* fn = nullptr;
|
||||
if (func) {
|
||||
fn = cns(func);
|
||||
} else {
|
||||
fn = gen(LdClsCtor, clss);
|
||||
}
|
||||
|
||||
SSATmp* obj2 = gen(IncRef, obj);
|
||||
int32_t numArgsAndCtorFlag = numParams | (1 << 31);
|
||||
emitFPushActRec(fn, obj2, numArgsAndCtorFlag, nullptr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1885,6 +1901,10 @@ void HhbcTranslator::emitRetFromInlined(Type type) {
|
||||
* value so stack offsets are properly tracked.
|
||||
*/
|
||||
m_tb->endInlining();
|
||||
// after end of inlining, m_stackDeficit should be set to
|
||||
// 0, and eval stack should be empty
|
||||
assert(m_evalStack.numCells() == 0);
|
||||
m_stackDeficit = 0;
|
||||
FTRACE(1, "]]] end inlining: {}\n", getCurFunc()->fullName()->data());
|
||||
m_bcStateStack.pop_back();
|
||||
m_fpiStack.pop();
|
||||
|
||||
@@ -315,12 +315,8 @@ O(NewObj, D(StkPtr), S(Cls) \
|
||||
C(Int) \
|
||||
S(StkPtr) \
|
||||
S(FramePtr), E|Mem|N|Refs|PRc|Er) \
|
||||
O(NewObjCached, D(StkPtr), C(Int) \
|
||||
S(Str) \
|
||||
S(StkPtr) \
|
||||
S(FramePtr), E|Mem|N|Refs|PRc|Er) \
|
||||
O(NewObjNoCtorCached, D(StkPtr), S(Str) \
|
||||
S(StkPtr), E|Mem|N|Refs|PRc|Er) \
|
||||
O(AllocObj, D(Obj), S(Cls), N) \
|
||||
O(LdClsCtor, D(Func), S(Cls), C|Er|N) \
|
||||
O(CreateCl, D(Obj), C(Cls) \
|
||||
C(Int) \
|
||||
S(FramePtr) \
|
||||
|
||||
@@ -370,8 +370,6 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
|
||||
opc == ExceptionBarrier ||
|
||||
opc == RetAdjustStack ||
|
||||
opc == NewObj ||
|
||||
opc == NewObjCached ||
|
||||
opc == NewObjNoCtorCached ||
|
||||
opc == InterpOne ||
|
||||
opc == GenericRetDecRefs ||
|
||||
opc == GuardStk ||
|
||||
|
||||
@@ -135,6 +135,8 @@ static CallMap s_callMap({
|
||||
{{SSA, 0}, {SSA, 1}}},
|
||||
{NewObj, (TCA)irNewInstanceHelper, DSSA, SSync,
|
||||
{{SSA, 0}, {SSA, 1}, {SSA, 2}, {SSA, 3}}},
|
||||
{LdClsCtor, (TCA)loadClassCtor, DSSA, SSync,
|
||||
{{SSA, 0}}},
|
||||
{PrintStr, (TCA)print_string, DNone, SNone, {{SSA, 0}}},
|
||||
{PrintInt, (TCA)print_int, DNone, SNone, {{SSA, 0}}},
|
||||
{PrintBool, (TCA)print_boolean, DNone, SNone, {{SSA, 0}}},
|
||||
|
||||
@@ -131,7 +131,6 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
|
||||
index - kNumActRecCells);
|
||||
|
||||
case NewObj:
|
||||
case NewObjCached:
|
||||
if (index == kNumActRecCells) {
|
||||
// newly allocated object, which we unfortunately don't have any
|
||||
// kind of handle to :-(
|
||||
@@ -142,12 +141,6 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
|
||||
// NewObj pushes an object and an ActRec
|
||||
index - (1 + kNumActRecCells));
|
||||
|
||||
case NewObjNoCtorCached:
|
||||
if (index == 0) {
|
||||
return StackValueInfo { Type::Obj };
|
||||
}
|
||||
return getStackValue(sp->inst()->getSrc(1), index - 1);
|
||||
|
||||
default:
|
||||
{
|
||||
// Assume it's a vector instruction. This will assert in
|
||||
@@ -396,9 +389,6 @@ SSATmp* Simplifier::simplifyCall(IRInstruction* inst) {
|
||||
}
|
||||
|
||||
SSATmp* sp = spillStack->getSrc(0);
|
||||
if (sp->inst()->op() == NewObjNoCtorCached) {
|
||||
inst->convertToNop();
|
||||
}
|
||||
int baseOffset = spillStack->getSrc(1)->getValInt() -
|
||||
spillValueCells(spillStack);
|
||||
auto const numSpillSrcs = spillVals.size();
|
||||
|
||||
@@ -478,17 +478,11 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
|
||||
break;
|
||||
|
||||
case NewObj:
|
||||
case NewObjCached:
|
||||
m_spValue = inst->getDst();
|
||||
// new obj leaves the new object and an actrec on the stack
|
||||
m_spOffset += kNumActRecCells + 1;
|
||||
break;
|
||||
|
||||
case NewObjNoCtorCached:
|
||||
m_spValue = inst->getDst();
|
||||
m_spOffset += 1;
|
||||
break;
|
||||
|
||||
case InterpOne: {
|
||||
m_spValue = inst->getDst();
|
||||
int64_t stackAdjustment = inst->getSrc(3)->getValInt();
|
||||
|
||||
@@ -258,7 +258,7 @@ struct IfCountNotStatic {
|
||||
}
|
||||
};
|
||||
|
||||
inline static bool
|
||||
bool
|
||||
classIsPersistent(const Class* cls) {
|
||||
return RuntimeOption::RepoAuthoritative &&
|
||||
cls &&
|
||||
|
||||
@@ -1199,6 +1199,7 @@ void dumpTranslationInfo(const Tracelet& t, TCA postGuards);
|
||||
bool classIsUnique(const Class* cls);
|
||||
bool classIsUniqueOrCtxParent(const Class* cls);
|
||||
bool classIsUniqueNormalClass(const Class* cls);
|
||||
bool classIsPersistent(const Class* cls);
|
||||
|
||||
// SpaceRecorder is used in translator-x64.cpp and in hopt/irtranslator.cpp
|
||||
// RAII logger for TC space consumption.
|
||||
|
||||
@@ -3003,7 +3003,8 @@ static bool shouldAnalyzeCallee(const NormalizedInstruction* fcall) {
|
||||
// Note: the IR assumes that $this is available in all inlined object
|
||||
// methods, which will need to be updated when we support
|
||||
// OpFPushClsMethod here.
|
||||
if (pushOp != OpFPushFuncD && pushOp != OpFPushObjMethodD) {
|
||||
if (pushOp != OpFPushFuncD && pushOp != OpFPushObjMethodD
|
||||
&& pushOp != OpFPushCtorD) {
|
||||
FTRACE(1, "analyzeCallee: push op ({}) was not supported\n",
|
||||
opcodeToName(pushOp));
|
||||
return false;
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário