| //===- subzero/src/IceVariableSplitting.cpp - Local variable splitting ----===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Aggressive block-local variable splitting to improve linear-scan | 
 | /// register allocation. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "IceVariableSplitting.h" | 
 |  | 
 | #include "IceCfg.h" | 
 | #include "IceCfgNode.h" | 
 | #include "IceClFlags.h" | 
 | #include "IceInst.h" | 
 | #include "IceOperand.h" | 
 | #include "IceTargetLowering.h" | 
 |  | 
 | namespace Ice { | 
 |  | 
 | namespace { | 
 |  | 
 | /// A Variable is "allocable" if it is a register allocation candidate but | 
 | /// doesn't already have a register. | 
 | bool isAllocable(const Variable *Var) { | 
 |   if (Var == nullptr) | 
 |     return false; | 
 |   return !Var->hasReg() && Var->mayHaveReg(); | 
 | } | 
 |  | 
 | /// A Variable is "inf" if it already has a register or is infinite-weight. | 
 | bool isInf(const Variable *Var) { | 
 |   if (Var == nullptr) | 
 |     return false; | 
 |   return Var->hasReg() || Var->mustHaveReg(); | 
 | } | 
 |  | 
 | /// VariableMap is a simple helper class that keeps track of the latest split | 
 | /// version of the original Variables, as well as the instruction containing the | 
 | /// last use of the Variable within the current block.  For each entry, the | 
 | /// Variable is tagged with the CfgNode that it is valid in, so that we don't | 
 | /// need to clear the entire Map[] vector for each block. | 
 | class VariableMap { | 
 | private: | 
 |   VariableMap() = delete; | 
 |   VariableMap(const VariableMap &) = delete; | 
 |   VariableMap &operator=(const VariableMap &) = delete; | 
 |  | 
 |   struct VarInfo { | 
 |     /// MappedVar is the latest mapped/split version of the Variable. | 
 |     Variable *MappedVar = nullptr; | 
 |     /// MappedVarNode is the block in which MappedVar is valid. | 
 |     const CfgNode *MappedVarNode = nullptr; | 
 |     /// LastUseInst is the last instruction in the block that uses the Variable | 
 |     /// as a source operand. | 
 |     const Inst *LastUseInst = nullptr; | 
 |     /// LastUseNode is the block in which LastUseInst is valid. | 
 |     const CfgNode *LastUseNode = nullptr; | 
 |     VarInfo() = default; | 
 |  | 
 |   private: | 
 |     VarInfo(const VarInfo &) = delete; | 
 |     VarInfo &operator=(const VarInfo &) = delete; | 
 |   }; | 
 |  | 
 | public: | 
 |   explicit VariableMap(Cfg *Func) | 
 |       : Func(Func), NumVars(Func->getNumVariables()), Map(NumVars) {} | 
 |   /// Reset the mappings at the start of a block. | 
 |   void reset(const CfgNode *CurNode) { | 
 |     Node = CurNode; | 
 |     // Do a prepass through all the instructions, marking which instruction is | 
 |     // the last use of each Variable within the block. | 
 |     for (const Inst &Instr : Node->getInsts()) { | 
 |       if (Instr.isDeleted()) | 
 |         continue; | 
 |       for (SizeT i = 0; i < Instr.getSrcSize(); ++i) { | 
 |         if (auto *SrcVar = llvm::dyn_cast<Variable>(Instr.getSrc(i))) { | 
 |           const SizeT VarNum = getVarNum(SrcVar); | 
 |           Map[VarNum].LastUseInst = &Instr; | 
 |           Map[VarNum].LastUseNode = Node; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   /// Get Var's current mapping (or Var itself if it has no mapping yet). | 
 |   Variable *get(Variable *Var) const { | 
 |     const SizeT VarNum = getVarNum(Var); | 
 |     Variable *MappedVar = Map[VarNum].MappedVar; | 
 |     if (MappedVar == nullptr) | 
 |       return Var; | 
 |     if (Map[VarNum].MappedVarNode != Node) | 
 |       return Var; | 
 |     return MappedVar; | 
 |   } | 
 |   /// Create a new linked Variable in the LinkedTo chain, and set it as Var's | 
 |   /// latest mapping. | 
 |   Variable *makeLinked(Variable *Var) { | 
 |     Variable *NewVar = Func->makeVariable(Var->getType()); | 
 |     NewVar->setRegClass(Var->getRegClass()); | 
 |     NewVar->setLinkedTo(get(Var)); | 
 |     const SizeT VarNum = getVarNum(Var); | 
 |     Map[VarNum].MappedVar = NewVar; | 
 |     Map[VarNum].MappedVarNode = Node; | 
 |     return NewVar; | 
 |   } | 
 |   /// Given Var that is LinkedTo some other variable, re-splice it into the | 
 |   /// LinkedTo chain so that the chain is ordered by Variable::getIndex(). | 
 |   void spliceBlockLocalLinkedToChain(Variable *Var) { | 
 |     Variable *LinkedTo = Var->getLinkedTo(); | 
 |     assert(LinkedTo != nullptr); | 
 |     assert(Var->getIndex() > LinkedTo->getIndex()); | 
 |     const SizeT VarNum = getVarNum(LinkedTo); | 
 |     Variable *Link = Map[VarNum].MappedVar; | 
 |     if (Link == nullptr || Map[VarNum].MappedVarNode != Node) | 
 |       return; | 
 |     Variable *LinkParent = Link->getLinkedTo(); | 
 |     while (LinkParent != nullptr && LinkParent->getIndex() >= Var->getIndex()) { | 
 |       Link = LinkParent; | 
 |       LinkParent = Link->getLinkedTo(); | 
 |     } | 
 |     Var->setLinkedTo(LinkParent); | 
 |     Link->setLinkedTo(Var); | 
 |   } | 
 |   /// Return whether the given Variable has any uses as a source operand within | 
 |   /// the current block.  If it has no source operand uses, but is assigned as a | 
 |   /// dest variable in some instruction in the block, then we needn't bother | 
 |   /// splitting it. | 
 |   bool isDestUsedInBlock(const Variable *Dest) const { | 
 |     return Map[getVarNum(Dest)].LastUseNode == Node; | 
 |   } | 
 |   /// Return whether the given instruction is the last use of the given Variable | 
 |   /// within the current block.  If it is, then we needn't bother splitting the | 
 |   /// Variable at this instruction. | 
 |   bool isInstLastUseOfVar(const Variable *Var, const Inst *Instr) { | 
 |     return Map[getVarNum(Var)].LastUseInst == Instr; | 
 |   } | 
 |  | 
 | private: | 
 |   Cfg *const Func; | 
 |   // NumVars is for the size of the Map array.  It can be const because any new | 
 |   // Variables created during the splitting pass don't need to be mapped. | 
 |   const SizeT NumVars; | 
 |   CfgVector<VarInfo> Map; | 
 |   const CfgNode *Node = nullptr; | 
 |   /// Get Var's VarNum, and do some validation. | 
 |   SizeT getVarNum(const Variable *Var) const { | 
 |     const SizeT VarNum = Var->getIndex(); | 
 |     assert(VarNum < NumVars); | 
 |     return VarNum; | 
 |   } | 
 | }; | 
 |  | 
 | /// LocalVariableSplitter tracks the necessary splitting state across | 
 | /// instructions. | 
 | class LocalVariableSplitter { | 
 |   LocalVariableSplitter() = delete; | 
 |   LocalVariableSplitter(const LocalVariableSplitter &) = delete; | 
 |   LocalVariableSplitter &operator=(const LocalVariableSplitter &) = delete; | 
 |  | 
 | public: | 
 |   explicit LocalVariableSplitter(Cfg *Func) | 
 |       : Target(Func->getTarget()), VarMap(Func) {} | 
 |   /// setNode() is called before processing the instructions of a block. | 
 |   void setNode(CfgNode *CurNode) { | 
 |     Node = CurNode; | 
 |     VarMap.reset(Node); | 
 |     LinkedToFixups.clear(); | 
 |   } | 
 |   /// finalizeNode() is called after all instructions in the block are | 
 |   /// processed. | 
 |   void finalizeNode() { | 
 |     // Splice in any preexisting LinkedTo links into the single chain.  These | 
 |     // are the ones that were recorded during setInst(). | 
 |     for (Variable *Var : LinkedToFixups) { | 
 |       VarMap.spliceBlockLocalLinkedToChain(Var); | 
 |     } | 
 |   } | 
 |   /// setInst() is called before processing the next instruction.  The iterators | 
 |   /// are the insertion points for a new instructions, depending on whether the | 
 |   /// new instruction should be inserted before or after the current | 
 |   /// instruction. | 
 |   void setInst(Inst *CurInst, InstList::iterator Cur, InstList::iterator Next) { | 
 |     Instr = CurInst; | 
 |     Dest = Instr->getDest(); | 
 |     IterCur = Cur; | 
 |     IterNext = Next; | 
 |     ShouldSkipRemainingInstructions = false; | 
 |     // Note any preexisting LinkedTo relationships that were created during | 
 |     // target lowering.  Record them in LinkedToFixups which is then processed | 
 |     // in finalizeNode(). | 
 |     if (Dest != nullptr && Dest->getLinkedTo() != nullptr) { | 
 |       LinkedToFixups.emplace_back(Dest); | 
 |     } | 
 |   } | 
 |   bool shouldSkipRemainingInstructions() const { | 
 |     return ShouldSkipRemainingInstructions; | 
 |   } | 
 |   bool isUnconditionallyExecuted() const { return WaitingForLabel == nullptr; } | 
 |  | 
 |   /// Note: the handle*() functions return true to indicate that the instruction | 
 |   /// has now been handled and that the instruction loop should continue to the | 
 |   /// next instruction in the block (and return false otherwise).  In addition, | 
 |   /// they set the ShouldSkipRemainingInstructions flag to indicate that no more | 
 |   /// instructions in the block should be processed. | 
 |  | 
 |   /// Handle an "unwanted" instruction by returning true; | 
 |   bool handleUnwantedInstruction() { | 
 |     // We can limit the splitting to an arbitrary subset of the instructions, | 
 |     // and still expect correct code.  As such, we can do instruction-subset | 
 |     // bisection to help debug any problems in this pass. | 
 |     static constexpr char AnInstructionHasNoName[] = ""; | 
 |     if (!BuildDefs::minimal() && | 
 |         !getFlags().matchSplitInsts(AnInstructionHasNoName, | 
 |                                     Instr->getNumber())) { | 
 |       return true; | 
 |     } | 
 |     if (!llvm::isa<InstTarget>(Instr)) { | 
 |       // Ignore non-lowered instructions like FakeDef/FakeUse. | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   /// Process a potential label instruction. | 
 |   bool handleLabel() { | 
 |     if (!Instr->isLabel()) | 
 |       return false; | 
 |     // A Label instruction shouldn't have any operands, so it can be handled | 
 |     // right here and then move on. | 
 |     assert(Dest == nullptr); | 
 |     assert(Instr->getSrcSize() == 0); | 
 |     if (Instr == WaitingForLabel) { | 
 |       // If we found the forward-branch-target Label instruction we're waiting | 
 |       // for, then clear the WaitingForLabel state. | 
 |       WaitingForLabel = nullptr; | 
 |     } else if (WaitingForLabel == nullptr && WaitingForBranchTo == nullptr) { | 
 |       // If we found a new Label instruction while the WaitingFor* state is | 
 |       // clear, then set things up for this being a backward branch target. | 
 |       WaitingForBranchTo = Instr; | 
 |     } else { | 
 |       // We see something we don't understand, so skip to the next block. | 
 |       ShouldSkipRemainingInstructions = true; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   /// Process a potential intra-block branch instruction. | 
 |   bool handleIntraBlockBranch() { | 
 |     const Inst *Label = Instr->getIntraBlockBranchTarget(); | 
 |     if (Label == nullptr) | 
 |       return false; | 
 |     // An intra-block branch instruction shouldn't have any operands, so it can | 
 |     // be handled right here and then move on. | 
 |     assert(Dest == nullptr); | 
 |     assert(Instr->getSrcSize() == 0); | 
 |     if (WaitingForBranchTo == Label && WaitingForLabel == nullptr) { | 
 |       WaitingForBranchTo = nullptr; | 
 |     } else if (WaitingForBranchTo == nullptr && | 
 |                (WaitingForLabel == nullptr || WaitingForLabel == Label)) { | 
 |       WaitingForLabel = Label; | 
 |     } else { | 
 |       // We see something we don't understand, so skip to the next block. | 
 |       ShouldSkipRemainingInstructions = true; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   /// Specially process a potential "Variable=Variable" assignment instruction, | 
 |   /// when it conforms to certain patterns. | 
 |   bool handleSimpleVarAssign() { | 
 |     if (!Instr->isVarAssign()) | 
 |       return false; | 
 |     const bool DestIsInf = isInf(Dest); | 
 |     const bool DestIsAllocable = isAllocable(Dest); | 
 |     auto *SrcVar = llvm::cast<Variable>(Instr->getSrc(0)); | 
 |     const bool SrcIsInf = isInf(SrcVar); | 
 |     const bool SrcIsAllocable = isAllocable(SrcVar); | 
 |     if (DestIsInf && SrcIsInf) { | 
 |       // The instruction: | 
 |       //   t:inf = u:inf | 
 |       // No transformation is needed. | 
 |       return true; | 
 |     } | 
 |     if (DestIsInf && SrcIsAllocable && Dest->getType() == SrcVar->getType()) { | 
 |       // The instruction: | 
 |       //   t:inf = v | 
 |       // gets transformed to: | 
 |       //   t:inf = v1 | 
 |       //   v2 = t:inf | 
 |       // where: | 
 |       //   v1 := map[v] | 
 |       //   v2 := linkTo(v) | 
 |       //   map[v] := v2 | 
 |       // | 
 |       // If both v2 and its linkedToStackRoot get a stack slot, then "v2=t:inf" | 
 |       // is recognized as a redundant assignment and elided. | 
 |       // | 
 |       // Note that if the dest and src types are different, then this is | 
 |       // actually a truncation operation, which would make "v2=t:inf" an invalid | 
 |       // instruction.  In this case, the type test will make it fall through to | 
 |       // the general case below. | 
 |       Variable *OldMapped = VarMap.get(SrcVar); | 
 |       Instr->replaceSource(0, OldMapped); | 
 |       if (isUnconditionallyExecuted()) { | 
 |         // Only create new mapping state if the instruction is unconditionally | 
 |         // executed. | 
 |         if (!VarMap.isInstLastUseOfVar(SrcVar, Instr)) { | 
 |           Variable *NewMapped = VarMap.makeLinked(SrcVar); | 
 |           Inst *Mov = Target->createLoweredMove(NewMapped, Dest); | 
 |           Node->getInsts().insert(IterNext, Mov); | 
 |         } | 
 |       } | 
 |       return true; | 
 |     } | 
 |     if (DestIsAllocable && SrcIsInf) { | 
 |       if (!VarMap.isDestUsedInBlock(Dest)) { | 
 |         return true; | 
 |       } | 
 |       // The instruction: | 
 |       //   v = t:inf | 
 |       // gets transformed to: | 
 |       //   v = t:inf | 
 |       //   v2 = t:inf | 
 |       // where: | 
 |       //   v2 := linkTo(v) | 
 |       //   map[v] := v2 | 
 |       // | 
 |       // If both v2 and v get a stack slot, then "v2=t:inf" is recognized as a | 
 |       // redundant assignment and elided. | 
 |       if (isUnconditionallyExecuted()) { | 
 |         // Only create new mapping state if the instruction is unconditionally | 
 |         // executed. | 
 |         Variable *NewMapped = VarMap.makeLinked(Dest); | 
 |         Inst *Mov = Target->createLoweredMove(NewMapped, SrcVar); | 
 |         Node->getInsts().insert(IterNext, Mov); | 
 |       } else { | 
 |         // For a conditionally executed instruction, add a redefinition of the | 
 |         // original Dest mapping, without creating a new linked variable. | 
 |         Variable *OldMapped = VarMap.get(Dest); | 
 |         Inst *Mov = Target->createLoweredMove(OldMapped, SrcVar); | 
 |         Mov->setDestRedefined(); | 
 |         Node->getInsts().insert(IterNext, Mov); | 
 |       } | 
 |       return true; | 
 |     } | 
 |     assert(!ShouldSkipRemainingInstructions); | 
 |     return false; | 
 |   } | 
 |  | 
 |   /// Process the dest Variable of a Phi instruction. | 
 |   bool handlePhi() { | 
 |     assert(llvm::isa<InstPhi>(Instr)); | 
 |     const bool DestIsAllocable = isAllocable(Dest); | 
 |     if (!DestIsAllocable) | 
 |       return true; | 
 |     if (!VarMap.isDestUsedInBlock(Dest)) | 
 |       return true; | 
 |     Variable *NewMapped = VarMap.makeLinked(Dest); | 
 |     Inst *Mov = Target->createLoweredMove(NewMapped, Dest); | 
 |     Node->getInsts().insert(IterCur, Mov); | 
 |     return true; | 
 |   } | 
 |  | 
 |   /// Process an arbitrary instruction. | 
 |   bool handleGeneralInst() { | 
 |     const bool DestIsAllocable = isAllocable(Dest); | 
 |     // The (non-variable-assignment) instruction: | 
 |     //   ... = F(v) | 
 |     // where v is not infinite-weight, gets transformed to: | 
 |     //   v2 = v1 | 
 |     //   ... = F(v1) | 
 |     // where: | 
 |     //   v1 := map[v] | 
 |     //   v2 := linkTo(v) | 
 |     //   map[v] := v2 | 
 |     // After that, if the "..." dest=u is not infinite-weight, append: | 
 |     //   u2 = u | 
 |     // where: | 
 |     //   u2 := linkTo(u) | 
 |     //   map[u] := u2 | 
 |     for (SizeT i = 0; i < Instr->getSrcSize(); ++i) { | 
 |       // Iterate over the top-level src vars.  Don't bother to dig into | 
 |       // e.g. MemOperands because their vars should all be infinite-weight. | 
 |       // (This assumption would need to change if the pass were done | 
 |       // pre-lowering.) | 
 |       if (auto *SrcVar = llvm::dyn_cast<Variable>(Instr->getSrc(i))) { | 
 |         const bool SrcIsAllocable = isAllocable(SrcVar); | 
 |         if (SrcIsAllocable) { | 
 |           Variable *OldMapped = VarMap.get(SrcVar); | 
 |           if (isUnconditionallyExecuted()) { | 
 |             if (!VarMap.isInstLastUseOfVar(SrcVar, Instr)) { | 
 |               Variable *NewMapped = VarMap.makeLinked(SrcVar); | 
 |               Inst *Mov = Target->createLoweredMove(NewMapped, OldMapped); | 
 |               Node->getInsts().insert(IterCur, Mov); | 
 |             } | 
 |           } | 
 |           Instr->replaceSource(i, OldMapped); | 
 |         } | 
 |       } | 
 |     } | 
 |     // Transformation of Dest is the same as the "v=t:inf" case above. | 
 |     if (DestIsAllocable && VarMap.isDestUsedInBlock(Dest)) { | 
 |       if (isUnconditionallyExecuted()) { | 
 |         Variable *NewMapped = VarMap.makeLinked(Dest); | 
 |         Inst *Mov = Target->createLoweredMove(NewMapped, Dest); | 
 |         Node->getInsts().insert(IterNext, Mov); | 
 |       } else { | 
 |         Variable *OldMapped = VarMap.get(Dest); | 
 |         Inst *Mov = Target->createLoweredMove(OldMapped, Dest); | 
 |         Mov->setDestRedefined(); | 
 |         Node->getInsts().insert(IterNext, Mov); | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 | private: | 
 |   TargetLowering *Target; | 
 |   CfgNode *Node = nullptr; | 
 |   Inst *Instr = nullptr; | 
 |   Variable *Dest = nullptr; | 
 |   InstList::iterator IterCur; | 
 |   InstList::iterator IterNext; | 
 |   bool ShouldSkipRemainingInstructions = false; | 
 |   VariableMap VarMap; | 
 |   CfgVector<Variable *> LinkedToFixups; | 
 |   /// WaitingForLabel and WaitingForBranchTo are for tracking intra-block | 
 |   /// control flow. | 
 |   const Inst *WaitingForLabel = nullptr; | 
 |   const Inst *WaitingForBranchTo = nullptr; | 
 | }; | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | /// Within each basic block, rewrite Variable references in terms of chained | 
 | /// copies of the original Variable.  For example: | 
 | ///   A = B + C | 
 | /// might be rewritten as: | 
 | ///   B1 = B | 
 | ///   C1 = C | 
 | ///   A = B + C | 
 | ///   A1 = A | 
 | /// and then: | 
 | ///   D = A + B | 
 | /// might be rewritten as: | 
 | ///   A2 = A1 | 
 | ///   B2 = B1 | 
 | ///   D = A1 + B1 | 
 | ///   D1 = D | 
 | /// | 
 | /// The purpose is to present the linear-scan register allocator with smaller | 
 | /// live ranges, to help mitigate its "all or nothing" allocation strategy, | 
 | /// while counting on its preference mechanism to keep the split versions in the | 
 | /// same register when possible. | 
 | /// | 
 | /// When creating new Variables, A2 is linked to A1 which is linked to A, and | 
 | /// similar for the other Variable linked-to chains.  Rewrites apply only to | 
 | /// Variables where mayHaveReg() is true. | 
 | /// | 
 | /// At code emission time, redundant linked-to stack assignments will be | 
 | /// recognized and elided.  To illustrate using the above example, if A1 gets a | 
 | /// register but A and A2 are on the stack, the "A2=A1" store instruction is | 
 | /// redundant since A and A2 share the same stack slot and A1 originated from A. | 
 | /// | 
 | /// Simple assignment instructions are rewritten slightly differently, to take | 
 | /// maximal advantage of Variables known to have registers. | 
 | /// | 
 | /// In general, there may be several valid ways to rewrite an instruction: add | 
 | /// the new assignment instruction either before or after the original | 
 | /// instruction, and rewrite the original instruction with either the old or the | 
 | /// new variable mapping.  We try to pick a strategy most likely to avoid | 
 | /// potential performance problems.  For example, try to avoid storing to the | 
 | /// stack and then immediately reloading from the same location.  One | 
 | /// consequence is that code might be generated that loads a register from a | 
 | /// stack location, followed almost immediately by another use of the same stack | 
 | /// location, despite its value already being available in a register as a | 
 | /// result of the first instruction.  However, the performance impact here is | 
 | /// likely to be negligible, and a simple availability peephole optimization | 
 | /// could clean it up. | 
 | /// | 
 | /// This pass potentially adds a lot of new instructions and variables, and as | 
 | /// such there are compile-time performance concerns, particularly with liveness | 
 | /// analysis and register allocation.  Note that for liveness analysis, the new | 
 | /// variables have single-block liveness, so they don't increase the size of the | 
 | /// liveness bit vectors that need to be merged across blocks.  As a result, the | 
 | /// performance impact is likely to be linearly related to the number of new | 
 | /// instructions, rather than number of new variables times number of blocks | 
 | /// which would be the case if they were multi-block variables. | 
 | void splitBlockLocalVariables(Cfg *Func) { | 
 |   if (!getFlags().getSplitLocalVars()) | 
 |     return; | 
 |   TimerMarker _(TimerStack::TT_splitLocalVars, Func); | 
 |   LocalVariableSplitter Splitter(Func); | 
 |   // TODO(stichnot): Fix this mechanism for LinkedTo variables and stack slot | 
 |   // assignment. | 
 |   // | 
 |   // To work around shortcomings with stack frame mapping, we want to arrange | 
 |   // LinkedTo structure such that within one block, the LinkedTo structure | 
 |   // leading to a root forms a list, not a tree.  A LinkedTo root can have | 
 |   // multiple children linking to it, but only one per block.  Furthermore, | 
 |   // because stack slot mapping processes variables in numerical order, the | 
 |   // LinkedTo chain needs to be ordered such that when A->getLinkedTo() == B, | 
 |   // then A->getIndex() > B->getIndex(). | 
 |   // | 
 |   // To effect this, while processing a block we keep track of preexisting | 
 |   // LinkedTo relationships via the LinkedToFixups vector, and at the end of the | 
 |   // block we splice them in such that the block has a single chain for each | 
 |   // root, ordered by getIndex() value. | 
 |   CfgVector<Variable *> LinkedToFixups; | 
 |   for (CfgNode *Node : Func->getNodes()) { | 
 |     // Clear the VarMap and LinkedToFixups at the start of every block. | 
 |     LinkedToFixups.clear(); | 
 |     Splitter.setNode(Node); | 
 |     auto &Insts = Node->getInsts(); | 
 |     auto Iter = Insts.begin(); | 
 |     auto IterEnd = Insts.end(); | 
 |     // TODO(stichnot): Figure out why Phi processing usually degrades | 
 |     // performance.  Disable for now. | 
 |     static constexpr bool ProcessPhis = false; | 
 |     if (ProcessPhis) { | 
 |       for (Inst &Instr : Node->getPhis()) { | 
 |         if (Instr.isDeleted()) | 
 |           continue; | 
 |         Splitter.setInst(&Instr, Iter, Iter); | 
 |         Splitter.handlePhi(); | 
 |       } | 
 |     } | 
 |     InstList::iterator NextIter; | 
 |     for (; Iter != IterEnd && !Splitter.shouldSkipRemainingInstructions(); | 
 |          Iter = NextIter) { | 
 |       NextIter = Iter; | 
 |       ++NextIter; | 
 |       Inst *Instr = iteratorToInst(Iter); | 
 |       if (Instr->isDeleted()) | 
 |         continue; | 
 |       Splitter.setInst(Instr, Iter, NextIter); | 
 |  | 
 |       // Before doing any transformations, take care of the bookkeeping for | 
 |       // intra-block branching. | 
 |       // | 
 |       // This is tricky because the transformation for one instruction may | 
 |       // depend on a transformation for a previous instruction, but if that | 
 |       // previous instruction is not dynamically executed due to intra-block | 
 |       // control flow, it may lead to an inconsistent state and incorrect code. | 
 |       // | 
 |       // We want to handle some simple cases, and reject some others: | 
 |       // | 
 |       // 1. For something like a select instruction, we could have: | 
 |       //   test cond | 
 |       //   dest = src_false | 
 |       //   branch conditionally to label | 
 |       //   dest = src_true | 
 |       //   label: | 
 |       // | 
 |       // Between the conditional branch and the label, we need to treat dest and | 
 |       // src variables specially, specifically not creating any new state. | 
 |       // | 
 |       // 2. Some 64-bit atomic instructions may be lowered to a loop: | 
 |       //   label: | 
 |       //   ... | 
 |       //   branch conditionally to label | 
 |       // | 
 |       // No special treatment is needed, but it's worth tracking so that case #1 | 
 |       // above can also be handled. | 
 |       // | 
 |       // 3. Advanced switch lowering can create really complex intra-block | 
 |       // control flow, so when we recognize this, we should just stop splitting | 
 |       // for the remainder of the block (which isn't much since a switch | 
 |       // instruction is a terminator). | 
 |       // | 
 |       // 4. Other complex lowering, e.g. an i64 icmp on a 32-bit architecture, | 
 |       // can result in an if/then/else like structure with two labels.  One | 
 |       // possibility would be to suspect splitting for the remainder of the | 
 |       // lowered instruction, and then resume for the remainder of the block, | 
 |       // but since we don't have high-level instruction markers, we might as | 
 |       // well just stop splitting for the remainder of the block. | 
 |       if (Splitter.handleLabel()) | 
 |         continue; | 
 |       if (Splitter.handleIntraBlockBranch()) | 
 |         continue; | 
 |       if (Splitter.handleUnwantedInstruction()) | 
 |         continue; | 
 |  | 
 |       // Intra-block bookkeeping is complete, now do the transformations. | 
 |  | 
 |       // Determine the transformation based on the kind of instruction, and | 
 |       // whether its Variables are infinite-weight.  New instructions can be | 
 |       // inserted before the current instruction via Iter, or after the current | 
 |       // instruction via NextIter. | 
 |       if (Splitter.handleSimpleVarAssign()) | 
 |         continue; | 
 |       if (Splitter.handleGeneralInst()) | 
 |         continue; | 
 |     } | 
 |     Splitter.finalizeNode(); | 
 |   } | 
 |  | 
 |   Func->dump("After splitting local variables"); | 
 | } | 
 |  | 
 | } // end of namespace Ice |