Remove rematerialization code
- Remove rematerialization since it doesn't buy us much and we've had issues with it
Esse commit está contido em:
@@ -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
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário