Move register allocator fields out of SSATmp
These are not used except between linearscan and codegen, so put them in a StateVector instead.
Esse commit está contido em:
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -20,6 +20,7 @@
|
||||
#include <vector>
|
||||
#include "runtime/vm/translator/hopt/ir.h"
|
||||
#include "runtime/vm/translator/hopt/irfactory.h"
|
||||
#include "runtime/vm/translator/hopt/linearscan.h"
|
||||
#include "runtime/vm/translator/targetcache.h"
|
||||
#include "runtime/vm/translator/translator-x64.h"
|
||||
#include "runtime/vm/translator/hopt/state_vector.h"
|
||||
@@ -72,11 +73,12 @@ typedef StateVector<IRInstruction, RegSet> LiveRegs;
|
||||
// Stuff we need to preserve between blocks while generating code,
|
||||
// and address information produced during codegen.
|
||||
struct CodegenState {
|
||||
CodegenState(const IRFactory* factory, const LiveRegs& liveRegs,
|
||||
const LifetimeInfo* lifetime,
|
||||
CodegenState(const IRFactory* factory, const RegAllocInfo& regs,
|
||||
const LiveRegs& liveRegs, const LifetimeInfo* lifetime,
|
||||
AsmInfo* asmInfo)
|
||||
: patches(factory, nullptr)
|
||||
, lastMarker(nullptr)
|
||||
, regs(regs)
|
||||
, liveRegs(liveRegs)
|
||||
, lifetime(lifetime)
|
||||
, asmInfo(asmInfo)
|
||||
@@ -93,6 +95,9 @@ struct CodegenState {
|
||||
// next block in the same assmbler.
|
||||
bool noTerminalJmp_;
|
||||
|
||||
// output from register allocator
|
||||
const RegAllocInfo& regs;
|
||||
|
||||
// for each instruction, holds the RegSet of registers that must be
|
||||
// preserved across that instruction. This is for push/pop of caller-saved
|
||||
// registers.
|
||||
@@ -111,8 +116,8 @@ struct CodeGenerator {
|
||||
|
||||
CodeGenerator(Trace* trace, Asm& as, Asm& astubs, Transl::TranslatorX64* tx64,
|
||||
CodegenState& state)
|
||||
: m_as(as), m_astubs(astubs), m_tx64(tx64), m_state(state)
|
||||
, m_curInst(nullptr), m_curTrace(trace) {
|
||||
: m_as(as), m_astubs(astubs), m_tx64(tx64), m_state(state),
|
||||
m_regs(state.regs), m_curInst(nullptr), m_curTrace(trace) {
|
||||
}
|
||||
|
||||
void cgBlock(Block* block, vector<TransBCMapping>* bcMap);
|
||||
@@ -205,7 +210,7 @@ private:
|
||||
void (Asm::*intRR)(RegType, RegType),
|
||||
void (Asm::*mov)(RegType, RegType),
|
||||
void (Asm::*fpRR)(RegXMM, RegXMM),
|
||||
void (*extend)(Asm&, const SSATmp*),
|
||||
void (*extend)(Asm&, const SSATmp*, const RegisterInfo&),
|
||||
Oper,
|
||||
RegType (*conv)(PhysReg),
|
||||
Commutativity);
|
||||
@@ -214,7 +219,7 @@ private:
|
||||
void (Asm::*intImm)(Immed, RegType),
|
||||
void (Asm::*intRR)(RegType, RegType),
|
||||
void (Asm::*mov)(RegType, RegType),
|
||||
void (*extend)(Asm&, const SSATmp*),
|
||||
void (*extend)(Asm&, const SSATmp*, const RegisterInfo&),
|
||||
Oper,
|
||||
RegType (*conv)(PhysReg),
|
||||
Commutativity);
|
||||
@@ -353,6 +358,7 @@ private:
|
||||
Asm& m_astubs; // assembler for stubs and other cold code.
|
||||
TranslatorX64* m_tx64;
|
||||
CodegenState& m_state;
|
||||
const RegAllocInfo& m_regs;
|
||||
IRInstruction* m_curInst; // current instruction being generated
|
||||
Trace* m_curTrace;
|
||||
};
|
||||
@@ -389,7 +395,7 @@ private: // These should be created using ArgGroup.
|
||||
, m_done(false)
|
||||
{}
|
||||
|
||||
explicit ArgDesc(SSATmp* tmp, bool val = true);
|
||||
explicit ArgDesc(SSATmp* tmp, const RegisterInfo& info, bool val = true);
|
||||
|
||||
private:
|
||||
Kind m_kind;
|
||||
@@ -415,8 +421,8 @@ private:
|
||||
struct ArgGroup {
|
||||
typedef std::vector<ArgDesc> ArgVec;
|
||||
|
||||
ArgGroup()
|
||||
: m_override(nullptr)
|
||||
explicit ArgGroup(const RegAllocInfo& regs)
|
||||
: m_regs(regs), m_override(nullptr)
|
||||
{}
|
||||
|
||||
size_t numRegArgs() const { return m_regArgs.size(); }
|
||||
@@ -456,13 +462,13 @@ struct ArgGroup {
|
||||
}
|
||||
|
||||
ArgGroup& ssa(SSATmp* tmp) {
|
||||
push_arg(ArgDesc(tmp));
|
||||
push_arg(ArgDesc(tmp, m_regs[tmp]));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ArgGroup& ssas(IRInstruction* inst, unsigned begin, unsigned count = 1) {
|
||||
for (SSATmp* s : inst->getSrcs().subpiece(begin, count)) {
|
||||
push_arg(ArgDesc(s));
|
||||
push_arg(ArgDesc(s, m_regs[s]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -505,7 +511,7 @@ private:
|
||||
* For passing the m_type field of a TypedValue.
|
||||
*/
|
||||
ArgGroup& type(SSATmp* tmp) {
|
||||
push_arg(ArgDesc(tmp, false));
|
||||
push_arg(ArgDesc(tmp, m_regs[tmp], false));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -521,6 +527,7 @@ private:
|
||||
return typedValue(key);
|
||||
}
|
||||
|
||||
const RegAllocInfo& m_regs;
|
||||
ArgVec* m_override; // used to force args to go into a specific ArgVec
|
||||
ArgVec m_regArgs;
|
||||
ArgVec m_stkArgs;
|
||||
@@ -536,6 +543,7 @@ void genCodeForTrace(Trace* trace,
|
||||
IRFactory* irFactory,
|
||||
vector<TransBCMapping>* bcMap,
|
||||
TranslatorX64* tx64,
|
||||
const RegAllocInfo& regs,
|
||||
const LifetimeInfo* lifetime = nullptr,
|
||||
AsmInfo* asmInfo = nullptr);
|
||||
|
||||
|
||||
@@ -753,30 +753,6 @@ int SSATmp::numNeededRegs() const {
|
||||
return t.needsReg() ? 2 : 1;
|
||||
}
|
||||
|
||||
int SSATmp::numAllocatedRegs() const {
|
||||
// If an SSATmp is spilled, it must've actually had a full set of
|
||||
// registers allocated to it.
|
||||
if (m_isSpilled) return numNeededRegs();
|
||||
|
||||
// Return the number of register slots that actually have an
|
||||
// allocated register. We may not have allocated a full
|
||||
// numNeededRegs() worth of registers in some cases (if the value
|
||||
// of this tmp wasn't used, etc).
|
||||
int i = 0;
|
||||
while (i < kMaxNumRegs && m_regs[i] != InvalidReg) {
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
RegSet SSATmp::getRegs() const {
|
||||
RegSet regs;
|
||||
for (int i = 0, n = numAllocatedRegs(); i < n; ++i) {
|
||||
if (hasReg(i)) regs.add(getReg(i));
|
||||
}
|
||||
return regs;
|
||||
}
|
||||
|
||||
bool SSATmp::getValBool() const {
|
||||
assert(isConst());
|
||||
assert(m_inst->getTypeParam().equals(Type::Bool));
|
||||
|
||||
@@ -1618,6 +1618,7 @@ class IRFactory;
|
||||
class Simplifier;
|
||||
struct Block;
|
||||
struct LifetimeInfo;
|
||||
struct RegAllocInfo;
|
||||
|
||||
bool isRefCounted(SSATmp* opnd);
|
||||
|
||||
@@ -1927,30 +1928,6 @@ Type outputType(const IRInstruction*, int dstId = 0);
|
||||
*/
|
||||
void assertOperandTypes(const IRInstruction*);
|
||||
|
||||
struct SpillInfo {
|
||||
enum Type { Memory }; // Currently only one type of spill supported.
|
||||
|
||||
explicit SpillInfo(uint32_t v) : m_type(Memory), m_val(v) {}
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
// return offset in 8-byte-words from stack pointer
|
||||
uint32_t mem() const { assert(m_type == Memory); return m_val; }
|
||||
|
||||
private:
|
||||
Type m_type : 1;
|
||||
uint32_t m_val : 31;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, SpillInfo si) {
|
||||
switch (si.type()) {
|
||||
case SpillInfo::Memory:
|
||||
os << "spill[" << si.mem() << "]";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
class SSATmp {
|
||||
public:
|
||||
uint32_t getId() const { return m_id; }
|
||||
@@ -1958,7 +1935,6 @@ public:
|
||||
void setInstruction(IRInstruction* i) { m_inst = i; }
|
||||
Type type() const { return m_type; }
|
||||
void setType(Type t) { m_type = t; }
|
||||
bool isSpilled() const { return m_isSpilled; }
|
||||
bool isBoxed() const { return type().isBoxed(); }
|
||||
bool isString() const { return isA(Type::Str); }
|
||||
bool isArray() const { return isA(Type::Arr); }
|
||||
@@ -2002,17 +1978,6 @@ public:
|
||||
return type().subtypeOf(tag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not a given register index is allocated to a
|
||||
* register, or returns false if it is spilled.
|
||||
*
|
||||
* Right now, we only spill both at the same time and only Spill and
|
||||
* Reload instructions need to deal with SSATmps that are spilled.
|
||||
*/
|
||||
bool hasReg(uint32_t i = 0) const {
|
||||
return !m_isSpilled && m_regs[i] != Transl::InvalidReg;
|
||||
}
|
||||
|
||||
/*
|
||||
* The maximum number of registers this SSATmp may need allocated.
|
||||
* This is based on the type of the temporary (some types never have
|
||||
@@ -2020,36 +1985,6 @@ public:
|
||||
*/
|
||||
int numNeededRegs() const;
|
||||
|
||||
/*
|
||||
* The number of regs actually allocated to this SSATmp. This might
|
||||
* end up fewer than numNeededRegs if the SSATmp isn't really
|
||||
* being used.
|
||||
*/
|
||||
int numAllocatedRegs() const;
|
||||
|
||||
/*
|
||||
* Access to allocated registers.
|
||||
*
|
||||
* Returns InvalidReg for slots that aren't allocated.
|
||||
*/
|
||||
PhysReg getReg() const { assert(!m_isSpilled); return m_regs[0]; }
|
||||
PhysReg getReg(uint32_t i) const { assert(!m_isSpilled); return m_regs[i]; }
|
||||
void setReg(PhysReg reg, uint32_t i) { m_regs[i] = reg; }
|
||||
RegSet getRegs() const;
|
||||
|
||||
/*
|
||||
* Returns information about how to spill/fill a SSATmp.
|
||||
*
|
||||
* These functions are only valid if this SSATmp is being spilled or
|
||||
* filled. In all normal instructions (i.e. other than Spill and
|
||||
* Reload), SSATmps are assigned registers instead of spill
|
||||
* locations.
|
||||
*/
|
||||
void setSpillInfo(int idx, SpillInfo si) { m_spillInfo[idx] = si;
|
||||
m_isSpilled = true; }
|
||||
SpillInfo getSpillInfo(int idx) const { assert(m_isSpilled);
|
||||
return m_spillInfo[idx]; }
|
||||
|
||||
private:
|
||||
friend class IRFactory;
|
||||
friend class TraceBuilder;
|
||||
@@ -2060,29 +1995,13 @@ private:
|
||||
: m_inst(i)
|
||||
, m_type(outputType(i, dstId))
|
||||
, m_id(opndId)
|
||||
, m_isSpilled(false)
|
||||
{
|
||||
m_regs[0] = m_regs[1] = Transl::InvalidReg;
|
||||
}
|
||||
{}
|
||||
SSATmp(const SSATmp&);
|
||||
SSATmp& operator=(const SSATmp&);
|
||||
|
||||
IRInstruction* m_inst;
|
||||
Type m_type; // type when defined
|
||||
uint32_t m_id;
|
||||
bool m_isSpilled;
|
||||
|
||||
/*
|
||||
* m_regs[0] is always the value of this SSATmp.
|
||||
*
|
||||
* Cell or Gen types use two registers: m_regs[1] is the runtime
|
||||
* type.
|
||||
*/
|
||||
static const int kMaxNumRegs = 2;
|
||||
union {
|
||||
PhysReg m_regs[kMaxNumRegs];
|
||||
SpillInfo m_spillInfo[kMaxNumRegs];
|
||||
};
|
||||
const uint32_t m_id;
|
||||
};
|
||||
|
||||
int vectorBaseIdx(Opcode opc);
|
||||
|
||||
@@ -1911,11 +1911,11 @@ void TranslatorX64::hhirTraceCodeGen(vector<TransBCMapping>* bcMap) {
|
||||
assert(m_useHHIR);
|
||||
|
||||
Trace* trace = m_hhbcTrans->getTrace();
|
||||
auto finishPass = [&](const char* msg,
|
||||
int level,
|
||||
auto finishPass = [&](const char* msg, int level,
|
||||
const RegAllocInfo* regs = nullptr,
|
||||
const LifetimeInfo* lifetime = nullptr) {
|
||||
assert(checkCfg(trace, *m_irFactory));
|
||||
dumpTrace(level, trace, msg, lifetime);
|
||||
dumpTrace(level, trace, msg, regs, lifetime);
|
||||
};
|
||||
|
||||
finishPass(" after initial translation ", kIRLevel);
|
||||
@@ -1924,22 +1924,23 @@ void TranslatorX64::hhirTraceCodeGen(vector<TransBCMapping>* bcMap) {
|
||||
|
||||
auto* factory = m_irFactory.get();
|
||||
if (dumpIREnabled() || RuntimeOption::EvalJitCompareHHIR) {
|
||||
LifetimeInfo lifetime(m_irFactory.get());
|
||||
allocRegsForTrace(trace, m_irFactory.get(), &lifetime);
|
||||
LifetimeInfo lifetime(factory);
|
||||
RegAllocInfo regs = allocRegsForTrace(trace, factory, &lifetime);
|
||||
AsmInfo ai(factory);
|
||||
genCodeForTrace(trace, a, astubs, factory, bcMap, this,
|
||||
&lifetime, &ai);
|
||||
finishPass(" after reg alloc ", kRegAllocLevel, &lifetime);
|
||||
genCodeForTrace(trace, a, astubs, factory, bcMap, this, regs,
|
||||
&lifetime, &ai);
|
||||
finishPass(" after reg alloc ", kRegAllocLevel, ®s, &lifetime);
|
||||
if (RuntimeOption::EvalJitCompareHHIR) {
|
||||
std::ostringstream out;
|
||||
dumpTraceImpl(trace, out, &lifetime, &ai);
|
||||
dumpTraceImpl(trace, out, ®s, &lifetime, &ai);
|
||||
m_lastHHIRDump = out.str();
|
||||
} else {
|
||||
dumpTrace(kCodeGenLevel, trace, " after code gen ", &lifetime, &ai);
|
||||
dumpTrace(kCodeGenLevel, trace, " after code gen ", ®s,
|
||||
&lifetime, &ai);
|
||||
}
|
||||
} else {
|
||||
allocRegsForTrace(trace, m_irFactory.get());
|
||||
genCodeForTrace(trace, a, astubs, factory, bcMap, this);
|
||||
RegAllocInfo regs = allocRegsForTrace(trace, factory);
|
||||
genCodeForTrace(trace, a, astubs, factory, bcMap, this, regs);
|
||||
finishPass(" after reg alloc ", kRegAllocLevel);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,31 @@ using namespace Transl::reg;
|
||||
|
||||
static const HPHP::Trace::Module TRACEMOD = HPHP::Trace::hhir;
|
||||
|
||||
int RegisterInfo::numAllocatedRegs() const {
|
||||
// Return the number of register slots that actually have an allocated
|
||||
// register or spill slot. We may not have allocated a full numNeededRegs()
|
||||
// worth of registers in some cases (if the value of this tmp wasn't used).
|
||||
// We rely on InvalidReg (-1) never being equal to a spill slot number.
|
||||
int i = 0;
|
||||
while (i < kMaxNumRegs && m_regs[i] != InvalidReg) {
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
RegSet RegisterInfo::getRegs() const {
|
||||
RegSet regs;
|
||||
for (int i = 0, n = numAllocatedRegs(); i < n; ++i) {
|
||||
if (hasReg(i)) regs.add(getReg(i));
|
||||
}
|
||||
return regs;
|
||||
}
|
||||
|
||||
struct LinearScan : private boost::noncopyable {
|
||||
static const int NumRegs = 16;
|
||||
|
||||
explicit LinearScan(IRFactory*);
|
||||
void allocRegs(Trace*, LifetimeInfo* lifetime);
|
||||
RegAllocInfo allocRegs(Trace*, LifetimeInfo*);
|
||||
|
||||
private:
|
||||
class RegState {
|
||||
@@ -149,7 +169,7 @@ private:
|
||||
void dumpIR(const Inner* in, const char* msg) {
|
||||
if (dumpIREnabled(DumpVal)) {
|
||||
std::ostringstream str;
|
||||
print(str, in, &m_lifetime);
|
||||
print(str, in, &m_allocInfo, &m_lifetime);
|
||||
HPHP::Trace::traceRelease("--- %s: %s\n", msg, str.str().c_str());
|
||||
}
|
||||
}
|
||||
@@ -188,6 +208,8 @@ private:
|
||||
// a source.
|
||||
typedef smart::vector<IRInstruction*> JmpList;
|
||||
StateVector<SSATmp, JmpList> m_jmps;
|
||||
|
||||
RegAllocInfo m_allocInfo; // final allocation for each SSATmp
|
||||
};
|
||||
|
||||
// This value must be consistent with the number of pre-allocated
|
||||
@@ -232,8 +254,8 @@ void LinearScan::StateSave::restore(LinearScan* ls) {
|
||||
if (reg->isReserved()) continue;
|
||||
if (reg->isAllocated()) {
|
||||
SSATmp* tmp = reg->m_ssaTmp;
|
||||
for (int r = 0; r < tmp->numAllocatedRegs(); r++) {
|
||||
if ((int)tmp->getReg(r) == i) {
|
||||
for (int r = 0; r < ls->m_allocInfo[tmp].numAllocatedRegs(); r++) {
|
||||
if ((int)ls->m_allocInfo[tmp].getReg(r) == i) {
|
||||
ls->allocRegToTmp(reg, tmp, r);
|
||||
}
|
||||
}
|
||||
@@ -250,6 +272,7 @@ LinearScan::LinearScan(IRFactory* irFactory)
|
||||
, m_linear(m_lifetime.linear)
|
||||
, m_uses(m_lifetime.uses)
|
||||
, m_jmps(irFactory, JmpList())
|
||||
, m_allocInfo(irFactory)
|
||||
{
|
||||
for (int i = 0; i < kNumX64Regs; i++) {
|
||||
m_regs[i].m_ssaTmp = nullptr;
|
||||
@@ -298,8 +321,8 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
|
||||
inst->setSrc(i, tmp);
|
||||
}
|
||||
if (!needsReloading[i]) {
|
||||
for (int i = 0, n = tmp->numAllocatedRegs(); i < n; ++i) {
|
||||
m_regs[int(tmp->getReg(i))].m_pinned = true;
|
||||
for (int i = 0, n = m_allocInfo[tmp].numAllocatedRegs(); i < n; ++i) {
|
||||
m_regs[int(m_allocInfo[tmp].getReg(i))].m_pinned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,7 +348,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
|
||||
// reloadTmp and tmp share the same type. Since it was spilled, it
|
||||
// must be using its entire needed-count of registers.
|
||||
assert(reloadTmp->type() == tmp->type());
|
||||
assert(tmp->numNeededRegs() == tmp->numAllocatedRegs());
|
||||
assert(tmp->numNeededRegs() == m_allocInfo[tmp].numAllocatedRegs());
|
||||
for (int locIndex = 0; locIndex < tmp->numNeededRegs(); ++locIndex) {
|
||||
allocRegToTmp(reloadTmp, locIndex);
|
||||
}
|
||||
@@ -480,7 +503,7 @@ void LinearScan::allocRegToTmp(SSATmp* ssaTmp, uint32_t index) {
|
||||
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);
|
||||
m_allocInfo[ssaTmp].setReg(PhysReg(reg->m_regNo), index);
|
||||
uint32_t lastUseId = m_uses[ssaTmp].lastUse;
|
||||
if (reg->isReserved()) {
|
||||
return;
|
||||
@@ -531,7 +554,7 @@ uint32_t LinearScan::assignSpillLoc() {
|
||||
TRACE(3, "[counter] 1 spill a tmp that spans native\n");
|
||||
}
|
||||
|
||||
dst->setSpillInfo(locIndex, SpillInfo(nextSpillLoc++));
|
||||
m_allocInfo[dst].setSpillInfo(locIndex, SpillInfo(nextSpillLoc++));
|
||||
TRACE(3, "[counter] 1 spill\n");
|
||||
}
|
||||
}
|
||||
@@ -754,14 +777,14 @@ void LinearScan::computePreColoringHint() {
|
||||
// Given a label, dest index for that label, and register index, scan
|
||||
// the sources of all incoming Jmp_s to see if any have a register
|
||||
// allocated at the specified index.
|
||||
static RegNumber findLabelSrcReg(IRInstruction* label, unsigned dstIdx,
|
||||
uint32_t regIndex) {
|
||||
static RegNumber findLabelSrcReg(const RegAllocInfo& regs, IRInstruction* label,
|
||||
unsigned dstIdx, uint32_t regIndex) {
|
||||
assert(label->op() == DefLabel);
|
||||
SSATmp* withReg = label->getBlock()->findSrc(dstIdx, [&](SSATmp* src) {
|
||||
return src->getReg(regIndex) != InvalidReg &&
|
||||
return regs[src].getReg(regIndex) != InvalidReg &&
|
||||
src->inst()->getBlock()->getHint() != Block::Unlikely;
|
||||
});
|
||||
return withReg ? withReg->getReg(regIndex) : reg::noreg;
|
||||
return withReg ? regs[withReg].getReg(regIndex) : reg::noreg;
|
||||
}
|
||||
|
||||
// This function attempts to find a pre-coloring hint from two
|
||||
@@ -779,7 +802,7 @@ RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex,
|
||||
// If we're precoloring a Reload of a temp that we'd normally find
|
||||
// a hint for, just return the register allocated to the spilled
|
||||
// temp.
|
||||
auto reg = tmp->getReg(regIndex);
|
||||
auto reg = m_allocInfo[tmp].getReg(regIndex);
|
||||
assert(reg != reg::noreg);
|
||||
return reg;
|
||||
}
|
||||
@@ -788,7 +811,7 @@ RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex,
|
||||
// Figure out which dst of the label is tmp
|
||||
for (unsigned i = 0, n = srcInst->getNumDsts(); i < n; ++i) {
|
||||
if (srcInst->getDst(i) == tmp) {
|
||||
auto reg = findLabelSrcReg(srcInst, i, regIndex);
|
||||
auto reg = findLabelSrcReg(m_allocInfo, srcInst, i, regIndex);
|
||||
// Until we handle loops, it's a bug to try and allocate a
|
||||
// register to a DefLabel's dest before all of its incoming
|
||||
// Jmp_s have had their srcs allocated.
|
||||
@@ -812,8 +835,9 @@ RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex,
|
||||
if (tmp == src) {
|
||||
// For now, a DefLabel should never have a register assigned
|
||||
// to it before any of its incoming Jmp_ instructions.
|
||||
always_assert(label->getDst(si)->getReg(regIndex) == reg::noreg);
|
||||
auto reg = findLabelSrcReg(label, si, regIndex);
|
||||
always_assert(m_allocInfo[label->getDst(si)].getReg(regIndex) ==
|
||||
reg::noreg);
|
||||
auto reg = findLabelSrcReg(m_allocInfo, label, si, regIndex);
|
||||
if (reg != reg::noreg) return reg;
|
||||
}
|
||||
}
|
||||
@@ -850,32 +874,29 @@ void LinearScan::coalesce(Trace* trace) {
|
||||
void LinearScan::preAllocSpillLoc(uint32_t numSpillLocs) {
|
||||
for (Block* block : m_blocks) {
|
||||
for (IRInstruction& inst : *block) {
|
||||
if (inst.op() == Spill) {
|
||||
SSATmp* dst = inst.getDst();
|
||||
for (int index = 0; index < dst->numNeededRegs(); ++index) {
|
||||
assert(!dst->hasReg(index));
|
||||
if (dst->getSpillInfo(index).type() == SpillInfo::Memory) {
|
||||
uint32_t spillLoc = dst->getSpillInfo(index).mem();
|
||||
// Native stack layout:
|
||||
// | |
|
||||
// +---------------+
|
||||
// | | <-- spill[5..]
|
||||
// | pre allocated | <-- spill[4]
|
||||
// | (16 slots) | <-- spill[3]
|
||||
// +---------------+
|
||||
// | return addr |
|
||||
// +---------------+
|
||||
// | extra | <-- spill[2]
|
||||
// | spill | <-- spill[1]
|
||||
// | locations | <-- spill[0]
|
||||
// +---------------+ <-- %rsp
|
||||
// If a spill location falls into the pre-allocated region, we
|
||||
// need to increase its index by 1 to avoid overwriting the
|
||||
// return address.
|
||||
if (spillLoc + NumPreAllocatedSpillLocs >= numSpillLocs) {
|
||||
dst->setSpillInfo(index, SpillInfo(spillLoc + 1));
|
||||
}
|
||||
}
|
||||
if (inst.op() != Spill) continue;
|
||||
SSATmp* dst = inst.getDst();
|
||||
for (int index = 0; index < dst->numNeededRegs(); ++index) {
|
||||
assert(!m_allocInfo[dst].hasReg(index));
|
||||
uint32_t spillLoc = m_allocInfo[dst].getSpillInfo(index).mem();
|
||||
// Native stack layout:
|
||||
// | |
|
||||
// +---------------+
|
||||
// | | <-- spill[5..]
|
||||
// | pre allocated | <-- spill[4]
|
||||
// | (16 slots) | <-- spill[3]
|
||||
// +---------------+
|
||||
// | return addr |
|
||||
// +---------------+
|
||||
// | extra | <-- spill[2]
|
||||
// | spill | <-- spill[1]
|
||||
// | locations | <-- spill[0]
|
||||
// +---------------+ <-- %rsp
|
||||
// If a spill location falls into the pre-allocated region, we
|
||||
// need to increase its index by 1 to avoid overwriting the
|
||||
// return address.
|
||||
if (spillLoc + NumPreAllocatedSpillLocs >= numSpillLocs) {
|
||||
m_allocInfo[dst].setSpillInfo(index, SpillInfo(spillLoc + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,7 +981,7 @@ void LinearScan::genSpillStats(Trace* trace, int numSpillLocs) {
|
||||
|
||||
}
|
||||
|
||||
void LinearScan::allocRegs(Trace* trace, LifetimeInfo* lifetime) {
|
||||
RegAllocInfo LinearScan::allocRegs(Trace* trace, LifetimeInfo* lifetime) {
|
||||
if (RuntimeOption::EvalHHIREnableCoalescing) {
|
||||
// <coalesce> doesn't need instruction numbering.
|
||||
coalesce(trace);
|
||||
@@ -1007,6 +1028,7 @@ void LinearScan::allocRegs(Trace* trace, LifetimeInfo* lifetime) {
|
||||
lifetime->linear = std::move(m_linear);
|
||||
lifetime->uses = std::move(m_uses);
|
||||
}
|
||||
return m_allocInfo;
|
||||
}
|
||||
|
||||
void LinearScan::allocRegsOneTrace(BlockList::iterator& blockIt,
|
||||
@@ -1104,7 +1126,7 @@ void LinearScan::allocRegsToTrace() {
|
||||
void LinearScan::rematerialize() {
|
||||
numberInstructions(m_blocks);
|
||||
dumpTrace(kExtraLevel, m_blocks.front()->getTrace(),
|
||||
" before rematerialization ", &m_lifetime);
|
||||
" before rematerialization ", &m_allocInfo, &m_lifetime);
|
||||
rematerializeAux();
|
||||
numberInstructions(m_blocks);
|
||||
// We only replaced Reloads in rematerializeAux().
|
||||
@@ -1113,7 +1135,8 @@ void LinearScan::rematerialize() {
|
||||
}
|
||||
|
||||
// Return true if it's safe to rematerialize inst at reload's position.
|
||||
bool srcsAreLive(IRInstruction* inst, IRInstruction* reload) {
|
||||
bool srcsAreLive(const RegAllocInfo& regs, IRInstruction* inst,
|
||||
IRInstruction* reload) {
|
||||
// It's only ok to rematerialize an instruction if its sources are
|
||||
// guaranteed to be in known registers at the point we would other wise
|
||||
// reload its spilled result.
|
||||
@@ -1125,8 +1148,8 @@ bool srcsAreLive(IRInstruction* inst, IRInstruction* reload) {
|
||||
// sources because they're implicitly turned into immediates by every
|
||||
// instruction that uses them.
|
||||
for (SSATmp* src : inst->getSrcs()) {
|
||||
if (!src->hasReg(0) && src->inst()->op() != DefConst) return false;
|
||||
auto reg = src->getReg(0);
|
||||
if (!regs[src].hasReg(0) && src->inst()->op() != DefConst) return false;
|
||||
auto reg = regs[src].getReg(0);
|
||||
if (reg != rVmSp && reg != rVmFp) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1192,7 +1215,7 @@ void LinearScan::rematerializeAux() {
|
||||
IRInstruction& inst = *it;
|
||||
Opcode opc = inst.op();
|
||||
if (opc == DefFP || opc == FreeActRec) {
|
||||
assert(inst.getDst()->getReg() == rVmFp);
|
||||
assert(m_allocInfo[inst.getDst()].getReg() == rVmFp);
|
||||
curFp = inst.getDst();
|
||||
} else if (opc == Reload) {
|
||||
// s = Spill t0
|
||||
@@ -1207,7 +1230,7 @@ void LinearScan::rematerializeAux() {
|
||||
// XXX: could change <newInst> to the non-check version.
|
||||
// Rematerialize those rematerializable instructions (i.e.,
|
||||
// isRematerializable returns true) and LdStack.
|
||||
if (srcsAreLive(spilledInst, &inst)) {
|
||||
if (srcsAreLive(m_allocInfo, spilledInst, &inst)) {
|
||||
newInst = spilledInst->clone(m_irFactory);
|
||||
// The new instruction needn't have an exit label; it must always
|
||||
// be dominated by the original instruction because reloads are
|
||||
@@ -1240,7 +1263,7 @@ void LinearScan::rematerializeAux() {
|
||||
}
|
||||
|
||||
// Updating curSp and localValues
|
||||
if (inst.hasDst() && inst.getDst()->getReg() == rVmSp) {
|
||||
if (inst.hasDst() && m_allocInfo[inst.getDst()].getReg() == rVmSp) {
|
||||
// inst modifies the stack pointer.
|
||||
curSp = inst.getDst();
|
||||
}
|
||||
@@ -1283,7 +1306,7 @@ void LinearScan::removeUnusedSpills() {
|
||||
// reg. We pick LdLoc and IncRef because they occur often.
|
||||
if (srcOpc == IncRef || srcOpc == LdLoc) {
|
||||
for (int i = 0, n = src->numNeededRegs(); i < n; ++i) {
|
||||
src->setReg(InvalidReg, i);
|
||||
m_allocInfo[src].setReg(InvalidReg, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1402,8 +1425,8 @@ LinearScan::RegState* LinearScan::popFreeReg(smart::list<RegState*>& freeList) {
|
||||
void LinearScan::spill(SSATmp* tmp) {
|
||||
dumpIR<SSATmp, kExtraLevel>(tmp, "spilling");
|
||||
// If we're spilling, we better actually have registers allocated.
|
||||
assert(tmp->numAllocatedRegs() > 0);
|
||||
assert(tmp->numAllocatedRegs() == tmp->numNeededRegs());
|
||||
assert(m_allocInfo[tmp].numAllocatedRegs() > 0);
|
||||
assert(m_allocInfo[tmp].numAllocatedRegs() == tmp->numNeededRegs());
|
||||
|
||||
// Free the registers used by <tmp>.
|
||||
// Need call freeReg and modify <m_allocatedRegs>.
|
||||
@@ -1503,9 +1526,9 @@ void LinearScan::PreColoringHint::add(SSATmp* tmp, uint32_t index, int argNum) {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void allocRegsForTrace(Trace* trace, IRFactory* irFactory,
|
||||
LifetimeInfo* lifetime) {
|
||||
LinearScan(irFactory).allocRegs(trace, lifetime);
|
||||
RegAllocInfo allocRegsForTrace(Trace* trace, IRFactory* irFactory,
|
||||
LifetimeInfo* lifetime) {
|
||||
return LinearScan(irFactory).allocRegs(trace, lifetime);
|
||||
}
|
||||
|
||||
}}} // HPHP::VM::JIT
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#define incl_HPHP_VM_LINEAR_SCAN_H_
|
||||
|
||||
#include "runtime/vm/translator/hopt/state_vector.h"
|
||||
#include "runtime/vm/translator/physreg.h"
|
||||
#include "runtime/vm/translator/abi-x64.h"
|
||||
|
||||
namespace HPHP { namespace VM { namespace JIT {
|
||||
|
||||
@@ -50,11 +52,125 @@ struct LifetimeInfo {
|
||||
UsesVector uses; // last use id and use count of each tmp
|
||||
};
|
||||
|
||||
// Information about one spilled value.
|
||||
struct SpillInfo {
|
||||
explicit SpillInfo(uint32_t v) : m_val(v) {
|
||||
assert(isValid());
|
||||
}
|
||||
|
||||
// return offset in 8-byte-words from stack pointer
|
||||
uint32_t mem() const { return m_val; }
|
||||
|
||||
bool isValid() const { return int(m_val) != int(Transl::InvalidReg); }
|
||||
|
||||
private:
|
||||
uint32_t m_val;
|
||||
};
|
||||
|
||||
// Register allocation info about one SSATmp
|
||||
class RegisterInfo {
|
||||
enum { kMaxNumRegs = 2 };
|
||||
|
||||
public:
|
||||
RegisterInfo() : m_isSpilled(false) {
|
||||
m_regs[0] = m_regs[1] = Transl::InvalidReg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not a given register index is allocated to a
|
||||
* register, or returns false if it is spilled.
|
||||
*
|
||||
* Right now, we only spill both at the same time and only Spill and
|
||||
* Reload instructions need to deal with SSATmps that are spilled.
|
||||
*/
|
||||
bool hasReg(uint32_t i = 0) const {
|
||||
return !m_isSpilled && m_regs[i] != Transl::InvalidReg;
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of regs actually allocated to this SSATmp. This might
|
||||
* end up fewer than numNeededRegs if the SSATmp isn't really
|
||||
* being used.
|
||||
*/
|
||||
int numAllocatedRegs() const;
|
||||
|
||||
/*
|
||||
* Access to allocated registers.
|
||||
*
|
||||
* Returns InvalidReg for slots that aren't allocated.
|
||||
*/
|
||||
PhysReg getReg() const {
|
||||
assert(!m_isSpilled);
|
||||
return m_regs[0];
|
||||
}
|
||||
|
||||
PhysReg getReg(uint32_t i) const {
|
||||
assert(!m_isSpilled);
|
||||
return m_regs[i];
|
||||
}
|
||||
|
||||
void setReg(PhysReg reg, uint32_t i) {
|
||||
assert(!m_isSpilled);
|
||||
m_regs[i] = reg;
|
||||
}
|
||||
|
||||
bool spilled() const {
|
||||
return m_isSpilled;
|
||||
}
|
||||
|
||||
/* Returns the set of registers in this RegisterInfo */
|
||||
RegSet getRegs() const;
|
||||
|
||||
/*
|
||||
* Returns information about how to spill/fill a SSATmp.
|
||||
*
|
||||
* These functions are only valid if this SSATmp is being spilled or
|
||||
* filled. In all normal instructions (i.e. other than Spill and
|
||||
* Reload), SSATmps are assigned registers instead of spill
|
||||
* locations.
|
||||
*/
|
||||
void setSpillInfo(int i, SpillInfo si) {
|
||||
assert(si.isValid());
|
||||
m_spillInfo[i] = si;
|
||||
m_isSpilled = true;
|
||||
}
|
||||
|
||||
SpillInfo getSpillInfo(int idx) const {
|
||||
assert(m_isSpilled);
|
||||
return m_spillInfo[idx];
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isSpilled;
|
||||
union {
|
||||
PhysReg m_regs[kMaxNumRegs];
|
||||
SpillInfo m_spillInfo[kMaxNumRegs];
|
||||
};
|
||||
};
|
||||
|
||||
struct RegAllocInfo {
|
||||
explicit RegAllocInfo(const IRFactory* factory)
|
||||
: m_regs(factory, RegisterInfo()) {}
|
||||
RegAllocInfo(const RegAllocInfo& other) : m_regs(other.m_regs) {}
|
||||
RegAllocInfo(RegAllocInfo&& other) : m_regs(other.m_regs) {}
|
||||
RegisterInfo& operator[](const SSATmp* k) { return m_regs[k]; }
|
||||
RegisterInfo& operator[](const SSATmp& k) { return m_regs[k]; }
|
||||
const RegisterInfo& operator[](const SSATmp* k) const { return m_regs[k]; }
|
||||
const RegisterInfo& operator[](const SSATmp& k) const { return m_regs[k]; }
|
||||
private:
|
||||
StateVector<SSATmp, RegisterInfo> m_regs;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, SpillInfo si) {
|
||||
os << "spill[" << si.mem() << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
/*
|
||||
* The main entry point for register allocation. Called prior to code
|
||||
* generation.
|
||||
*/
|
||||
void allocRegsForTrace(Trace*, IRFactory*, LifetimeInfo* lifetime = nullptr);
|
||||
RegAllocInfo allocRegsForTrace(Trace*, IRFactory*, LifetimeInfo* = nullptr);
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -66,27 +66,27 @@ void printOpcode(std::ostream& os, const IRInstruction* inst) {
|
||||
}
|
||||
|
||||
void printDst(std::ostream& os, const IRInstruction* inst,
|
||||
const LifetimeInfo* lifetime) {
|
||||
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
|
||||
if (inst->getNumDsts() == 0) return;
|
||||
|
||||
const char* sep = "";
|
||||
for (const SSATmp& dst : inst->getDsts()) {
|
||||
os << punc(sep);
|
||||
print(os, &dst, lifetime, true);
|
||||
print(os, &dst, regs, lifetime, true);
|
||||
sep = ", ";
|
||||
}
|
||||
os << punc(" = ");
|
||||
}
|
||||
|
||||
void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i,
|
||||
const LifetimeInfo* lifetime) {
|
||||
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
|
||||
SSATmp* src = inst->getSrc(i);
|
||||
if (src != nullptr) {
|
||||
if (lifetime && lifetime->linear[inst] != 0 && !src->isConst() &&
|
||||
lifetime->uses[src].lastUse == lifetime->linear[inst]) {
|
||||
ostream << "~";
|
||||
}
|
||||
print(ostream, src, lifetime);
|
||||
print(ostream, src, regs, lifetime);
|
||||
} else {
|
||||
ostream << color(ANSI_COLOR_RED)
|
||||
<< "!!!NULL @ " << i
|
||||
@@ -96,6 +96,7 @@ void printSrc(std::ostream& ostream, const IRInstruction* inst, uint32_t i,
|
||||
}
|
||||
|
||||
void printSrcs(std::ostream& os, const IRInstruction* inst,
|
||||
const RegAllocInfo* regs,
|
||||
const LifetimeInfo* lifetime) {
|
||||
bool first = true;
|
||||
if (inst->op() == IncStat) {
|
||||
@@ -110,7 +111,7 @@ void printSrcs(std::ostream& os, const IRInstruction* inst,
|
||||
os << " ";
|
||||
first = false;
|
||||
}
|
||||
printSrc(os, inst, i, lifetime);
|
||||
printSrc(os, inst, i, regs, lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +123,7 @@ void printLabel(std::ostream& os, const Block* block) {
|
||||
}
|
||||
|
||||
void print(std::ostream& ostream, const IRInstruction* inst,
|
||||
const LifetimeInfo* lifetime) {
|
||||
const RegAllocInfo* regs, const LifetimeInfo* lifetime) {
|
||||
if (inst->op() == Marker) {
|
||||
auto* marker = inst->getExtra<Marker>();
|
||||
ostream << color(ANSI_COLOR_BLUE)
|
||||
@@ -144,9 +145,9 @@ void print(std::ostream& ostream, const IRInstruction* inst,
|
||||
}
|
||||
ostream << color(ANSI_COLOR_END);
|
||||
}
|
||||
printDst(ostream, inst, lifetime);
|
||||
printDst(ostream, inst, regs, lifetime);
|
||||
printOpcode(ostream, inst);
|
||||
printSrcs(ostream, inst, lifetime);
|
||||
printSrcs(ostream, inst, regs, lifetime);
|
||||
|
||||
if (Block* taken = inst->getTaken()) {
|
||||
ostream << punc(" -> ");
|
||||
@@ -227,8 +228,8 @@ static void printConst(std::ostream& os, IRInstruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void print(std::ostream& os, const SSATmp* tmp, const LifetimeInfo* lifetime,
|
||||
bool printLastUse) {
|
||||
void print(std::ostream& os, const SSATmp* tmp, const RegAllocInfo* regs,
|
||||
const LifetimeInfo* lifetime, bool printLastUse) {
|
||||
if (tmp->inst()->op() == DefConst) {
|
||||
printConst(os, tmp->inst());
|
||||
return;
|
||||
@@ -241,20 +242,23 @@ void print(std::ostream& os, const SSATmp* tmp, const LifetimeInfo* lifetime,
|
||||
<< "@" << lifetime->uses[tmp].lastUse << "#" << lifetime->uses[tmp].count
|
||||
<< color(ANSI_COLOR_END);
|
||||
}
|
||||
if (tmp->isSpilled() || tmp->numAllocatedRegs() > 0) {
|
||||
os << color(ANSI_COLOR_BROWN) << '(';
|
||||
if (!tmp->isSpilled()) {
|
||||
for (int i = 0, sz = tmp->numAllocatedRegs(); i < sz; ++i) {
|
||||
if (i != 0) os << ",";
|
||||
os << reg::regname(Reg64(tmp->getReg(i)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, sz = tmp->numNeededRegs(); i < sz; ++i) {
|
||||
if (i != 0) os << ",";
|
||||
os << tmp->getSpillInfo(i);
|
||||
if (regs) {
|
||||
const RegisterInfo& info = (*regs)[tmp];
|
||||
if (info.spilled() || info.numAllocatedRegs() > 0) {
|
||||
os << color(ANSI_COLOR_BROWN) << '(';
|
||||
if (!info.spilled()) {
|
||||
for (int i = 0, sz = info.numAllocatedRegs(); i < sz; ++i) {
|
||||
if (i != 0) os << ",";
|
||||
os << reg::regname(Reg64(info.getReg(i)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, sz = tmp->numNeededRegs(); i < sz; ++i) {
|
||||
if (i != 0) os << ",";
|
||||
os << info.getSpillInfo(i);
|
||||
}
|
||||
}
|
||||
os << ')' << color(ANSI_COLOR_END);
|
||||
}
|
||||
os << ')' << color(ANSI_COLOR_END);
|
||||
}
|
||||
os << punc(":")
|
||||
<< color(ANSI_COLOR_GREEN)
|
||||
@@ -272,8 +276,8 @@ void print(const Trace* trace) {
|
||||
print(std::cout, trace);
|
||||
}
|
||||
|
||||
void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime,
|
||||
const AsmInfo* asmInfo) {
|
||||
void print(std::ostream& os, const Trace* trace, const RegAllocInfo* regs,
|
||||
const LifetimeInfo* lifetime, const AsmInfo* asmInfo) {
|
||||
static const int kIndent = 4;
|
||||
Disasm disasm(Disasm::Options().indent(kIndent + 4)
|
||||
.printEncoding(dumpIREnabled(kExtraLevel))
|
||||
@@ -298,7 +302,7 @@ void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime,
|
||||
|
||||
if (inst.op() == Marker) {
|
||||
os << std::string(kIndent, ' ');
|
||||
JIT::print(os, &inst, lifetime);
|
||||
JIT::print(os, &inst, regs, lifetime);
|
||||
os << '\n';
|
||||
|
||||
// Don't print bytecode in a non-main trace.
|
||||
@@ -331,14 +335,14 @@ void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime,
|
||||
os << std::string(kIndent +
|
||||
folly::format("({}) ", inst.getIId()).str().size(),
|
||||
' ');
|
||||
JIT::print(os, inst.getDst(i), lifetime, false);
|
||||
JIT::print(os, inst.getDst(i), regs, 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, lifetime);
|
||||
printSrc(os, jmp, i, regs, lifetime);
|
||||
os << punc("@");
|
||||
printLabel(os, jmp->getBlock());
|
||||
});
|
||||
@@ -347,7 +351,7 @@ void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime,
|
||||
}
|
||||
|
||||
os << std::string(kIndent, ' ');
|
||||
JIT::print(os, &inst, lifetime);
|
||||
JIT::print(os, &inst, regs, lifetime);
|
||||
os << '\n';
|
||||
|
||||
if (asmInfo) {
|
||||
@@ -385,20 +389,22 @@ void print(std::ostream& os, const Trace* trace, const LifetimeInfo* lifetime,
|
||||
os << "\n" << color(ANSI_COLOR_GREEN)
|
||||
<< " ------- Exit Trace -------"
|
||||
<< color(ANSI_COLOR_END) << '\n';
|
||||
print(os, exitTrace, lifetime, asmInfo);
|
||||
print(os, exitTrace, regs, lifetime, asmInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpTraceImpl(const Trace* trace, std::ostream& out,
|
||||
const RegAllocInfo* regs,
|
||||
const LifetimeInfo* lifetime,
|
||||
const AsmInfo* asmInfo) {
|
||||
print(out, trace, lifetime, asmInfo);
|
||||
print(out, trace, regs, lifetime, asmInfo);
|
||||
}
|
||||
|
||||
// Suggested captions: "before jiffy removal", "after goat saturation",
|
||||
// etc.
|
||||
void dumpTrace(int level, const Trace* trace, const char* caption,
|
||||
const LifetimeInfo* lifetime, AsmInfo* ai) {
|
||||
const RegAllocInfo* regs, const LifetimeInfo* lifetime,
|
||||
AsmInfo* ai) {
|
||||
if (dumpIREnabled(level)) {
|
||||
std::ostringstream str;
|
||||
auto bannerFmt = "{:-^80}\n";
|
||||
@@ -406,7 +412,7 @@ void dumpTrace(int level, const Trace* trace, const char* caption,
|
||||
<< folly::format(bannerFmt, caption)
|
||||
<< color(ANSI_COLOR_END)
|
||||
;
|
||||
dumpTraceImpl(trace, str, lifetime, ai);
|
||||
dumpTraceImpl(trace, str, regs, lifetime, ai);
|
||||
str << color(ANSI_COLOR_BLACK, ANSI_BGCOLOR_GREEN)
|
||||
<< folly::format(bannerFmt, "")
|
||||
<< color(ANSI_COLOR_END)
|
||||
|
||||
@@ -34,18 +34,22 @@ struct LifetimeInfo;
|
||||
|
||||
// IRInstruction
|
||||
void print(std::ostream& ostream, const IRInstruction*,
|
||||
const RegAllocInfo* regs = nullptr,
|
||||
const LifetimeInfo* lifetime = nullptr);
|
||||
void print(const IRInstruction*);
|
||||
void printSrc(std::ostream& ostream, const IRInstruction*, uint32_t srcIndex,
|
||||
const LifetimeInfo* lifetime);
|
||||
const RegAllocInfo* regs, const LifetimeInfo* lifetime);
|
||||
|
||||
// SSATmp
|
||||
void print(std::ostream& ostream, const SSATmp*,
|
||||
const LifetimeInfo* lifetime = nullptr, bool printLastUse = false);
|
||||
const RegAllocInfo* regs = nullptr,
|
||||
const LifetimeInfo* lifetime = nullptr,
|
||||
bool printLastUse = false);
|
||||
void print(const SSATmp*);
|
||||
|
||||
// Trace
|
||||
void print(std::ostream& ostream, const Trace*,
|
||||
const RegAllocInfo* regs = nullptr,
|
||||
const LifetimeInfo* lifetime = nullptr,
|
||||
const AsmInfo* asmInfo = nullptr);
|
||||
void print(const Trace*);
|
||||
@@ -65,8 +69,9 @@ static const int kOptLevel = 4;
|
||||
static const int kExtraLevel = 6;
|
||||
|
||||
void dumpTraceImpl(const Trace* trace, std::ostream& out,
|
||||
const LifetimeInfo*, const AsmInfo*);
|
||||
const RegAllocInfo*, const LifetimeInfo*, const AsmInfo*);
|
||||
void dumpTrace(int level, const Trace* trace, const char* caption,
|
||||
const RegAllocInfo* regs = nullptr,
|
||||
const LifetimeInfo* lifetime = nullptr,
|
||||
AsmInfo* ai = nullptr);
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário