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);