[llvm] [llvm][AArch64] Add pipeliner remarks (PR #213157)
Jon Roelofs via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 07:21:48 PDT 2026
https://github.com/jroelofs updated https://github.com/llvm/llvm-project/pull/213157
>From 493a073b450d63601c19b25397a1460e02e8196b 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..a5a292c244e10
--- /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: 0, RecMII: 0, 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..df3f86b4863bb 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|Resource)}}
;ENABLED: Pipelined succesfully!
entry:
%.pre = load i32, ptr @y, align 4
More information about the llvm-commits
mailing list