[llvm] [CodeGen] Add complete-schedule search strategy API (PR #220644)

Kevin Choi via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 10:54:17 PDT 2026


https://github.com/choikwa updated https://github.com/llvm/llvm-project/pull/220644

>From c14a26782ff76cbcb4b48172653c833c317f32d5 Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Tue, 1 Sep 2026 22:12:08 +0000
Subject: [PATCH 1/5] [CodeGen] Add complete-schedule search strategy API

Add target-independent support for schedulers that explore complete legal instruction orders instead of choosing one ready node at a time. MachineSchedSearchRegion exposes stable region-local ordinals, strong dependency legality, a topological fallback, and legal relocation ranges. MachineSchedCompleteScheduleOptimizer receives an explicit founder schedule and may return a replacement, leaving target-specific search, scoring, feature extraction, and model inference outside generic CodeGen.

Provide two integration paths for the optimizer. MachineSchedCompleteScheduleReplayer adapts a complete result to the existing incremental MachineSchedStrategy loop, while ScheduleDAGMI can invoke the same optimizer after an ordinary scheduler has materialized its actual founder. Results are validated before use; declined, invalid, and incomplete-cutoff cases preserve the founder. Schedule application reuses LLVM instruction movement and LiveIntervals maintenance, with ScheduleDAGMILive repairing liveness flags after a post-schedule rewrite. These entry points allow targets to plug in SA, learned search, replay, or trajectory collection without placing those policies in the base scheduler.
---
 .../include/llvm/CodeGen/MachineSchedSearch.h | 127 ++++++++
 llvm/include/llvm/CodeGen/MachineScheduler.h  |  49 ++++
 llvm/lib/CodeGen/CMakeLists.txt               |   1 +
 llvm/lib/CodeGen/MachineSchedSearch.cpp       | 245 ++++++++++++++++
 llvm/lib/CodeGen/MachineScheduler.cpp         |   5 +
 llvm/unittests/CodeGen/CMakeLists.txt         |   1 +
 .../CodeGen/MachineSchedSearchTest.cpp        | 277 ++++++++++++++++++
 7 files changed, 705 insertions(+)
 create mode 100644 llvm/include/llvm/CodeGen/MachineSchedSearch.h
 create mode 100644 llvm/lib/CodeGen/MachineSchedSearch.cpp
 create mode 100644 llvm/unittests/CodeGen/MachineSchedSearchTest.cpp

diff --git a/llvm/include/llvm/CodeGen/MachineSchedSearch.h b/llvm/include/llvm/CodeGen/MachineSchedSearch.h
new file mode 100644
index 0000000000000..6476b185f99dc
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/MachineSchedSearch.h
@@ -0,0 +1,127 @@
+//===- MachineSchedSearch.h - Complete schedule search ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides common support for exploring complete instruction orders.
+// A complete-schedule optimizer can either replace the normal scheduling
+// strategy through a replay adapter or refine the schedule materialized by an
+// existing strategy through ScheduleDAGMI's post-scheduling hook.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CODEGEN_MACHINESCHEDSEARCH_H
+#define LLVM_CODEGEN_MACHINESCHEDSEARCH_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/MachineScheduler.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+/// A read-only, region-local view of a fully constructed machine scheduling
+/// DAG for complete-schedule exploration.
+///
+/// Nodes are identified by stable ordinals in [0, size()). Candidate schedules
+/// are complete permutations of those ordinals. Only strong dependencies are
+/// legality constraints; weak edges remain scheduling preferences.
+///
+/// This view is intended for search-based schedulers that evaluate multiple
+/// complete schedules without mutating the MachineFunction. It is valid only
+/// while the underlying SUnit storage remains alive and unchanged.
+class LLVM_ABI MachineSchedSearchRegion {
+public:
+  struct MoveRange {
+    /// First legal final position after removing and reinserting the node.
+    unsigned Begin;
+    /// Last legal final position after removing and reinserting the node.
+    unsigned End;
+  };
+
+private:
+  const ScheduleDAGMI *DAG = nullptr;
+  ArrayRef<SUnit> Nodes;
+  SmallVector<SmallVector<unsigned, 4>, 0> Predecessors;
+  SmallVector<SmallVector<unsigned, 4>, 0> Successors;
+
+public:
+  explicit MachineSchedSearchRegion(ArrayRef<SUnit> Nodes);
+  explicit MachineSchedSearchRegion(ScheduleDAGMI &DAG);
+
+  /// Return the underlying scheduler DAG, or nullptr when the view was
+  /// constructed directly from SUnit storage.
+  const ScheduleDAGMI *getDAG() const { return DAG; }
+  unsigned size() const { return Predecessors.size(); }
+  const SUnit &getSUnit(unsigned Node) const;
+
+  ArrayRef<unsigned> predecessors(unsigned Node) const {
+    return Predecessors[Node];
+  }
+  ArrayRef<unsigned> successors(unsigned Node) const {
+    return Successors[Node];
+  }
+
+  /// Return the order in which nodes appeared when the DAG was built.
+  SmallVector<unsigned, 0> getInitialOrder() const;
+
+  /// Return a stable topological order, preferring lower node ordinals when
+  /// more than one node is ready.
+  SmallVector<unsigned, 0> getTopologicalOrder() const;
+
+  /// Return whether \p Order is a complete permutation that preserves every
+  /// strong dependency in the scheduling DAG.
+  bool isLegalOrder(ArrayRef<unsigned> Order) const;
+
+  /// Return the inclusive range of final positions to which \p Node may be
+  /// relocated while all other nodes retain their relative order. Returns
+  /// false if the input order is not legal or the node ordinal is invalid.
+  bool getLegalMoveRange(ArrayRef<unsigned> Order, unsigned Node,
+                         MoveRange &Range) const;
+};
+
+/// Adapter for schedulers that compute a complete schedule before LLVM begins
+/// applying scheduling decisions.
+///
+/// An owned MachineSchedCompleteScheduleOptimizer receives the incoming legal
+/// order as its founder and may return a replacement. The selected order is
+/// validated before use and replayed top-down through ScheduleDAGMI's
+/// incremental MachineSchedStrategy interface. When used with
+/// ScheduleDAGMILive, instruction movement, LiveIntervals, and
+/// register-pressure accounting therefore remain owned by the existing
+/// scheduler.
+///
+/// This is not a common base for all search-based schedulers. Strategies that
+/// choose each node from the current ready set should implement
+/// MachineSchedStrategy directly or derive from another incremental strategy.
+///
+/// If the optimizer declines to provide an order or returns an invalid one, the
+/// existing order is preserved when legal. Otherwise, a stable topological
+/// order is used.
+class LLVM_ABI MachineSchedCompleteScheduleReplayer
+    : public MachineSchedStrategy {
+  std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer;
+  SmallVector<SUnit *, 0> CompleteSchedule;
+  unsigned NextNodeToReplay = 0;
+  bool UsedOptimizedSchedule = false;
+
+public:
+  explicit MachineSchedCompleteScheduleReplayer(
+      std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer);
+  ~MachineSchedCompleteScheduleReplayer() override;
+
+  void initialize(ScheduleDAGMI *DAG) override;
+  SUnit *pickNode(bool &IsTopNode) override;
+  void schedNode(SUnit *, bool) override {}
+  void releaseTopNode(SUnit *) override {}
+  void releaseBottomNode(SUnit *) override {}
+
+  bool usedOptimizedSchedule() const { return UsedOptimizedSchedule; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_CODEGEN_MACHINESCHEDSEARCH_H
diff --git a/llvm/include/llvm/CodeGen/MachineScheduler.h b/llvm/include/llvm/CodeGen/MachineScheduler.h
index 5280a2376c34a..cac8b2f9e9afc 100644
--- a/llvm/include/llvm/CodeGen/MachineScheduler.h
+++ b/llvm/include/llvm/CodeGen/MachineScheduler.h
@@ -133,6 +133,7 @@ class LiveIntervals;
 class MachineFunction;
 class MachineInstr;
 class MachineLoopInfo;
+class MachineSchedSearchRegion;
 class RegisterClassInfo;
 class SchedDFSResult;
 class TargetInstrInfo;
@@ -305,6 +306,31 @@ class LLVM_ABI MachineSchedStrategy {
   virtual void releaseBottomNode(SUnit *SU) = 0;
 };
 
+/// Interface for transforming one complete legal schedule into another.
+///
+/// Founder is explicit so the same optimizer can run either before scheduling,
+/// with the incoming order as founder, or after an existing strategy has
+/// materialized a target-selected founder.
+class LLVM_ABI MachineSchedCompleteScheduleOptimizer {
+public:
+  virtual ~MachineSchedCompleteScheduleOptimizer();
+
+  /// Optimize Founder and write a complete region-local SUnit permutation to
+  /// Result. Return false to preserve Founder. The caller validates generic
+  /// permutation and strong-dependency legality before applying Result.
+  virtual bool optimizeCompleteSchedule(const MachineSchedSearchRegion &Region,
+                                        ArrayRef<unsigned> Founder,
+                                        SmallVectorImpl<unsigned> &Result) = 0;
+
+  /// Perform any additional client-specific validation that is not represented
+  /// by strong DAG dependencies. This is called only for a complete,
+  /// dependency-preserving result returned by optimizeCompleteSchedule().
+  virtual bool validateCompleteSchedule(const MachineSchedSearchRegion &Region,
+                                        ArrayRef<unsigned> Result) {
+    return true;
+  }
+};
+
 /// ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply
 /// schedules machine instructions according to the given MachineSchedStrategy
 /// without much extra book-keeping. This is the common functionality between
@@ -316,6 +342,10 @@ class LLVM_ABI ScheduleDAGMI : public ScheduleDAGInstrs {
   MachineBlockFrequencyInfo *MBFI;
   std::unique_ptr<MachineSchedStrategy> SchedImpl;
 
+  /// Optional optimizer invoked after SchedImpl has materialized a complete
+  /// schedule for a region.
+  std::unique_ptr<MachineSchedCompleteScheduleOptimizer> PostSchedOptimizer;
+
   /// Ordered list of DAG postprocessing steps.
   std::vector<std::unique_ptr<ScheduleDAGMutation>> Mutations;
 
@@ -363,6 +393,11 @@ class LLVM_ABI ScheduleDAGMI : public ScheduleDAGInstrs {
       Mutations.push_back(std::move(Mutation));
   }
 
+  /// Install an optimizer that may replace the complete schedule produced by
+  /// SchedImpl. ScheduleDAGMI takes ownership of the optimizer.
+  void setPostScheduleOptimizer(
+      std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer);
+
   MachineBasicBlock::iterator top() const { return CurrentTop; }
   MachineBasicBlock::iterator bottom() const { return CurrentBottom; }
 
@@ -395,6 +430,18 @@ class LLVM_ABI ScheduleDAGMI : public ScheduleDAGInstrs {
   /// instances of ScheduleDAGMI to perform custom DAG postprocessing.
   void postProcessDAG();
 
+  /// Invoke PostSchedOptimizer on the materialized schedule, if present.
+  /// This runs only after every SUnit has been scheduled. It does not rebuild
+  /// ready queues or the register-pressure state used while selecting nodes.
+  void runPostScheduleOptimizer();
+
+  /// Apply a complete region-local SUnit order to the instruction stream.
+  /// The order must already have been validated against the current DAG.
+  /// Target DAGs may override this to update target metadata derived from the
+  /// final instruction order, but should delegate instruction movement and
+  /// generic liveness maintenance to the base implementation.
+  virtual void applyCompleteSchedule(ArrayRef<unsigned> Order);
+
   /// Release ExitSU predecessors and setup scheduler queues.
   void initQueues(ArrayRef<SUnit*> TopRoots, ArrayRef<SUnit*> BotRoots);
 
@@ -426,6 +473,8 @@ class LLVM_ABI ScheduleDAGMI : public ScheduleDAGInstrs {
 /// machine instructions while updating LiveIntervals and tracking regpressure.
 class LLVM_ABI ScheduleDAGMILive : public ScheduleDAGMI {
 protected:
+  void applyCompleteSchedule(ArrayRef<unsigned> Order) override;
+
   RegisterClassInfo *RegClassInfo;
 
   /// Information about DAG subtrees. If DFSResult is NULL, then SchedulerTrees
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index 99dfb4bb09df7..1af30b8c2033f 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -158,6 +158,7 @@ add_llvm_component_library(LLVMCodeGen
   MachineRegionInfo.cpp
   MachineRegisterInfo.cpp
   MachineScheduler.cpp
+  MachineSchedSearch.cpp
   MachineSink.cpp
   MachineSizeOpts.cpp
   MachineSSAContext.cpp
diff --git a/llvm/lib/CodeGen/MachineSchedSearch.cpp b/llvm/lib/CodeGen/MachineSchedSearch.cpp
new file mode 100644
index 0000000000000..fa5ef43f1715f
--- /dev/null
+++ b/llvm/lib/CodeGen/MachineSchedSearch.cpp
@@ -0,0 +1,245 @@
+//===- MachineSchedSearch.cpp - Complete schedule search ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/MachineSchedSearch.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include <algorithm>
+#include <functional>
+#include <numeric>
+#include <queue>
+#include <vector>
+
+using namespace llvm;
+
+MachineSchedCompleteScheduleOptimizer::
+    ~MachineSchedCompleteScheduleOptimizer() = default;
+
+MachineSchedSearchRegion::MachineSchedSearchRegion(ArrayRef<SUnit> Nodes)
+    : Nodes(Nodes), Predecessors(Nodes.size()), Successors(Nodes.size()) {
+  DenseMap<const SUnit *, unsigned> Ordinals;
+  for (auto [Ordinal, SU] : enumerate(Nodes))
+    Ordinals[&SU] = Ordinal;
+
+  for (auto [Ordinal, SU] : enumerate(Nodes)) {
+    for (const SDep &Pred : SU.Preds) {
+      const SUnit *PredSU = Pred.getSUnit();
+      if (Pred.isWeak() || PredSU->isBoundaryNode())
+        continue;
+      auto PredOrdinal = Ordinals.find(PredSU);
+      assert(PredOrdinal != Ordinals.end() &&
+             "predecessor must belong to the scheduling region");
+      if (PredOrdinal == Ordinals.end())
+        continue;
+      Predecessors[Ordinal].push_back(PredOrdinal->second);
+      Successors[PredOrdinal->second].push_back(Ordinal);
+    }
+  }
+}
+
+MachineSchedSearchRegion::MachineSchedSearchRegion(ScheduleDAGMI &DAG)
+    : MachineSchedSearchRegion(DAG.SUnits) {
+  this->DAG = &DAG;
+}
+
+const SUnit &MachineSchedSearchRegion::getSUnit(unsigned Node) const {
+  assert(Node < size() && "invalid scheduling node ordinal");
+  return Nodes[Node];
+}
+
+SmallVector<unsigned, 0> MachineSchedSearchRegion::getInitialOrder() const {
+  SmallVector<unsigned, 0> Order(size());
+  std::iota(Order.begin(), Order.end(), 0);
+  return Order;
+}
+
+SmallVector<unsigned, 0> MachineSchedSearchRegion::getTopologicalOrder() const {
+  SmallVector<unsigned, 0> RemainingPredecessors;
+  RemainingPredecessors.reserve(size());
+  for (unsigned Node = 0; Node != size(); ++Node)
+    RemainingPredecessors.push_back(predecessors(Node).size());
+
+  std::priority_queue<unsigned, std::vector<unsigned>, std::greater<unsigned>>
+      Ready;
+  for (unsigned Node = 0; Node != size(); ++Node)
+    if (RemainingPredecessors[Node] == 0)
+      Ready.push(Node);
+
+  SmallVector<unsigned, 0> Order;
+  Order.reserve(size());
+  while (!Ready.empty()) {
+    unsigned Node = Ready.top();
+    Ready.pop();
+    Order.push_back(Node);
+    for (unsigned Succ : successors(Node)) {
+      assert(RemainingPredecessors[Succ] != 0 &&
+             "inconsistent machine scheduling DAG");
+      --RemainingPredecessors[Succ];
+      if (RemainingPredecessors[Succ] == 0)
+        Ready.push(Succ);
+    }
+  }
+  assert(Order.size() == size() &&
+         "machine scheduling DAG contains a strong dependency cycle");
+  return Order;
+}
+
+bool MachineSchedSearchRegion::isLegalOrder(ArrayRef<unsigned> Order) const {
+  if (Order.size() != size())
+    return false;
+
+  SmallVector<unsigned, 0> Position(size(), size());
+  for (auto [Pos, Node] : enumerate(Order)) {
+    if (Node >= size() || Position[Node] != size())
+      return false;
+    Position[Node] = Pos;
+  }
+
+  for (unsigned Node = 0; Node != size(); ++Node)
+    for (unsigned Pred : predecessors(Node))
+      if (Position[Pred] >= Position[Node])
+        return false;
+  return true;
+}
+
+bool MachineSchedSearchRegion::getLegalMoveRange(ArrayRef<unsigned> Order,
+                                                 unsigned Node,
+                                                 MoveRange &Range) const {
+  if (Node >= size() || !isLegalOrder(Order))
+    return false;
+
+  SmallVector<unsigned, 0> Position(size());
+  for (auto [Pos, Ordinal] : enumerate(Order))
+    Position[Ordinal] = Pos;
+
+  Range.Begin = 0;
+  Range.End = size() - 1;
+  for (unsigned Pred : predecessors(Node))
+    Range.Begin = std::max(Range.Begin, Position[Pred] + 1);
+  for (unsigned Succ : successors(Node))
+    Range.End = std::min(Range.End, Position[Succ] - 1);
+  return true;
+}
+
+MachineSchedCompleteScheduleReplayer::MachineSchedCompleteScheduleReplayer(
+    std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer)
+    : Optimizer(std::move(Optimizer)) {
+  assert(this->Optimizer && "complete-schedule optimizer must be provided");
+}
+
+MachineSchedCompleteScheduleReplayer::~MachineSchedCompleteScheduleReplayer() =
+    default;
+
+void MachineSchedCompleteScheduleReplayer::initialize(ScheduleDAGMI *DAG) {
+  MachineSchedSearchRegion Region(*DAG);
+  SmallVector<unsigned, 0> Founder = Region.getInitialOrder();
+  if (!Region.isLegalOrder(Founder))
+    Founder = Region.getTopologicalOrder();
+
+  SmallVector<unsigned, 0> Order;
+  UsedOptimizedSchedule =
+      Optimizer->optimizeCompleteSchedule(Region, Founder, Order) &&
+      Region.isLegalOrder(Order) &&
+      Optimizer->validateCompleteSchedule(Region, Order);
+  if (!UsedOptimizedSchedule) {
+    Order = std::move(Founder);
+  }
+
+  CompleteSchedule.clear();
+  CompleteSchedule.reserve(Order.size());
+  for (unsigned Node : Order)
+    CompleteSchedule.push_back(&DAG->SUnits[Node]);
+  NextNodeToReplay = 0;
+}
+
+SUnit *MachineSchedCompleteScheduleReplayer::pickNode(bool &IsTopNode) {
+  if (NextNodeToReplay == CompleteSchedule.size())
+    return nullptr;
+  SUnit *SU = CompleteSchedule[NextNodeToReplay++];
+  assert(SU->isTopReady() &&
+         "validated complete schedule contains an unavailable node");
+  IsTopNode = true;
+  return SU;
+}
+
+void ScheduleDAGMI::setPostScheduleOptimizer(
+    std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer) {
+  PostSchedOptimizer = std::move(Optimizer);
+}
+
+void ScheduleDAGMI::runPostScheduleOptimizer() {
+  if (!PostSchedOptimizer || SUnits.empty())
+    return;
+
+  // A debug scheduling cutoff makes CurrentTop meet CurrentBottom without
+  // necessarily scheduling the whole region. There is no complete founder to
+  // optimize in that case.
+  if (!llvm::all_of(SUnits, [](const SUnit &SU) { return SU.isScheduled; }))
+    return;
+
+  MachineSchedSearchRegion Region(*this);
+  DenseMap<const MachineInstr *, unsigned> Ordinals;
+  for (auto [Ordinal, SU] : enumerate(SUnits))
+    Ordinals[SU.getInstr()] = Ordinal;
+
+  SmallVector<unsigned, 0> Founder;
+  Founder.reserve(SUnits.size());
+  for (auto I = RegionBegin; I != RegionEnd; ++I) {
+    auto Ordinal = Ordinals.find(&*I);
+    if (Ordinal != Ordinals.end())
+      Founder.push_back(Ordinal->second);
+  }
+  if (!Region.isLegalOrder(Founder))
+    return;
+
+  SmallVector<unsigned, 0> Result;
+  if (!PostSchedOptimizer->optimizeCompleteSchedule(Region, Founder, Result) ||
+      !Region.isLegalOrder(Result) ||
+      !PostSchedOptimizer->validateCompleteSchedule(Region, Result) ||
+      Result == Founder)
+    return;
+
+  applyCompleteSchedule(Result);
+}
+
+void ScheduleDAGMI::applyCompleteSchedule(ArrayRef<unsigned> Order) {
+  MachineBasicBlock::iterator InsertPos = RegionBegin;
+  for (unsigned Node : Order) {
+    MachineInstr *MI = SUnits[Node].getInstr();
+    while (InsertPos != RegionEnd && InsertPos->isDebugInstr())
+      ++InsertPos;
+    if (InsertPos != RegionEnd && &*InsertPos == MI) {
+      ++InsertPos;
+      continue;
+    }
+    moveInstruction(MI, InsertPos);
+    InsertPos = std::next(MI->getIterator());
+  }
+}
+
+void ScheduleDAGMILive::applyCompleteSchedule(ArrayRef<unsigned> Order) {
+  ScheduleDAGMI::applyCompleteSchedule(Order);
+  if (!ShouldTrackPressure)
+    return;
+
+  for (unsigned Node : Order) {
+    MachineInstr *MI = SUnits[Node].getInstr();
+    // A prior ordering may have introduced read-undef flags that are no longer
+    // valid. Recompute them together with the other liveness flags below.
+    for (MachineOperand &Op : MI->all_defs())
+      Op.setIsUndef(false);
+
+    RegisterOperands RegOpers;
+    RegOpers.collect(*MI, *TRI, MRI, ShouldTrackLaneMasks,
+                     /*IgnoreDead=*/false);
+    if (ShouldTrackLaneMasks)
+      RegOpers.adjustLaneLiveness(*LIS, MRI, *MI);
+    else
+      RegOpers.detectDeadDefs(*MI, *LIS);
+  }
+}
diff --git a/llvm/lib/CodeGen/MachineScheduler.cpp b/llvm/lib/CodeGen/MachineScheduler.cpp
index 814ea3f8eeb05..fea955e4a4342 100644
--- a/llvm/lib/CodeGen/MachineScheduler.cpp
+++ b/llvm/lib/CodeGen/MachineScheduler.cpp
@@ -32,6 +32,7 @@
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachinePassRegistry.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/MachineSchedSearch.h"
 #include "llvm/CodeGen/RegisterClassInfo.h"
 #include "llvm/CodeGen/RegisterPressure.h"
 #include "llvm/CodeGen/ScheduleDAG.h"
@@ -1118,6 +1119,8 @@ void ScheduleDAGMI::schedule() {
   }
   assert(CurrentTop == CurrentBottom && "Nonempty unscheduled zone.");
 
+  runPostScheduleOptimizer();
+
   placeDebugValues();
 
   LLVM_DEBUG({
@@ -1738,6 +1741,8 @@ void ScheduleDAGMILive::schedule() {
   }
   assert(CurrentTop == CurrentBottom && "Nonempty unscheduled zone.");
 
+  runPostScheduleOptimizer();
+
   placeDebugValues();
 
   LLVM_DEBUG({
diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt
index deaa3dbcfcc5b..eb632f15d21d0 100644
--- a/llvm/unittests/CodeGen/CMakeLists.txt
+++ b/llvm/unittests/CodeGen/CMakeLists.txt
@@ -39,6 +39,7 @@ add_llvm_unittest(CodeGenTests
   MachineInstrTest.cpp
   MachineModuleInfoTest.cpp
   MachineOperandTest.cpp
+  MachineSchedSearchTest.cpp
   MIR2VecTest.cpp
   RegAllocBasicTest.cpp
   RegAllocScoreTest.cpp
diff --git a/llvm/unittests/CodeGen/MachineSchedSearchTest.cpp b/llvm/unittests/CodeGen/MachineSchedSearchTest.cpp
new file mode 100644
index 0000000000000..9ab9a4732798d
--- /dev/null
+++ b/llvm/unittests/CodeGen/MachineSchedSearchTest.cpp
@@ -0,0 +1,277 @@
+#include "llvm/CodeGen/MachineSchedSearch.h"
+#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/Module.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/TargetParser/Triple.h"
+#include "gtest/gtest.h"
+#include <array>
+
+using namespace llvm;
+
+namespace {
+
+// Include helper functions to construct a target-independent MachineFunction.
+#include "MFCommon.inc"
+
+void initializeTestDAG(std::array<SUnit, 5> &Nodes) {
+  for (unsigned I = 0; I != Nodes.size(); ++I)
+    Nodes[I].NodeNum = I;
+
+  // 0 --\
+  //       2 -> 3    4
+  // 1 --/
+  Nodes[2].addPred(SDep(&Nodes[0], SDep::Artificial));
+  Nodes[2].addPred(SDep(&Nodes[1], SDep::Artificial));
+  Nodes[3].addPred(SDep(&Nodes[2], SDep::Artificial));
+}
+
+class TestCompleteScheduleOptimizer final
+    : public MachineSchedCompleteScheduleOptimizer {
+  SmallVector<unsigned> Result;
+  SmallVector<unsigned> *SeenFounder;
+  bool AcceptResult;
+  bool AcceptValidation;
+
+public:
+  TestCompleteScheduleOptimizer(ArrayRef<unsigned> Result,
+                                bool AcceptResult = true,
+                                bool AcceptValidation = true,
+                                SmallVector<unsigned> *SeenFounder = nullptr)
+      : Result(Result), SeenFounder(SeenFounder), AcceptResult(AcceptResult),
+        AcceptValidation(AcceptValidation) {}
+
+  bool optimizeCompleteSchedule(const MachineSchedSearchRegion &,
+                                ArrayRef<unsigned> Founder,
+                                SmallVectorImpl<unsigned> &Order) override {
+    if (SeenFounder)
+      SeenFounder->assign(Founder.begin(), Founder.end());
+    Order.assign(Result.begin(), Result.end());
+    return AcceptResult;
+  }
+
+  bool validateCompleteSchedule(const MachineSchedSearchRegion &,
+                                ArrayRef<unsigned>) override {
+    return AcceptValidation;
+  }
+};
+
+class TestScheduleDAGMI final : public ScheduleDAGMI {
+public:
+  TestScheduleDAGMI(MachineSchedContext *Context,
+                    std::unique_ptr<MachineSchedStrategy> Strategy)
+      : ScheduleDAGMI(Context, std::move(Strategy),
+                      /*RemoveKillFlags=*/false) {}
+
+  using ScheduleDAGMI::runPostScheduleOptimizer;
+};
+
+SmallVector<unsigned> initializeAndPick(ArrayRef<unsigned> Result,
+                                        bool AcceptResult = true,
+                                        bool AcceptValidation = true) {
+  LLVMContext Context;
+  Module M("MachineSchedSearchTest", Context);
+  std::unique_ptr<MachineFunction> MF = createMachineFunction(Context, M);
+  MachineSchedContext SchedContext;
+  SchedContext.MF = MF.get();
+
+  auto Optimizer = std::make_unique<TestCompleteScheduleOptimizer>(
+      Result, AcceptResult, AcceptValidation);
+  auto Strategy = std::make_unique<MachineSchedCompleteScheduleReplayer>(
+      std::move(Optimizer));
+  MachineSchedCompleteScheduleReplayer *StrategyPtr = Strategy.get();
+  ScheduleDAGMI DAG(&SchedContext, std::move(Strategy),
+                    /*RemoveKillFlags=*/false);
+  DAG.SUnits.resize(3);
+  for (unsigned I = 0; I != DAG.SUnits.size(); ++I)
+    DAG.SUnits[I].NodeNum = I;
+
+  StrategyPtr->initialize(&DAG);
+  SmallVector<unsigned> Picked;
+  bool IsTopNode = false;
+  while (SUnit *SU = StrategyPtr->pickNode(IsTopNode)) {
+    EXPECT_TRUE(IsTopNode);
+    Picked.push_back(SU->NodeNum);
+  }
+  return Picked;
+}
+
+SmallVector<unsigned>
+runPostOptimization(ArrayRef<unsigned> Result, bool AcceptResult = true,
+                    bool AcceptValidation = true,
+                    bool MaterializeCompleteFounder = true,
+                    SmallVector<unsigned> *SeenFounder = nullptr,
+                    ArrayRef<unsigned> MaterializedFounder = {}) {
+  LLVMContext Context;
+  Module M("MachineSchedCompleteScheduleOptimizerTest", Context);
+  std::unique_ptr<MachineFunction> MF = createMachineFunction(Context, M);
+  MachineBasicBlock *MBB = MF->CreateMachineBasicBlock();
+  MF->push_back(MBB);
+
+  MCInstrDesc Desc = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  std::array<MachineInstr *, 3> Instructions;
+  for (MachineInstr *&MI : Instructions)
+    MI = MF->CreateMachineInstr(Desc, DebugLoc());
+  if (MaterializedFounder.empty()) {
+    for (MachineInstr *MI : Instructions)
+      MBB->push_back(MI);
+  } else {
+    for (unsigned Node : MaterializedFounder)
+      MBB->push_back(Instructions[Node]);
+  }
+
+  MachineSchedContext SchedContext;
+  SchedContext.MF = MF.get();
+  auto ReplayOptimizer = std::make_unique<TestCompleteScheduleOptimizer>(
+      SmallVector<unsigned, 3>{0, 1, 2});
+  auto Strategy = std::make_unique<MachineSchedCompleteScheduleReplayer>(
+      std::move(ReplayOptimizer));
+  TestScheduleDAGMI DAG(&SchedContext, std::move(Strategy));
+  DAG.startBlock(MBB);
+  DAG.enterRegion(MBB, MBB->begin(), MBB->end(), Instructions.size());
+  for (auto [Ordinal, MI] : enumerate(Instructions)) {
+    DAG.SUnits.emplace_back(MI, Ordinal);
+    DAG.SUnits.back().isScheduled = MaterializeCompleteFounder;
+  }
+
+  DAG.setPostScheduleOptimizer(std::make_unique<TestCompleteScheduleOptimizer>(
+      Result, AcceptResult, AcceptValidation, SeenFounder));
+  DAG.runPostScheduleOptimizer();
+
+  SmallVector<unsigned> Order;
+  for (MachineInstr &MI : *MBB) {
+    auto I = llvm::find(Instructions, &MI);
+    if (I == Instructions.end()) {
+      ADD_FAILURE() << "instruction not found in original region";
+      return {};
+    }
+    Order.push_back(std::distance(Instructions.begin(), I));
+  }
+  return Order;
+}
+
+} // namespace
+
+TEST(MachineSchedSearchRegion, ValidatesCompleteOrders) {
+  std::array<SUnit, 5> Nodes;
+  initializeTestDAG(Nodes);
+  MachineSchedSearchRegion Region(Nodes);
+
+  EXPECT_TRUE(Region.isLegalOrder({0, 1, 2, 3, 4}));
+  EXPECT_TRUE(Region.isLegalOrder({4, 1, 0, 2, 3}));
+
+  EXPECT_FALSE(Region.isLegalOrder({0, 1, 2, 3}));
+  EXPECT_FALSE(Region.isLegalOrder({0, 1, 2, 3, 5}));
+  EXPECT_FALSE(Region.isLegalOrder({0, 1, 2, 3, 3}));
+  EXPECT_FALSE(Region.isLegalOrder({2, 0, 1, 3, 4}));
+  EXPECT_FALSE(Region.isLegalOrder({0, 1, 3, 2, 4}));
+}
+
+TEST(MachineSchedSearchRegion, WeakEdgesArePreferences) {
+  std::array<SUnit, 2> Nodes;
+  for (unsigned I = 0; I != Nodes.size(); ++I)
+    Nodes[I].NodeNum = I;
+  Nodes[1].addPred(SDep(&Nodes[0], SDep::Weak));
+
+  MachineSchedSearchRegion Region(Nodes);
+  EXPECT_TRUE(Region.isLegalOrder({0, 1}));
+  EXPECT_TRUE(Region.isLegalOrder({1, 0}));
+}
+
+TEST(MachineSchedSearchRegion, UsesViewLocalOrdinals) {
+  std::array<SUnit, 2> Nodes;
+  Nodes[0].NodeNum = 17;
+  Nodes[1].NodeNum = 9;
+  Nodes[1].addPred(SDep(&Nodes[0], SDep::Artificial));
+
+  MachineSchedSearchRegion Region(Nodes);
+  EXPECT_TRUE(Region.isLegalOrder({0, 1}));
+  EXPECT_FALSE(Region.isLegalOrder({1, 0}));
+  EXPECT_EQ(&Region.getSUnit(0), &Nodes[0]);
+  EXPECT_EQ(&Region.getSUnit(1), &Nodes[1]);
+}
+
+TEST(MachineSchedSearchRegion, BuildsStableTopologicalFallback) {
+  std::array<SUnit, 3> Nodes;
+  for (unsigned I = 0; I != Nodes.size(); ++I)
+    Nodes[I].NodeNum = I;
+  Nodes[0].addPred(SDep(&Nodes[1], SDep::Artificial));
+
+  MachineSchedSearchRegion Region(Nodes);
+  EXPECT_FALSE(Region.isLegalOrder(Region.getInitialOrder()));
+  EXPECT_EQ(Region.getTopologicalOrder(), (SmallVector<unsigned, 3>{1, 0, 2}));
+}
+
+TEST(MachineSchedSearchRegion, ComputesLegalRelocationRange) {
+  std::array<SUnit, 5> Nodes;
+  initializeTestDAG(Nodes);
+  MachineSchedSearchRegion Region(Nodes);
+  const unsigned Order[] = {0, 1, 2, 3, 4};
+  MachineSchedSearchRegion::MoveRange Range;
+
+  ASSERT_TRUE(Region.getLegalMoveRange(Order, 0, Range));
+  EXPECT_EQ(Range.Begin, 0u);
+  EXPECT_EQ(Range.End, 1u);
+
+  ASSERT_TRUE(Region.getLegalMoveRange(Order, 2, Range));
+  EXPECT_EQ(Range.Begin, 2u);
+  EXPECT_EQ(Range.End, 2u);
+
+  ASSERT_TRUE(Region.getLegalMoveRange(Order, 4, Range));
+  EXPECT_EQ(Range.Begin, 0u);
+  EXPECT_EQ(Range.End, 4u);
+
+  EXPECT_FALSE(Region.getLegalMoveRange({2, 0, 1, 3, 4}, 0, Range));
+  EXPECT_FALSE(Region.getLegalMoveRange(Order, 5, Range));
+}
+
+TEST(MachineSchedCompleteScheduleReplayer, ReplaysValidatedCompleteOrder) {
+  EXPECT_EQ(initializeAndPick({2, 0, 1}), (SmallVector<unsigned, 3>{2, 0, 1}));
+}
+
+TEST(MachineSchedCompleteScheduleReplayer, PreservesExistingOrderOnFailure) {
+  EXPECT_EQ(initializeAndPick({2, 2, 1}), (SmallVector<unsigned, 3>{0, 1, 2}));
+  EXPECT_EQ(initializeAndPick({2, 0, 1}, /*AcceptResult=*/false),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+  EXPECT_EQ(initializeAndPick({2, 0, 1}, /*AcceptResult=*/true,
+                              /*AcceptValidation=*/false),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+}
+
+TEST(MachineSchedCompleteScheduleOptimizer, OptimizesMaterializedFounder) {
+  SmallVector<unsigned> SeenFounder;
+  EXPECT_EQ(runPostOptimization({2, 0, 1}, /*AcceptResult=*/true,
+                                /*AcceptValidation=*/true,
+                                /*MaterializeCompleteFounder=*/true,
+                                &SeenFounder,
+                                /*MaterializedFounder=*/{1, 2, 0}),
+            (SmallVector<unsigned, 3>{2, 0, 1}));
+  EXPECT_EQ(SeenFounder, (SmallVector<unsigned, 3>{1, 2, 0}));
+}
+
+TEST(MachineSchedCompleteScheduleOptimizer, PreservesFounderOnFailure) {
+  EXPECT_EQ(runPostOptimization({2, 2, 1}),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+  EXPECT_EQ(runPostOptimization({2, 0, 1}, /*AcceptResult=*/false),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+  EXPECT_EQ(runPostOptimization({2, 0, 1}, /*AcceptResult=*/true,
+                                /*AcceptValidation=*/false),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+}
+
+TEST(MachineSchedCompleteScheduleOptimizer, SkipsIncompleteFounder) {
+  SmallVector<unsigned> SeenFounder;
+  EXPECT_EQ(runPostOptimization({2, 0, 1}, /*AcceptResult=*/true,
+                                /*AcceptValidation=*/true,
+                                /*MaterializeCompleteFounder=*/false,
+                                &SeenFounder),
+            (SmallVector<unsigned, 3>{0, 1, 2}));
+  EXPECT_TRUE(SeenFounder.empty());
+}

>From 0979aa0a1e9b6aa1884e323d197906bcde97614d Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Wed, 2 Sep 2026 19:50:33 +0000
Subject: [PATCH 2/5] address review

---
 llvm/include/llvm/CodeGen/MachineScheduler.h | 2 +-
 llvm/lib/CodeGen/MachineSchedSearch.cpp      | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineScheduler.h b/llvm/include/llvm/CodeGen/MachineScheduler.h
index cac8b2f9e9afc..7a49a4c2c8489 100644
--- a/llvm/include/llvm/CodeGen/MachineScheduler.h
+++ b/llvm/include/llvm/CodeGen/MachineScheduler.h
@@ -311,7 +311,7 @@ class LLVM_ABI MachineSchedStrategy {
 /// Founder is explicit so the same optimizer can run either before scheduling,
 /// with the incoming order as founder, or after an existing strategy has
 /// materialized a target-selected founder.
-class LLVM_ABI MachineSchedCompleteScheduleOptimizer {
+struct LLVM_ABI MachineSchedCompleteScheduleOptimizer {
 public:
   virtual ~MachineSchedCompleteScheduleOptimizer();
 
diff --git a/llvm/lib/CodeGen/MachineSchedSearch.cpp b/llvm/lib/CodeGen/MachineSchedSearch.cpp
index fa5ef43f1715f..f127c2045771b 100644
--- a/llvm/lib/CodeGen/MachineSchedSearch.cpp
+++ b/llvm/lib/CodeGen/MachineSchedSearch.cpp
@@ -9,9 +9,9 @@
 #include "llvm/CodeGen/MachineSchedSearch.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
 #include <algorithm>
 #include <functional>
-#include <numeric>
 #include <queue>
 #include <vector>
 
@@ -53,8 +53,7 @@ const SUnit &MachineSchedSearchRegion::getSUnit(unsigned Node) const {
 }
 
 SmallVector<unsigned, 0> MachineSchedSearchRegion::getInitialOrder() const {
-  SmallVector<unsigned, 0> Order(size());
-  std::iota(Order.begin(), Order.end(), 0);
+  SmallVector<unsigned, 0> Order = to_vector<0>(seq<unsigned>(size()));;
   return Order;
 }
 

>From d0b5a36a8ff1e27713450f6e964d337230e61210 Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Wed, 2 Sep 2026 19:56:42 +0000
Subject: [PATCH 3/5] NFC, add Founder definition in comment

---
 llvm/include/llvm/CodeGen/MachineScheduler.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineScheduler.h b/llvm/include/llvm/CodeGen/MachineScheduler.h
index 7a49a4c2c8489..d01989e2750e0 100644
--- a/llvm/include/llvm/CodeGen/MachineScheduler.h
+++ b/llvm/include/llvm/CodeGen/MachineScheduler.h
@@ -308,9 +308,9 @@ class LLVM_ABI MachineSchedStrategy {
 
 /// Interface for transforming one complete legal schedule into another.
 ///
-/// Founder is explicit so the same optimizer can run either before scheduling,
-/// with the incoming order as founder, or after an existing strategy has
-/// materialized a target-selected founder.
+/// The incoming complete schedule is denoted as Founder. It can be either
+/// the initial serialized nodenum schedule before scheduler runs or the
+/// completed schedule after scheduler ran.
 struct LLVM_ABI MachineSchedCompleteScheduleOptimizer {
 public:
   virtual ~MachineSchedCompleteScheduleOptimizer();

>From f4cb7db7fb0a77d52f078566cb1149ba473d2c46 Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Wed, 2 Sep 2026 19:58:17 +0000
Subject: [PATCH 4/5] remove extra ;

---
 llvm/lib/CodeGen/MachineSchedSearch.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/MachineSchedSearch.cpp b/llvm/lib/CodeGen/MachineSchedSearch.cpp
index f127c2045771b..70c0636026e3a 100644
--- a/llvm/lib/CodeGen/MachineSchedSearch.cpp
+++ b/llvm/lib/CodeGen/MachineSchedSearch.cpp
@@ -53,7 +53,7 @@ const SUnit &MachineSchedSearchRegion::getSUnit(unsigned Node) const {
 }
 
 SmallVector<unsigned, 0> MachineSchedSearchRegion::getInitialOrder() const {
-  SmallVector<unsigned, 0> Order = to_vector<0>(seq<unsigned>(size()));;
+  SmallVector<unsigned, 0> Order = to_vector<0>(seq<unsigned>(size()));
   return Order;
 }
 

>From f424fbcb9c419ea2a7918ab3a7c1b80e5b830863 Mon Sep 17 00:00:00 2001
From: choikwa <code.kchoi at gmail.com>
Date: Wed, 30 Sep 2026 17:53:24 +0000
Subject: [PATCH 5/5] Address review

---
 .../include/llvm/CodeGen/MachineSchedSearch.h | 25 ++++++-------------
 llvm/lib/CodeGen/MachineSchedSearch.cpp       |  8 +++---
 2 files changed, 11 insertions(+), 22 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineSchedSearch.h b/llvm/include/llvm/CodeGen/MachineSchedSearch.h
index 6476b185f99dc..a7e45817451c5 100644
--- a/llvm/include/llvm/CodeGen/MachineSchedSearch.h
+++ b/llvm/include/llvm/CodeGen/MachineSchedSearch.h
@@ -74,6 +74,7 @@ class LLVM_ABI MachineSchedSearchRegion {
 
   /// Return whether \p Order is a complete permutation that preserves every
   /// strong dependency in the scheduling DAG.
+  /// TODO: Optimize legality check. ie. can only check a delta
   bool isLegalOrder(ArrayRef<unsigned> Order) const;
 
   /// Return the inclusive range of final positions to which \p Node may be
@@ -83,24 +84,12 @@ class LLVM_ABI MachineSchedSearchRegion {
                          MoveRange &Range) const;
 };
 
-/// Adapter for schedulers that compute a complete schedule before LLVM begins
-/// applying scheduling decisions.
-///
-/// An owned MachineSchedCompleteScheduleOptimizer receives the incoming legal
-/// order as its founder and may return a replacement. The selected order is
-/// validated before use and replayed top-down through ScheduleDAGMI's
-/// incremental MachineSchedStrategy interface. When used with
-/// ScheduleDAGMILive, instruction movement, LiveIntervals, and
-/// register-pressure accounting therefore remain owned by the existing
-/// scheduler.
-///
-/// This is not a common base for all search-based schedulers. Strategies that
-/// choose each node from the current ready set should implement
-/// MachineSchedStrategy directly or derive from another incremental strategy.
-///
-/// If the optimizer declines to provide an order or returns an invalid one, the
-/// existing order is preserved when legal. Otherwise, a stable topological
-/// order is used.
+/// Replay a complete schedule through ScheduleDAGMI's MachineSchedStrategy
+/// interface. The MachineSchedCompleteScheduleOptimizer may replace the initial
+/// legal order. After validating the replacement, pickNode() returns its nodes
+/// sequentially from the top, letting ScheduleDAGMI apply each scheduling
+/// decision. If the optimizer declines or its order is invalid, the initial
+/// order is replayed.
 class LLVM_ABI MachineSchedCompleteScheduleReplayer
     : public MachineSchedStrategy {
   std::unique_ptr<MachineSchedCompleteScheduleOptimizer> Optimizer;
diff --git a/llvm/lib/CodeGen/MachineSchedSearch.cpp b/llvm/lib/CodeGen/MachineSchedSearch.cpp
index 70c0636026e3a..add395bc9f599 100644
--- a/llvm/lib/CodeGen/MachineSchedSearch.cpp
+++ b/llvm/lib/CodeGen/MachineSchedSearch.cpp
@@ -32,8 +32,6 @@ MachineSchedSearchRegion::MachineSchedSearchRegion(ArrayRef<SUnit> Nodes)
       if (Pred.isWeak() || PredSU->isBoundaryNode())
         continue;
       auto PredOrdinal = Ordinals.find(PredSU);
-      assert(PredOrdinal != Ordinals.end() &&
-             "predecessor must belong to the scheduling region");
       if (PredOrdinal == Ordinals.end())
         continue;
       Predecessors[Ordinal].push_back(PredOrdinal->second);
@@ -99,10 +97,12 @@ bool MachineSchedSearchRegion::isLegalOrder(ArrayRef<unsigned> Order) const {
     Position[Node] = Pos;
   }
 
-  for (unsigned Node = 0; Node != size(); ++Node)
-    for (unsigned Pred : predecessors(Node))
+  for (unsigned Node = 0; Node != size(); ++Node) {
+    for (unsigned Pred : predecessors(Node)) {
       if (Position[Pred] >= Position[Node])
         return false;
+    }
+  }
   return true;
 }
 



More information about the llvm-commits mailing list