diff options
Diffstat (limited to 'contrib/llvm/lib/CodeGen/MachineScheduler.cpp')
| -rw-r--r-- | contrib/llvm/lib/CodeGen/MachineScheduler.cpp | 614 | 
1 files changed, 614 insertions, 0 deletions
diff --git a/contrib/llvm/lib/CodeGen/MachineScheduler.cpp b/contrib/llvm/lib/CodeGen/MachineScheduler.cpp new file mode 100644 index 000000000000..1d3241b8cc6b --- /dev/null +++ b/contrib/llvm/lib/CodeGen/MachineScheduler.cpp @@ -0,0 +1,614 @@ +//===- MachineScheduler.cpp - Machine Instruction Scheduler ---------------===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// MachineScheduler schedules machine instructions after phi elimination. It +// preserves LiveIntervals so it can be invoked before register allocation. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "misched" + +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MachineScheduler.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/ScheduleDAGInstrs.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/PriorityQueue.h" + +#include <queue> + +using namespace llvm; + +static cl::opt<bool> ForceTopDown("misched-topdown", cl::Hidden, +                                  cl::desc("Force top-down list scheduling")); +static cl::opt<bool> ForceBottomUp("misched-bottomup", cl::Hidden, +                                  cl::desc("Force bottom-up list scheduling")); + +#ifndef NDEBUG +static cl::opt<bool> ViewMISchedDAGs("view-misched-dags", cl::Hidden, +  cl::desc("Pop up a window to show MISched dags after they are processed")); + +static cl::opt<unsigned> MISchedCutoff("misched-cutoff", cl::Hidden, +  cl::desc("Stop scheduling after N instructions"), cl::init(~0U)); +#else +static bool ViewMISchedDAGs = false; +#endif // NDEBUG + +//===----------------------------------------------------------------------===// +// Machine Instruction Scheduling Pass and Registry +//===----------------------------------------------------------------------===// + +namespace { +/// MachineScheduler runs after coalescing and before register allocation. +class MachineScheduler : public MachineSchedContext, +                         public MachineFunctionPass { +public: +  MachineScheduler(); + +  virtual void getAnalysisUsage(AnalysisUsage &AU) const; + +  virtual void releaseMemory() {} + +  virtual bool runOnMachineFunction(MachineFunction&); + +  virtual void print(raw_ostream &O, const Module* = 0) const; + +  static char ID; // Class identification, replacement for typeinfo +}; +} // namespace + +char MachineScheduler::ID = 0; + +char &llvm::MachineSchedulerID = MachineScheduler::ID; + +INITIALIZE_PASS_BEGIN(MachineScheduler, "misched", +                      "Machine Instruction Scheduler", false, false) +INITIALIZE_AG_DEPENDENCY(AliasAnalysis) +INITIALIZE_PASS_DEPENDENCY(SlotIndexes) +INITIALIZE_PASS_DEPENDENCY(LiveIntervals) +INITIALIZE_PASS_END(MachineScheduler, "misched", +                    "Machine Instruction Scheduler", false, false) + +MachineScheduler::MachineScheduler() +: MachineFunctionPass(ID) { +  initializeMachineSchedulerPass(*PassRegistry::getPassRegistry()); +} + +void MachineScheduler::getAnalysisUsage(AnalysisUsage &AU) const { +  AU.setPreservesCFG(); +  AU.addRequiredID(MachineDominatorsID); +  AU.addRequired<MachineLoopInfo>(); +  AU.addRequired<AliasAnalysis>(); +  AU.addRequired<TargetPassConfig>(); +  AU.addRequired<SlotIndexes>(); +  AU.addPreserved<SlotIndexes>(); +  AU.addRequired<LiveIntervals>(); +  AU.addPreserved<LiveIntervals>(); +  MachineFunctionPass::getAnalysisUsage(AU); +} + +MachinePassRegistry MachineSchedRegistry::Registry; + +/// A dummy default scheduler factory indicates whether the scheduler +/// is overridden on the command line. +static ScheduleDAGInstrs *useDefaultMachineSched(MachineSchedContext *C) { +  return 0; +} + +/// MachineSchedOpt allows command line selection of the scheduler. +static cl::opt<MachineSchedRegistry::ScheduleDAGCtor, false, +               RegisterPassParser<MachineSchedRegistry> > +MachineSchedOpt("misched", +                cl::init(&useDefaultMachineSched), cl::Hidden, +                cl::desc("Machine instruction scheduler to use")); + +static MachineSchedRegistry +DefaultSchedRegistry("default", "Use the target's default scheduler choice.", +                     useDefaultMachineSched); + +/// Forward declare the standard machine scheduler. This will be used as the +/// default scheduler if the target does not set a default. +static ScheduleDAGInstrs *createConvergingSched(MachineSchedContext *C); + +/// Top-level MachineScheduler pass driver. +/// +/// Visit blocks in function order. Divide each block into scheduling regions +/// and visit them bottom-up. Visiting regions bottom-up is not required, but is +/// consistent with the DAG builder, which traverses the interior of the +/// scheduling regions bottom-up. +/// +/// This design avoids exposing scheduling boundaries to the DAG builder, +/// simplifying the DAG builder's support for "special" target instructions. +/// At the same time the design allows target schedulers to operate across +/// scheduling boundaries, for example to bundle the boudary instructions +/// without reordering them. This creates complexity, because the target +/// scheduler must update the RegionBegin and RegionEnd positions cached by +/// ScheduleDAGInstrs whenever adding or removing instructions. A much simpler +/// design would be to split blocks at scheduling boundaries, but LLVM has a +/// general bias against block splitting purely for implementation simplicity. +bool MachineScheduler::runOnMachineFunction(MachineFunction &mf) { +  // Initialize the context of the pass. +  MF = &mf; +  MLI = &getAnalysis<MachineLoopInfo>(); +  MDT = &getAnalysis<MachineDominatorTree>(); +  PassConfig = &getAnalysis<TargetPassConfig>(); +  AA = &getAnalysis<AliasAnalysis>(); + +  LIS = &getAnalysis<LiveIntervals>(); +  const TargetInstrInfo *TII = MF->getTarget().getInstrInfo(); + +  // Select the scheduler, or set the default. +  MachineSchedRegistry::ScheduleDAGCtor Ctor = MachineSchedOpt; +  if (Ctor == useDefaultMachineSched) { +    // Get the default scheduler set by the target. +    Ctor = MachineSchedRegistry::getDefault(); +    if (!Ctor) { +      Ctor = createConvergingSched; +      MachineSchedRegistry::setDefault(Ctor); +    } +  } +  // Instantiate the selected scheduler. +  OwningPtr<ScheduleDAGInstrs> Scheduler(Ctor(this)); + +  // Visit all machine basic blocks. +  for (MachineFunction::iterator MBB = MF->begin(), MBBEnd = MF->end(); +       MBB != MBBEnd; ++MBB) { + +    Scheduler->startBlock(MBB); + +    // Break the block into scheduling regions [I, RegionEnd), and schedule each +    // region as soon as it is discovered. RegionEnd points the the scheduling +    // boundary at the bottom of the region. The DAG does not include RegionEnd, +    // but the region does (i.e. the next RegionEnd is above the previous +    // RegionBegin). If the current block has no terminator then RegionEnd == +    // MBB->end() for the bottom region. +    // +    // The Scheduler may insert instructions during either schedule() or +    // exitRegion(), even for empty regions. So the local iterators 'I' and +    // 'RegionEnd' are invalid across these calls. +    unsigned RemainingCount = MBB->size(); +    for(MachineBasicBlock::iterator RegionEnd = MBB->end(); +        RegionEnd != MBB->begin(); RegionEnd = Scheduler->begin()) { +      // Avoid decrementing RegionEnd for blocks with no terminator. +      if (RegionEnd != MBB->end() +          || TII->isSchedulingBoundary(llvm::prior(RegionEnd), MBB, *MF)) { +        --RegionEnd; +        // Count the boundary instruction. +        --RemainingCount; +      } + +      // The next region starts above the previous region. Look backward in the +      // instruction stream until we find the nearest boundary. +      MachineBasicBlock::iterator I = RegionEnd; +      for(;I != MBB->begin(); --I, --RemainingCount) { +        if (TII->isSchedulingBoundary(llvm::prior(I), MBB, *MF)) +          break; +      } +      // Notify the scheduler of the region, even if we may skip scheduling +      // it. Perhaps it still needs to be bundled. +      Scheduler->enterRegion(MBB, I, RegionEnd, RemainingCount); + +      // Skip empty scheduling regions (0 or 1 schedulable instructions). +      if (I == RegionEnd || I == llvm::prior(RegionEnd)) { +        // Close the current region. Bundle the terminator if needed. +        // This invalidates 'RegionEnd' and 'I'. +        Scheduler->exitRegion(); +        continue; +      } +      DEBUG(dbgs() << "MachineScheduling " << MF->getFunction()->getName() +            << ":BB#" << MBB->getNumber() << "\n  From: " << *I << "    To: "; +            if (RegionEnd != MBB->end()) dbgs() << *RegionEnd; +            else dbgs() << "End"; +            dbgs() << " Remaining: " << RemainingCount << "\n"); + +      // Schedule a region: possibly reorder instructions. +      // This invalidates 'RegionEnd' and 'I'. +      Scheduler->schedule(); + +      // Close the current region. +      Scheduler->exitRegion(); + +      // Scheduling has invalidated the current iterator 'I'. Ask the +      // scheduler for the top of it's scheduled region. +      RegionEnd = Scheduler->begin(); +    } +    assert(RemainingCount == 0 && "Instruction count mismatch!"); +    Scheduler->finishBlock(); +  } +  Scheduler->finalizeSchedule(); +  DEBUG(LIS->print(dbgs())); +  return true; +} + +void MachineScheduler::print(raw_ostream &O, const Module* m) const { +  // unimplemented +} + +//===----------------------------------------------------------------------===// +// MachineSchedStrategy - Interface to a machine scheduling algorithm. +//===----------------------------------------------------------------------===// + +namespace { +class ScheduleDAGMI; + +/// MachineSchedStrategy - Interface used by ScheduleDAGMI to drive the selected +/// scheduling algorithm. +/// +/// If this works well and targets wish to reuse ScheduleDAGMI, we may expose it +/// in ScheduleDAGInstrs.h +class MachineSchedStrategy { +public: +  virtual ~MachineSchedStrategy() {} + +  /// Initialize the strategy after building the DAG for a new region. +  virtual void initialize(ScheduleDAGMI *DAG) = 0; + +  /// Pick the next node to schedule, or return NULL. Set IsTopNode to true to +  /// schedule the node at the top of the unscheduled region. Otherwise it will +  /// be scheduled at the bottom. +  virtual SUnit *pickNode(bool &IsTopNode) = 0; + +  /// When all predecessor dependencies have been resolved, free this node for +  /// top-down scheduling. +  virtual void releaseTopNode(SUnit *SU) = 0; +  /// When all successor dependencies have been resolved, free this node for +  /// bottom-up scheduling. +  virtual void releaseBottomNode(SUnit *SU) = 0; +}; +} // namespace + +//===----------------------------------------------------------------------===// +// ScheduleDAGMI - Base class for MachineInstr scheduling with LiveIntervals +// preservation. +//===----------------------------------------------------------------------===// + +namespace { +/// ScheduleDAGMI is an implementation of ScheduleDAGInstrs that schedules +/// machine instructions while updating LiveIntervals. +class ScheduleDAGMI : public ScheduleDAGInstrs { +  AliasAnalysis *AA; +  MachineSchedStrategy *SchedImpl; + +  /// The top of the unscheduled zone. +  MachineBasicBlock::iterator CurrentTop; + +  /// The bottom of the unscheduled zone. +  MachineBasicBlock::iterator CurrentBottom; + +  /// The number of instructions scheduled so far. Used to cut off the +  /// scheduler at the point determined by misched-cutoff. +  unsigned NumInstrsScheduled; +public: +  ScheduleDAGMI(MachineSchedContext *C, MachineSchedStrategy *S): +    ScheduleDAGInstrs(*C->MF, *C->MLI, *C->MDT, /*IsPostRA=*/false, C->LIS), +    AA(C->AA), SchedImpl(S), CurrentTop(), CurrentBottom(), +    NumInstrsScheduled(0) {} + +  ~ScheduleDAGMI() { +    delete SchedImpl; +  } + +  MachineBasicBlock::iterator top() const { return CurrentTop; } +  MachineBasicBlock::iterator bottom() const { return CurrentBottom; } + +  /// Implement ScheduleDAGInstrs interface. +  void schedule(); + +protected: +  void moveInstruction(MachineInstr *MI, MachineBasicBlock::iterator InsertPos); +  bool checkSchedLimit(); + +  void releaseSucc(SUnit *SU, SDep *SuccEdge); +  void releaseSuccessors(SUnit *SU); +  void releasePred(SUnit *SU, SDep *PredEdge); +  void releasePredecessors(SUnit *SU); +}; +} // namespace + +/// ReleaseSucc - Decrement the NumPredsLeft count of a successor. When +/// NumPredsLeft reaches zero, release the successor node. +void ScheduleDAGMI::releaseSucc(SUnit *SU, SDep *SuccEdge) { +  SUnit *SuccSU = SuccEdge->getSUnit(); + +#ifndef NDEBUG +  if (SuccSU->NumPredsLeft == 0) { +    dbgs() << "*** Scheduling failed! ***\n"; +    SuccSU->dump(this); +    dbgs() << " has been released too many times!\n"; +    llvm_unreachable(0); +  } +#endif +  --SuccSU->NumPredsLeft; +  if (SuccSU->NumPredsLeft == 0 && SuccSU != &ExitSU) +    SchedImpl->releaseTopNode(SuccSU); +} + +/// releaseSuccessors - Call releaseSucc on each of SU's successors. +void ScheduleDAGMI::releaseSuccessors(SUnit *SU) { +  for (SUnit::succ_iterator I = SU->Succs.begin(), E = SU->Succs.end(); +       I != E; ++I) { +    releaseSucc(SU, &*I); +  } +} + +/// ReleasePred - Decrement the NumSuccsLeft count of a predecessor. When +/// NumSuccsLeft reaches zero, release the predecessor node. +void ScheduleDAGMI::releasePred(SUnit *SU, SDep *PredEdge) { +  SUnit *PredSU = PredEdge->getSUnit(); + +#ifndef NDEBUG +  if (PredSU->NumSuccsLeft == 0) { +    dbgs() << "*** Scheduling failed! ***\n"; +    PredSU->dump(this); +    dbgs() << " has been released too many times!\n"; +    llvm_unreachable(0); +  } +#endif +  --PredSU->NumSuccsLeft; +  if (PredSU->NumSuccsLeft == 0 && PredSU != &EntrySU) +    SchedImpl->releaseBottomNode(PredSU); +} + +/// releasePredecessors - Call releasePred on each of SU's predecessors. +void ScheduleDAGMI::releasePredecessors(SUnit *SU) { +  for (SUnit::pred_iterator I = SU->Preds.begin(), E = SU->Preds.end(); +       I != E; ++I) { +    releasePred(SU, &*I); +  } +} + +void ScheduleDAGMI::moveInstruction(MachineInstr *MI, +                                    MachineBasicBlock::iterator InsertPos) { +  // Fix RegionBegin if the first instruction moves down. +  if (&*RegionBegin == MI) +    RegionBegin = llvm::next(RegionBegin); +  BB->splice(InsertPos, BB, MI); +  LIS->handleMove(MI); +  // Fix RegionBegin if another instruction moves above the first instruction. +  if (RegionBegin == InsertPos) +    RegionBegin = MI; +} + +bool ScheduleDAGMI::checkSchedLimit() { +#ifndef NDEBUG +  if (NumInstrsScheduled == MISchedCutoff && MISchedCutoff != ~0U) { +    CurrentTop = CurrentBottom; +    return false; +  } +  ++NumInstrsScheduled; +#endif +  return true; +} + +/// schedule - Called back from MachineScheduler::runOnMachineFunction +/// after setting up the current scheduling region. +void ScheduleDAGMI::schedule() { +  buildSchedGraph(AA); + +  DEBUG(dbgs() << "********** MI Scheduling **********\n"); +  DEBUG(for (unsigned su = 0, e = SUnits.size(); su != e; ++su) +          SUnits[su].dumpAll(this)); + +  if (ViewMISchedDAGs) viewGraph(); + +  SchedImpl->initialize(this); + +  // Release edges from the special Entry node or to the special Exit node. +  releaseSuccessors(&EntrySU); +  releasePredecessors(&ExitSU); + +  // Release all DAG roots for scheduling. +  for (std::vector<SUnit>::iterator I = SUnits.begin(), E = SUnits.end(); +       I != E; ++I) { +    // A SUnit is ready to top schedule if it has no predecessors. +    if (I->Preds.empty()) +      SchedImpl->releaseTopNode(&(*I)); +    // A SUnit is ready to bottom schedule if it has no successors. +    if (I->Succs.empty()) +      SchedImpl->releaseBottomNode(&(*I)); +  } + +  CurrentTop = RegionBegin; +  CurrentBottom = RegionEnd; +  bool IsTopNode = false; +  while (SUnit *SU = SchedImpl->pickNode(IsTopNode)) { +    DEBUG(dbgs() << "*** " << (IsTopNode ? "Top" : "Bottom") +          << " Scheduling Instruction:\n"; SU->dump(this)); +    if (!checkSchedLimit()) +      break; + +    // Move the instruction to its new location in the instruction stream. +    MachineInstr *MI = SU->getInstr(); + +    if (IsTopNode) { +      assert(SU->isTopReady() && "node still has unscheduled dependencies"); +      if (&*CurrentTop == MI) +        ++CurrentTop; +      else +        moveInstruction(MI, CurrentTop); +      // Release dependent instructions for scheduling. +      releaseSuccessors(SU); +    } +    else { +      assert(SU->isBottomReady() && "node still has unscheduled dependencies"); +      if (&*llvm::prior(CurrentBottom) == MI) +        --CurrentBottom; +      else { +        if (&*CurrentTop == MI) +          CurrentTop = llvm::next(CurrentTop); +        moveInstruction(MI, CurrentBottom); +        CurrentBottom = MI; +      } +      // Release dependent instructions for scheduling. +      releasePredecessors(SU); +    } +    SU->isScheduled = true; +  } +  assert(CurrentTop == CurrentBottom && "Nonempty unscheduled zone."); +} + +//===----------------------------------------------------------------------===// +// ConvergingScheduler - Implementation of the standard MachineSchedStrategy. +//===----------------------------------------------------------------------===// + +namespace { +/// ConvergingScheduler shrinks the unscheduled zone using heuristics to balance +/// the schedule. +class ConvergingScheduler : public MachineSchedStrategy { +  ScheduleDAGMI *DAG; + +  unsigned NumTopReady; +  unsigned NumBottomReady; + +public: +  virtual void initialize(ScheduleDAGMI *dag) { +    DAG = dag; + +    assert((!ForceTopDown || !ForceBottomUp) && +           "-misched-topdown incompatible with -misched-bottomup"); +  } + +  virtual SUnit *pickNode(bool &IsTopNode) { +    if (DAG->top() == DAG->bottom()) +      return NULL; + +    // As an initial placeholder heuristic, schedule in the direction that has +    // the fewest choices. +    SUnit *SU; +    if (ForceTopDown || (!ForceBottomUp && NumTopReady <= NumBottomReady)) { +      SU = DAG->getSUnit(DAG->top()); +      IsTopNode = true; +    } +    else { +      SU = DAG->getSUnit(llvm::prior(DAG->bottom())); +      IsTopNode = false; +    } +    if (SU->isTopReady()) { +      assert(NumTopReady > 0 && "bad ready count"); +      --NumTopReady; +    } +    if (SU->isBottomReady()) { +      assert(NumBottomReady > 0 && "bad ready count"); +      --NumBottomReady; +    } +    return SU; +  } + +  virtual void releaseTopNode(SUnit *SU) { +    ++NumTopReady; +  } +  virtual void releaseBottomNode(SUnit *SU) { +    ++NumBottomReady; +  } +}; +} // namespace + +/// Create the standard converging machine scheduler. This will be used as the +/// default scheduler if the target does not set a default. +static ScheduleDAGInstrs *createConvergingSched(MachineSchedContext *C) { +  assert((!ForceTopDown || !ForceBottomUp) && +         "-misched-topdown incompatible with -misched-bottomup"); +  return new ScheduleDAGMI(C, new ConvergingScheduler()); +} +static MachineSchedRegistry +ConvergingSchedRegistry("converge", "Standard converging scheduler.", +                        createConvergingSched); + +//===----------------------------------------------------------------------===// +// Machine Instruction Shuffler for Correctness Testing +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +namespace { +/// Apply a less-than relation on the node order, which corresponds to the +/// instruction order prior to scheduling. IsReverse implements greater-than. +template<bool IsReverse> +struct SUnitOrder { +  bool operator()(SUnit *A, SUnit *B) const { +    if (IsReverse) +      return A->NodeNum > B->NodeNum; +    else +      return A->NodeNum < B->NodeNum; +  } +}; + +/// Reorder instructions as much as possible. +class InstructionShuffler : public MachineSchedStrategy { +  bool IsAlternating; +  bool IsTopDown; + +  // Using a less-than relation (SUnitOrder<false>) for the TopQ priority +  // gives nodes with a higher number higher priority causing the latest +  // instructions to be scheduled first. +  PriorityQueue<SUnit*, std::vector<SUnit*>, SUnitOrder<false> > +    TopQ; +  // When scheduling bottom-up, use greater-than as the queue priority. +  PriorityQueue<SUnit*, std::vector<SUnit*>, SUnitOrder<true> > +    BottomQ; +public: +  InstructionShuffler(bool alternate, bool topdown) +    : IsAlternating(alternate), IsTopDown(topdown) {} + +  virtual void initialize(ScheduleDAGMI *) { +    TopQ.clear(); +    BottomQ.clear(); +  } + +  /// Implement MachineSchedStrategy interface. +  /// ----------------------------------------- + +  virtual SUnit *pickNode(bool &IsTopNode) { +    SUnit *SU; +    if (IsTopDown) { +      do { +        if (TopQ.empty()) return NULL; +        SU = TopQ.top(); +        TopQ.pop(); +      } while (SU->isScheduled); +      IsTopNode = true; +    } +    else { +      do { +        if (BottomQ.empty()) return NULL; +        SU = BottomQ.top(); +        BottomQ.pop(); +      } while (SU->isScheduled); +      IsTopNode = false; +    } +    if (IsAlternating) +      IsTopDown = !IsTopDown; +    return SU; +  } + +  virtual void releaseTopNode(SUnit *SU) { +    TopQ.push(SU); +  } +  virtual void releaseBottomNode(SUnit *SU) { +    BottomQ.push(SU); +  } +}; +} // namespace + +static ScheduleDAGInstrs *createInstructionShuffler(MachineSchedContext *C) { +  bool Alternate = !ForceTopDown && !ForceBottomUp; +  bool TopDown = !ForceBottomUp; +  assert((TopDown || !ForceTopDown) && +         "-misched-topdown incompatible with -misched-bottomup"); +  return new ScheduleDAGMI(C, new InstructionShuffler(Alternate, TopDown)); +} +static MachineSchedRegistry ShufflerRegistry( +  "shuffle", "Shuffle machine instructions alternating directions", +  createInstructionShuffler); +#endif // !NDEBUG  | 
