Remove rematerialization code

- Remove rematerialization since it doesn't buy us much and we've had issues with it
Esse commit está contido em:
Sean Cannella
2013-06-15 16:40:09 -07:00
commit de Sara Golemon
commit e89645d960
6 arquivos alterados com 10 adições e 222 exclusões
-5
Ver Arquivo
@@ -279,11 +279,6 @@ MayModifyRefs
The instruction may modify inner cells on KindOfRef values.
Rematerializable
The instruction is a candidate for rematerialization if the the
register allocator needs to spill its destination.
MayRaiseError
The instruction may raise an error, and therefore has an implicit
-1
Ver Arquivo
@@ -428,7 +428,6 @@ public:
F(bool, HHIRExtraOptPass, true) \
F(uint32_t, HHIRNumFreeRegs, -1) \
F(bool, HHIREnableGenTimeInlining, true) \
F(bool, HHIREnableRematerialization, true) \
F(bool, HHIREnableCalleeSavedOpt, true) \
F(bool, HHIREnablePreColoring, true) \
F(bool, HHIREnableCoalescing, true) \
-6
Ver Arquivo
@@ -94,7 +94,6 @@ namespace {
#define PRc ProducesRC
#define CRc ConsumesRC
#define Refs MayModifyRefs
#define Rm Rematerializable
#define Er MayRaiseError
#define Mem MemEffects
#define T Terminal
@@ -138,7 +137,6 @@ struct {
#undef PRc
#undef CRc
#undef Refs
#undef Rm
#undef Er
#undef Mem
#undef T
@@ -353,10 +351,6 @@ bool IRInstruction::producesReference() const {
return opcodeHasFlags(op(), ProducesRC);
}
bool IRInstruction::isRematerializable() const {
return opcodeHasFlags(op(), Rematerializable);
}
bool IRInstruction::hasMemEffects() const {
return opcodeHasFlags(op(), MemEffects) || mayReenterHelper();
}
+10 -11
Ver Arquivo
@@ -139,7 +139,6 @@ class FailedIRGen : public std::exception {
* PRc producesRC
* CRc consumesRC
* Refs mayModifyRefs
* Rm isRematerializable
* Er mayRaiseError
* Mem hasMemEffects
* T isTerminal
@@ -201,22 +200,22 @@ O(ConvStrToBool, D(Bool), S(Str), N) \
O(ConvCellToBool, D(Bool), S(Cell), N) \
\
O(ConvArrToDbl, D(Dbl), S(Arr), C|N) \
O(ConvBoolToDbl, D(Dbl), S(Bool), C|Rm) \
O(ConvIntToDbl, D(Dbl), S(Int), C|Rm) \
O(ConvBoolToDbl, D(Dbl), S(Bool), C) \
O(ConvIntToDbl, D(Dbl), S(Int), C) \
O(ConvObjToDbl, D(Dbl), S(Obj), N|Er|CRc|K) \
O(ConvStrToDbl, D(Dbl), S(Str), N|CRc|K) \
O(ConvCellToDbl, D(Dbl), S(Cell), N|Er|CRc|K) \
\
O(ConvArrToInt, D(Int), S(Arr), C|N) \
O(ConvBoolToInt, D(Int), S(Bool), C|Rm) \
O(ConvDblToInt, D(Int), S(Dbl), C|N|Rm) \
O(ConvBoolToInt, D(Int), S(Bool), C) \
O(ConvDblToInt, D(Int), S(Dbl), C|N) \
O(ConvObjToInt, D(Int), S(Obj), N|Er|CRc|K) \
O(ConvStrToInt, D(Int), S(Str), N) \
O(ConvCellToInt, D(Int), S(Cell), N|Er|CRc|K) \
\
O(ConvCellToObj, D(Obj), S(Cell), N|CRc|K) \
\
O(ConvBoolToStr, D(StaticStr), S(Bool), C|Rm) \
O(ConvBoolToStr, D(StaticStr), S(Bool), C) \
O(ConvDblToStr, D(Str), S(Dbl), N) \
O(ConvIntToStr, D(Str), S(Int), N) \
O(ConvObjToStr, D(Str), S(Obj), N|Er|CRc|K) \
@@ -291,12 +290,12 @@ O(LdLocAddr, DParam, S(FramePtr), C) \
O(LdMem, DParam, S(PtrToGen) C(Int), NF) \
O(LdProp, DParam, S(Obj) C(Int), NF) \
O(LdRef, DParam, S(BoxedCell), NF) \
O(LdThis, D(Obj), S(FramePtr), C|Rm) \
O(LdThis, D(Obj), S(FramePtr), C) \
O(LdRetAddr, D(RetAddr), S(FramePtr), NF) \
O(LdConst, DParam, NA, C|Rm) \
O(LdConst, DParam, NA, C) \
O(DefConst, DParam, NA, C) \
O(LdCtx, D(Ctx), S(FramePtr) S(Func), C|Rm) \
O(LdCctx, D(Cctx), S(FramePtr), C|Rm) \
O(LdCtx, D(Ctx), S(FramePtr) S(Func), C) \
O(LdCctx, D(Cctx), S(FramePtr), C) \
O(LdCls, D(Cls), S(Str) C(Cls), C|E|N|Refs|Er|Mem) \
O(LdClsCached, D(Cls), CStr, C|E|N|Refs|Er|Mem) \
O(LdClsCachedSafe, D(Cls), CStr, C) \
@@ -722,7 +721,7 @@ enum OpcodeFlag : uint64_t {
ConsumesRC = 1ULL << 5,
ProducesRC = 1ULL << 6,
MayModifyRefs = 1ULL << 7,
Rematerializable = 1ULL << 8, // TODO: implies HasDest
// Unused
MayRaiseError = 1ULL << 9,
Terminal = 1ULL << 10, // has no next instruction
NaryDest = 1ULL << 11, // has 0 or more destinations
-1
Ver Arquivo
@@ -274,7 +274,6 @@ struct IRInstruction {
bool hasDst() const;
bool naryDst() const;
bool hasMemEffects() const;
bool isRematerializable() const;
bool isNative() const;
bool consumesReferences() const;
bool consumesReference(int srcNo) const;
-198
Ver Arquivo
@@ -150,9 +150,6 @@ private:
static SSATmp* getSpilledTmp(SSATmp* tmp);
static SSATmp* getOrigTmp(SSATmp* tmp);
uint32_t assignSpillLoc();
void rematerialize();
void rematerializeAux();
void removeUnusedSpills();
void collectInfo(BlockList::iterator it, IRTrace* trace);
RegNumber getJmpPreColor(SSATmp* tmp, uint32_t regIndx, bool isReload);
void computePreColoringHint();
@@ -1094,10 +1091,6 @@ RegAllocInfo LinearScan::allocRegs(IRTrace* trace, LifetimeInfo* lifetime) {
allocRegsToTrace();
if (RuntimeOption::EvalHHIREnableRematerialization && m_slots.size() > 0) {
// Don't bother rematerializing the trace if it has no Spill/Reload.
rematerialize();
}
numberInstructions(m_blocks);
// Make sure rsp is 16-aligned.
@@ -1244,197 +1237,6 @@ void LinearScan::allocRegsToTrace() {
}
}
void LinearScan::rematerialize() {
numberInstructions(m_blocks);
dumpTrace(kExtraLevel, m_blocks.front()->trace(),
" before rematerialization ", &m_allocInfo, &m_lifetime);
rematerializeAux();
numberInstructions(m_blocks);
// We only replaced Reloads in rematerializeAux().
// Here, we remove Spills that are never reloaded.
removeUnusedSpills();
}
// Return true if it's safe to rematerialize inst at reload's position.
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.
// Although we can inspect each SSAtmp's live range (linear id of its
// definition point and last use), we don't trust that live range given
// the state of the IR after register allocation has inserted spills
// and reloads. So only rematerialize instructions whose sources are
// in registers we trust to be live: rVmSp and rVmFp. Ignore DefConst
// sources because they're implicitly turned into immediates by every
// instruction that uses them.
for (SSATmp* src : inst->srcs()) {
if (!regs[src].hasReg(0) && src->inst()->op() != DefConst) return false;
auto reg = regs[src].reg(0);
if (reg != rVmSp && reg != rVmFp) return false;
}
return true;
};
void LinearScan::rematerializeAux() {
struct State {
SSATmp *sp, *fp;
smart::vector<SSATmp*> values;
};
StateVector<Block, State*> states(m_irFactory, nullptr);
SCOPE_EXIT { for (State* s : states) delete s; };
SSATmp* curSp = nullptr;
SSATmp* curFp = nullptr;
smart::vector<SSATmp*> localValues;
auto killLocal = [&](IRInstruction& inst, unsigned src) {
if (src < inst.numSrcs()) {
unsigned loc = inst.src(src)->getValInt();
if (loc < localValues.size()) localValues[loc] = nullptr;
}
};
auto setLocal = [&](unsigned loc, SSATmp* value) {
// Note that when we implement inlining, we will need to deal
// with the new local id space of the inlined function.
if (loc >= localValues.size()) localValues.resize(loc + 1);
localValues[loc] = canonicalize(value);
};
// Search for a local that stores <value>
auto findLocal = [&](SSATmp* value) -> int {
auto pos = std::find(localValues.begin(), localValues.end(),
canonicalize(value));
return pos != localValues.end() ? pos - localValues.begin() : -1;
};
// save the current state for future use by block; merge if necessary.
auto saveState = [&](Block* block) {
if (State* state = states[block]) {
// merge with saved state
assert(curFp == state->fp);
if (curSp != state->sp) state->sp = nullptr;
for (unsigned i = 0; i < state->values.size(); ++i) {
if (i >= localValues.size() || localValues[i] != state->values[i]) {
state->values[i] = nullptr;
}
}
} else {
// snapshot state for use at target.
state = states[block] = new State;
state->sp = curSp;
state->fp = curFp;
state->values = localValues;
}
};
for (Block* block : m_blocks) {
if (State* state = states[block]) {
states[block] = nullptr;
localValues = state->values;
curSp = state->sp;
curFp = state->fp;
delete state;
}
for (auto it = block->begin(); it != block->end(); ++it) {
IRInstruction& inst = *it;
Opcode opc = inst.op();
if (opc == DefFP || opc == FreeActRec) {
assert(m_allocInfo[inst.dst()].reg() == rVmFp);
curFp = inst.dst();
} else if (opc == Reload) {
// s = Spill t0
// t = Reload s
SSATmp* dst = inst.dst();
SSATmp* spilledTmp = getSpilledTmp(dst);
IRInstruction* spilledInst = spilledTmp->inst();
IRInstruction* newInst = NULL;
if (spilledInst->isRematerializable() ||
(spilledInst->op() == LdStack &&
spilledInst->src(0) == curSp)) {
// XXX: could change <newInst> to the non-check version.
// Rematerialize those rematerializable instructions (i.e.,
// isRematerializable returns true) and LdStack.
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
// inserted just before uses, which must be dominated by the
// original (spilled) def.
newInst->setTaken(nullptr);
}
} else if (curFp) {
// Rematerialize LdLoc.
int loc = findLocal(spilledTmp);
if (loc != -1) {
newInst = m_irFactory->gen(
LdLoc,
dst->type(),
LocalId(loc),
curFp
);
}
}
if (newInst) {
UNUSED Type oldType = dst->type();
newInst->setDst(dst);
dst->setInstruction(newInst);
assert(outputType(newInst) == oldType);
auto* block = inst.block();
auto newIt = block->insert(it, newInst);
block->erase(it);
it = newIt;
}
}
// Updating curSp and localValues
if (inst.hasDst() && m_allocInfo[inst.dst()].reg() == rVmSp) {
// inst modifies the stack pointer.
curSp = inst.dst();
}
if (opc == LdLoc || opc == StLoc || opc == StLocNT) {
setLocal(inst.extra<LocalId>()->locId,
opc == LdLoc ? inst.dst() : inst.src(1));
}
// Other instructions that may have side effects on locals must
// kill the local variable values.
else if (opc == IterInit || opc == WIterInit) {
killLocal(inst, 3);
} else if (opc == IterInitK || opc == WIterInitK) {
killLocal(inst, 3);
killLocal(inst, 4);
} else if (opc == IterNext || opc == WIterNext) {
killLocal(inst, 2);
} else if (opc == IterNextK || opc == WIterNextK) {
killLocal(inst, 2);
killLocal(inst, 3);
}
}
if (Block* taken = block->taken()) saveState(taken);
if (Block* next = block->next()) saveState(next);
}
}
void LinearScan::removeUnusedSpills() {
for (SlotInfo& slot : m_slots) {
IRInstruction* spill = slot.spillTmp->inst();
if (m_uses[spill->dst()].count == 0) {
Block* block = spill->block();
block->erase(block->iteratorTo(spill));
SSATmp* src = spill->src(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.
if (srcOpc == IncRef || srcOpc == LdLoc) {
for (int i = 0, n = src->numNeededRegs(); i < n; ++i) {
m_allocInfo[src].setReg(InvalidReg, i);
}
}
}
}
}
}
void LinearScan::freeRegsAtId(uint32_t id) {
// free all registers whose lifetime ends at this id
// Note that we free registers before we allocate a register