diff --git a/hphp/runtime/vm/translator/hopt/codegen.cpp b/hphp/runtime/vm/translator/hopt/codegen.cpp index fc3aa3a2b..665557a3e 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.cpp +++ b/hphp/runtime/vm/translator/hopt/codegen.cpp @@ -5113,7 +5113,7 @@ void cgTrace(Trace* trace, Asm& amain, Asm& astubs, Transl::TranslatorX64* tx64, } void CodeGenerator::print() const { - JIT::print(std::cout, m_curTrace, m_state.asmInfo); + JIT::print(std::cout, m_curTrace, m_state.lifetime, m_state.asmInfo); } /* @@ -5152,10 +5152,11 @@ void genCodeForTrace(Trace* trace, IRFactory* irFactory, vector* bcMap, Transl::TranslatorX64* tx64, + const LifetimeInfo* lifetime, AsmInfo* asmInfo) { assert(trace->isMain()); LiveRegs live_regs = computeLiveRegs(irFactory, trace->front()); - CodegenState state(irFactory, live_regs, asmInfo); + CodegenState state(irFactory, live_regs, lifetime, asmInfo); cgTrace(trace, as, astubs, tx64, bcMap, state); for (Trace* exit : trace->getExitTraces()) { cgTrace(exit, astubs, astubs, tx64, nullptr, state); diff --git a/hphp/runtime/vm/translator/hopt/codegen.h b/hphp/runtime/vm/translator/hopt/codegen.h index 56e1f0665..272515c94 100644 --- a/hphp/runtime/vm/translator/hopt/codegen.h +++ b/hphp/runtime/vm/translator/hopt/codegen.h @@ -73,10 +73,12 @@ typedef StateVector LiveRegs; // and address information produced during codegen. struct CodegenState { CodegenState(const IRFactory* factory, const LiveRegs& liveRegs, + const LifetimeInfo* lifetime, AsmInfo* asmInfo) : patches(factory, nullptr) , lastMarker(nullptr) , liveRegs(liveRegs) + , lifetime(lifetime) , asmInfo(asmInfo) {} @@ -96,6 +98,10 @@ struct CodegenState { // registers. const LiveRegs& liveRegs; + // Optional information used when pretty-printing code after codegen. + // when not available, these are nullptrs. + const LifetimeInfo* lifetime; + // Output: start/end ranges of machine code addresses of each instruction. AsmInfo* asmInfo; }; @@ -535,7 +541,8 @@ void genCodeForTrace(Trace* trace, IRFactory* irFactory, vector* bcMap, TranslatorX64* tx64, - AsmInfo *asmInfo = nullptr); + const LifetimeInfo* lifetime = nullptr, + AsmInfo* asmInfo = nullptr); }}} diff --git a/hphp/runtime/vm/translator/hopt/ir.cpp b/hphp/runtime/vm/translator/hopt/ir.cpp index 7eac1ab56..394d7ff7c 100644 --- a/hphp/runtime/vm/translator/hopt/ir.cpp +++ b/hphp/runtime/vm/translator/hopt/ir.cpp @@ -35,7 +35,6 @@ #include "runtime/vm/translator/hopt/cse.h" #include "runtime/vm/translator/hopt/simplifier.h" #include "runtime/vm/translator/hopt/print.h" -#include "runtime/vm/translator/hopt/codegen.h" // Include last to localize effects to this file #include "util/assert_throw.h" @@ -296,7 +295,6 @@ IRInstruction::IRInstruction(Arena& arena, const IRInstruction* inst, IId iid) , m_numSrcs(inst->m_numSrcs) , m_numDsts(inst->m_numDsts) , m_iid(iid) - , m_id(0) , m_srcs(m_numSrcs ? new (arena) SSATmp*[m_numSrcs] : nullptr) , m_dst(nullptr) , m_taken(nullptr) @@ -595,7 +593,6 @@ void IRInstruction::convertToNop() { m_op = nop.m_op; m_typeParam = nop.m_typeParam; m_numSrcs = nop.m_numSrcs; - m_id = nop.m_id; m_srcs = nop.m_srcs; m_numDsts = nop.m_numDsts; m_dst = nop.m_dst; @@ -633,7 +630,6 @@ void IRInstruction::become(IRFactory* factory, IRInstruction* other) { // dests---the whole point of become() is things still point to us. m_op = other->m_op; m_typeParam = other->m_typeParam; - m_id = other->m_id; m_taken = other->m_taken; m_tca = other->m_tca; m_numSrcs = other->m_numSrcs; diff --git a/hphp/runtime/vm/translator/hopt/ir.h b/hphp/runtime/vm/translator/hopt/ir.h index 924c42c43..7088b6bda 100644 --- a/hphp/runtime/vm/translator/hopt/ir.h +++ b/hphp/runtime/vm/translator/hopt/ir.h @@ -1621,6 +1621,7 @@ struct AsmInfo; class IRFactory; class Simplifier; struct Block; +struct LifetimeInfo; bool isRefCounted(SSATmp* opnd); @@ -1649,7 +1650,6 @@ struct IRInstruction { , m_numSrcs(numSrcs) , m_numDsts(0) , m_iid(kTransient) - , m_id(0) , m_srcs(srcs) , m_dst(nullptr) , m_taken(nullptr) @@ -1665,8 +1665,7 @@ struct IRInstruction { * Construct an IRInstruction as a deep copy of `inst', using * arena to allocate memory for its srcs/dests. */ - explicit IRInstruction(Arena& arena, const IRInstruction* inst, - IId iid); + explicit IRInstruction(Arena& arena, const IRInstruction* inst, IId iid); /* * Initialize the source list for this IRInstruction. We must not @@ -1824,20 +1823,13 @@ struct IRInstruction { m_dst = newDsts; } - TCA getTCA() const { return m_tca; } - void setTCA(TCA newTCA) { m_tca = newTCA; } - - /* - * An instruction's 'id' has different meanings depending on the - * compilation phase. - */ - uint32_t getId() const { return m_id; } - void setId(uint32_t newId) { m_id = newId; } + TCA getTCA() const { return m_tca; } + void setTCA(TCA newTCA) { m_tca = newTCA; } /* * Instruction id (iid) is stable and useful as an array index. */ - uint32_t getIId() const { + uint32_t getIId() const { assert(m_iid != kTransient); return m_iid; } @@ -1909,7 +1901,6 @@ private: uint16_t m_numSrcs; uint16_t m_numDsts; const IId m_iid; - uint32_t m_id; SSATmp** m_srcs; SSATmp* m_dst; // if HasDest or NaryDest Block* m_taken; // for branches, guards, and jmp @@ -1971,12 +1962,6 @@ public: void setInstruction(IRInstruction* i) { m_inst = i; } Type type() const { return m_type; } void setType(Type t) { m_type = t; } - uint32_t getLastUseId() const { return m_lastUseId; } - void setLastUseId(uint32_t newId) { m_lastUseId = newId; } - uint32_t getUseCount() const { return m_useCount; } - void setUseCount(uint32_t count) { m_useCount = count; } - void incUseCount() { m_useCount++; } - uint32_t decUseCount() { return --m_useCount; } bool isSpilled() const { return m_isSpilled; } bool isBoxed() const { return type().isBoxed(); } bool isString() const { return isA(Type::Str); } @@ -2079,8 +2064,6 @@ private: : m_inst(i) , m_type(outputType(i, dstId)) , m_id(opndId) - , m_lastUseId(0) - , m_useCount(0) , m_isSpilled(false) { m_regs[0] = m_regs[1] = Transl::InvalidReg; @@ -2090,9 +2073,7 @@ private: IRInstruction* m_inst; Type m_type; // type when defined - const uint32_t m_id; - uint32_t m_lastUseId; - uint16_t m_useCount; + uint32_t m_id; bool m_isSpilled; /* diff --git a/hphp/runtime/vm/translator/hopt/irfactory.h b/hphp/runtime/vm/translator/hopt/irfactory.h index f09d3cac6..51b58a111 100644 --- a/hphp/runtime/vm/translator/hopt/irfactory.h +++ b/hphp/runtime/vm/translator/hopt/irfactory.h @@ -24,6 +24,7 @@ #include "util/arena.h" #include "runtime/vm/translator/hopt/ir.h" #include "runtime/vm/translator/hopt/cse.h" +#include "runtime/base/memory/memory_manager.h" namespace HPHP { namespace VM { namespace JIT { diff --git a/hphp/runtime/vm/translator/hopt/irtranslator.cpp b/hphp/runtime/vm/translator/hopt/irtranslator.cpp index d9ac0d252..45f437aa9 100644 --- a/hphp/runtime/vm/translator/hopt/irtranslator.cpp +++ b/hphp/runtime/vm/translator/hopt/irtranslator.cpp @@ -1907,29 +1907,34 @@ void TranslatorX64::hhirTraceCodeGen(vector* bcMap) { assert(m_useHHIR); JIT::Trace* trace = m_hhbcTrans->getTrace(); - auto finishPass = [&](const char* msg) { - dumpTrace(1, trace, msg); + auto finishPass = [&](const char* msg, + const JIT::LifetimeInfo* lifetime = nullptr) { + dumpTrace(1, trace, msg, lifetime); assert(JIT::checkCfg(trace, *m_irFactory)); }; finishPass(" after initial translation "); JIT::optimizeTrace(trace, m_hhbcTrans->getTraceBuilder()); finishPass(" after optimizing "); - JIT::allocRegsForTrace(trace, m_irFactory.get()); - finishPass(" after reg alloc "); auto* factory = m_irFactory.get(); if (JIT::dumpIREnabled() || RuntimeOption::EvalJitCompareHHIR) { + JIT::LifetimeInfo lifetime(m_irFactory.get()); + JIT::allocRegsForTrace(trace, m_irFactory.get(), &lifetime); + finishPass(" after reg alloc ", &lifetime); JIT::AsmInfo ai(factory); - JIT::genCodeForTrace(trace, a, astubs, factory, bcMap, this, &ai); + JIT::genCodeForTrace(trace, a, astubs, factory, bcMap, this, + &lifetime, &ai); if (RuntimeOption::EvalJitCompareHHIR) { std::ostringstream out; - dumpTraceImpl(trace, out, &ai); + dumpTraceImpl(trace, out, &lifetime, &ai); m_lastHHIRDump = out.str(); } else { - dumpTrace(1, trace, " after code gen ", &ai); + dumpTrace(1, trace, " after code gen ", &lifetime, &ai); } } else { + JIT::allocRegsForTrace(trace, m_irFactory.get()); + finishPass(" after reg alloc "); JIT::genCodeForTrace(trace, a, astubs, factory, bcMap, this); } diff --git a/hphp/runtime/vm/translator/hopt/linearscan.cpp b/hphp/runtime/vm/translator/hopt/linearscan.cpp index 384e6cdad..82e029b9c 100644 --- a/hphp/runtime/vm/translator/hopt/linearscan.cpp +++ b/hphp/runtime/vm/translator/hopt/linearscan.cpp @@ -39,7 +39,7 @@ struct LinearScan : private boost::noncopyable { static const int NumRegs = 16; explicit LinearScan(IRFactory*); - void allocRegs(Trace*); + void allocRegs(Trace*, LifetimeInfo* lifetime); private: class RegState { @@ -145,6 +145,15 @@ private: RegState* getFreeReg(bool preferCallerSaved); RegState* getReg(RegState* reg); + template + void dumpIR(const Inner* in, const char* msg) { + if (dumpIREnabled(DumpVal)) { + std::ostringstream str; + print(str, in, &m_lifetime); + HPHP::Trace::traceRelease("--- %s: %s\n", msg, str.str().c_str()); + } + } + private: // Register allocation may generate Spill/Reload. IRFactory* const m_irFactory; @@ -164,6 +173,10 @@ private: // have not spilled have -1. StateVector m_spillSlots; + LifetimeInfo m_lifetime; // Internal lifetime state + LinearIdVector& m_linear; // linear id for each inst + UsesVector& m_uses; // use count of each tmp + // the list of native instructions in the trace sorted by instruction ID; // i.e. a filtered list in the same order as visited by m_blocks. smart::list m_natives; @@ -233,6 +246,9 @@ void LinearScan::StateSave::restore(LinearScan* ls) { LinearScan::LinearScan(IRFactory* irFactory) : m_irFactory(irFactory) , m_spillSlots(irFactory, -1) + , m_lifetime(irFactory) + , m_linear(m_lifetime.linear) + , m_uses(m_lifetime.uses) , m_jmps(irFactory, JmpList()) { for (int i = 0; i < kNumX64Regs; i++) { @@ -262,15 +278,6 @@ LinearScan::LinearScan(IRFactory* irFactory) } } -template -static inline void dumpIR(const Inner* in, const char* msg) { - if (dumpIREnabled(DumpVal)) { - std::ostringstream str; - print(str, in); - HPHP::Trace::traceRelease("--- %s: %s\n", msg, str.str().c_str()); - } -} - void LinearScan::allocRegToInstruction(InstructionList::iterator it) { IRInstruction* inst = &*it; dumpIR(inst, "allocating to instruction"); @@ -312,7 +319,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) { // 's last use ID. // Replace with in . SSATmp* reloadTmp = reload->getDst(); - reloadTmp->setLastUseId(spillTmp->getLastUseId()); + m_uses[reloadTmp].lastUse = m_uses[spillTmp].lastUse; m_spillSlots[reloadTmp] = slotId; inst->setSrc(i, reloadTmp); // reloadTmp and tmp share the same type. Since it was spilled, it @@ -328,7 +335,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) { } } - freeRegsAtId(inst->getId()); + freeRegsAtId(m_linear[inst]); // Update next native. if (getNextNative() == inst) { assert(!m_natives.empty()); @@ -391,14 +398,14 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) { assert(!dst.isA(Type::FramePtr) || abnormalFramePtr); assert(!dst.isA(Type::StkPtr) || abnormalStkPtr); - if (!RuntimeOption::EvalHHIRDeadCodeElim || dst.getLastUseId() != 0) { + if (!RuntimeOption::EvalHHIRDeadCodeElim || m_uses[dst].lastUse != 0) { allocRegToTmp(&dst, i); } } } if (!RuntimeOption::EvalHHIRDeadCodeElim) { // if any outputs were unused, free regs now. - freeRegsAtId(inst->getId()); + freeRegsAtId(m_linear[inst]); } } @@ -406,7 +413,7 @@ void LinearScan::allocRegToTmp(SSATmp* ssaTmp, uint32_t index) { bool preferCallerSaved = true; if (RuntimeOption::EvalHHIREnableCalleeSavedOpt) { // Prefer caller-saved registers iff doesn't span native. - preferCallerSaved = (ssaTmp->getLastUseId() <= getNextNativeId()); + preferCallerSaved = (m_uses[ssaTmp].lastUse <= getNextNativeId()); } RegState* reg = nullptr; @@ -465,7 +472,7 @@ void LinearScan::allocRegToTmp(SSATmp* ssaTmp, uint32_t index) { if (m_spillSlots[ssaTmp] == -1) { createSpillSlot(ssaTmp); } - ssaTmp->setLastUseId(getNextNativeId()); + m_uses[ssaTmp].lastUse = getNextNativeId(); } allocRegToTmp(reg, ssaTmp, index); @@ -475,14 +482,14 @@ void LinearScan::allocRegToTmp(RegState* reg, SSATmp* ssaTmp, uint32_t index) { reg->m_ssaTmp = ssaTmp; // mark inst as using this register ssaTmp->setReg(PhysReg(reg->m_regNo), index); - uint32_t lastUseId = ssaTmp->getLastUseId(); + uint32_t lastUseId = m_uses[ssaTmp].lastUse; if (reg->isReserved()) { return; } // insert into the list of assigned registers sorted by last use id auto it = m_allocatedRegs.begin(); for (; it != m_allocatedRegs.end(); ++it) { - if (lastUseId > (*it)->m_ssaTmp->getLastUseId()) { + if (lastUseId > m_uses[(*it)->m_ssaTmp].lastUse) { break; } } @@ -519,7 +526,7 @@ uint32_t LinearScan::assignSpillLoc() { for (int locIndex = 0; locIndex < src->numNeededRegs(); ++locIndex) { - if (dst->getLastUseId() <= getNextNativeId()) { + if (m_uses[dst].lastUse <= getNextNativeId()) { TRACE(3, "[counter] 1 spill a tmp that does not span native\n"); } else { TRACE(3, "[counter] 1 spill a tmp that spans native\n"); @@ -594,14 +601,7 @@ void LinearScan::insertAllocFreeSpillAux(Trace* trace, void LinearScan::collectInfo(BlockList::iterator it, Trace* trace) { m_natives.clear(); m_jmps.reset(); - - for (auto* block : m_blocks) { - for (auto& inst : *block) { - for (auto& dst : inst.getDsts()) { - dst.setLastUseId(0); - } - } - } + m_uses.reset(); while (it != m_blocks.end()) { Block* block = *it++; @@ -611,15 +611,15 @@ void LinearScan::collectInfo(BlockList::iterator it, Trace* trace) { int lastId = block->getTrace()->getData(); for (IRInstruction& inst : *block) { for (auto* src : inst.getSrcs()) { - if (lastId > src->getLastUseId()) { - src->setLastUseId(lastId); + if (lastId > m_uses[src].lastUse) { + m_uses[src].lastUse = lastId; } } } } else { for (IRInstruction& inst : *block) { for (auto* src : inst.getSrcs()) { - src->setLastUseId(inst.getId()); + m_uses[src].lastUse = m_linear[inst]; } if (inst.isNative()) m_natives.push_back(&inst); } @@ -886,24 +886,16 @@ void LinearScan::preAllocSpillLoc(uint32_t numSpillLocs) { // Assign ids to each instruction in linear order. void LinearScan::numberInstructions(const BlockList& blocks) { m_spillSlots.reset(); - forEachInst( - blocks, - [](IRInstruction* inst) { - for (SSATmp& dst : inst->getDsts()) { - dst.setLastUseId(0); - dst.setUseCount(0); - } - } - ); + m_uses.reset(); uint32_t nextId = 1; for (auto* block : blocks) { for (auto& inst : *block) { if (inst.op() == Marker) continue; // don't number markers uint32_t id = nextId++; - inst.setId(id); + m_linear[inst] = id; for (SSATmp* tmp : inst.getSrcs()) { - tmp->setLastUseId(id); - tmp->incUseCount(); + m_uses[tmp].lastUse = id; + m_uses[tmp].count++; } } if (block->getTaken() && block->isMain() && !block->getTaken()->isMain()) { @@ -969,7 +961,7 @@ void LinearScan::genSpillStats(Trace* trace, int numSpillLocs) { } -void LinearScan::allocRegs(Trace* trace) { +void LinearScan::allocRegs(Trace* trace, LifetimeInfo* lifetime) { if (RuntimeOption::EvalHHIREnableCoalescing) { // doesn't need instruction numbering. coalesce(trace); @@ -1011,6 +1003,11 @@ void LinearScan::allocRegs(Trace* trace) { } if (m_slots.size()) genSpillStats(trace, numSpillLocs); + + if (lifetime) { + lifetime->linear = std::move(m_linear); + lifetime->uses = std::move(m_uses); + } } void LinearScan::allocRegsOneTrace(BlockList::iterator& blockIt, @@ -1107,8 +1104,10 @@ void LinearScan::allocRegsToTrace() { void LinearScan::rematerialize() { numberInstructions(m_blocks); - dumpTrace(6, m_blocks.front()->getTrace(), "before rematerialization"); - + if (dumpIREnabled(6)) { + dumpTrace(6, m_blocks.front()->getTrace(), "before rematerialization", + &m_lifetime); + } rematerializeAux(); numberInstructions(m_blocks); // We only replaced Reloads in rematerializeAux(). @@ -1275,11 +1274,13 @@ void LinearScan::rematerializeAux() { void LinearScan::removeUnusedSpills() { for (SlotInfo& slot : m_slots) { IRInstruction* spill = slot.spillTmp->inst(); - if (spill->getDst()->getUseCount() == 0) { + if (m_uses[spill->getDst()].count == 0) { Block* block = spill->getBlock(); block->erase(block->iteratorTo(spill)); SSATmp* src = spill->getSrc(0); - if (src->decUseCount() == 0) { + auto uses = m_uses[src].count - 1; + m_uses[src].count = uses; + if (uses == 0) { Opcode srcOpc = src->inst()->op(); // Not all instructions are able to take noreg as its dest // reg. We pick LdLoc and IncRef because they occur often. @@ -1302,7 +1303,7 @@ void LinearScan::freeRegsAtId(uint32_t id) { auto next = it; ++next; RegState* reg = *it; assert(reg->m_ssaTmp); - if (reg->m_ssaTmp->getLastUseId() <= id) { + if (m_uses[reg->m_ssaTmp].lastUse <= id) { m_allocatedRegs.erase(it); freeReg(reg); } @@ -1441,7 +1442,7 @@ uint32_t LinearScan::createSpillSlot(SSATmp* tmp) { si.latestReload = tmp; m_slots.push_back(si); // The spill slot inherits the last use ID of the spilled tmp. - si.spillTmp->setLastUseId(tmp->getLastUseId()); + m_uses[si.spillTmp].lastUse = m_uses[tmp].lastUse; return slotId; } @@ -1451,7 +1452,7 @@ IRInstruction* LinearScan::getNextNative() const { uint32_t LinearScan::getNextNativeId() const { IRInstruction* nextNative = getNextNative(); - return (nextNative ? nextNative->getId() : -1); + return nextNative ? m_linear[nextNative] : -1; } SSATmp* LinearScan::getSpilledTmp(SSATmp* tmp) { @@ -1505,8 +1506,9 @@ void LinearScan::PreColoringHint::add(SSATmp* tmp, uint32_t index, int argNum) { ////////////////////////////////////////////////////////////////////// -void allocRegsForTrace(Trace* trace, IRFactory* irFactory) { - LinearScan(irFactory).allocRegs(trace); +void allocRegsForTrace(Trace* trace, IRFactory* irFactory, + LifetimeInfo* lifetime) { + LinearScan(irFactory).allocRegs(trace, lifetime); } }}} // HPHP::VM::JIT diff --git a/hphp/runtime/vm/translator/hopt/linearscan.h b/hphp/runtime/vm/translator/hopt/linearscan.h index 375f65be5..e5f52cbb4 100644 --- a/hphp/runtime/vm/translator/hopt/linearscan.h +++ b/hphp/runtime/vm/translator/hopt/linearscan.h @@ -17,16 +17,44 @@ #ifndef incl_HPHP_VM_LINEAR_SCAN_H_ #define incl_HPHP_VM_LINEAR_SCAN_H_ +#include "runtime/vm/translator/hopt/state_vector.h" + namespace HPHP { namespace VM { namespace JIT { class Trace; class IRFactory; +struct UseInfo { + UseInfo() : lastUse(0), count(0) {} + uint32_t lastUse; // linear id of last use + uint32_t count; // number of uses +}; + +typedef StateVector LinearIdVector; +typedef StateVector UsesVector; + +struct LifetimeInfo { + explicit LifetimeInfo(const IRFactory* factory) + : linear(factory, 0), uses(factory, UseInfo()) { + } + explicit LifetimeInfo(const LinearIdVector& linear, + const UsesVector& uses) + : linear(linear), uses(uses) { + } + explicit LifetimeInfo(LinearIdVector&& linear, + UsesVector&& uses) + : linear(linear), uses(uses) { + } + + LinearIdVector linear; // linear id of each instruction + UsesVector uses; // last use id and use count of each tmp +}; + /* * The main entry point for register allocation. Called prior to code * generation. */ -void allocRegsForTrace(Trace*, IRFactory*); +void allocRegsForTrace(Trace*, IRFactory*, LifetimeInfo* lifetime = nullptr); }}} diff --git a/hphp/runtime/vm/translator/hopt/print.cpp b/hphp/runtime/vm/translator/hopt/print.cpp index 554efd610..226cd1693 100644 --- a/hphp/runtime/vm/translator/hopt/print.cpp +++ b/hphp/runtime/vm/translator/hopt/print.cpp @@ -65,26 +65,28 @@ void printOpcode(std::ostream& os, const IRInstruction* inst) { << color(ANSI_COLOR_END); } -void printDst(std::ostream& os, const IRInstruction* inst) { +void printDst(std::ostream& os, const IRInstruction* inst, + const LifetimeInfo* lifetime) { if (inst->getNumDsts() == 0) return; const char* sep = ""; for (const SSATmp& dst : inst->getDsts()) { os << punc(sep); - print(os, &dst, true); + print(os, &dst, lifetime, true); sep = ", "; } os << punc(" = "); } -void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i) { +void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i, + const LifetimeInfo* lifetime) { SSATmp* src = inst->getSrc(i); if (src != nullptr) { - if (inst->getId() != 0 && !src->isConst() && - src->getLastUseId() == inst->getId()) { + if (lifetime && lifetime->linear[inst] != 0 && !src->isConst() && + lifetime->uses[src].lastUse == lifetime->linear[inst]) { ostream << "~"; } - print(ostream, src); + print(ostream, src, lifetime); } else { ostream << color(ANSI_COLOR_RED) << "!!!NULL @ " << i @@ -93,7 +95,8 @@ void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i) { } } -void printSrcs(std::ostream& os, const IRInstruction* inst) { +void printSrcs(std::ostream& os, const IRInstruction* inst, + const LifetimeInfo* lifetime) { bool first = true; if (inst->op() == IncStat) { os << " " << Stats::g_counterNames[inst->getSrc(0)->getValInt()] @@ -107,7 +110,7 @@ void printSrcs(std::ostream& os, const IRInstruction* inst) { os << " "; first = false; } - printSrc(os, inst, i); + printSrc(os, inst, i, lifetime); } } @@ -118,7 +121,8 @@ void printLabel(std::ostream& os, const Block* block) { os << color(ANSI_COLOR_END); } -void print(std::ostream& ostream, const IRInstruction* inst) { +void print(std::ostream& ostream, const IRInstruction* inst, + const LifetimeInfo* lifetime) { if (inst->op() == Marker) { auto* marker = inst->getExtra(); ostream << color(ANSI_COLOR_BLUE) @@ -132,17 +136,17 @@ void print(std::ostream& ostream, const IRInstruction* inst) { if (!inst->isTransient()) { ostream << color(ANSI_COLOR_YELLOW); - if (!inst->getId()) { + if (!lifetime || !lifetime->linear[inst]) { ostream << folly::format("({:02d}) ", inst->getIId()); } else { ostream << folly::format("({:02d}@{:02d}) ", inst->getIId(), - inst->getId()); + lifetime->linear[inst]); } ostream << color(ANSI_COLOR_END); } - printDst(ostream, inst); + printDst(ostream, inst, lifetime); printOpcode(ostream, inst); - printSrcs(ostream, inst); + printSrcs(ostream, inst, lifetime); if (Block* taken = inst->getTaken()) { ostream << punc(" -> "); @@ -223,7 +227,8 @@ static void printConst(std::ostream& os, IRInstruction* inst) { } } -void print(std::ostream& os, const SSATmp* tmp, bool printLastUse) { +void print(std::ostream& os, const SSATmp* tmp, const LifetimeInfo* lifetime, + bool printLastUse) { if (tmp->inst()->op() == DefConst) { printConst(os, tmp->inst()); return; @@ -231,9 +236,9 @@ void print(std::ostream& os, const SSATmp* tmp, bool printLastUse) { os << color(ANSI_COLOR_WHITE); os << "t" << tmp->getId(); os << color(ANSI_COLOR_END); - if (printLastUse && tmp->getLastUseId() != 0) { + if (printLastUse && lifetime && lifetime->uses[tmp].lastUse != 0) { os << color(ANSI_COLOR_GRAY) - << "@" << tmp->getLastUseId() << "#" << tmp->getUseCount() + << "@" << lifetime->uses[tmp].lastUse << "#" << lifetime->uses[tmp].count << color(ANSI_COLOR_END); } if (tmp->isSpilled() || tmp->numAllocatedRegs() > 0) { @@ -264,10 +269,11 @@ void print(const SSATmp* tmp) { } void print(const Trace* trace) { - print(std::cout, trace, nullptr); + print(std::cout, trace); } -void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { +void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime, + const AsmInfo* asmInfo) { static const int kIndent = 4; Disasm disasm(Disasm::Options().indent(kIndent + 4) .printEncoding(dumpIREnabled(6)) @@ -292,7 +298,7 @@ void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { if (inst.op() == Marker) { os << std::string(kIndent, ' '); - JIT::print(os, &inst); + JIT::print(os, &inst, lifetime); os << '\n'; // Don't print bytecode in a non-main trace. @@ -325,14 +331,14 @@ void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { os << std::string(kIndent + folly::format("({}) ", inst.getIId()).str().size(), ' '); - JIT::print(os, inst.getDst(i), false); + JIT::print(os, inst.getDst(i), lifetime, false); os << punc(" = ") << color(ANSI_COLOR_CYAN) << "phi " << color(ANSI_COLOR_END); bool first = true; inst.getBlock()->forEachSrc(i, [&](IRInstruction* jmp, SSATmp*) { if (!first) os << punc(", "); first = false; - printSrc(os, jmp, i); + printSrc(os, jmp, i, lifetime); os << punc("@"); printLabel(os, jmp->getBlock()); }); @@ -341,7 +347,7 @@ void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { } os << std::string(kIndent, ' '); - JIT::print(os, &inst); + JIT::print(os, &inst, lifetime); os << '\n'; if (asmInfo) { @@ -379,19 +385,20 @@ void print(std::ostream& os, const Trace* trace, const AsmInfo* asmInfo) { os << "\n" << color(ANSI_COLOR_GREEN) << " ------- Exit Trace -------" << color(ANSI_COLOR_END) << '\n'; - print(os, exitTrace, asmInfo); + print(os, exitTrace, lifetime, asmInfo); } } void dumpTraceImpl(const Trace* trace, std::ostream& out, + const LifetimeInfo* lifetime, const AsmInfo* asmInfo) { - print(out, trace, asmInfo); + print(out, trace, lifetime, asmInfo); } // Suggested captions: "before jiffy removal", "after goat saturation", // etc. void dumpTrace(int level, const Trace* trace, const char* caption, - AsmInfo* ai) { + const LifetimeInfo* lifetime, AsmInfo* ai) { if (dumpIREnabled(level)) { std::ostringstream str; auto bannerFmt = "{:-^40}\n"; @@ -399,7 +406,7 @@ void dumpTrace(int level, const Trace* trace, const char* caption, << folly::format(bannerFmt, caption) << color(ANSI_COLOR_END) ; - dumpTraceImpl(trace, str, ai); + dumpTraceImpl(trace, str, lifetime, ai); str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN) << folly::format(bannerFmt, "") << color(ANSI_COLOR_END) diff --git a/hphp/runtime/vm/translator/hopt/print.h b/hphp/runtime/vm/translator/hopt/print.h index bb8f44789..4a8b53f29 100644 --- a/hphp/runtime/vm/translator/hopt/print.h +++ b/hphp/runtime/vm/translator/hopt/print.h @@ -19,6 +19,7 @@ #include #include "util/trace.h" +#include "runtime/vm/translator/hopt/linearscan.h" namespace HPHP { namespace VM { @@ -29,19 +30,23 @@ class SSATmp; struct Block; struct AsmInfo; class Trace; +struct LifetimeInfo; // IRInstruction -void print(std::ostream& ostream, const IRInstruction*); +void print(std::ostream& ostream, const IRInstruction*, + const LifetimeInfo* lifetime = nullptr); void print(const IRInstruction*); -void printSrc(std::ostream& ostream, const IRInstruction*, uint32_t srcIndex); +void printSrc(std::ostream& ostream, const IRInstruction*, uint32_t srcIndex, + const LifetimeInfo* lifetime); // SSATmp void print(std::ostream& ostream, const SSATmp*, - bool printLastUse = false); + const LifetimeInfo* lifetime = nullptr, bool printLastUse = false); void print(const SSATmp*); // Trace void print(std::ostream& ostream, const Trace*, + const LifetimeInfo* lifetime = nullptr, const AsmInfo* asmInfo = nullptr); void print(const Trace*); @@ -54,8 +59,9 @@ static inline bool dumpIREnabled(int level = 1) { } void dumpTraceImpl(const Trace* trace, std::ostream& out, - const AsmInfo* asmInfo = nullptr); + const LifetimeInfo*, const AsmInfo*); void dumpTrace(int level, const Trace* trace, const char* caption, + const LifetimeInfo* lifetime = nullptr, AsmInfo* ai = nullptr); }}} diff --git a/hphp/runtime/vm/translator/hopt/state_vector.h b/hphp/runtime/vm/translator/hopt/state_vector.h index 8a653be1d..57b5a4108 100644 --- a/hphp/runtime/vm/translator/hopt/state_vector.h +++ b/hphp/runtime/vm/translator/hopt/state_vector.h @@ -61,8 +61,9 @@ struct StateVector { const_reference operator[](const Key& k) const { return (*this)[&k]; } const_reference operator[](const Key* k) const { - assert(factoryId(k) < m_info.size()); - return m_info[factoryId(k)]; + assert(factoryId(k) < count(m_factory, (Key*)nullptr)); + auto id = factoryId(k); + return id < m_info.size() ? m_info[id] : m_init; } iterator begin() { return m_info.begin(); }