Implement StaticLocInit in hhir

This is almost the same as tx64's implementation, with one
substantial difference: the targetcache slots hold RefData* instead of
TypedValue*. This is ok because cache handles aren't shared between
translations. I also had to rework some code in dce to handle
consuming a reference through a phi node.
Esse commit está contido em:
bsimmers
2013-04-02 12:40:38 -07:00
commit de Sara Golemon
commit 284d8f144d
12 arquivos alterados com 218 adições e 36 exclusões
+20
Ver Arquivo
@@ -775,6 +775,10 @@ D:T = LdRaw<T> S0:Ptr S1:ConstInt
describes the offset from the base, and the size. The value in D is
assumed to be of type T.
D:BoxedCell = LdStaticLocCached S0:ConstCacheHandle -> L
Load static local value from the targetcache from handle S0. If the
slot is uninitialized, branch to L.
8. Allocation
@@ -1085,6 +1089,22 @@ RaiseUndefProp S0:Obj S1:ConstStr
Raise a notice for an undefined property named S1 on the class of S0.
D:BoxedCell = StaticLocInit S0:ConstStr S1:StkPtr S2:Cell
Get boxed value to initialize static local named S0 in frame S1. If
the static local has not yet been initialized, its value will be set
to S2. The returned value is not stored in the frame S1.
D:BoxedCell = StaticLocInitCached S0:ConstStr S1:StkPtr S2:Cell
S3:ConstCacheHandle
Get boxed value to initialize static local named S0 in frame S1. If
the static local has not yet been initialized, its value will be set
to S2. The returned value is stored in the targetcache at handle S3,
but is not stored in the frame S1. This instruction assumes that the
current function is not a closure body or a generator from a
closure.
Print
AddElem
AddNewElem
+11
Ver Arquivo
@@ -333,6 +333,8 @@ CALL_OPCODE(ThrowNonObjProp)
CALL_OPCODE(RaiseUndefProp)
CALL_OPCODE(RaiseError)
CALL_OPCODE(IncStatGrouped)
CALL_OPCODE(StaticLocInit)
CALL_OPCODE(StaticLocInitCached)
// Vector instruction helpers
CALL_OPCODE(BaseG)
@@ -3690,6 +3692,15 @@ void CodeGenerator::cgStRaw(IRInstruction* inst) {
}
}
void CodeGenerator::cgLdStaticLocCached(IRInstruction* inst) {
auto ch = inst->getSrc(0)->getValRawInt();
auto outReg = inst->getDst()->getReg();
m_as.loadq (rVmTl[ch], outReg);
m_as.testq (outReg, outReg);
emitFwdJcc(m_as, CC_Z, inst->getTaken());
}
// If label is set and type is not Gen, this method generates a check
// that bails to the label if the loaded typed value doesn't match type.
void CodeGenerator::cgLoadTypedValue(PhysReg base,
+74 -20
Ver Arquivo
@@ -83,6 +83,8 @@ static_assert(sizeof(DceFlags) == 1, "sizeof(DceFlags) should be 1 byte");
// DCE state indexed by instr->getIId.
typedef StateVector<IRInstruction, DceFlags> DceState;
typedef hphp_hash_set<const SSATmp*, pointer_hash<SSATmp>> SSASet;
typedef StateVector<SSATmp, SSASet> SSACache;
typedef std::list<const IRInstruction*> WorkList;
void removeDeadInstructions(Trace* trace, const DceState& state) {
@@ -331,6 +333,72 @@ void sinkIncRefs(Trace* trace, IRFactory* irFactory, DceState& state) {
copyProp(trace);
}
// Assuming that the 'consumer' instruction consumes 'src', trace back through
// src's instruction to the real origin of the value. Currently this traces
// through GuardType and DefLabel.
void consumeIncRef(const IRInstruction* consumer, const SSATmp* src,
DceState& state, SSACache& ssas, SSASet visitedSrcs) {
assert(!visitedSrcs.count(src) && "Cycle detected in dataflow graph");
auto const& cache = ssas[src];
if (!cache.empty()) {
// We've already traced this path. Use the cache.
for (const SSATmp* cached : cache) {
consumeIncRef(consumer, cached, state, ssas, SSASet());
}
return;
}
const IRInstruction* srcInst = src->getInstruction();
visitedSrcs.insert(src);
if (srcInst->getOpcode() == GuardType &&
srcInst->getTypeParam().maybeCounted()) {
// srcInst is a GuardType that guards to a refcounted type. We need to
// trace through to its source. If the GuardType guards to a non-refcounted
// type then the reference is consumed by GuardType itself.
consumeIncRef(consumer, srcInst->getSrc(0), state, ssas, visitedSrcs);
} else if (srcInst->getOpcode() == DefLabel) {
// srcInst is a DefLabel that may be a join node. We need to find
// the dst index of src in srcInst and trace through to each jump
// providing a value for it.
for (unsigned i = 0, n = srcInst->getNumDsts(); i < n; ++i) {
if (srcInst->getDst(i) == src) {
srcInst->getBlock()->forEachSrc(i,
[&](IRInstruction* jmp, SSATmp* val) {
consumeIncRef(consumer, val, state, ssas, visitedSrcs);
}
);
break;
}
}
} else {
// src is the canonical representation of everything in visitedSrcs. Put
// that knowledge in the cache.
for (const SSATmp* visited : visitedSrcs) {
// We don't need to store the fact that src is its own canonical
// representation.
if (visited != src) {
ssas[visited].insert(src);
}
}
if (srcInst->getOpcode() == IncRef) {
// <inst> consumes <srcInst> which is an IncRef, so we mark <srcInst> as
// REFCOUNT_CONSUMED.
if (consumer->getTrace()->isMain() || !srcInst->getTrace()->isMain()) {
// <srcInst> is consumed from its own trace.
state[srcInst].setCountConsumed();
} else {
// <srcInst> is consumed off trace.
if (!state[srcInst].countConsumed()) {
// mark <srcInst> as REFCOUNT_CONSUMED_OFF_TRACE unless it is
// also consumed from its own trace.
state[srcInst].setCountConsumedOffTrace();
}
}
}
}
}
} // anonymous namespace
// Publicly exported functions:
@@ -363,6 +431,7 @@ void eliminateDeadCode(Trace* trace, IRFactory* irFactory) {
// work list; this will also mark reachable exit traces. All
// other instructions marked dead.
DceState state(irFactory, DceFlags());
SSACache ssaOriginals(irFactory, SSASet());
WorkList wl = initInstructions(blocks, state);
// process the worklist
@@ -380,26 +449,11 @@ void eliminateDeadCode(Trace* trace, IRFactory* irFactory) {
state[srcInst].setLive();
wl.push_back(srcInst);
}
// <inst> consumes <srcInst> which is an IncRef, so we mark <srcInst> as
// REFCOUNT_CONSUMED. If the source instruction is a GuardType and guards
// to a maybeCounted type, we need to trace through to the source for
// refcounting purposes.
while (srcInst->getOpcode() == GuardType &&
srcInst->getTypeParam().maybeCounted()) {
srcInst = srcInst->getSrc(0)->getInstruction();
}
if (inst->consumesReference(i) && srcInst->getOpcode() == IncRef) {
if (inst->getTrace()->isMain() || !srcInst->getTrace()->isMain()) {
// <srcInst> is consumed from its own trace.
state[srcInst].setCountConsumed();
} else {
// <srcInst> is consumed off trace.
if (!state[srcInst].countConsumed()) {
// mark <srcInst> as REFCOUNT_CONSUMED_OFF_TRACE unless it is
// also consumed from its own trace.
state[srcInst].setCountConsumedOffTrace();
}
}
// If inst consumes this source, find the true source instruction and
// mark it as consumed if it's an IncRef.
if (inst->consumesReference(i)) {
consumeIncRef(inst, src, state, ssaOriginals, SSASet());
}
}
}
@@ -590,8 +590,37 @@ void HhbcTranslator::emitTraitExists(const StringData* traitName) {
emitClassExists(traitName);
}
void HhbcTranslator::emitStaticLocInit(uint32_t varId, uint32_t listStrId) {
emitInterpOneOrPunt(Type::None, 1);
void HhbcTranslator::emitStaticLocInit(uint32_t locId, uint32_t litStrId) {
const StringData* name = lookupStringId(litStrId);
LocalId id(locId);
SSATmp* value = popC();
SSATmp* box;
// Closures and generators from closures don't satisfy the "one static per
// source location" rule that the inline fastpath requires
if (getCurFunc()->isClosureBody() || getCurFunc()->isGeneratorFromClosure()) {
box = m_tb->gen(StaticLocInit, cns(name), m_tb->getFp(), value);
} else {
SSATmp* ch =
m_tb->genDefConst(TargetCache::allocStatic(), Type::CacheHandle);
SSATmp* cachedBox = nullptr;
box = m_tb->cond(getCurFunc(),
[&](Block* taken) {
// Careful: cachedBox is only ok to use in the 'next' branch.
cachedBox = m_tb->gen(LdStaticLocCached, taken, ch);
},
[&] { // next: The local is already initialized
return m_tb->gen(IncRef, cachedBox);
},
[&] { // taken: We missed in the cache
m_tb->hint(Block::Unlikely);
return m_tb->gen(StaticLocInitCached,
cns(name), m_tb->getFp(), value, ch);
}
);
}
m_tb->gen(StLoc, &id, m_tb->getFp(), box);
m_tb->gen(DecRef, value);
}
void HhbcTranslator::emitReqDoc(const StringData* name) {
+7 -5
Ver Arquivo
@@ -851,26 +851,28 @@ static void printConst(std::ostream& os, IRInstruction* inst) {
}
} else if (t.isNull()) {
os << t.toString();
} else if (t == Type::Func) {
} else if (t.subtypeOf(Type::Func)) {
auto func = c->as<const Func*>();
os << "Func(" << (func ? func->fullName()->data() : "0") << ")";
} else if (t == Type::Cls) {
} else if (t.subtypeOf(Type::Cls)) {
auto cls = c->as<const Class*>();
os << "Cls(" << (cls ? cls->name()->data() : "0") << ")";
} else if (t == Type::NamedEntity) {
} else if (t.subtypeOf(Type::NamedEntity)) {
auto ne = c->as<const NamedEntity*>();
os << "NamedEntity(" << ne << ")";
} else if (t == Type::TCA) {
} else if (t.subtypeOf(Type::TCA)) {
TCA tca = c->as<TCA>();
char* nameRaw = Util::getNativeFunctionName(tca);
SCOPE_EXIT { free(nameRaw); };
std::string name(nameRaw);
boost::algorithm::trim(name);
os << folly::format("TCA: {}({})", tca, name);
} else if (t == Type::None) {
} else if (t.subtypeOf(Type::None)) {
os << "None:" << c->as<int64_t>();
} else if (t.isPtr()) {
os << folly::format("{}({:#x})", t.toString(), c->as<uint64_t>());
} else if (t.subtypeOf(Type::CacheHandle)) {
os << folly::format("CacheHandle({:#x})", c->as<int64_t>());
} else {
not_reached();
}
+14 -6
Ver Arquivo
@@ -321,6 +321,12 @@ O(StLocNT, ND, S(StkPtr) S(Gen), E|Mem|CRc) \
O(StRef, DBox(1), SUnk, E|Mem|CRc|Refs) \
O(StRefNT, DBox(1), SUnk, E|Mem|CRc) \
O(StRaw, ND, SUnk, E|Mem) \
O(LdStaticLocCached, D(BoxedCell), C(CacheHandle), NF) \
O(StaticLocInit, D(BoxedCell), CStr S(StkPtr) S(Cell), PRc|E|N|Mem) \
O(StaticLocInitCached, D(BoxedCell), CStr \
S(StkPtr) \
S(Cell) \
C(CacheHandle), PRc|E|N|Mem) \
O(SpillStack, D(StkPtr), SUnk, E|Mem|CRc) \
O(ExitTrace, ND, SUnk, T|E) \
O(ExitTraceCc, ND, SUnk, T|E) \
@@ -938,7 +944,8 @@ Opcode getStackModifyingOpcode(Opcode opc);
IRT(StkPtr, 1ULL << 48) /* any pointer into VM stack: VmSP or VmFP*/ \
IRT(TCA, 1ULL << 49) \
IRT(ActRec, 1ULL << 50) \
IRT(None, 1ULL << 51)
IRT(None, 1ULL << 51) \
IRT(CacheHandle, 1ULL << 52) /* TargetCache::CacheHandle */
// The definitions for these are in ir.cpp
#define IRT_UNIONS \
@@ -2150,9 +2157,10 @@ struct Block : boost::noncopyable {
return m_instrs.iterator_to(*inst);
}
// visit each src that provides a value to label->dsts[i]
template <class Body>
void forEachSrc(unsigned i, Body body) {
// visit each src that provides a value to label->dsts[i]. body
// should take an IRInstruction* and an SSATmp*.
template<typename L>
void forEachSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
assert(n->jmp->getOpcode() == Jmp_ && n->jmp->getTaken() == this);
body(n->jmp, n->jmp->getSrc(i));
@@ -2161,8 +2169,8 @@ struct Block : boost::noncopyable {
// return the first src providing a value to label->dsts[i] for
// which body(src) returns true, or nullptr if none are found.
SSATmp* findSrc(unsigned i,
std::function<bool(SSATmp*)> body) {
template<typename L>
SSATmp* findSrc(unsigned i, L body) {
for (const EdgeData* n = m_preds; n; n = n->next) {
SSATmp* src = n->jmp->getSrc(i);
if (body(src)) return src;
@@ -1143,7 +1143,7 @@ TranslatorX64::irTranslateColAddElemC(const Tracelet& t,
void
TranslatorX64::irTranslateStaticLocInit(const Tracelet& t,
const NormalizedInstruction& i) {
const NormalizedInstruction& i) {
HHIR_EMIT(StaticLocInit, i.imm[0].u_IVA, i.imm[1].u_SA);
}
@@ -90,6 +90,10 @@ static CallMap s_callMap({
{RaiseError, (TCA)raise_error_sd, DNone, SSync, {{SSA, 0}}},
{IncStatGrouped, (TCA)Stats::incStatGrouped, DNone, SNone,
{{SSA, 0}, {SSA, 1}, {SSA, 2}}},
{StaticLocInit, (TCA)staticLocInit, DSSA, SNone,
{{SSA, 0}, {SSA, 1}, {TV, 2}}},
{StaticLocInitCached, (TCA)staticLocInitCached, DSSA, SNone,
{{SSA, 0}, {SSA, 1}, {TV, 2}, {SSA, 3}}},
/* Switch helpers */
{LdSwitchDblIndex, (TCA)switchDoubleHelper, DSSA, SSync,
@@ -272,6 +272,12 @@ public:
return gen(DefConst, typeForConst(val), &cdata);
}
template<typename T>
SSATmp* genDefConst(T val, Type type) {
ConstData cdata(val);
return gen(DefConst, type, &cdata);
}
template<typename T>
SSATmp* genLdConst(T val) {
ConstData cdata(val);
+3 -2
Ver Arquivo
@@ -106,10 +106,11 @@ CacheHandle ptrToHandle(const void*);
TCA fcallHelper(ActRec* ar);
static inline void*
template<typename T = void>
static inline T*
handleToPtr(CacheHandle h) {
assert(h < RuntimeOption::EvalJitTargetCacheSize);
return (char*)tl_targetCaches + h;
return (T*)((char*)tl_targetCaches + h);
}
template<class T>
@@ -110,4 +110,46 @@ void VerifyParamTypeSlow(const Class* cls,
VerifyParamTypeFail(param);
}
template<bool useTargetCache>
RefData* staticLocInitImpl(StringData* name, ActRec* fp, TypedValue val,
TargetCache::CacheHandle ch) {
assert(useTargetCache == (bool)ch);
HphpArray* map;
if (useTargetCache) {
// If we have a cache handle, we know the current func isn't a
// closure or generator closure so we can directly grab its static
// locals map.
const Func* func = fp->m_func;
assert(!(func->isClosureBody() || func->isGeneratorFromClosure()));
map = func->getStaticLocals();
} else {
map = get_static_locals(fp);
}
TypedValue *mapVal = map->nvGet(name);
if (!mapVal) {
map->nvSet(name, &val, false);
mapVal = map->nvGet(name);
}
if (mapVal->m_type != KindOfRef) {
tvBox(mapVal);
}
assert(mapVal->m_type == KindOfRef);
RefData* ret = mapVal->m_data.pref;
if (useTargetCache) {
*TargetCache::handleToPtr<RefData*>(ch) = ret;
}
ret->incRefCount();
return ret;
}
RefData* staticLocInit(StringData* name, ActRec* fp, TypedValue val) {
return staticLocInitImpl<false>(name, fp, val, 0);
}
RefData* staticLocInitCached(StringData* name, ActRec* fp, TypedValue val,
TargetCache::CacheHandle ch) {
return staticLocInitImpl<true>(name, fp, val, ch);
}
} } }
@@ -18,6 +18,7 @@
#include "runtime/base/types.h"
#include "runtime/vm/translator/abi-x64.h"
#include "runtime/vm/translator/targetcache.h"
namespace HPHP { namespace VM { namespace Transl {
@@ -73,6 +74,10 @@ int64_t switchDoubleHelper(int64_t val, int64_t base, int64_t nTargets);
int64_t switchStringHelper(StringData* s, int64_t base, int64_t nTargets);
int64_t switchObjHelper(ObjectData* o, int64_t base, int64_t nTargets);
RefData* staticLocInit(StringData* name, ActRec* fp, TypedValue val);
RefData* staticLocInitCached(StringData* name, ActRec* fp, TypedValue val,
TargetCache::CacheHandle ch);
} } }
#endif