Fix a bug in CreateCl, combine helper calls and use precoloring
The CreateCl translation dereferenced its targetcache handle at compile time instead of in the TC, so we could get "class undefined" errors if the targetcache slot in the thread that jitted it didn't happen to have that closure defined. Fix that, combine the two helper calls, and use nativecalls so we get register precoloring.
Esse commit está contido em:
@@ -3619,8 +3619,10 @@ AKExists [C C] -> [C:Bool]
|
||||
|
||||
CreateCl <num args> <class name> [F..F] -> [C]
|
||||
|
||||
Creates an instance of <class name> and pushes it on the stack.
|
||||
<class name> must be a subclass of "Closure".
|
||||
Creates an instance of <class name> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<c_Closure*>(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<Class**>(handleToPtr(ch));
|
||||
|
||||
Instance* (*helper)(Class** classCache, const StringData* clsName) =
|
||||
newInstanceHelperNoCtorCachedInstance;
|
||||
cgCallHelper(
|
||||
m_as,
|
||||
(TCA)helper,
|
||||
rScratch,
|
||||
kSyncPoint,
|
||||
ArgGroup()
|
||||
.immPtr<Class*>(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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
+24
-10
@@ -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];
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário