Use target cache for static properties of persistent classes
Accessibility of properties of persistent classes can be checked at JIT time, so use the SProp-based fast path for these cases too. Also, enhance HhbcTranslator to try to emit LdClsPropAddrCached, instead of relying on the Simplifier to transform LdClsPropAddr into LdClsPropAddrCached. Doing this only in the Simplifier results in unecessary ExceptionBarrier before LdClsPropAddrCached. However, there's still value in keeping the optimization in the Simplifier, so that it can leverage opportunities exposed by other optimizations. So factor out the logic to check whether to use the SProp cache.
Esse commit está contido em:
@@ -1123,8 +1123,28 @@ SSATmp* HhbcTranslator::getStrName(const StringData* knownName) {
|
||||
return name;
|
||||
}
|
||||
|
||||
SSATmp* HhbcTranslator::emitLdClsPropAddrCached(const StringData* propName,
|
||||
Block* block) {
|
||||
SSATmp* cls = popA();
|
||||
const StringData* clsName = findClassName(cls);
|
||||
assert(clsName);
|
||||
|
||||
SSATmp* prop = getStrName(propName);
|
||||
SSATmp* addr = gen(LdClsPropAddrCached,
|
||||
block,
|
||||
cls,
|
||||
prop,
|
||||
cns(clsName),
|
||||
cns(getCurClass()));
|
||||
return addr;
|
||||
}
|
||||
|
||||
SSATmp* HhbcTranslator::emitLdClsPropAddrOrExit(const StringData* propName,
|
||||
Block* block) {
|
||||
if (canUseSPropCache(m_evalStack.top(), propName, getCurClass())) {
|
||||
return emitLdClsPropAddrCached(propName, block);
|
||||
}
|
||||
|
||||
if (!block) exceptionBarrier();
|
||||
|
||||
SSATmp* clsTmp = popA();
|
||||
@@ -1522,7 +1542,7 @@ void HhbcTranslator::emitFPushCtorD(int32_t numParams, int32_t classNameStrId) {
|
||||
|
||||
const Class* cls = Unit::lookupUniqueClass(className);
|
||||
bool uniqueCls = classIsUnique(cls);
|
||||
bool persistentCls = classIsPersistent(cls);
|
||||
bool persistentCls = TargetCache::classIsPersistent(cls);
|
||||
bool canInstantiate = canInstantiateClass(cls);
|
||||
bool fastAlloc = !RuntimeOption::EnableObjDestructCall &&
|
||||
persistentCls && canInstantiate;
|
||||
|
||||
@@ -533,6 +533,8 @@ private:
|
||||
SSATmp* emitLdClsPropAddr(const StringData* propName) {
|
||||
return emitLdClsPropAddrOrExit(propName, nullptr);
|
||||
}
|
||||
SSATmp* emitLdClsPropAddrCached(const StringData* propName,
|
||||
Block* block);
|
||||
SSATmp* getStrName(const StringData* propName = nullptr);
|
||||
SSATmp* emitLdGblAddrDef(const StringData* gblName = nullptr);
|
||||
SSATmp* emitLdGblAddr(const StringData* gblName, Block* block);
|
||||
|
||||
@@ -525,6 +525,24 @@ Range<const SSATmp*> IRInstruction::getDsts() const {
|
||||
return Range<const SSATmp*>(m_dst, m_numDsts);
|
||||
}
|
||||
|
||||
const StringData* findClassName(SSATmp* cls) {
|
||||
assert(cls->isA(Type::Cls));
|
||||
|
||||
if (cls->isConst()) {
|
||||
return cls->getValClass()->preClass()->name();
|
||||
}
|
||||
// Try to get the class name from a LdCls
|
||||
IRInstruction* clsInst = cls->inst();
|
||||
if (clsInst->op() == LdCls || clsInst->op() == LdClsCached) {
|
||||
SSATmp* clsName = clsInst->getSrc(0);
|
||||
assert(clsName->isA(Type::Str));
|
||||
if (clsName->isConst()) {
|
||||
return clsName->getValStr();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Opcode negateQueryOp(Opcode opc) {
|
||||
assert(isQueryOp(opc));
|
||||
|
||||
|
||||
@@ -1916,6 +1916,12 @@ typedef boost::intrusive::member_hook<IRInstruction,
|
||||
typedef boost::intrusive::list<IRInstruction, IRInstructionHookOption>
|
||||
InstructionList;
|
||||
|
||||
/*
|
||||
* Given an SSATmp of type Cls, try to find the name of the class.
|
||||
* Returns nullptr if can't find it.
|
||||
*/
|
||||
const StringData* findClassName(SSATmp* cls);
|
||||
|
||||
/*
|
||||
* Return the output type from a given IRInstruction.
|
||||
*
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include <runtime/base/type_conversions.h>
|
||||
#include "runtime/base/type_conversions.h"
|
||||
#include "runtime/vm/translator/hopt/tracebuilder.h"
|
||||
#include "runtime/vm/runtime.h"
|
||||
|
||||
@@ -178,6 +178,42 @@ void copyProp(IRInstruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if property propName of class clsTmp, called from context class ctx,
|
||||
* can be accessed via the static property cache.
|
||||
* Right now, this returns true for two cases:
|
||||
* (a) the property is accessed from within the class containing it
|
||||
* (b) the property belongs to a persistent class and it's accessible from ctx
|
||||
*/
|
||||
bool canUseSPropCache(SSATmp* clsTmp,
|
||||
const StringData* propName,
|
||||
const Class* ctx) {
|
||||
if (propName == nullptr) return false;
|
||||
|
||||
const StringData* clsName = findClassName(clsTmp);
|
||||
if (ctx) {
|
||||
const StringData* ctxName = ctx->preClass()->name();;
|
||||
if (clsName && ctxName && clsName->isame(ctxName)) return true;
|
||||
}
|
||||
|
||||
if (!clsTmp->isConst()) return false;
|
||||
|
||||
const Class* cls = clsTmp->getValClass();
|
||||
|
||||
if (!Transl::TargetCache::classIsPersistent(cls)) return false;
|
||||
|
||||
// If the class requires initialization, it might not have been
|
||||
// initialized yet. getSProp() below will trigger initialization,
|
||||
// but that's only valid to do earlier if it doesn't require any
|
||||
// property initializer ([sp]init methods).
|
||||
if (cls->needInitialization()) return false;
|
||||
|
||||
bool visible, accessible;
|
||||
cls->getSProp(const_cast<Class*>(ctx), propName, visible, accessible);
|
||||
|
||||
return visible && accessible;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class... Args> SSATmp* Simplifier::cns(Args&&... cns) {
|
||||
@@ -1391,41 +1427,25 @@ SSATmp* Simplifier::simplifyConvCellToBool(IRInstruction* inst) {
|
||||
}
|
||||
|
||||
SSATmp* Simplifier::simplifyLdClsPropAddr(IRInstruction* inst) {
|
||||
SSATmp* propName = inst->getSrc(1);
|
||||
SSATmp* propName = inst->getSrc(1);
|
||||
if (!propName->isConst()) return nullptr;
|
||||
|
||||
SSATmp* cls = inst->getSrc(0);
|
||||
const StringData* clsNameString = cls->isConst()
|
||||
? cls->getValClass()->preClass()->name()
|
||||
: nullptr;
|
||||
if (!clsNameString) {
|
||||
// see if you can get the class name from a LdCls
|
||||
IRInstruction* clsInst = cls->inst();
|
||||
if (clsInst->op() == LdCls || clsInst->op() == LdClsCached) {
|
||||
SSATmp* clsName = clsInst->getSrc(0);
|
||||
assert(clsName->isA(Type::Str));
|
||||
if (clsName->isConst()) {
|
||||
clsNameString = clsName->getValStr();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!clsNameString) return nullptr;
|
||||
SSATmp* cls = inst->getSrc(0);
|
||||
auto ctxCls = inst->getSrc(2)->getValClass();
|
||||
|
||||
// We known both the class name and the property name statically so
|
||||
// we can use the caching version of LdClsPropAddr. To avoid doing
|
||||
// accessibility checks, we only do this if the context class is the
|
||||
// same as the actual class the property is on.
|
||||
auto const ctxCls = inst->getSrc(2)->getValClass();
|
||||
if (!ctxCls || !clsNameString->isame(ctxCls->preClass()->name())) {
|
||||
return nullptr;
|
||||
if (canUseSPropCache(cls, propName->getValStr(), ctxCls)) {
|
||||
|
||||
const StringData* clsNameStr = findClassName(cls);
|
||||
|
||||
return gen(LdClsPropAddrCached,
|
||||
inst->getTaken(),
|
||||
cls,
|
||||
propName,
|
||||
cns(clsNameStr),
|
||||
inst->getSrc(2));
|
||||
}
|
||||
|
||||
return gen(LdClsPropAddrCached,
|
||||
inst->getTaken(),
|
||||
cls,
|
||||
propName,
|
||||
cns(clsNameString),
|
||||
inst->getSrc(2));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -174,6 +174,14 @@ StackValueInfo getStackValue(SSATmp* stack, uint32_t index);
|
||||
*/
|
||||
void copyProp(IRInstruction*);
|
||||
|
||||
/*
|
||||
* Checks if property propName of class clsTmp, called from context class ctx,
|
||||
* can be accessed via the static property cache.
|
||||
*/
|
||||
bool canUseSPropCache(SSATmp* clsTmp,
|
||||
const StringData* propName,
|
||||
const Class* ctx);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
}}}
|
||||
|
||||
@@ -234,6 +234,12 @@ bool isPersistentHandle(Handle handle) {
|
||||
return handle >= (unsigned)s_persistent_start;
|
||||
}
|
||||
|
||||
bool classIsPersistent(const Class* cls) {
|
||||
return (RuntimeOption::RepoAuthoritative &&
|
||||
cls &&
|
||||
isPersistentHandle(cls->m_cachedOffset));
|
||||
}
|
||||
|
||||
static Handle allocLocked(bool persistent, int numBytes, int align) {
|
||||
s_handleMutex.assertOwnedBySelf();
|
||||
align = Util::roundUpToPowerOfTwo(align);
|
||||
|
||||
@@ -105,6 +105,7 @@ bool testBit(size_t bit);
|
||||
bool testAndSetBit(CacheHandle handle, uint32_t mask);
|
||||
bool testAndSetBit(size_t bit);
|
||||
bool isPersistentHandle(CacheHandle handle);
|
||||
bool classIsPersistent(const Class* cls);
|
||||
|
||||
CacheHandle ptrToHandle(const void*);
|
||||
|
||||
|
||||
@@ -258,13 +258,6 @@ struct IfCountNotStatic {
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
classIsPersistent(const Class* cls) {
|
||||
return RuntimeOption::RepoAuthoritative &&
|
||||
cls &&
|
||||
TargetCache::isPersistentHandle(cls->m_cachedOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
classIsUnique(const Class* cls) {
|
||||
return RuntimeOption::RepoAuthoritative &&
|
||||
|
||||
@@ -1185,7 +1185,6 @@ void dumpTranslationInfo(const Tracelet& t, TCA postGuards);
|
||||
bool classIsUnique(const Class* cls);
|
||||
bool classIsUniqueOrCtxParent(const Class* cls);
|
||||
bool classIsUniqueNormalClass(const Class* cls);
|
||||
bool classIsPersistent(const Class* cls);
|
||||
|
||||
// SpaceRecorder is used in translator-x64.cpp and in hopt/irtranslator.cpp
|
||||
// RAII logger for TC space consumption.
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário