[llvm] [llvm][AArch64] Add pipeliner remarks (PR #213157)

Jon Roelofs via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 30 16:48:28 PDT 2026


https://github.com/jroelofs updated https://github.com/llvm/llvm-project/pull/213157

>From 84bdd8a0f535c22c52dfff4e972f9d553bf2c974 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Thu, 30 Jul 2026 15:12:52 -0700
Subject: [PATCH] [llvm][AArch64] Add pipeliner remarks

---
 llvm/include/llvm/CodeGen/MachinePipeliner.h  |   6 +
 llvm/include/llvm/CodeGen/TargetInstrInfo.h   |   8 +-
 llvm/include/llvm/CodeGen/WindowScheduler.h   |   4 +
 llvm/lib/CodeGen/MachinePipeliner.cpp         |  25 ++-
 llvm/lib/Target/AArch64/AArch64InstrInfo.cpp  | 106 +++++++++--
 llvm/lib/Target/AArch64/AArch64InstrInfo.h    |   5 +-
 llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp      |   3 +-
 llvm/lib/Target/ARM/ARMBaseInstrInfo.h        |   5 +-
 llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp  |   3 +-
 llvm/lib/Target/Hexagon/HexagonInstrInfo.h    |   5 +-
 llvm/lib/Target/PowerPC/PPCInstrInfo.cpp      |   3 +-
 llvm/lib/Target/PowerPC/PPCInstrInfo.h        |   5 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.cpp      |   3 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.h        |   5 +-
 .../AArch64/sms-analyzeloop-remarks.mir       | 168 ++++++++++++++++++
 .../CodeGen/AArch64/sms-schedule-remark.mir   |  83 +++++++++
 llvm/test/CodeGen/Hexagon/swp-ws-exp.mir      |   5 +
 llvm/test/CodeGen/Hexagon/swp-ws-fail-0.mir   |   5 +
 llvm/test/CodeGen/PowerPC/sms-remark.ll       |   2 +-
 19 files changed, 417 insertions(+), 32 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/sms-analyzeloop-remarks.mir
 create mode 100644 llvm/test/CodeGen/AArch64/sms-schedule-remark.mir

diff --git a/llvm/include/llvm/CodeGen/MachinePipeliner.h b/llvm/include/llvm/CodeGen/MachinePipeliner.h
index 19699f069ae72..769d3edbf1aef 100644
--- a/llvm/include/llvm/CodeGen/MachinePipeliner.h
+++ b/llvm/include/llvm/CodeGen/MachinePipeliner.h
@@ -288,6 +288,12 @@ class LLVM_ABI SwingSchedulerDAG : public ScheduleDAGInstrs {
   unsigned MII = 0;
   /// The maximum initiation interval between iterations for this schedule.
   unsigned MAX_II = 0;
+  /// The resource-constrained minimum initiation interval, i.e. the lower
+  /// bound on MII imposed by the availability of processor resources.
+  unsigned ResMII = 0;
+  /// The recurrence-constrained minimum initiation interval, i.e. the lower
+  /// bound on MII imposed by loop-carried dependence cycles.
+  unsigned RecMII = 0;
   /// Set to true if a valid pipelined schedule is found for the loop.
   bool Scheduled = false;
   MachineLoop &Loop;
diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index 4749d06501cb2..d3227758fb4e9 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -50,6 +50,7 @@ class MachineLoop;
 class MachineLoopInfo;
 class MachineMemOperand;
 class MachineModuleInfo;
+class MachineOptimizationRemarkEmitter;
 class MachineRegisterInfo;
 class MCAsmInfo;
 class MCInst;
@@ -897,8 +898,11 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
 
   /// Analyze loop L, which must be a single-basic-block loop, and if the
   /// conditions can be understood enough produce a PipelinerLoopInfo object.
-  virtual std::unique_ptr<PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+  /// \p ORE, if non-null, may be used by targets to emit optimization remarks
+  /// explaining why the loop was rejected for pipelining.
+  virtual std::unique_ptr<PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const {
     return nullptr;
   }
 
diff --git a/llvm/include/llvm/CodeGen/WindowScheduler.h b/llvm/include/llvm/CodeGen/WindowScheduler.h
index 12b3474dfa784..d8dbade1a421c 100644
--- a/llvm/include/llvm/CodeGen/WindowScheduler.h
+++ b/llvm/include/llvm/CodeGen/WindowScheduler.h
@@ -109,6 +109,10 @@ class LLVM_ABI WindowScheduler {
 
   bool run();
 
+  /// Returns the Initiation Interval of the best scheduling result found, or
+  /// UINT_MAX if run() has not been called or found no valid schedule.
+  unsigned getBestII() const { return BestII; }
+
 protected:
   /// Two types of ScheduleDAGs are needed, one for creating dependency graphs
   /// only, and the other for list scheduling as determined by the target.
diff --git a/llvm/lib/CodeGen/MachinePipeliner.cpp b/llvm/lib/CodeGen/MachinePipeliner.cpp
index a97e0702d1911..531a7a2cabed6 100644
--- a/llvm/lib/CodeGen/MachinePipeliner.cpp
+++ b/llvm/lib/CodeGen/MachinePipeliner.cpp
@@ -588,7 +588,7 @@ bool MachinePipeliner::canPipelineLoop(MachineLoop &L) {
 
   LI.LoopInductionVar = nullptr;
   LI.LoopCompare = nullptr;
-  LI.LoopPipelinerInfo = TII->analyzeLoopForPipelining(L.getTopBlock());
+  LI.LoopPipelinerInfo = TII->analyzeLoopForPipelining(L.getTopBlock(), ORE);
   if (!LI.LoopPipelinerInfo) {
     LLVM_DEBUG(dbgs() << "Unable to analyzeLoop, can NOT pipeline Loop\n");
     NumFailLoop++;
@@ -721,7 +721,23 @@ bool MachinePipeliner::runWindowScheduler(MachineLoop &L) {
   Context.RegClassInfo =
       &getAnalysis<MachineRegisterClassInfoWrapperPass>().getRCI();
   WindowScheduler WS(&Context, L);
-  return WS.run();
+  bool Scheduled = WS.run();
+  if (Scheduled) {
+    unsigned II = WS.getBestII();
+    ORE->emit([&]() {
+      return MachineOptimizationRemark(DEBUG_TYPE, "window-schedule",
+                                       L.getStartLoc(), L.getHeader())
+             << "Window scheduled with Initiation Interval: "
+             << ore::NV("II", II);
+    });
+  } else {
+    ORE->emit([&]() {
+      return MachineOptimizationRemarkMissed(DEBUG_TYPE, "window-schedule",
+                                             L.getStartLoc(), L.getHeader())
+             << "Failed to find a valid window schedule";
+    });
+  }
+  return Scheduled;
 }
 
 bool MachinePipeliner::useSwingModuloScheduler() {
@@ -2902,7 +2918,10 @@ bool SwingSchedulerDAG::schedulePipeline(SMSchedule &Schedule) {
              << "Schedule found with Initiation Interval: "
              << ore::NV("II", Schedule.getInitiationInterval())
              << ", MaxStageCount: "
-             << ore::NV("MaxStageCount", Schedule.getMaxStageCount());
+             << ore::NV("MaxStageCount", Schedule.getMaxStageCount())
+             << ", ResMII: " << ore::NV("ResMII", ResMII)
+             << ", RecMII: " << ore::NV("RecMII", RecMII) << ", Bound: "
+             << ore::NV("Bound", ResMII >= RecMII ? "Resource" : "Recurrence");
     });
   } else
     Schedule.reset();
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index ec4fe39aafd80..03fe0e76597be 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -25,6 +25,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/CodeGen/CFIInstBuilder.h"
 #include "llvm/CodeGen/LivePhysRegs.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
@@ -36,6 +37,7 @@
 #include "llvm/CodeGen/MachineMemOperand.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/RegisterScavenging.h"
 #include "llvm/CodeGen/StackMaps.h"
@@ -12029,7 +12031,8 @@ static bool getIndVarInfo(Register Reg, const MachineBasicBlock *LoopBB,
 }
 
 std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-AArch64InstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+AArch64InstrInfo::analyzeLoopForPipelining(
+    MachineBasicBlock *LoopBB, MachineOptimizationRemarkEmitter *ORE) const {
   // Accept loops that meet the following conditions
   // * The conditional branch is BCC
   // * The compare instruction is ADDS/SUBS/WHILEXX
@@ -12038,24 +12041,66 @@ AArch64InstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
   // * The induction variable is incremented/decremented by a single instruction
   // * Does not contain CALL or instructions which have unmodeled side effects
 
-  for (MachineInstr &MI : *LoopBB)
-    if (MI.isCall() || MI.hasUnmodeledSideEffects())
-      // This instruction may use NZCV, which interferes with the instruction to
-      // be inserted for loop control.
+  for (MachineInstr &MI : *LoopBB) {
+    // This instruction may use NZCV, which interferes with the instruction to
+    // be inserted for loop control.
+    if (MI.isCall()) {
+      if (ORE)
+        ORE->emit([&]() {
+          return MachineOptimizationRemarkAnalysis("pipeliner", "analyzeLoop",
+                                                   &MI)
+                 << "loop contains a call and therefore cannot be pipelined";
+        });
+      return nullptr;
+    }
+    if (MI.hasUnmodeledSideEffects()) {
+      if (ORE)
+        ORE->emit([&]() {
+          return MachineOptimizationRemarkAnalysis("pipeliner", "analyzeLoop",
+                                                   &MI)
+                 << "loop contains an instruction with unmodeled side effects, "
+                    "and therefore cannot be pipelined";
+        });
       return nullptr;
+    }
+  }
 
   MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
   SmallVector<MachineOperand, 4> Cond;
-  if (analyzeBranch(*LoopBB, TBB, FBB, Cond))
+  if (analyzeBranch(*LoopBB, TBB, FBB, Cond)) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis(
+                   "pipeliner", "analyzeLoop",
+                   LoopBB->findDebugLoc(LoopBB->getFirstTerminator()), LoopBB)
+               << "branch cannot be analyzed";
+      });
     return nullptr;
+  }
 
   // Infinite loops are not supported
-  if (TBB == LoopBB && FBB == LoopBB)
+  if (TBB == LoopBB && FBB == LoopBB) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis(
+                   "pipeliner", "analyzeLoop",
+                   LoopBB->findDebugLoc(LoopBB->getFirstTerminator()), LoopBB)
+               << "infinite loops are not supported";
+      });
     return nullptr;
+  }
 
   // Must be conditional branch
-  if (TBB != LoopBB && FBB == nullptr)
+  if (TBB != LoopBB && FBB == nullptr) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis(
+                   "pipeliner", "analyzeLoop",
+                   LoopBB->findDebugLoc(LoopBB->getFirstTerminator()), LoopBB)
+               << "loop is not terminated by a conditional branch";
+      });
     return nullptr;
+  }
 
   assert((TBB == LoopBB || FBB == LoopBB) &&
          "The Loop must be a single-basic-block loop");
@@ -12063,8 +12108,16 @@ AArch64InstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
   MachineInstr *CondBranch = &*LoopBB->getFirstTerminator();
   const TargetRegisterInfo &TRI = getRegisterInfo();
 
-  if (CondBranch->getOpcode() != AArch64::Bcc)
+  if (CondBranch->getOpcode() != AArch64::Bcc) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis("pipeliner", "analyzeLoop",
+                                                 CondBranch)
+               << "branch opcode not yet supported for pipelining: "
+               << ore::NV("Opcode", getName(CondBranch->getOpcode()));
+      });
     return nullptr;
+  }
 
   // Normalization for createTripCountGreaterCondition()
   if (TBB == LoopBB)
@@ -12096,6 +12149,13 @@ AArch64InstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
           Comp = &MI;
           break;
         }
+        if (ORE)
+          ORE->emit([&]() {
+            return MachineOptimizationRemarkAnalysis("pipeliner", "analyzeLoop",
+                                                     &MI)
+                   << "compare instruction not recognized: "
+                   << ore::NV("Opcode", getName(MI.getOpcode()));
+          });
         return nullptr;
       }
 
@@ -12104,22 +12164,44 @@ AArch64InstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
           CompCounterOprNum = 2;
         else if (isDefinedOutside(Comp->getOperand(2).getReg(), LoopBB))
           CompCounterOprNum = 1;
-        else
+        else {
+          if (ORE)
+            ORE->emit([&]() {
+              return MachineOptimizationRemarkAnalysis("pipeliner",
+                                                       "analyzeLoop", Comp)
+                     << "neither operand of the compare is loop invariant";
+            });
           return nullptr;
+        }
       }
       break;
     }
   }
-  if (!Comp)
+  if (!Comp) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis(
+                   "pipeliner", "analyzeLoop",
+                   LoopBB->findDebugLoc(LoopBB->getFirstTerminator()), LoopBB)
+               << "no NZCV-modifying compare instruction found";
+      });
     return nullptr;
+  }
 
   MachineInstr *Update = nullptr;
   Register Init;
   bool IsUpdatePriorComp;
   unsigned UpdateCounterOprNum;
   if (!getIndVarInfo(Comp->getOperand(CompCounterOprNum).getReg(), LoopBB,
-                     Update, UpdateCounterOprNum, Init, IsUpdatePriorComp))
+                     Update, UpdateCounterOprNum, Init, IsUpdatePriorComp)) {
+    if (ORE)
+      ORE->emit([&]() {
+        return MachineOptimizationRemarkAnalysis("pipeliner", "analyzeLoop",
+                                                 Comp)
+               << "loop induction variable pattern not recognized";
+      });
     return nullptr;
+  }
 
   return std::make_unique<AArch64PipelinerLoopInfo>(
       LoopBB, CondBranch, Comp, CompCounterOprNum, Update, UpdateCounterOprNum,
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h
index 15bd832de8d25..2b30bd912fe44 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h
@@ -414,8 +414,9 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo {
                         const DebugLoc &DL,
                         int *BytesAdded = nullptr) const override;
 
-  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const override;
+  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const override;
 
   bool
   reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index a922024b032db..e6ea273c41b50 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -6737,7 +6737,8 @@ bool ARMPipelinerLoopInfo::tooMuchRegisterPressure(SwingSchedulerDAG &SSD,
 } // namespace
 
 std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-ARMBaseInstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+ARMBaseInstrInfo::analyzeLoopForPipelining(
+    MachineBasicBlock *LoopBB, MachineOptimizationRemarkEmitter *ORE) const {
   MachineBasicBlock::iterator I = LoopBB->getFirstTerminator();
   MachineBasicBlock *Preheader = *LoopBB->pred_begin();
   if (Preheader == LoopBB)
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
index 94595ab2b338b..9ecabf17103c8 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h
@@ -382,8 +382,9 @@ class ARMBaseInstrInfo : public ARMGenInstrInfo {
 
   /// Analyze loop L, which must be a single-basic-block loop, and if the
   /// conditions can be understood enough produce a PipelinerLoopInfo object.
-  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const override;
+  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const override;
 
 private:
   /// Returns an unused general-purpose register which can be used for
diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp
index daeb384515578..c2519faa34778 100644
--- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp
@@ -806,7 +806,8 @@ class HexagonPipelinerLoopInfo : public TargetInstrInfo::PipelinerLoopInfo {
 } // namespace
 
 std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-HexagonInstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+HexagonInstrInfo::analyzeLoopForPipelining(
+    MachineBasicBlock *LoopBB, MachineOptimizationRemarkEmitter *ORE) const {
   // We really "analyze" only hardware loops right now.
   MachineBasicBlock::iterator I = LoopBB->getFirstTerminator();
 
diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h
index 1901b260926d2..f11ac7b7989a5 100644
--- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h
+++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h
@@ -139,8 +139,9 @@ class HexagonInstrInfo : public HexagonGenInstrInfo {
 
   /// Analyze loop L, which must be a single-basic-block loop, and if the
   /// conditions can be understood enough produce a PipelinerLoopInfo object.
-  std::unique_ptr<PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const override;
+  std::unique_ptr<PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const override;
 
   /// Return true if it's profitable to predicate
   /// instructions with accumulated instruction latency of "NumCycles"
diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp
index f415a9776da94..8965fd5fe1590 100644
--- a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp
+++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp
@@ -5790,7 +5790,8 @@ class PPCPipelinerLoopInfo : public TargetInstrInfo::PipelinerLoopInfo {
 } // namespace
 
 std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-PPCInstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+PPCInstrInfo::analyzeLoopForPipelining(
+    MachineBasicBlock *LoopBB, MachineOptimizationRemarkEmitter *ORE) const {
   // We really "analyze" only hardware loops right now.
   MachineBasicBlock::iterator I = LoopBB->getFirstTerminator();
   MachineBasicBlock *Preheader = *LoopBB->pred_begin();
diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h
index c8a747ac829ec..b99326b829be9 100644
--- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h
+++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h
@@ -789,8 +789,9 @@ class PPCInstrInfo : public PPCGenInstrInfo {
 
   /// Analyze loop L, which must be a single-basic-block loop, and if the
   /// conditions can be understood enough produce a PipelinerLoopInfo object.
-  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const override;
+  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const override;
 };
 
 }
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 50f548857a97b..7d9415ba8f6da 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -5392,7 +5392,8 @@ class RISCVPipelinerLoopInfo : public TargetInstrInfo::PipelinerLoopInfo {
 } // namespace
 
 std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-RISCVInstrInfo::analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const {
+RISCVInstrInfo::analyzeLoopForPipelining(
+    MachineBasicBlock *LoopBB, MachineOptimizationRemarkEmitter *ORE) const {
   MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
   SmallVector<MachineOperand, 4> Cond;
   if (analyzeBranch(*LoopBB, TBB, FBB, Cond, /*AllowModify=*/false))
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
index c75335ba7d145..7f61c850c48b8 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
@@ -328,8 +328,9 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
 
   unsigned getTailDuplicateSize(CodeGenOptLevel OptLevel) const override;
 
-  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo>
-  analyzeLoopForPipelining(MachineBasicBlock *LoopBB) const override;
+  std::unique_ptr<TargetInstrInfo::PipelinerLoopInfo> analyzeLoopForPipelining(
+      MachineBasicBlock *LoopBB,
+      MachineOptimizationRemarkEmitter *ORE = nullptr) const override;
 
   bool isHighLatencyDef(int Opc) const override;
 
diff --git a/llvm/test/CodeGen/AArch64/sms-analyzeloop-remarks.mir b/llvm/test/CodeGen/AArch64/sms-analyzeloop-remarks.mir
new file mode 100644
index 0000000000000..d043760aaf050
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sms-analyzeloop-remarks.mir
@@ -0,0 +1,168 @@
+# RUN: llc --verify-machineinstrs -mtriple=aarch64 -o /dev/null %s -run-pass pipeliner \
+# RUN:   -aarch64-enable-pipeliner -pass-remarks-analysis=pipeliner \
+# RUN:   -pass-remarks-missed=pipeliner 2>&1 | FileCheck %s
+
+# Regression test for the analyzeLoopForPipelining() optimization remarks:
+# each rejection point in AArch64InstrInfo::analyzeLoopForPipelining() should
+# report a distinct, specific reason via the "pipeliner"/"analyzeLoop" remark,
+# rather than only the generic "The loop structure is not supported" remark
+# from canPipelineLoop().
+
+--- |
+  define dso_local void @call(ptr noalias nocapture noundef writeonly %a, i32 noundef %n) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+  declare void @callee()
+
+  define dso_local void @infiniteloop(ptr noalias nocapture noundef writeonly %a, i32 noundef %n) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+
+  define dso_local void @unsupportedbranch(ptr noalias nocapture noundef writeonly %a) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+
+  define dso_local void @badcompare(ptr noalias nocapture noundef writeonly %a, i32 noundef %n) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+
+  define dso_local void @noninvariantcompare(i64 noundef %x, i64 noundef %y) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+
+  define dso_local void @badindvar(i64 noundef %x, i64 noundef %y) local_unnamed_addr #0 {
+  entry:
+    ret void
+  }
+...
+---
+name:            call
+tracksRegLiveness: true
+body:             |
+  ; A call in the loop body may use NZCV, which interferes with the
+  ; instruction to be inserted for loop control.
+  ; CHECK: loop contains a call and therefore cannot be pipelined
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0, $w1
+
+    %10:gpr64sp = COPY $x0
+    %11:gpr32common = COPY $w1
+
+  bb.1:
+    %12:gpr64sp = PHI %10, %bb.0, %13, %bb.1
+    %13:gpr64sp = ADDXri %12, 1, 0
+    BL @callee, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp
+    dead $wzr = SUBSWri %11, 1, 0, implicit-def $nzcv
+    Bcc 1, %bb.1, implicit $nzcv
+    B %bb.2
+
+  bb.2:
+...
+---
+name:            infiniteloop
+tracksRegLiveness: true
+body:             |
+  ; Both branch targets are the loop itself: nothing exits the loop.
+  ; CHECK: infinite loops are not supported
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0, $w1
+
+    %10:gpr64sp = COPY $x0
+    %11:gpr32common = COPY $w1
+
+  bb.1:
+    %12:gpr64sp = PHI %10, %bb.0, %13, %bb.1
+    %13:gpr64sp = ADDXri %12, 1, 0
+    dead $wzr = SUBSWri %11, 0, 0, implicit-def $nzcv
+    Bcc 0, %bb.1, implicit $nzcv
+    B %bb.1
+...
+---
+name:            unsupportedbranch
+tracksRegLiveness: true
+body:             |
+  ; The loop terminator is an unconditional branch (Bcc is required).
+  ; CHECK: branch opcode not yet supported for pipelining: B
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0
+
+    %10:gpr64sp = COPY $x0
+
+  bb.1:
+    %12:gpr64sp = PHI %10, %bb.0, %13, %bb.1
+    %13:gpr64sp = ADDXri %12, 1, 0
+    B %bb.1
+...
+---
+name:            badcompare
+tracksRegLiveness: true
+body:             |
+  ; The NZCV-modifying instruction is not a recognized compare opcode.
+  ; CHECK: compare instruction not recognized: ANDSWri
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0, $w1
+
+    %10:gpr64sp = COPY $x0
+    %11:gpr32common = COPY $w1
+
+  bb.1:
+    %12:gpr64sp = PHI %10, %bb.0, %13, %bb.1
+    %13:gpr64sp = ADDXri %12, 1, 0
+    dead $wzr = ANDSWri %11, 1, implicit-def $nzcv
+    Bcc 1, %bb.1, implicit $nzcv
+    B %bb.2
+
+  bb.2:
+...
+---
+name:            noninvariantcompare
+tracksRegLiveness: true
+body:             |
+  ; Neither operand of the compare instruction is a loop invariant value.
+  ; CHECK: neither operand of the compare is loop invariant
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0, $x1
+    %10:gpr64 = COPY $x0
+    %11:gpr64 = COPY $x1
+
+  bb.1:
+    %12:gpr64 = PHI %11, %bb.0, %13, %bb.1
+    %13:gpr64 = ADDXrr %12, %11
+    dead $xzr = SUBSXrr %13, %13, implicit-def $nzcv
+    Bcc 1, %bb.1, implicit $nzcv
+    B %bb.2
+
+  bb.2:
+...
+---
+name:            badindvar
+tracksRegLiveness: true
+body:             |
+  ; The value compared against is not updated by a recognized increment
+  ; instruction.
+  ; CHECK: loop induction variable pattern not recognized
+  ; CHECK: The loop structure is not supported
+  bb.0.entry:
+    liveins: $x0, $x1
+    %10:gpr64 = COPY $x0
+    %11:gpr64 = COPY $x1
+
+  bb.1:
+    %12:gpr64 = PHI %11, %bb.0, %13, %bb.1
+    %13:gpr64 = ORRXrr %12, %12
+    dead $xzr = SUBSXrr %12, %10, implicit-def $nzcv
+    Bcc 1, %bb.1, implicit $nzcv
+    B %bb.2
+
+  bb.2:
+...
diff --git a/llvm/test/CodeGen/AArch64/sms-schedule-remark.mir b/llvm/test/CodeGen/AArch64/sms-schedule-remark.mir
new file mode 100644
index 0000000000000..0a69e3d41681e
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sms-schedule-remark.mir
@@ -0,0 +1,83 @@
+# RUN: llc --verify-machineinstrs -mtriple=aarch64 -o /dev/null %s -run-pass pipeliner \
+# RUN:   -aarch64-enable-pipeliner -pass-remarks-analysis=pipeliner 2>&1 | FileCheck %s
+
+# Regression test for the "schedule" success remark: it should report ResMII,
+# RecMII, and which of the two is the binding constraint on the chosen II, in
+# addition to the Initiation Interval and MaxStageCount that were already
+# reported.
+
+# CHECK: Schedule found with Initiation Interval: 3, MaxStageCount: 2, ResMII: 2, RecMII: 2, Bound: Resource
+
+--- |
+  define dso_local void @func(ptr noalias nocapture noundef writeonly %a, ptr nocapture noundef readonly %b, i32 noundef %n) local_unnamed_addr #0 {
+  entry:
+    %cmp6 = icmp sgt i32 %n, 0
+    br i1 %cmp6, label %for.body.preheader, label %for.cond.cleanup
+
+  for.body.preheader:                               ; preds = %entry
+    %wide.trip.count = zext nneg i32 %n to i64
+    br label %for.body
+
+  for.cond.cleanup:                                 ; preds = %for.body, %entry
+    ret void
+
+  for.body:                                         ; preds = %for.body.preheader, %for.body
+    %lsr.iv11 = phi i64 [ %wide.trip.count, %for.body.preheader ], [ %lsr.iv.next, %for.body ]
+    %lsr.iv9 = phi ptr [ %b, %for.body.preheader ], [ %scevgep10, %for.body ]
+    %lsr.iv = phi ptr [ %a, %for.body.preheader ], [ %scevgep, %for.body ]
+    %0 = load float, ptr %lsr.iv9, align 4
+    %add = fadd float %0, 1.000000e+00
+    store float %add, ptr %lsr.iv, align 4
+    %scevgep = getelementptr i8, ptr %lsr.iv, i64 4
+    %scevgep10 = getelementptr i8, ptr %lsr.iv9, i64 4
+    %lsr.iv.next = add nsw i64 %lsr.iv11, -1
+    %exitcond.not = icmp eq i64 %lsr.iv.next, 0
+    br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
+  }
+
+...
+---
+name:            func
+tracksRegLiveness: true
+liveins:
+  - { reg: '$x0', virtual-reg: '%7' }
+  - { reg: '$x1', virtual-reg: '%8' }
+  - { reg: '$w2', virtual-reg: '%9' }
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x50000000), %bb.2(0x30000000)
+    liveins: $x0, $x1, $w2
+
+    %9:gpr32common = COPY $w2
+    %8:gpr64 = COPY $x1
+    %7:gpr64 = COPY $x0
+    dead $wzr = SUBSWri %9, 1, 0, implicit-def $nzcv
+    Bcc 11, %bb.2, implicit $nzcv
+    B %bb.1
+
+  bb.1.for.body.preheader:
+    %11:gpr32 = ORRWrs $wzr, %9, 0
+    %0:gpr64all = SUBREG_TO_REG killed %11, %subreg.sub_32
+    %14:fpr32 = FMOVSi 112
+    B %bb.3
+
+  bb.2.for.cond.cleanup:
+    RET_ReallyLR
+
+  bb.3.for.body:
+    successors: %bb.2(0x04000000), %bb.3(0x7c000000)
+
+    %1:gpr64sp = PHI %0, %bb.1, %6, %bb.3
+    %2:gpr64sp = PHI %8, %bb.1, %5, %bb.3
+    %3:gpr64sp = PHI %7, %bb.1, %4, %bb.3
+    early-clobber %12:gpr64sp, %13:fpr32 = LDRSpost %2, 4 :: (load (s32) from %ir.lsr.iv9)
+    %15:fpr32 = nofpexcept FADDSrr killed %13, %14, implicit $fpcr
+    early-clobber %16:gpr64sp = STRSpost killed %15, %3, 4 :: (store (s32) into %ir.lsr.iv)
+    %4:gpr64all = COPY %16
+    %5:gpr64all = COPY %12
+    %17:gpr64 = nsw SUBSXri %1, 1, 0, implicit-def $nzcv
+    %6:gpr64all = COPY %17
+    Bcc 0, %bb.2, implicit $nzcv
+    B %bb.3
+
+...
diff --git a/llvm/test/CodeGen/Hexagon/swp-ws-exp.mir b/llvm/test/CodeGen/Hexagon/swp-ws-exp.mir
index c97fdbf0e09e9..95eba5fe1f9c3 100644
--- a/llvm/test/CodeGen/Hexagon/swp-ws-exp.mir
+++ b/llvm/test/CodeGen/Hexagon/swp-ws-exp.mir
@@ -2,9 +2,14 @@
 # RUN: llc --mtriple=hexagon %s -run-pass=pipeliner -debug-only=pipeliner \
 # RUN: -window-sched=force -filetype=null -verify-machineinstrs 2>&1 \
 # RUN: | FileCheck %s
+# RUN: llc --mtriple=hexagon %s -run-pass=pipeliner \
+# RUN: -window-sched=force -filetype=null -verify-machineinstrs \
+# RUN: -pass-remarks=pipeliner 2>&1 | FileCheck %s --check-prefix=REMARK
 
 # CHECK: Best window offset is {{[0-9]+}} and Best II is {{[0-9]+}}.
 
+# REMARK: remark: {{.*}}Window scheduled with Initiation Interval: {{[0-9]+}}
+
 --- |
   define void @exp_approx_top_six(i32 %N, ptr noalias %x, ptr noalias %y) #0 {
   entry:
diff --git a/llvm/test/CodeGen/Hexagon/swp-ws-fail-0.mir b/llvm/test/CodeGen/Hexagon/swp-ws-fail-0.mir
index e86ef4376d0ac..4a9a253ae453d 100644
--- a/llvm/test/CodeGen/Hexagon/swp-ws-fail-0.mir
+++ b/llvm/test/CodeGen/Hexagon/swp-ws-fail-0.mir
@@ -9,6 +9,9 @@
 # RUN: llc --mtriple=hexagon %s -run-pass=pipeliner -debug-only=pipeliner \
 # RUN: -window-sched=force -window-region-limit=1 -window-search-ratio=80 \
 # RUN: -filetype=null 2>&1 | FileCheck %s --check-prefix=CHECK-SCHED-NOT-NEEDED
+# RUN: llc --mtriple=hexagon %s -run-pass=pipeliner \
+# RUN: -window-sched=force -filetype=null \
+# RUN: -pass-remarks-missed=pipeliner 2>&1 | FileCheck %s --check-prefix=REMARK
 
 # CHECK-INITIALIZE: There are too few MIs in the window region!
 # CHECK-INITIALIZE: The WindowScheduler failed to initialize!
@@ -16,6 +19,8 @@
 # CHECK-ANALYSE-II: Window scheduling is not needed!
 # CHECK-SCHED-NOT-NEEDED: Window scheduling is not needed!
 
+# REMARK: remark: {{.*}}Failed to find a valid window schedule
+
 ---
 name:            relu
 tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PowerPC/sms-remark.ll b/llvm/test/CodeGen/PowerPC/sms-remark.ll
index adecf488c9b1e..73078ee68bdc6 100644
--- a/llvm/test/CodeGen/PowerPC/sms-remark.ll
+++ b/llvm/test/CodeGen/PowerPC/sms-remark.ll
@@ -15,7 +15,7 @@
 @y = dso_local global [1024 x i32] zeroinitializer, align 4
 
 define dso_local ptr @foo() local_unnamed_addr {
-;ENABLED: Schedule found with Initiation Interval
+;ENABLED: Schedule found with Initiation Interval: {{[0-9]+}}, MaxStageCount: {{[0-9]+}}, ResMII: {{[0-9]+}}, RecMII: {{[0-9]+}}, Bound: Recurrence
 ;ENABLED: Pipelined succesfully!
 entry:
   %.pre = load i32, ptr @y, align 4



More information about the llvm-commits mailing list