From 486ca81213de5e8200e182930e4939b9ead7bfec Mon Sep 17 00:00:00 2001 From: jdelong Date: Tue, 2 Apr 2013 20:07:48 -0700 Subject: [PATCH] Use hphpc-inferred object property types to avoid KindOfUninit checks The frontend appears to alread pessimize its prediction of object property types whenever an UnsetContext might affect a given property. We can use this to avoid checking for KindOfUninit when doing specialized prop gets in the vectortranslator. This is hopefully going to be worth more than it sounds, because it will let us avoid a spillStack, which is a use of the frame and will prevent an inlined getter from removing the ActRec (unless we implement some kind of sinking for frames). --- hphp/compiler/analysis/emitter.cpp | 16 ++- hphp/runtime/vm/as.cpp | 3 +- hphp/runtime/vm/class.cpp | 48 +++++-- hphp/runtime/vm/class.h | 50 ++++++-- .../vm/translator/hopt/hhbctranslator.h | 11 +- .../vm/translator/hopt/vectortranslator.cpp | 119 +++++++++++------- .../vm/translator/translator-x64-vector.cpp | 64 +++++----- hphp/runtime/vm/translator/translator-x64.h | 29 +++-- 8 files changed, 231 insertions(+), 109 deletions(-) diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 45ebd3e00..56f30bd49 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -3816,7 +3816,8 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) { TypedValue uninit; tvWriteUninit(&uninit); for (auto& useVar : useVars) { - pce->addProperty(useVar.first, AttrPrivate, nullptr, nullptr, &uninit); + pce->addProperty(useVar.first, AttrPrivate, nullptr, nullptr, + &uninit, KindOfInvalid); } // The constructor. This is entirely generated; all it does is stash its @@ -6230,6 +6231,13 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode, var = static_pointer_cast(exp); } + // A non-invalid HPHPC type for a property implies the + // property's type is !KindOfUninit, and always + // hphpcType|KindOfNull. + const auto hphpcType = var->getSymbol() + ? var->getSymbol()->getFinalType()->getDataType() + : KindOfInvalid; + StringData* propName = StringData::GetStaticString(var->getName()); StringData* propDoc = empty_string.get(); TypedValue tvVal; @@ -6257,7 +6265,8 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode, tvWriteNull(&tvVal); } bool added UNUSED = - pce->addProperty(propName, attrs, typeConstraint, propDoc, &tvVal); + pce->addProperty(propName, attrs, typeConstraint, propDoc, &tvVal, + hphpcType); assert(added); } } else if (ClassConstantPtr cc = @@ -7218,7 +7227,8 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses, Attr(attr), nullptr, propInfo->docComment ? StringData::GetStaticString(propInfo->docComment) : nullptr, - &tvNull + &tvNull, + KindOfInvalid ); } } diff --git a/hphp/runtime/vm/as.cpp b/hphp/runtime/vm/as.cpp index 375ecf530..d8681ba6f 100644 --- a/hphp/runtime/vm/as.cpp +++ b/hphp/runtime/vm/as.cpp @@ -1517,7 +1517,8 @@ void parse_property(AsmState& as) { as.pce->addProperty(StringData::GetStaticString(name), attrs, empty_string.get(), empty_string.get(), - &tvInit); + &tvInit, + KindOfInvalid); } /* diff --git a/hphp/runtime/vm/class.cpp b/hphp/runtime/vm/class.cpp index b62268c7d..1394180d3 100644 --- a/hphp/runtime/vm/class.cpp +++ b/hphp/runtime/vm/class.cpp @@ -84,11 +84,20 @@ static const StringData* manglePropName(const StringData* className, //============================================================================= // PreClass::Prop. -PreClass::Prop::Prop(PreClass* preClass, const StringData* n, Attr attrs, +PreClass::Prop::Prop(PreClass* preClass, + const StringData* n, + Attr attrs, const StringData* typeConstraint, - const StringData* docComment, const TypedValue& val) - : m_preClass(preClass), m_name(n), m_attrs(attrs), - m_typeConstraint(typeConstraint), m_docComment(docComment) { + const StringData* docComment, + const TypedValue& val, + DataType hphpcType) + : m_preClass(preClass) + , m_name(n) + , m_attrs(attrs) + , m_typeConstraint(typeConstraint) + , m_docComment(docComment) + , m_hphpcType(hphpcType) +{ m_mangledName = manglePropName(preClass->name(), n, attrs); memcpy(&m_val, &val, sizeof(TypedValue)); } @@ -193,11 +202,19 @@ void PreClass::prettyPrint(std::ostream &out) const { //============================================================================= // PreClassEmitter::Prop. -PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, const StringData* n, - Attr attrs, const StringData* typeConstraint, - const StringData* docComment, TypedValue* val) - : m_name(n), m_attrs(attrs), m_typeConstraint(typeConstraint), - m_docComment(docComment) { +PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, + const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType) + : m_name(n) + , m_attrs(attrs) + , m_typeConstraint(typeConstraint) + , m_docComment(docComment) + , m_hphpcType(hphpcType) +{ m_mangledName = manglePropName(pce->name(), n, attrs); memcpy(&m_val, val, sizeof(TypedValue)); } @@ -255,12 +272,14 @@ bool PreClassEmitter::addMethod(FuncEmitter* method) { bool PreClassEmitter::addProperty(const StringData* n, Attr attrs, const StringData* typeConstraint, const StringData* docComment, - TypedValue* val) { + TypedValue* val, + DataType hphpcType) { PropMap::Builder::const_iterator it = m_propMap.find(n); if (it != m_propMap.end()) { return false; } - PreClassEmitter::Prop prop(this, n, attrs, typeConstraint, docComment, val); + PreClassEmitter::Prop prop(this, n, attrs, typeConstraint, docComment, val, + hphpcType); m_propMap.add(prop.name(), prop); return true; } @@ -368,7 +387,8 @@ PreClass* PreClassEmitter::create(Unit& unit) const { prop.attrs(), prop.typeConstraint(), prop.docComment(), - prop.val())); + prop.val(), + prop.hphpcType())); } pc->m_properties.create(propBuild); @@ -1841,6 +1861,7 @@ void Class::setProperties() { prop.m_docComment = parentProp.m_docComment; prop.m_typeConstraint = parentProp.m_typeConstraint; prop.m_name = parentProp.m_name; + prop.m_hphpcType = parentProp.m_hphpcType; if (!(parentProp.m_attrs & AttrPrivate)) { curPropMap.add(prop.m_name, prop); } else { @@ -1914,6 +1935,7 @@ void Class::setProperties() { prop.m_class = this; prop.m_typeConstraint = preProp->typeConstraint(); prop.m_docComment = preProp->docComment(); + prop.m_hphpcType = preProp->hphpcType(); curPropMap.add(preProp->name(), prop); m_declPropInit.push_back(m_preClass->lookupProp(preProp->name()) ->val()); @@ -1943,6 +1965,7 @@ void Class::setProperties() { // This is the first class to declare this property prop.m_class = this; prop.m_docComment = preProp->docComment(); + prop.m_hphpcType = preProp->hphpcType(); curPropMap.add(preProp->name(), prop); m_declPropInit.push_back(m_preClass->lookupProp(preProp->name()) ->val()); @@ -1979,6 +2002,7 @@ void Class::setProperties() { // This is the first class to declare this property prop.m_class = this; prop.m_docComment = preProp->docComment(); + prop.m_hphpcType = preProp->hphpcType(); curPropMap.add(preProp->name(), prop); m_declPropInit.push_back(m_preClass->lookupProp(preProp->name()) ->val()); diff --git a/hphp/runtime/vm/class.h b/hphp/runtime/vm/class.h index 7c9058820..3923df150 100644 --- a/hphp/runtime/vm/class.h +++ b/hphp/runtime/vm/class.h @@ -122,9 +122,13 @@ class PreClass : public AtomicCountable { struct Prop { Prop() {} - Prop(PreClass* preClass, const StringData* n, Attr attrs, - const StringData* typeConstraint, const StringData* docComment, - const TypedValue& val); + Prop(PreClass* preClass, + const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + const TypedValue& val, + DataType hphpcType); void prettyPrint(std::ostream& out) const; @@ -135,6 +139,7 @@ class PreClass : public AtomicCountable { CStrRef mangledNameRef() const { return *(String*)(&m_mangledName); } Attr attrs() const { return m_attrs; } const StringData* typeConstraint() const { return m_typeConstraint; } + DataType hphpcType() const { return m_hphpcType; } const StringData* docComment() const { return m_docComment; } const TypedValue& val() const { return m_val; } @@ -146,6 +151,7 @@ class PreClass : public AtomicCountable { const StringData* m_typeConstraint; const StringData* m_docComment; TypedValue m_val; + DataType m_hphpcType; }; struct Const { @@ -367,11 +373,16 @@ class PreClassEmitter { , m_attrs(AttrNone) , m_typeConstraint(0) , m_docComment(0) + , m_hphpcType(KindOfInvalid) {} - Prop(const PreClassEmitter* pce, const StringData* n, Attr attrs, - const StringData* typeConstraint, const StringData* docComment, - TypedValue* val); + Prop(const PreClassEmitter* pce, + const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType); ~Prop(); const StringData* name() const { return m_name; } @@ -380,6 +391,7 @@ class PreClassEmitter { const StringData* typeConstraint() const { return m_typeConstraint; } const StringData* docComment() const { return m_docComment; } const TypedValue& val() const { return m_val; } + DataType hphpcType() const { return m_hphpcType; } template void serde(SerDe& sd) { sd(m_name) @@ -388,6 +400,7 @@ class PreClassEmitter { (m_typeConstraint) (m_docComment) (m_val) + (m_hphpcType) ; } @@ -398,6 +411,7 @@ class PreClassEmitter { const StringData* m_typeConstraint; const StringData* m_docComment; TypedValue m_val; + DataType m_hphpcType; }; class Const { @@ -446,9 +460,12 @@ class PreClassEmitter { void addInterface(const StringData* n); bool addMethod(FuncEmitter* method); - bool addProperty(const StringData* n, Attr attrs, - const StringData* typeConstraint, - const StringData* docComment, TypedValue* val); + bool addProperty(const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType); const Prop& lookupProp(const StringData* propName) const; bool addConstant(const StringData* n, const StringData* typeConstraint, TypedValue* val, const StringData* phpCode); @@ -561,6 +578,15 @@ public: Class* m_class; // First parent class that declares this property. Attr m_attrs; const StringData* m_typeConstraint; + + /* + * Set when the frontend can infer a particular type for a + * declared property. When this is not KindOfInvalid, the type + * here is actually m_hphpcType OR KindOfNull, but we know + * KindOfUninit is not possible. + */ + DataType m_hphpcType; + const StringData* m_docComment; }; @@ -803,6 +829,12 @@ public: return sizeof(ObjectData) + m_builtinPropSize + index * sizeof(TypedValue); } + + DataType declPropHphpcType(Slot index) const { + auto& prop = m_declProperties[index]; + return prop.m_hphpcType; + } + unsigned classVecLen() const { return m_classVecLen; } diff --git a/hphp/runtime/vm/translator/hopt/hhbctranslator.h b/hphp/runtime/vm/translator/hopt/hhbctranslator.h index 60a36f5ef..dfc766531 100644 --- a/hphp/runtime/vm/translator/hopt/hhbctranslator.h +++ b/hphp/runtime/vm/translator/hopt/hhbctranslator.h @@ -30,8 +30,8 @@ using namespace HPHP::VM::Transl; namespace HPHP { -class StringData; namespace VM { +namespace Transl { struct PropInfo; } namespace JIT { struct EvalStack { @@ -380,7 +380,7 @@ private: void emitIntermediateOp(); void emitProp(); void emitPropGeneric(); - void emitPropSpecialized(const MInstrAttr mia, int propOffset); + void emitPropSpecialized(const MInstrAttr mia, PropInfo); void emitElem(); void emitNewElem(); void emitRatchetRefs(); @@ -415,8 +415,11 @@ private: const int kValIdx = 0; return getInput(kValIdx); } - SSATmp* checkInitProp(SSATmp* baseAsObj, SSATmp* propAddr, int propOffset, - bool warn, bool define); + SSATmp* checkInitProp(SSATmp* baseAsObj, + SSATmp* propAddr, + PropInfo propOffset, + bool warn, + bool define); /* * genStk is a wrapper around TraceBuilder::gen() to deal with instructions diff --git a/hphp/runtime/vm/translator/hopt/vectortranslator.cpp b/hphp/runtime/vm/translator/hopt/vectortranslator.cpp index e931da41d..f75a05089 100644 --- a/hphp/runtime/vm/translator/hopt/vectortranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/vectortranslator.cpp @@ -357,8 +357,20 @@ void HhbcTranslator::VectorTranslator::checkMIState() { * that can throw. Currently this means we have to have a spillStack * so the unwinder can handle it (eventually we'll hook this into an * unwind codepath). TODO(#2162354) + * + * We handle one special case where we know a spillStack won't be + * needed: in a simple CGetM of a single property where hphpc has + * told us it can't be KindOfUninit. */ - m_ht.spillStack(); + if (isCGetM && isSingle && simpleProp) { + auto info = getFinalPropertyOffset(m_ni, m_mii); + assert(info.offset != -1); + if (info.hphpcType == KindOfInvalid) { + m_ht.spillStack(); + } + } else { + m_ht.spillStack(); + } } void HhbcTranslator::VectorTranslator::emitMPre() { @@ -694,13 +706,13 @@ void HhbcTranslator::VectorTranslator::emitIntermediateOp() { void HhbcTranslator::VectorTranslator::emitProp() { const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(m_ni, knownCls, m_mii, + const auto propInfo = getPropertyOffset(m_ni, knownCls, m_mii, m_mInd, m_iInd); auto mia = m_mii.getAttr(m_ni.immVecM[m_mInd]); - if (propOffset == -1 || (mia & Unset)) { + if (propInfo.offset == -1 || (mia & Unset)) { emitPropGeneric(); } else { - emitPropSpecialized(mia, propOffset); + emitPropSpecialized(mia, propInfo); } } @@ -791,45 +803,61 @@ void HhbcTranslator::VectorTranslator::emitPropGeneric() { * Helper for emitPropSpecialized to check if a property is Uninit. It * returns a pointer to the property's address, or init_null_variant * if the property was Uninit and doDefine is false. + * + * We can omit the uninit check for properties that we know may not be + * uninit due to the frontend's type inference. */ SSATmp* HhbcTranslator::VectorTranslator::checkInitProp( - SSATmp* baseAsObj, SSATmp* propAddr, - int propOffset, bool doWarn, bool doDefine) { + SSATmp* baseAsObj, + SSATmp* propAddr, + PropInfo propInfo, + bool doWarn, + bool doDefine) { SSATmp* key = getInput(m_iInd); assert(key->isA(Type::StaticStr)); assert(baseAsObj->isA(Type::Obj)); assert(propAddr->getType().isPtr()); - // The m_mInd check is to avoid initializing a property to - // InitNull right before it's going to be set to something else. - if (doWarn || (doDefine && m_mInd < m_ni.immVecM.size() - 1)) { - return m_tb.cond(m_ht.getCurFunc(), - [&] (Block* taken) { - m_tb.gen(CheckInitMem, taken, propAddr, cns(0)); - }, - [&] { // Next: Property isn't Uninit. Do nothing. - return propAddr; - }, - [&] { // Taken: Property is Uninit. Raise a warning and return - // a pointer to InitNull, either in the object or - // init_null_variant. - m_tb.hint(Block::Unlikely); - if (doWarn) { - m_tb.gen(RaiseUndefProp, baseAsObj, key); - } - if (doDefine) { - m_tb.gen(StProp, baseAsObj, cns(propOffset), m_tb.genDefInitNull()); - return propAddr; - } - return cns((const TypedValue*)&init_null_variant);; + + auto const needsCheck = + propInfo.hphpcType == KindOfInvalid && + // The m_mInd check is to avoid initializing a property to + // InitNull right before it's going to be set to something else. + (doWarn || (doDefine && m_mInd < m_ni.immVecM.size() - 1)); + + if (!needsCheck) return propAddr; + + return m_tb.cond(m_ht.getCurFunc(), + [&] (Block* taken) { + m_tb.gen(CheckInitMem, taken, propAddr, cns(0)); + }, + [&] { // Next: Property isn't Uninit. Do nothing. + return propAddr; + }, + [&] { // Taken: Property is Uninit. Raise a warning and return + // a pointer to InitNull, either in the object or + // init_null_variant. + m_tb.hint(Block::Unlikely); + if (doWarn) { + // We did the spillStack for this back in emitMPre. + m_tb.gen(RaiseUndefProp, baseAsObj, key); } - ); - } - // No need to do the check - return propAddr; + if (doDefine) { + m_tb.gen( + StProp, + baseAsObj, + cns(propInfo.offset), + m_tb.genDefInitNull() + ); + return propAddr; + } + return cns((const TypedValue*)&init_null_variant); + } + ); } -void HhbcTranslator::VectorTranslator::emitPropSpecialized(const MInstrAttr mia, - int propOffset) { +void HhbcTranslator::VectorTranslator::emitPropSpecialized( + const MInstrAttr mia, + PropInfo propInfo) { assert(!(mia & MIA_warn) || !(mia & MIA_unset)); const bool doWarn = mia & MIA_warn; const bool doDefine = mia & MIA_define || mia & MIA_unset; @@ -849,8 +877,8 @@ void HhbcTranslator::VectorTranslator::emitPropSpecialized(const MInstrAttr mia, * check against null here in the intermediate cases. */ if (m_base->isA(Type::Obj)) { - SSATmp* propAddr = m_tb.genLdPropAddr(m_base, cns(propOffset)); - m_base = checkInitProp(m_base, propAddr, propOffset, doWarn, doDefine); + SSATmp* propAddr = m_tb.genLdPropAddr(m_base, cns(propInfo.offset)); + m_base = checkInitProp(m_base, propAddr, propInfo, doWarn, doDefine); } else { SSATmp* baseAsObj = nullptr; m_base = m_tb.cond(m_ht.getCurFunc(), @@ -861,8 +889,11 @@ void HhbcTranslator::VectorTranslator::emitPropSpecialized(const MInstrAttr mia, [&] { // Next: Base is an object. Load property address and // check for uninit return checkInitProp(baseAsObj, - m_tb.genLdPropAddr(baseAsObj, cns(propOffset)), - propOffset, doWarn, doDefine); + m_tb.genLdPropAddr(baseAsObj, + cns(propInfo.offset)), + propInfo, + doWarn, + doDefine); }, [&] { // Taken: Base is Null. Raise warnings/errors and return InitNull. m_tb.hint(Block::Unlikely); @@ -1129,10 +1160,10 @@ void HhbcTranslator::VectorTranslator::emitCGetProp() { assert(!m_ni.outLocal); const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(m_ni, knownCls, + const auto propInfo = getPropertyOffset(m_ni, knownCls, m_mii, m_mInd, m_iInd); - if (propOffset != -1) { - emitPropSpecialized(MIA_warn, propOffset); + if (propInfo.offset != -1) { + emitPropSpecialized(MIA_warn, propInfo); SSATmp* cellPtr = m_tb.genUnboxPtr(m_base); SSATmp* propVal = m_tb.gen(LdMem, Type::Cell, cellPtr, cns(0)); m_result = m_tb.genIncRef(propVal); @@ -1292,10 +1323,10 @@ void HhbcTranslator::VectorTranslator::emitSetProp() { /* If we know the class for the current base, emit a direct property set. */ const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(m_ni, knownCls, + const auto propInfo = getPropertyOffset(m_ni, knownCls, m_mii, m_mInd, m_iInd); - if (propOffset != -1) { - emitPropSpecialized(MIA_define, propOffset); + if (propInfo.offset != -1) { + emitPropSpecialized(MIA_define, propInfo); SSATmp* cellPtr = m_tb.genUnboxPtr(m_base); SSATmp* oldVal = m_tb.gen(LdMem, Type::Cell, cellPtr, cns(0)); // The object owns a reference now diff --git a/hphp/runtime/vm/translator/translator-x64-vector.cpp b/hphp/runtime/vm/translator/translator-x64-vector.cpp index 61e5ddc5b..67a401c05 100644 --- a/hphp/runtime/vm/translator/translator-x64-vector.cpp +++ b/hphp/runtime/vm/translator/translator-x64-vector.cpp @@ -859,10 +859,10 @@ void TranslatorX64::emitPropGeneric(const Tracelet& t, } #undef HELPER_TABLE -int getPropertyOffset(const NormalizedInstruction& ni, - const Class*& baseClass, - const MInstrInfo& mii, - unsigned mInd, unsigned iInd) { +PropInfo getPropertyOffset(const NormalizedInstruction& ni, + const Class*& baseClass, + const MInstrInfo& mii, + unsigned mInd, unsigned iInd) { if (mInd == 0) { auto const baseIndex = mii.valCount(); baseClass = ni.inputs[baseIndex]->rtt.isObject() @@ -871,13 +871,13 @@ int getPropertyOffset(const NormalizedInstruction& ni, } else { baseClass = ni.immVecClasses[mInd - 1]; } - if (!baseClass) return -1; + if (!baseClass) return PropInfo(); if (!ni.inputs[iInd]->rtt.isString()) { - return -1; + return PropInfo(); } auto* const name = ni.inputs[iInd]->rtt.valueString(); - if (!name) return -1; + if (!name) return PropInfo(); bool accessible; Class* ctx = curFunc()->cls(); @@ -885,7 +885,7 @@ int getPropertyOffset(const NormalizedInstruction& ni, // baseClass cannot change in between requests if (!RuntimeOption::RepoAuthoritative || !(baseClass->preClass()->attrs() & AttrUnique)) { - if (!ctx) return -1; + if (!ctx) return PropInfo(); if (!ctx->classof(baseClass)) { if (baseClass->classof(ctx)) { // baseClass can change on us in between requests, but since @@ -895,7 +895,7 @@ int getPropertyOffset(const NormalizedInstruction& ni, } else { // baseClass can change on us in between requests and it is // not related to ctx, so bail out - return -1; + return PropInfo(); } } } @@ -904,12 +904,24 @@ int getPropertyOffset(const NormalizedInstruction& ni, // If we couldn't find a property that is accessible in the current // context, bail out if (idx == kInvalidSlot || !accessible) { - return -1; + return PropInfo(); } // If it's a declared property we're good to go: even if a subclass // redefines an accessible property with the same name it's guaranteed // to be at the same offset - return baseClass->declPropOffset(idx); + return PropInfo( + baseClass->declPropOffset(idx), + baseClass->declPropHphpcType(idx) + ); +} + +PropInfo getFinalPropertyOffset(const NormalizedInstruction& ni, + const MInstrInfo& mii) { + unsigned mInd = ni.immVecM.size() - 1; + unsigned iInd = mii.valCount() + 1 + mInd; + + const Class* cls = nullptr; + return getPropertyOffset(ni, cls, mii, mInd, iInd); } void TranslatorX64::emitPropSpecialized(MInstrAttr const mia, @@ -1030,14 +1042,14 @@ void TranslatorX64::emitProp(const MInstrInfo& mii, __func__, long(a.code.frontier), mInd, iInd); const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(*m_curNI, knownCls, mii, + const auto propInfo = getPropertyOffset(*m_curNI, knownCls, mii, mInd, iInd); - if (propOffset == -1) { + if (propInfo.offset == -1) { emitPropGeneric(*m_curTrace, *m_curNI, mii, mInd, iInd, rBase); } else { auto attrs = mii.getAttr(m_curNI->immVecM[mInd]); - emitPropSpecialized(attrs, knownCls, propOffset, mInd, iInd, rBase); + emitPropSpecialized(attrs, knownCls, propInfo.offset, mInd, iInd, rBase); } } @@ -1244,10 +1256,11 @@ void TranslatorX64::emitCGetProp(const Tracelet& t, * access. */ const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(*m_curNI, knownCls, + const auto propInfo = getPropertyOffset(*m_curNI, knownCls, mii, mInd, iInd); - if (propOffset != -1) { - emitPropSpecialized(MIA_warn, knownCls, propOffset, mInd, iInd, rBase); + if (propInfo.offset != -1) { + emitPropSpecialized(MIA_warn, knownCls, propInfo.offset, + mInd, iInd, rBase); emitDerefIfVariant(a, r(rBase)); emitIncRefGeneric(r(rBase), 0); @@ -1660,10 +1673,11 @@ void TranslatorX64::emitSetProp(const Tracelet& t, * set. */ const Class* knownCls = nullptr; - const int propOffset = getPropertyOffset(*m_curNI, knownCls, + const auto propInfo = getPropertyOffset(*m_curNI, knownCls, mii, mInd, iInd); - if (propOffset != -1 && !ni.outLocal && !ni.outStack) { - emitPropSpecialized(MIA_define, knownCls, propOffset, mInd, iInd, rBase); + if (propInfo.offset != -1 && !ni.outLocal && !ni.outStack) { + emitPropSpecialized(MIA_define, knownCls, propInfo.offset, + mInd, iInd, rBase); m_regMap.allocInputReg(*m_curNI, kRhsIdx); PhysReg rhsReg = getReg(val.location); @@ -2706,14 +2720,6 @@ isNormalPropertyAccess(const NormalizedInstruction& i, i.inputs[objInput]->valueType() == KindOfObject; } -int getNormalPropertyOffset(const NormalizedInstruction& i, - const MInstrInfo& mii, - int propInput, int objInput) { - assert(isNormalPropertyAccess(i, propInput, objInput)); - const Class* baseClass = nullptr; - return getPropertyOffset(i, baseClass, mii, objInput, propInput); -} - bool mInstrHasUnknownOffsets(const NormalizedInstruction& ni) { const MInstrInfo& mii = getMInstrInfo(ni.mInstrOp()); @@ -2723,7 +2729,7 @@ mInstrHasUnknownOffsets(const NormalizedInstruction& ni) { MemberCode mc = ni.immVecM[mi]; if (mcodeMaybePropName(mc)) { const Class* cls = nullptr; - if (getPropertyOffset(ni, cls, mii, mi, ii) == -1) { + if (getPropertyOffset(ni, cls, mii, mi, ii).offset == -1) { return true; } ++ii; diff --git a/hphp/runtime/vm/translator/translator-x64.h b/hphp/runtime/vm/translator/translator-x64.h index e94fb7c0e..d00b7643e 100644 --- a/hphp/runtime/vm/translator/translator-x64.h +++ b/hphp/runtime/vm/translator/translator-x64.h @@ -1159,14 +1159,29 @@ SrcKey nextSrcKey(const Tracelet& t, const NormalizedInstruction& i); bool isNormalPropertyAccess(const NormalizedInstruction& i, int propInput, int objInput); -int getNormalPropertyOffset(const NormalizedInstruction& i, - const MInstrInfo&, - int propInput, int objInput); bool mInstrHasUnknownOffsets(const NormalizedInstruction& i); -int getPropertyOffset(const NormalizedInstruction& ni, - const Class*& baseClass, - const MInstrInfo& mii, - unsigned mInd, unsigned iInd); + +struct PropInfo { + PropInfo() + : offset(-1) + , hphpcType(KindOfInvalid) + {} + explicit PropInfo(int offset, DataType hphpcType) + : offset(offset) + , hphpcType(hphpcType) + {} + + int offset; + DataType hphpcType; +}; + +PropInfo getPropertyOffset(const NormalizedInstruction& ni, + const Class*& baseClass, + const MInstrInfo& mii, + unsigned mInd, unsigned iInd); +PropInfo getFinalPropertyOffset(const NormalizedInstruction&, + const MInstrInfo&); + bool isSupportedCGetM_LE(const NormalizedInstruction& i); bool isSupportedCGetM_RE(const NormalizedInstruction& i); bool isSupportedCGetM(const NormalizedInstruction& i);