1//===- MachinePipeliner.h - Machine Software Pipeliner Pass -------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// An implementation of the Swing Modulo Scheduling (SMS) software pipeliner.
10//
11// Software pipelining (SWP) is an instruction scheduling technique for loops
12// that overlap loop iterations and exploits ILP via a compiler transformation.
13//
14// Swing Modulo Scheduling is an implementation of software pipelining
15// that generates schedules that are near optimal in terms of initiation
16// interval, register requirements, and stage count. See the papers:
17//
18// "Swing Modulo Scheduling: A Lifetime-Sensitive Approach", by J. Llosa,
19// A. Gonzalez, E. Ayguade, and M. Valero. In PACT '96 Proceedings of the 1996
20// Conference on Parallel Architectures and Compilation Techiniques.
21//
22// "Lifetime-Sensitive Modulo Scheduling in a Production Environment", by J.
23// Llosa, E. Ayguade, A. Gonzalez, M. Valero, and J. Eckhardt. In IEEE
24// Transactions on Computers, Vol. 50, No. 3, 2001.
25//
26// "An Implementation of Swing Modulo Scheduling With Extensions for
27// Superblocks", by T. Lattner, Master's Thesis, University of Illinois at
28// Urbana-Champaign, 2005.
29//
30//
31// The SMS algorithm consists of three main steps after computing the minimal
32// initiation interval (MII).
33// 1) Analyze the dependence graph and compute information about each
34//    instruction in the graph.
35// 2) Order the nodes (instructions) by priority based upon the heuristics
36//    described in the algorithm.
37// 3) Attempt to schedule the nodes in the specified order using the MII.
38//
39//===----------------------------------------------------------------------===//
40#ifndef LLVM_LIB_CODEGEN_MACHINEPIPELINER_H
41#define LLVM_LIB_CODEGEN_MACHINEPIPELINER_H
42
43#include "llvm/Analysis/AliasAnalysis.h"
44
45#include "llvm/CodeGen/MachineDominators.h"
46#include "llvm/CodeGen/RegisterClassInfo.h"
47#include "llvm/CodeGen/ScheduleDAGInstrs.h"
48#include "llvm/CodeGen/TargetInstrInfo.h"
49#include "llvm/InitializePasses.h"
50
51namespace llvm {
52
53class NodeSet;
54class SMSchedule;
55
56extern cl::opt<bool> SwpEnableCopyToPhi;
57
58/// The main class in the implementation of the target independent
59/// software pipeliner pass.
60class MachinePipeliner : public MachineFunctionPass {
61public:
62  MachineFunction *MF = nullptr;
63  const MachineLoopInfo *MLI = nullptr;
64  const MachineDominatorTree *MDT = nullptr;
65  const InstrItineraryData *InstrItins;
66  const TargetInstrInfo *TII = nullptr;
67  RegisterClassInfo RegClassInfo;
68  bool disabledByPragma = false;
69  unsigned II_setByPragma = 0;
70
71#ifndef NDEBUG
72  static int NumTries;
73#endif
74
75  /// Cache the target analysis information about the loop.
76  struct LoopInfo {
77    MachineBasicBlock *TBB = nullptr;
78    MachineBasicBlock *FBB = nullptr;
79    SmallVector<MachineOperand, 4> BrCond;
80    MachineInstr *LoopInductionVar = nullptr;
81    MachineInstr *LoopCompare = nullptr;
82  };
83  LoopInfo LI;
84
85  static char ID;
86
87  MachinePipeliner() : MachineFunctionPass(ID) {
88    initializeMachinePipelinerPass(*PassRegistry::getPassRegistry());
89  }
90
91  bool runOnMachineFunction(MachineFunction &MF) override;
92
93  void getAnalysisUsage(AnalysisUsage &AU) const override {
94    AU.addRequired<AAResultsWrapperPass>();
95    AU.addPreserved<AAResultsWrapperPass>();
96    AU.addRequired<MachineLoopInfo>();
97    AU.addRequired<MachineDominatorTree>();
98    AU.addRequired<LiveIntervals>();
99    MachineFunctionPass::getAnalysisUsage(AU);
100  }
101
102private:
103  void preprocessPhiNodes(MachineBasicBlock &B);
104  bool canPipelineLoop(MachineLoop &L);
105  bool scheduleLoop(MachineLoop &L);
106  bool swingModuloScheduler(MachineLoop &L);
107  void setPragmaPipelineOptions(MachineLoop &L);
108};
109
110/// This class builds the dependence graph for the instructions in a loop,
111/// and attempts to schedule the instructions using the SMS algorithm.
112class SwingSchedulerDAG : public ScheduleDAGInstrs {
113  MachinePipeliner &Pass;
114  /// The minimum initiation interval between iterations for this schedule.
115  unsigned MII = 0;
116  /// The maximum initiation interval between iterations for this schedule.
117  unsigned MAX_II = 0;
118  /// Set to true if a valid pipelined schedule is found for the loop.
119  bool Scheduled = false;
120  MachineLoop &Loop;
121  LiveIntervals &LIS;
122  const RegisterClassInfo &RegClassInfo;
123  unsigned II_setByPragma = 0;
124
125  /// A toplogical ordering of the SUnits, which is needed for changing
126  /// dependences and iterating over the SUnits.
127  ScheduleDAGTopologicalSort Topo;
128
129  struct NodeInfo {
130    int ASAP = 0;
131    int ALAP = 0;
132    int ZeroLatencyDepth = 0;
133    int ZeroLatencyHeight = 0;
134
135    NodeInfo() = default;
136  };
137  /// Computed properties for each node in the graph.
138  std::vector<NodeInfo> ScheduleInfo;
139
140  enum OrderKind { BottomUp = 0, TopDown = 1 };
141  /// Computed node ordering for scheduling.
142  SetVector<SUnit *> NodeOrder;
143
144  using NodeSetType = SmallVector<NodeSet, 8>;
145  using ValueMapTy = DenseMap<unsigned, unsigned>;
146  using MBBVectorTy = SmallVectorImpl<MachineBasicBlock *>;
147  using InstrMapTy = DenseMap<MachineInstr *, MachineInstr *>;
148
149  /// Instructions to change when emitting the final schedule.
150  DenseMap<SUnit *, std::pair<unsigned, int64_t>> InstrChanges;
151
152  /// We may create a new instruction, so remember it because it
153  /// must be deleted when the pass is finished.
154  DenseMap<MachineInstr*, MachineInstr *> NewMIs;
155
156  /// Ordered list of DAG postprocessing steps.
157  std::vector<std::unique_ptr<ScheduleDAGMutation>> Mutations;
158
159  /// Helper class to implement Johnson's circuit finding algorithm.
160  class Circuits {
161    std::vector<SUnit> &SUnits;
162    SetVector<SUnit *> Stack;
163    BitVector Blocked;
164    SmallVector<SmallPtrSet<SUnit *, 4>, 10> B;
165    SmallVector<SmallVector<int, 4>, 16> AdjK;
166    // Node to Index from ScheduleDAGTopologicalSort
167    std::vector<int> *Node2Idx;
168    unsigned NumPaths;
169    static unsigned MaxPaths;
170
171  public:
172    Circuits(std::vector<SUnit> &SUs, ScheduleDAGTopologicalSort &Topo)
173        : SUnits(SUs), Blocked(SUs.size()), B(SUs.size()), AdjK(SUs.size()) {
174      Node2Idx = new std::vector<int>(SUs.size());
175      unsigned Idx = 0;
176      for (const auto &NodeNum : Topo)
177        Node2Idx->at(NodeNum) = Idx++;
178    }
179
180    ~Circuits() { delete Node2Idx; }
181
182    /// Reset the data structures used in the circuit algorithm.
183    void reset() {
184      Stack.clear();
185      Blocked.reset();
186      B.assign(SUnits.size(), SmallPtrSet<SUnit *, 4>());
187      NumPaths = 0;
188    }
189
190    void createAdjacencyStructure(SwingSchedulerDAG *DAG);
191    bool circuit(int V, int S, NodeSetType &NodeSets, bool HasBackedge = false);
192    void unblock(int U);
193  };
194
195  struct CopyToPhiMutation : public ScheduleDAGMutation {
196    void apply(ScheduleDAGInstrs *DAG) override;
197  };
198
199public:
200  SwingSchedulerDAG(MachinePipeliner &P, MachineLoop &L, LiveIntervals &lis,
201                    const RegisterClassInfo &rci, unsigned II)
202      : ScheduleDAGInstrs(*P.MF, P.MLI, false), Pass(P), Loop(L), LIS(lis),
203        RegClassInfo(rci), II_setByPragma(II), Topo(SUnits, &ExitSU) {
204    P.MF->getSubtarget().getSMSMutations(Mutations);
205    if (SwpEnableCopyToPhi)
206      Mutations.push_back(std::make_unique<CopyToPhiMutation>());
207  }
208
209  void schedule() override;
210  void finishBlock() override;
211
212  /// Return true if the loop kernel has been scheduled.
213  bool hasNewSchedule() { return Scheduled; }
214
215  /// Return the earliest time an instruction may be scheduled.
216  int getASAP(SUnit *Node) { return ScheduleInfo[Node->NodeNum].ASAP; }
217
218  /// Return the latest time an instruction my be scheduled.
219  int getALAP(SUnit *Node) { return ScheduleInfo[Node->NodeNum].ALAP; }
220
221  /// The mobility function, which the number of slots in which
222  /// an instruction may be scheduled.
223  int getMOV(SUnit *Node) { return getALAP(Node) - getASAP(Node); }
224
225  /// The depth, in the dependence graph, for a node.
226  unsigned getDepth(SUnit *Node) { return Node->getDepth(); }
227
228  /// The maximum unweighted length of a path from an arbitrary node to the
229  /// given node in which each edge has latency 0
230  int getZeroLatencyDepth(SUnit *Node) {
231    return ScheduleInfo[Node->NodeNum].ZeroLatencyDepth;
232  }
233
234  /// The height, in the dependence graph, for a node.
235  unsigned getHeight(SUnit *Node) { return Node->getHeight(); }
236
237  /// The maximum unweighted length of a path from the given node to an
238  /// arbitrary node in which each edge has latency 0
239  int getZeroLatencyHeight(SUnit *Node) {
240    return ScheduleInfo[Node->NodeNum].ZeroLatencyHeight;
241  }
242
243  /// Return true if the dependence is a back-edge in the data dependence graph.
244  /// Since the DAG doesn't contain cycles, we represent a cycle in the graph
245  /// using an anti dependence from a Phi to an instruction.
246  bool isBackedge(SUnit *Source, const SDep &Dep) {
247    if (Dep.getKind() != SDep::Anti)
248      return false;
249    return Source->getInstr()->isPHI() || Dep.getSUnit()->getInstr()->isPHI();
250  }
251
252  bool isLoopCarriedDep(SUnit *Source, const SDep &Dep, bool isSucc = true);
253
254  /// The distance function, which indicates that operation V of iteration I
255  /// depends on operations U of iteration I-distance.
256  unsigned getDistance(SUnit *U, SUnit *V, const SDep &Dep) {
257    // Instructions that feed a Phi have a distance of 1. Computing larger
258    // values for arrays requires data dependence information.
259    if (V->getInstr()->isPHI() && Dep.getKind() == SDep::Anti)
260      return 1;
261    return 0;
262  }
263
264  void applyInstrChange(MachineInstr *MI, SMSchedule &Schedule);
265
266  void fixupRegisterOverlaps(std::deque<SUnit *> &Instrs);
267
268  /// Return the new base register that was stored away for the changed
269  /// instruction.
270  unsigned getInstrBaseReg(SUnit *SU) {
271    DenseMap<SUnit *, std::pair<unsigned, int64_t>>::iterator It =
272        InstrChanges.find(SU);
273    if (It != InstrChanges.end())
274      return It->second.first;
275    return 0;
276  }
277
278  void addMutation(std::unique_ptr<ScheduleDAGMutation> Mutation) {
279    Mutations.push_back(std::move(Mutation));
280  }
281
282  static bool classof(const ScheduleDAGInstrs *DAG) { return true; }
283
284private:
285  void addLoopCarriedDependences(AliasAnalysis *AA);
286  void updatePhiDependences();
287  void changeDependences();
288  unsigned calculateResMII();
289  unsigned calculateRecMII(NodeSetType &RecNodeSets);
290  void findCircuits(NodeSetType &NodeSets);
291  void fuseRecs(NodeSetType &NodeSets);
292  void removeDuplicateNodes(NodeSetType &NodeSets);
293  void computeNodeFunctions(NodeSetType &NodeSets);
294  void registerPressureFilter(NodeSetType &NodeSets);
295  void colocateNodeSets(NodeSetType &NodeSets);
296  void checkNodeSets(NodeSetType &NodeSets);
297  void groupRemainingNodes(NodeSetType &NodeSets);
298  void addConnectedNodes(SUnit *SU, NodeSet &NewSet,
299                         SetVector<SUnit *> &NodesAdded);
300  void computeNodeOrder(NodeSetType &NodeSets);
301  void checkValidNodeOrder(const NodeSetType &Circuits) const;
302  bool schedulePipeline(SMSchedule &Schedule);
303  bool computeDelta(MachineInstr &MI, unsigned &Delta);
304  MachineInstr *findDefInLoop(unsigned Reg);
305  bool canUseLastOffsetValue(MachineInstr *MI, unsigned &BasePos,
306                             unsigned &OffsetPos, unsigned &NewBase,
307                             int64_t &NewOffset);
308  void postprocessDAG();
309  /// Set the Minimum Initiation Interval for this schedule attempt.
310  void setMII(unsigned ResMII, unsigned RecMII);
311  /// Set the Maximum Initiation Interval for this schedule attempt.
312  void setMAX_II();
313};
314
315/// A NodeSet contains a set of SUnit DAG nodes with additional information
316/// that assigns a priority to the set.
317class NodeSet {
318  SetVector<SUnit *> Nodes;
319  bool HasRecurrence = false;
320  unsigned RecMII = 0;
321  int MaxMOV = 0;
322  unsigned MaxDepth = 0;
323  unsigned Colocate = 0;
324  SUnit *ExceedPressure = nullptr;
325  unsigned Latency = 0;
326
327public:
328  using iterator = SetVector<SUnit *>::const_iterator;
329
330  NodeSet() = default;
331  NodeSet(iterator S, iterator E) : Nodes(S, E), HasRecurrence(true) {
332    Latency = 0;
333    for (unsigned i = 0, e = Nodes.size(); i < e; ++i)
334      for (const SDep &Succ : Nodes[i]->Succs)
335        if (Nodes.count(Succ.getSUnit()))
336          Latency += Succ.getLatency();
337  }
338
339  bool insert(SUnit *SU) { return Nodes.insert(SU); }
340
341  void insert(iterator S, iterator E) { Nodes.insert(S, E); }
342
343  template <typename UnaryPredicate> bool remove_if(UnaryPredicate P) {
344    return Nodes.remove_if(P);
345  }
346
347  unsigned count(SUnit *SU) const { return Nodes.count(SU); }
348
349  bool hasRecurrence() { return HasRecurrence; };
350
351  unsigned size() const { return Nodes.size(); }
352
353  bool empty() const { return Nodes.empty(); }
354
355  SUnit *getNode(unsigned i) const { return Nodes[i]; };
356
357  void setRecMII(unsigned mii) { RecMII = mii; };
358
359  void setColocate(unsigned c) { Colocate = c; };
360
361  void setExceedPressure(SUnit *SU) { ExceedPressure = SU; }
362
363  bool isExceedSU(SUnit *SU) { return ExceedPressure == SU; }
364
365  int compareRecMII(NodeSet &RHS) { return RecMII - RHS.RecMII; }
366
367  int getRecMII() { return RecMII; }
368
369  /// Summarize node functions for the entire node set.
370  void computeNodeSetInfo(SwingSchedulerDAG *SSD) {
371    for (SUnit *SU : *this) {
372      MaxMOV = std::max(MaxMOV, SSD->getMOV(SU));
373      MaxDepth = std::max(MaxDepth, SSD->getDepth(SU));
374    }
375  }
376
377  unsigned getLatency() { return Latency; }
378
379  unsigned getMaxDepth() { return MaxDepth; }
380
381  void clear() {
382    Nodes.clear();
383    RecMII = 0;
384    HasRecurrence = false;
385    MaxMOV = 0;
386    MaxDepth = 0;
387    Colocate = 0;
388    ExceedPressure = nullptr;
389  }
390
391  operator SetVector<SUnit *> &() { return Nodes; }
392
393  /// Sort the node sets by importance. First, rank them by recurrence MII,
394  /// then by mobility (least mobile done first), and finally by depth.
395  /// Each node set may contain a colocate value which is used as the first
396  /// tie breaker, if it's set.
397  bool operator>(const NodeSet &RHS) const {
398    if (RecMII == RHS.RecMII) {
399      if (Colocate != 0 && RHS.Colocate != 0 && Colocate != RHS.Colocate)
400        return Colocate < RHS.Colocate;
401      if (MaxMOV == RHS.MaxMOV)
402        return MaxDepth > RHS.MaxDepth;
403      return MaxMOV < RHS.MaxMOV;
404    }
405    return RecMII > RHS.RecMII;
406  }
407
408  bool operator==(const NodeSet &RHS) const {
409    return RecMII == RHS.RecMII && MaxMOV == RHS.MaxMOV &&
410           MaxDepth == RHS.MaxDepth;
411  }
412
413  bool operator!=(const NodeSet &RHS) const { return !operator==(RHS); }
414
415  iterator begin() { return Nodes.begin(); }
416  iterator end() { return Nodes.end(); }
417  void print(raw_ostream &os) const;
418
419#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
420  LLVM_DUMP_METHOD void dump() const;
421#endif
422};
423
424// 16 was selected based on the number of ProcResource kinds for all
425// existing Subtargets, so that SmallVector don't need to resize too often.
426static const int DefaultProcResSize = 16;
427
428class ResourceManager {
429private:
430  const MCSubtargetInfo *STI;
431  const MCSchedModel &SM;
432  const bool UseDFA;
433  std::unique_ptr<DFAPacketizer> DFAResources;
434  /// Each processor resource is associated with a so-called processor resource
435  /// mask. This vector allows to correlate processor resource IDs with
436  /// processor resource masks. There is exactly one element per each processor
437  /// resource declared by the scheduling model.
438  llvm::SmallVector<uint64_t, DefaultProcResSize> ProcResourceMasks;
439
440  llvm::SmallVector<uint64_t, DefaultProcResSize> ProcResourceCount;
441
442public:
443  ResourceManager(const TargetSubtargetInfo *ST)
444      : STI(ST), SM(ST->getSchedModel()), UseDFA(ST->useDFAforSMS()),
445        ProcResourceMasks(SM.getNumProcResourceKinds(), 0),
446        ProcResourceCount(SM.getNumProcResourceKinds(), 0) {
447    if (UseDFA)
448      DFAResources.reset(ST->getInstrInfo()->CreateTargetScheduleState(*ST));
449    initProcResourceVectors(SM, ProcResourceMasks);
450  }
451
452  void initProcResourceVectors(const MCSchedModel &SM,
453                               SmallVectorImpl<uint64_t> &Masks);
454  /// Check if the resources occupied by a MCInstrDesc are available in
455  /// the current state.
456  bool canReserveResources(const MCInstrDesc *MID) const;
457
458  /// Reserve the resources occupied by a MCInstrDesc and change the current
459  /// state to reflect that change.
460  void reserveResources(const MCInstrDesc *MID);
461
462  /// Check if the resources occupied by a machine instruction are available
463  /// in the current state.
464  bool canReserveResources(const MachineInstr &MI) const;
465
466  /// Reserve the resources occupied by a machine instruction and change the
467  /// current state to reflect that change.
468  void reserveResources(const MachineInstr &MI);
469
470  /// Reset the state
471  void clearResources();
472};
473
474/// This class represents the scheduled code.  The main data structure is a
475/// map from scheduled cycle to instructions.  During scheduling, the
476/// data structure explicitly represents all stages/iterations.   When
477/// the algorithm finshes, the schedule is collapsed into a single stage,
478/// which represents instructions from different loop iterations.
479///
480/// The SMS algorithm allows negative values for cycles, so the first cycle
481/// in the schedule is the smallest cycle value.
482class SMSchedule {
483private:
484  /// Map from execution cycle to instructions.
485  DenseMap<int, std::deque<SUnit *>> ScheduledInstrs;
486
487  /// Map from instruction to execution cycle.
488  std::map<SUnit *, int> InstrToCycle;
489
490  /// Keep track of the first cycle value in the schedule.  It starts
491  /// as zero, but the algorithm allows negative values.
492  int FirstCycle = 0;
493
494  /// Keep track of the last cycle value in the schedule.
495  int LastCycle = 0;
496
497  /// The initiation interval (II) for the schedule.
498  int InitiationInterval = 0;
499
500  /// Target machine information.
501  const TargetSubtargetInfo &ST;
502
503  /// Virtual register information.
504  MachineRegisterInfo &MRI;
505
506  ResourceManager ProcItinResources;
507
508public:
509  SMSchedule(MachineFunction *mf)
510      : ST(mf->getSubtarget()), MRI(mf->getRegInfo()), ProcItinResources(&ST) {}
511
512  void reset() {
513    ScheduledInstrs.clear();
514    InstrToCycle.clear();
515    FirstCycle = 0;
516    LastCycle = 0;
517    InitiationInterval = 0;
518  }
519
520  /// Set the initiation interval for this schedule.
521  void setInitiationInterval(int ii) { InitiationInterval = ii; }
522
523  /// Return the first cycle in the completed schedule.  This
524  /// can be a negative value.
525  int getFirstCycle() const { return FirstCycle; }
526
527  /// Return the last cycle in the finalized schedule.
528  int getFinalCycle() const { return FirstCycle + InitiationInterval - 1; }
529
530  /// Return the cycle of the earliest scheduled instruction in the dependence
531  /// chain.
532  int earliestCycleInChain(const SDep &Dep);
533
534  /// Return the cycle of the latest scheduled instruction in the dependence
535  /// chain.
536  int latestCycleInChain(const SDep &Dep);
537
538  void computeStart(SUnit *SU, int *MaxEarlyStart, int *MinLateStart,
539                    int *MinEnd, int *MaxStart, int II, SwingSchedulerDAG *DAG);
540  bool insert(SUnit *SU, int StartCycle, int EndCycle, int II);
541
542  /// Iterators for the cycle to instruction map.
543  using sched_iterator = DenseMap<int, std::deque<SUnit *>>::iterator;
544  using const_sched_iterator =
545      DenseMap<int, std::deque<SUnit *>>::const_iterator;
546
547  /// Return true if the instruction is scheduled at the specified stage.
548  bool isScheduledAtStage(SUnit *SU, unsigned StageNum) {
549    return (stageScheduled(SU) == (int)StageNum);
550  }
551
552  /// Return the stage for a scheduled instruction.  Return -1 if
553  /// the instruction has not been scheduled.
554  int stageScheduled(SUnit *SU) const {
555    std::map<SUnit *, int>::const_iterator it = InstrToCycle.find(SU);
556    if (it == InstrToCycle.end())
557      return -1;
558    return (it->second - FirstCycle) / InitiationInterval;
559  }
560
561  /// Return the cycle for a scheduled instruction. This function normalizes
562  /// the first cycle to be 0.
563  unsigned cycleScheduled(SUnit *SU) const {
564    std::map<SUnit *, int>::const_iterator it = InstrToCycle.find(SU);
565    assert(it != InstrToCycle.end() && "Instruction hasn't been scheduled.");
566    return (it->second - FirstCycle) % InitiationInterval;
567  }
568
569  /// Return the maximum stage count needed for this schedule.
570  unsigned getMaxStageCount() {
571    return (LastCycle - FirstCycle) / InitiationInterval;
572  }
573
574  /// Return the instructions that are scheduled at the specified cycle.
575  std::deque<SUnit *> &getInstructions(int cycle) {
576    return ScheduledInstrs[cycle];
577  }
578
579  bool isValidSchedule(SwingSchedulerDAG *SSD);
580  void finalizeSchedule(SwingSchedulerDAG *SSD);
581  void orderDependence(SwingSchedulerDAG *SSD, SUnit *SU,
582                       std::deque<SUnit *> &Insts);
583  bool isLoopCarried(SwingSchedulerDAG *SSD, MachineInstr &Phi);
584  bool isLoopCarriedDefOfUse(SwingSchedulerDAG *SSD, MachineInstr *Def,
585                             MachineOperand &MO);
586  void print(raw_ostream &os) const;
587  void dump() const;
588};
589
590} // end namespace llvm
591
592#endif // LLVM_LIB_CODEGEN_MACHINEPIPELINER_H
593