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