Implement fast allocation path for objects in HHIR
Tx64 generates a fast path for allocating objects of a persistent class that inlines a bunch of the helper work into the TC. This diff is basically a translation of that path into HHIR codegen.
Esse commit está contido em:
@@ -821,6 +821,12 @@ D:Obj = AllocObj S1:Cls
|
||||
|
||||
Allocates a new object of class S1.
|
||||
|
||||
D:Obj = AllocObjFast S1:ConstCls
|
||||
|
||||
Allocate a new object of class S1 where S1 is persistent and able to
|
||||
be instantiated (i.e., S1 is not an abstract class, interface, or
|
||||
trait).
|
||||
|
||||
NewArray
|
||||
NewTuple
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ class PreClass : public AtomicCountable {
|
||||
}
|
||||
|
||||
BuiltinCtorFunction instanceCtor() const { return m_InstanceCtor; }
|
||||
int builtinPropSize() { return m_builtinPropSize; }
|
||||
int builtinPropSize() const { return m_builtinPropSize; }
|
||||
|
||||
void prettyPrint(std::ostream& out) const;
|
||||
|
||||
@@ -734,8 +734,8 @@ public:
|
||||
// ObjectData attributes, to be set during Instance initialization.
|
||||
int getODAttrs() const { return m_ODAttrs; }
|
||||
|
||||
int builtinPropSize() { return m_builtinPropSize; }
|
||||
BuiltinCtorFunction instanceCtor() { return m_InstanceCtor; }
|
||||
int builtinPropSize() const { return m_builtinPropSize; }
|
||||
BuiltinCtorFunction instanceCtor() const { return m_InstanceCtor; }
|
||||
|
||||
// Interfaces this class declares in its "implements" clause.
|
||||
const std::vector<ClassPtr>& declInterfaces() const {
|
||||
|
||||
@@ -3286,6 +3286,123 @@ Instance* createClHelper(Class* cls, int numArgs, ActRec* ar, TypedValue* sp) {
|
||||
return static_cast<c_Closure*>(newObj)->init(numArgs, ar, sp);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgAllocObjFast(IRInstruction* inst) {
|
||||
const Class* cls = inst->getSrc(0)->getValClass();
|
||||
auto dstReg = m_regs[inst->getDst()].getReg();
|
||||
|
||||
// First, make sure our property init vectors are all set up
|
||||
bool props = cls->pinitVec().size() > 0;
|
||||
bool sprops = cls->numStaticProperties() > 0;
|
||||
assert((props || sprops) == cls->needInitialization());
|
||||
if (cls->needInitialization()) {
|
||||
if (props) {
|
||||
cls->initPropHandle();
|
||||
m_as.testq(-1, rVmTl[cls->propHandle()]);
|
||||
unlikelyIfBlock(CC_Z, [&] (Asm& a) {
|
||||
cgCallHelper(a,
|
||||
(TCA)getMethodPtr(&Class::initProps),
|
||||
InvalidReg,
|
||||
kSyncPoint,
|
||||
ArgGroup(m_regs).imm((uint64_t)cls));
|
||||
});
|
||||
}
|
||||
if (sprops) {
|
||||
cls->initSPropHandle();
|
||||
m_as.testq(-1, rVmTl[cls->sPropHandle()]);
|
||||
unlikelyIfBlock(CC_Z, [&] (Asm& a) {
|
||||
cgCallHelper(a,
|
||||
(TCA)getMethodPtr(&Class::initSProps),
|
||||
InvalidReg,
|
||||
kSyncPoint,
|
||||
ArgGroup(m_regs).imm((uint64_t)cls));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Next, allocate the object
|
||||
if (cls->instanceCtor()) {
|
||||
cgCallHelper(m_as,
|
||||
(TCA)cls->instanceCtor(),
|
||||
dstReg,
|
||||
kSyncPoint,
|
||||
ArgGroup(m_regs).imm((uint64_t)cls));
|
||||
} else {
|
||||
size_t size = Instance::sizeForNProps(cls->numDeclProperties());
|
||||
int allocator = object_alloc_size_to_index(size);
|
||||
assert(allocator != -1);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)getMethodPtr(&Instance::newInstanceRaw),
|
||||
dstReg,
|
||||
kSyncPoint,
|
||||
ArgGroup(m_regs).imm((uint64_t)cls).imm(allocator));
|
||||
}
|
||||
|
||||
// Set the attributes, if any
|
||||
int odAttrs = cls->getODAttrs();
|
||||
if (odAttrs) {
|
||||
// o_attribute is 16 bits but the fact that we're or-ing a mask makes it ok
|
||||
assert(!(odAttrs & 0xffff0000));
|
||||
m_as.orq(odAttrs, dstReg[ObjectData::attributeOff()]);
|
||||
}
|
||||
|
||||
// Initialize the properties
|
||||
size_t nProps = cls->numDeclProperties();
|
||||
if (nProps > 0) {
|
||||
m_as.push(dstReg);
|
||||
m_as.subq(8, reg::rsp);
|
||||
if (cls->pinitVec().size() == 0) {
|
||||
// Fast case: copy from a known address in the Class
|
||||
ArgGroup args = ArgGroup(m_regs)
|
||||
.addr(dstReg, sizeof(ObjectData) + cls->builtinPropSize())
|
||||
.imm(int64_t(&cls->declPropInit()[0]))
|
||||
.imm(cellsToBytes(nProps));
|
||||
cgCallHelper(m_as,
|
||||
(TCA)memcpy,
|
||||
InvalidReg,
|
||||
kNoSyncPoint,
|
||||
args);
|
||||
} else {
|
||||
// Slower case: we have to load the src address from the targetcache
|
||||
auto rPropData = rScratch;
|
||||
// Load the Class's propInitVec from the targetcache
|
||||
m_as.loadq(rVmTl[cls->propHandle()], rPropData);
|
||||
// propData holds the PropInitVec. We want &(*propData)[0]
|
||||
m_as.loadq(rPropData[Class::PropInitVec::dataOff()], rPropData);
|
||||
if (!cls->hasDeepInitProps()) {
|
||||
ArgGroup args = ArgGroup(m_regs)
|
||||
.addr(dstReg, sizeof(ObjectData) + cls->builtinPropSize())
|
||||
.reg(rPropData)
|
||||
.imm(cellsToBytes(nProps));
|
||||
cgCallHelper(m_as,
|
||||
(TCA)memcpy,
|
||||
InvalidReg,
|
||||
kNoSyncPoint,
|
||||
args);
|
||||
} else {
|
||||
ArgGroup args = ArgGroup(m_regs)
|
||||
.addr(dstReg, sizeof(ObjectData) + cls->builtinPropSize())
|
||||
.reg(rPropData)
|
||||
.imm(nProps);
|
||||
cgCallHelper(m_as,
|
||||
(TCA)deepInitHelper,
|
||||
InvalidReg,
|
||||
kNoSyncPoint,
|
||||
args);
|
||||
}
|
||||
}
|
||||
m_as.addq(8, reg::rsp);
|
||||
m_as.pop(dstReg);
|
||||
}
|
||||
if (cls->callsCustomInstanceInit()) {
|
||||
// callCustomInstanceInit returns the instance in rax
|
||||
cgCallHelper(m_as,
|
||||
(TCA)getMethodPtr(&Instance::callCustomInstanceInit),
|
||||
dstReg,
|
||||
kSyncPoint,
|
||||
ArgGroup(m_regs).reg(dstReg));
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::cgInlineCreateCont(IRInstruction* inst) {
|
||||
auto const& data = *inst->getExtra<InlineCreateCont>();
|
||||
auto const helper = data.origFunc->isMethod()
|
||||
|
||||
@@ -1488,11 +1488,10 @@ void HhbcTranslator::emitFPushActRec(SSATmp* func,
|
||||
}
|
||||
|
||||
void HhbcTranslator::emitFPushCtorCommon(SSATmp* cls,
|
||||
SSATmp* obj,
|
||||
const Func* func,
|
||||
int32_t numParams) {
|
||||
SSATmp* obj = gen(IncRef, gen(AllocObj, cls));
|
||||
push(obj);
|
||||
|
||||
SSATmp* fn = nullptr;
|
||||
if (func) {
|
||||
fn = cns(func);
|
||||
@@ -1507,7 +1506,14 @@ void HhbcTranslator::emitFPushCtorCommon(SSATmp* cls,
|
||||
void HhbcTranslator::emitFPushCtor(int32_t numParams) {
|
||||
SSATmp* cls = popA();
|
||||
exceptionBarrier();
|
||||
emitFPushCtorCommon(cls, nullptr, numParams);
|
||||
SSATmp* obj = gen(IncRef, gen(AllocObj, cls));
|
||||
emitFPushCtorCommon(cls, obj, nullptr, numParams);
|
||||
}
|
||||
|
||||
bool
|
||||
canInstantiateClass(const Class* cls) {
|
||||
return cls &&
|
||||
!(cls->attrs() & (AttrAbstract | AttrInterface | AttrTrait));
|
||||
}
|
||||
|
||||
void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
|
||||
@@ -1517,6 +1523,9 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
|
||||
const Class* cls = Unit::lookupUniqueClass(className);
|
||||
bool uniqueCls = classIsUnique(cls);
|
||||
bool persistentCls = classIsPersistent(cls);
|
||||
bool canInstantiate = canInstantiateClass(cls);
|
||||
bool fastAlloc = !RuntimeOption::EnableObjDestructCall &&
|
||||
persistentCls && canInstantiate;
|
||||
|
||||
const Func* func = uniqueCls ? cls->getCtor() : nullptr;
|
||||
if (func && !(func->attrs() & AttrPublic)) {
|
||||
@@ -1537,7 +1546,15 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
|
||||
} else {
|
||||
clss = gen(LdClsCached, cns(className));
|
||||
}
|
||||
emitFPushCtorCommon(clss, func, numParams);
|
||||
|
||||
SSATmp* obj = nullptr;
|
||||
if (fastAlloc) {
|
||||
obj = gen(IncRef, gen(AllocObjFast, clss));
|
||||
} else {
|
||||
obj = gen(IncRef, gen(AllocObj, clss));
|
||||
}
|
||||
|
||||
emitFPushCtorCommon(clss, obj, func, numParams);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -239,7 +239,10 @@ struct HhbcTranslator {
|
||||
const StringData* methName);
|
||||
void emitFPushCtorD(int32_t numParams, int32_t classNameStrId);
|
||||
void emitFPushCtor(int32_t numParams);
|
||||
void emitFPushCtorCommon(SSATmp* cls, const Func* func, int32_t numParams);
|
||||
void emitFPushCtorCommon(SSATmp* cls,
|
||||
SSATmp* obj,
|
||||
const Func* func,
|
||||
int32_t numParams);
|
||||
void emitCreateCl(int32_t numParams, int32_t classNameStrId);
|
||||
void emitFCallArray(const Offset pcOffset, const Offset after);
|
||||
void emitFCall(uint32_t numParams,
|
||||
|
||||
@@ -312,6 +312,7 @@ O(LdSwitchStrIndex, D(Int), S(Str) S(Int) S(Int), CRc|N) \
|
||||
O(LdSwitchObjIndex, D(Int), S(Obj) S(Int) S(Int), CRc|N|Er) \
|
||||
O(JmpSwitchDest, ND, S(Int), T|E) \
|
||||
O(AllocObj, D(Obj), S(Cls), N) \
|
||||
O(AllocObjFast, D(Obj), C(Cls), N) \
|
||||
O(LdClsCtor, D(Func), S(Cls), C|Er|N) \
|
||||
O(CreateCl, D(Obj), C(Cls) \
|
||||
C(Int) \
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário