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:
Edwin Smith
2013-05-04 14:36:03 -07:00
commit de Sara Golemon
commit e1ee546e33
9 arquivos alterados com 596 adições e 535 exclusões
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+20 -12
Ver Arquivo
@@ -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);
-24
Ver Arquivo
@@ -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));
+3 -84
Ver Arquivo
@@ -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);
+13 -12
Ver Arquivo
@@ -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, &regs, &lifetime);
if (RuntimeOption::EvalJitCompareHHIR) {
std::ostringstream out;
dumpTraceImpl(trace, out, &lifetime, &ai);
dumpTraceImpl(trace, out, &regs, &lifetime, &ai);
m_lastHHIRDump = out.str();
} else {
dumpTrace(kCodeGenLevel, trace, " after code gen ", &lifetime, &ai);
dumpTrace(kCodeGenLevel, trace, " after code gen ", &regs,
&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);
}
+80 -57
Ver Arquivo
@@ -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
+117 -1
Ver Arquivo
@@ -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);
}}}
+38 -32
Ver Arquivo
@@ -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)
+8 -3
Ver Arquivo
@@ -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);