Move m_id and m_lastUseId to LifetimeInfo
These two fields represent the results of liveness analysis, so group them together that way.
Esse commit está contido em:
@@ -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<TransBCMapping>* 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);
|
||||
|
||||
@@ -73,10 +73,12 @@ typedef StateVector<IRInstruction, RegSet> 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<TransBCMapping>* bcMap,
|
||||
TranslatorX64* tx64,
|
||||
AsmInfo *asmInfo = nullptr);
|
||||
const LifetimeInfo* lifetime = nullptr,
|
||||
AsmInfo* asmInfo = nullptr);
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -1907,29 +1907,34 @@ void TranslatorX64::hhirTraceCodeGen(vector<TransBCMapping>* 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<typename Inner, int DumpVal=4>
|
||||
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<SSATmp, int32_t> 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<IRInstruction*> 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<typename Inner, int DumpVal=4>
|
||||
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<IRInstruction, 4>(inst, "allocating to instruction");
|
||||
@@ -312,7 +319,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
|
||||
// <spillTmp>'s last use ID.
|
||||
// Replace <tmp> with <reloadTmp> in <inst>.
|
||||
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 <ssaTmp> 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) {
|
||||
// <coalesce> 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
|
||||
|
||||
@@ -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<IRInstruction, uint32_t> LinearIdVector;
|
||||
typedef StateVector<SSATmp, UseInfo> 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);
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -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<Marker>();
|
||||
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)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#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);
|
||||
|
||||
}}}
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário