diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index ab290904f..fa2887628 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -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 diff --git a/hphp/runtime/vm/class.h b/hphp/runtime/vm/class.h index e6565a89b..8f846b5b9 100644 --- a/hphp/runtime/vm/class.h +++ b/hphp/runtime/vm/class.h @@ -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& declInterfaces() const { diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index 1c77505bb..95300227d 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -3286,6 +3286,123 @@ Instance* createClHelper(Class* cls, int numArgs, ActRec* ar, TypedValue* sp) { return static_cast(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(); auto const helper = data.origFunc->isMethod() diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp index 29b0b15ff..daa293bde 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.cpp @@ -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); } /* diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index 92836a1f6..40c8cdb11 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -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, diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index d1a7ceb3b..d5d2e4565 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -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) \