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).
Esse commit está contido em:
jdelong
2013-04-02 20:07:48 -07:00
commit de Sara Golemon
commit 486ca81213
8 arquivos alterados com 231 adições e 109 exclusões
+13 -3
Ver Arquivo
@@ -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<SimpleVariable>(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
);
}
}
+2 -1
Ver Arquivo
@@ -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);
}
/*
+36 -12
Ver Arquivo
@@ -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());
+41 -9
Ver Arquivo
@@ -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<class SerDe> 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;
}
@@ -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
@@ -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
@@ -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;
+22 -7
Ver Arquivo
@@ -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);