diff --git a/hphp/doc/ir.specification b/hphp/doc/ir.specification index 0f8ade512..da4b28113 100644 --- a/hphp/doc/ir.specification +++ b/hphp/doc/ir.specification @@ -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 diff --git a/hphp/runtime/base/runtime_option.h b/hphp/runtime/base/runtime_option.h index 574f69178..d80550d1e 100644 --- a/hphp/runtime/base/runtime_option.h +++ b/hphp/runtime/base/runtime_option.h @@ -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) \ diff --git a/hphp/runtime/vm/jit/ir.cpp b/hphp/runtime/vm/jit/ir.cpp index 57b142496..2c8aa2fb1 100644 --- a/hphp/runtime/vm/jit/ir.cpp +++ b/hphp/runtime/vm/jit/ir.cpp @@ -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(); } diff --git a/hphp/runtime/vm/jit/ir.h b/hphp/runtime/vm/jit/ir.h index 6b504c43f..18f9015f5 100644 --- a/hphp/runtime/vm/jit/ir.h +++ b/hphp/runtime/vm/jit/ir.h @@ -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 diff --git a/hphp/runtime/vm/jit/irinstruction.h b/hphp/runtime/vm/jit/irinstruction.h index a6f515d5e..75f40d3b1 100644 --- a/hphp/runtime/vm/jit/irinstruction.h +++ b/hphp/runtime/vm/jit/irinstruction.h @@ -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; diff --git a/hphp/runtime/vm/jit/linearscan.cpp b/hphp/runtime/vm/jit/linearscan.cpp index b6e1836a3..f1c0b022d 100644 --- a/hphp/runtime/vm/jit/linearscan.cpp +++ b/hphp/runtime/vm/jit/linearscan.cpp @@ -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 values; - }; - StateVector states(m_irFactory, nullptr); - SCOPE_EXIT { for (State* s : states) delete s; }; - SSATmp* curSp = nullptr; - SSATmp* curFp = nullptr; - smart::vector 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 - 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 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()->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