[llvm] [RISCV] Handle recurrences in RISCVVLOptimizer (PR #151285)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 4 04:06:59 PDT 2025
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/151285
>From 9aba34270549573e49fcc60c5c1bc78ba7342718 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 30 Jul 2025 14:20:22 +0800
Subject: [PATCH 1/5] Precommit tests
---
llvm/test/CodeGen/RISCV/rvv/vl-opt.ll | 53 +++++++++++++++++++
llvm/test/CodeGen/RISCV/rvv/vl-opt.mir | 71 +++++++++++++++++++++++++-
2 files changed, 123 insertions(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
index cd282c265ae47..8651eaad4ef28 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
@@ -198,3 +198,56 @@ define void @fadd_fcmp_select_copy(<vscale x 4 x float> %v, <vscale x 4 x i1> %c
call void @llvm.riscv.vsm(<vscale x 4 x i1> %select, ptr %p, iXLen %vl)
ret void
}
+
+define void @recurrence(<vscale x 4 x i32> %v, ptr %p, iXLen %n, iXLen %vl) {
+; CHECK-LABEL: recurrence:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: vsetvli a3, zero, e32, m2, ta, ma
+; CHECK-NEXT: vmv.v.i v10, 0
+; CHECK-NEXT: .LBB13_1: # %loop
+; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
+; CHECK-NEXT: addi a1, a1, -1
+; CHECK-NEXT: vadd.vv v10, v10, v8
+; CHECK-NEXT: bnez a1, .LBB13_1
+; CHECK-NEXT: # %bb.2: # %exit
+; CHECK-NEXT: vsetvli zero, a2, e32, m2, ta, ma
+; CHECK-NEXT: vse32.v v10, (a0)
+; CHECK-NEXT: ret
+entry:
+ br label %loop
+loop:
+ %iv = phi iXLen [ 0, %entry ], [ %iv.next, %loop ]
+ %phi = phi <vscale x 4 x i32> [ zeroinitializer, %entry ], [ %x, %loop ]
+ %x = add <vscale x 4 x i32> %phi, %v
+ %iv.next = add iXLen %iv, 1
+ %done = icmp eq iXLen %iv.next, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ call void @llvm.riscv.vse(<vscale x 4 x i32> %x, ptr %p, iXLen %vl)
+ ret void
+}
+
+define <vscale x 4 x i32> @phi(<vscale x 4 x i32> %v, i1 %cond, iXLen %vl) {
+; CHECK-LABEL: phi:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: andi a0, a0, 1
+; CHECK-NEXT: vsetivli zero, 2, e32, m2, ta, ma
+; CHECK-NEXT: vadd.vi v8, v8, 1
+; CHECK-NEXT: beqz a0, .LBB14_2
+; CHECK-NEXT: # %bb.1: # %foo
+; CHECK-NEXT: vsetivli zero, 1, e32, m2, ta, ma
+; CHECK-NEXT: vadd.vi v8, v8, 1
+; CHECK-NEXT: ret
+; CHECK-NEXT: .LBB14_2: # %bar
+; CHECK-NEXT: vadd.vi v8, v8, 2
+; CHECK-NEXT: ret
+entry:
+ %a = call <vscale x 4 x i32> @llvm.riscv.vadd(<vscale x 4 x i32> poison, <vscale x 4 x i32> %v, iXLen 1, iXLen -1)
+ br i1 %cond, label %foo, label %bar
+foo:
+ %b = call <vscale x 4 x i32> @llvm.riscv.vadd(<vscale x 4 x i32> poison, <vscale x 4 x i32> %a, iXLen 1, iXLen 1)
+ ret <vscale x 4 x i32> %b
+bar:
+ %c = call <vscale x 4 x i32> @llvm.riscv.vadd(<vscale x 4 x i32> poison, <vscale x 4 x i32> %a, iXLen 2, iXLen 2)
+ ret <vscale x 4 x i32> %c
+}
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
index 60398cdf1db66..538b68a70908b 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
@@ -603,4 +603,73 @@ body: |
$x10 = COPY %9
PseudoRET implicit $x10
...
-
+---
+name: recurrence
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: recurrence
+ ; CHECK: bb.0:
+ ; CHECK-NEXT: successors: %bb.1(0x80000000)
+ ; CHECK-NEXT: liveins: $x8
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %avl:gprnox0 = COPY $x8
+ ; CHECK-NEXT: %start:vr = PseudoVMV_V_I_M1 $noreg, 0, -1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: PseudoBR %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1:
+ ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %phi:vr = PHI %start, %bb.0, %inc, %bb.1
+ ; CHECK-NEXT: %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, -1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: BNE $noreg, $noreg, %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.2:
+ ; CHECK-NEXT: PseudoVSE8_V_M1 %inc, $noreg, %avl, 3 /* e8 */
+ bb.0:
+ liveins: $x8
+ %avl:gprnox0 = COPY $x8
+ %start:vr = PseudoVMV_V_I_M1 $noreg, 0, -1, 3 /* e8 */, 3, /* ta, ma */
+ PseudoBR %bb.1
+ bb.1:
+ %phi:vr = PHI %start, %bb.0, %inc, %bb.1
+ %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, -1, 3 /* e8 */, 3 /* ta, ma */
+ BNE $noreg, $noreg, %bb.1
+ bb.2:
+ PseudoVSE8_V_M1 %inc, $noreg, %avl, 3 /* e8 */
+...
+---
+name: recurrence_cant_reduce
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: recurrence_cant_reduce
+ ; CHECK: bb.0:
+ ; CHECK-NEXT: successors: %bb.1(0x80000000)
+ ; CHECK-NEXT: liveins: $x8, $x9
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %avl1:gprnox0 = COPY $x8
+ ; CHECK-NEXT: %avl2:gprnox0 = COPY $x8
+ ; CHECK-NEXT: %start:vr = PseudoVMV_V_I_M1 $noreg, 0, %avl1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: PseudoBR %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1:
+ ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %phi:vr = PHI %start, %bb.0, %inc, %bb.1
+ ; CHECK-NEXT: %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, %avl1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: BNE $noreg, $noreg, %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.2:
+ ; CHECK-NEXT: PseudoVSE8_V_M1 %inc, $noreg, %avl2, 3 /* e8 */
+ bb.0:
+ liveins: $x8, $x9
+ %avl1:gprnox0 = COPY $x8
+ %avl2:gprnox0 = COPY $x8
+ %start:vr = PseudoVMV_V_I_M1 $noreg, 0, -1, 3 /* e8 */, 3, /* ta, ma */
+ PseudoBR %bb.1
+ bb.1:
+ %phi:vr = PHI %start, %bb.0, %inc, %bb.1
+ %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, %avl1, 3 /* e8 */, 3 /* ta, ma */
+ BNE $noreg, $noreg, %bb.1
+ bb.2:
+ PseudoVSE8_V_M1 %inc, $noreg, %avl2, 3 /* e8 */
+...
>From ea2b86183d8d0adc95e9d6314ee4a30ddd266dad Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Fri, 7 Feb 2025 11:37:56 +0800
Subject: [PATCH 2/5] [RISCV] Handle recurrences in RISCVVLOptimizer
---
llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp | 132 +++++++++++-------
.../CodeGen/RISCV/rvv/reproducer-pr146855.ll | 4 +-
llvm/test/CodeGen/RISCV/rvv/vl-opt.ll | 3 +-
llvm/test/CodeGen/RISCV/rvv/vl-opt.mir | 4 +-
llvm/test/CodeGen/RISCV/rvv/vlopt-same-vl.ll | 4 +-
5 files changed, 92 insertions(+), 55 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index f973e75840dc0..3ed4448915e02 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -30,6 +30,27 @@ using namespace llvm;
namespace {
+/// Wrapper around MachineOperand that defaults to immediate 0.
+struct DemandedVL {
+ MachineOperand VL;
+ DemandedVL() : VL(MachineOperand::CreateImm(0)) {}
+ DemandedVL(MachineOperand VL) : VL(VL) {}
+ static DemandedVL vlmax() {
+ return DemandedVL(MachineOperand::CreateImm(RISCV::VLMaxSentinel));
+ }
+ bool operator!=(const DemandedVL &Other) const {
+ return !VL.isIdenticalTo(Other.VL);
+ }
+};
+
+static DemandedVL max(const DemandedVL &LHS, const DemandedVL &RHS) {
+ if (RISCV::isVLKnownLE(LHS.VL, RHS.VL))
+ return RHS;
+ if (RISCV::isVLKnownLE(RHS.VL, LHS.VL))
+ return LHS;
+ return DemandedVL::vlmax();
+}
+
class RISCVVLOptimizer : public MachineFunctionPass {
const MachineRegisterInfo *MRI;
const MachineDominatorTree *MDT;
@@ -51,17 +72,26 @@ class RISCVVLOptimizer : public MachineFunctionPass {
StringRef getPassName() const override { return PASS_NAME; }
private:
- std::optional<MachineOperand>
- getMinimumVLForUser(const MachineOperand &UserOp) const;
- /// Returns the largest common VL MachineOperand that may be used to optimize
- /// MI. Returns std::nullopt if it failed to find a suitable VL.
- std::optional<MachineOperand> checkUsers(const MachineInstr &MI) const;
+ DemandedVL getMinimumVLForUser(const MachineOperand &UserOp) const;
+ /// Returns true if the users of \p MI have compatible EEWs and SEWs.
+ bool checkUsers(const MachineInstr &MI) const;
bool tryReduceVL(MachineInstr &MI) const;
bool isCandidate(const MachineInstr &MI) const;
+ void transfer(const MachineInstr &MI);
+
+ /// Returns all uses of vector virtual registers.
+ auto vector_uses(const MachineInstr &MI) const {
+ auto Pred = [this](const MachineOperand &MO) -> bool {
+ return MO.isReg() && MO.getReg().isVirtual() &&
+ RISCVRegisterInfo::isRVVRegClass(MRI->getRegClass(MO.getReg()));
+ };
+ return make_filter_range(MI.uses(), Pred);
+ }
/// For a given instruction, records what elements of it are demanded by
/// downstream users.
- DenseMap<const MachineInstr *, std::optional<MachineOperand>> DemandedVLs;
+ DenseMap<const MachineInstr *, DemandedVL> DemandedVLs;
+ SetVector<const MachineInstr *> Worklist;
};
/// Represents the EMUL and EEW of a MachineOperand.
@@ -821,6 +851,9 @@ getOperandInfo(const MachineOperand &MO, const MachineRegisterInfo *MRI) {
/// white-list approach simplifies this optimization for instructions that may
/// have more complex semantics with relation to how it uses VL.
static bool isSupportedInstr(const MachineInstr &MI) {
+ if (MI.isPHI() || MI.isFullCopy())
+ return true;
+
const RISCVVPseudosTable::PseudoInfo *RVV =
RISCVVPseudosTable::getPseudoInfo(MI.getOpcode());
@@ -1321,21 +1354,24 @@ bool RISCVVLOptimizer::isCandidate(const MachineInstr &MI) const {
return true;
}
-std::optional<MachineOperand>
+DemandedVL
RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
const MachineInstr &UserMI = *UserOp.getParent();
const MCInstrDesc &Desc = UserMI.getDesc();
+ if (UserMI.isPHI() || UserMI.isFullCopy())
+ return DemandedVLs.lookup(&UserMI);
+
if (!RISCVII::hasVLOp(Desc.TSFlags) || !RISCVII::hasSEWOp(Desc.TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of VL, assume that"
" use VLMAX\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
if (RISCVII::readsPastVL(
TII->get(RISCV::getRVVMCOpcode(UserMI.getOpcode())).TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort because used by unsafe instruction\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
@@ -1349,11 +1385,10 @@ RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
if (UserOp.isTied()) {
assert(UserOp.getOperandNo() == UserMI.getNumExplicitDefs() &&
RISCVII::isFirstDefTiedToFirstUse(UserMI.getDesc()));
- auto DemandedVL = DemandedVLs.lookup(&UserMI);
- if (!DemandedVL || !RISCV::isVLKnownLE(*DemandedVL, VLOp)) {
+ if (!RISCV::isVLKnownLE(DemandedVLs.lookup(&UserMI).VL, VLOp)) {
LLVM_DEBUG(dbgs() << " Abort because user is passthru in "
"instruction with demanded tail\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
}
@@ -1366,18 +1401,16 @@ RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
// If we know the demanded VL of UserMI, then we can reduce the VL it
// requires.
- if (auto DemandedVL = DemandedVLs.lookup(&UserMI)) {
- assert(isCandidate(UserMI));
- if (RISCV::isVLKnownLE(*DemandedVL, VLOp))
- return DemandedVL;
- }
+ if (RISCV::isVLKnownLE(DemandedVLs.lookup(&UserMI).VL, VLOp))
+ return DemandedVLs.lookup(&UserMI);
return VLOp;
}
-std::optional<MachineOperand>
-RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
- std::optional<MachineOperand> CommonVL;
+bool RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
+ if (MI.isPHI() || MI.isFullCopy())
+ return true;
+
SmallSetVector<MachineOperand *, 8> Worklist;
SmallPtrSet<const MachineInstr *, 4> PHISeen;
for (auto &UserOp : MRI->use_operands(MI.getOperand(0).getReg()))
@@ -1405,23 +1438,9 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
continue;
}
- auto VLOp = getMinimumVLForUser(UserOp);
- if (!VLOp)
- return std::nullopt;
-
- // Use the largest VL among all the users. If we cannot determine this
- // statically, then we cannot optimize the VL.
- if (!CommonVL || RISCV::isVLKnownLE(*CommonVL, *VLOp)) {
- CommonVL = *VLOp;
- LLVM_DEBUG(dbgs() << " User VL is: " << VLOp << "\n");
- } else if (!RISCV::isVLKnownLE(*VLOp, *CommonVL)) {
- LLVM_DEBUG(dbgs() << " Abort because cannot determine a common VL\n");
- return std::nullopt;
- }
-
if (!RISCVII::hasSEWOp(UserMI.getDesc().TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of SEW operand\n");
- return std::nullopt;
+ return false;
}
std::optional<OperandInfo> ConsumerInfo = getOperandInfo(UserOp, MRI);
@@ -1431,7 +1450,7 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
LLVM_DEBUG(dbgs() << " Abort due to unknown operand information.\n");
LLVM_DEBUG(dbgs() << " ConsumerInfo is: " << ConsumerInfo << "\n");
LLVM_DEBUG(dbgs() << " ProducerInfo is: " << ProducerInfo << "\n");
- return std::nullopt;
+ return false;
}
if (!OperandInfo::areCompatible(*ProducerInfo, *ConsumerInfo)) {
@@ -1440,11 +1459,11 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
<< " Abort due to incompatible information for EMUL or EEW.\n");
LLVM_DEBUG(dbgs() << " ConsumerInfo is: " << ConsumerInfo << "\n");
LLVM_DEBUG(dbgs() << " ProducerInfo is: " << ProducerInfo << "\n");
- return std::nullopt;
+ return false;
}
}
- return CommonVL;
+ return true;
}
bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
@@ -1460,9 +1479,7 @@ bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
return false;
}
- auto CommonVL = DemandedVLs.lookup(&MI);
- if (!CommonVL)
- return false;
+ auto *CommonVL = &DemandedVLs.at(&MI).VL;
assert((CommonVL->isImm() || CommonVL->getReg().isVirtual()) &&
"Expected VL to be an Imm or virtual Reg");
@@ -1497,6 +1514,24 @@ bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
return true;
}
+static bool isPhysical(const MachineOperand &MO) {
+ return MO.isReg() && MO.getReg().isPhysical();
+}
+
+/// Look through \p MI's operands and propagate what it demands to its uses.
+void RISCVVLOptimizer::transfer(const MachineInstr &MI) {
+ if (!isSupportedInstr(MI) || !checkUsers(MI) || any_of(MI.defs(), isPhysical))
+ DemandedVLs[&MI] = DemandedVL::vlmax();
+
+ for (const MachineOperand &MO : vector_uses(MI)) {
+ const MachineInstr *Def = MRI->getVRegDef(MO.getReg());
+ DemandedVL Prev = DemandedVLs[Def];
+ DemandedVLs[Def] = max(DemandedVLs[Def], getMinimumVLForUser(MO));
+ if (DemandedVLs[Def] != Prev)
+ Worklist.insert(Def);
+ }
+}
+
bool RISCVVLOptimizer::runOnMachineFunction(MachineFunction &MF) {
if (skipFunction(MF.getFunction()))
return false;
@@ -1513,14 +1548,17 @@ bool RISCVVLOptimizer::runOnMachineFunction(MachineFunction &MF) {
assert(DemandedVLs.empty());
// For each instruction that defines a vector, compute what VL its
- // downstream users demand.
+ // upstream uses demand.
for (MachineBasicBlock *MBB : post_order(&MF)) {
assert(MDT->isReachableFromEntry(MBB));
- for (MachineInstr &MI : reverse(*MBB)) {
- if (!isCandidate(MI))
- continue;
- DemandedVLs.insert({&MI, checkUsers(MI)});
- }
+ for (MachineInstr &MI : reverse(*MBB))
+ Worklist.insert(&MI);
+ }
+
+ while (!Worklist.empty()) {
+ const MachineInstr *MI = Worklist.front();
+ Worklist.remove(MI);
+ transfer(*MI);
}
// Then go through and see if we can reduce the VL of any instructions to
diff --git a/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll b/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
index cca00bf58063d..2d64defe8c7b1 100644
--- a/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
@@ -6,7 +6,7 @@ target triple = "riscv64-unknown-linux-gnu"
define i32 @_ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_(<vscale x 4 x i32> %wide.load, <vscale x 4 x i1> %0, <vscale x 4 x i1> %1, <vscale x 4 x i1> %2, <vscale x 4 x i1> %3) #0 {
; CHECK-LABEL: _ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_:
; CHECK: # %bb.0: # %entry
-; CHECK-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; CHECK-NEXT: vsetivli zero, 0, e32, m2, ta, ma
; CHECK-NEXT: vmv1r.v v8, v0
; CHECK-NEXT: li a0, 0
; CHECK-NEXT: vmv.v.i v10, 0
@@ -14,7 +14,7 @@ define i32 @_ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_(<vscale x 4 x i32> %wi
; CHECK-NEXT: vmv.v.i v14, 0
; CHECK-NEXT: .LBB0_1: # %vector.body
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
-; CHECK-NEXT: vsetvli a1, zero, e32, m2, ta, mu
+; CHECK-NEXT: vsetivli zero, 0, e32, m2, ta, mu
; CHECK-NEXT: vmv1r.v v0, v8
; CHECK-NEXT: slli a0, a0, 2
; CHECK-NEXT: vmv2r.v v16, v10
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
index 8651eaad4ef28..ecea4efa4e768 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
@@ -202,7 +202,7 @@ define void @fadd_fcmp_select_copy(<vscale x 4 x float> %v, <vscale x 4 x i1> %c
define void @recurrence(<vscale x 4 x i32> %v, ptr %p, iXLen %n, iXLen %vl) {
; CHECK-LABEL: recurrence:
; CHECK: # %bb.0: # %entry
-; CHECK-NEXT: vsetvli a3, zero, e32, m2, ta, ma
+; CHECK-NEXT: vsetvli zero, a2, e32, m2, ta, ma
; CHECK-NEXT: vmv.v.i v10, 0
; CHECK-NEXT: .LBB13_1: # %loop
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
@@ -210,7 +210,6 @@ define void @recurrence(<vscale x 4 x i32> %v, ptr %p, iXLen %n, iXLen %vl) {
; CHECK-NEXT: vadd.vv v10, v10, v8
; CHECK-NEXT: bnez a1, .LBB13_1
; CHECK-NEXT: # %bb.2: # %exit
-; CHECK-NEXT: vsetvli zero, a2, e32, m2, ta, ma
; CHECK-NEXT: vse32.v v10, (a0)
; CHECK-NEXT: ret
entry:
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
index 538b68a70908b..2fcb8da339b06 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.mir
@@ -613,14 +613,14 @@ body: |
; CHECK-NEXT: liveins: $x8
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: %avl:gprnox0 = COPY $x8
- ; CHECK-NEXT: %start:vr = PseudoVMV_V_I_M1 $noreg, 0, -1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: %start:vr = PseudoVMV_V_I_M1 $noreg, 0, %avl, 3 /* e8 */, 3 /* ta, ma */
; CHECK-NEXT: PseudoBR %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1:
; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: %phi:vr = PHI %start, %bb.0, %inc, %bb.1
- ; CHECK-NEXT: %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, -1, 3 /* e8 */, 3 /* ta, ma */
+ ; CHECK-NEXT: %inc:vr = PseudoVADD_VI_M1 $noreg, %phi, 1, %avl, 3 /* e8 */, 3 /* ta, ma */
; CHECK-NEXT: BNE $noreg, $noreg, %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2:
diff --git a/llvm/test/CodeGen/RISCV/rvv/vlopt-same-vl.ll b/llvm/test/CodeGen/RISCV/rvv/vlopt-same-vl.ll
index 4b9f9a0579c48..3a05477e64ccd 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vlopt-same-vl.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vlopt-same-vl.ll
@@ -11,7 +11,7 @@
; which was responsible for speeding it up.
define <vscale x 4 x i32> @same_vl_imm(<vscale x 4 x i32> %passthru, <vscale x 4 x i32> %a, <vscale x 4 x i32> %b) {
- ; CHECK: User VL is: 4
+ ; CHECK: Trying to reduce VL for %{{.+}}:vrm2 = PseudoVADD_VV_M2
; CHECK: Abort due to CommonVL == VLOp, no point in reducing.
%v = call <vscale x 4 x i32> @llvm.riscv.vadd.nxv4i32.nxv4i32(<vscale x 4 x i32> poison, <vscale x 4 x i32> %a, <vscale x 4 x i32> %b, i64 4)
%w = call <vscale x 4 x i32> @llvm.riscv.vadd.nxv4i32.nxv4i32(<vscale x 4 x i32> poison, <vscale x 4 x i32> %v, <vscale x 4 x i32> %a, i64 4)
@@ -19,7 +19,7 @@ define <vscale x 4 x i32> @same_vl_imm(<vscale x 4 x i32> %passthru, <vscale x 4
}
define <vscale x 4 x i32> @same_vl_reg(<vscale x 4 x i32> %passthru, <vscale x 4 x i32> %a, <vscale x 4 x i32> %b, i64 %vl) {
- ; CHECK: User VL is: %3:gprnox0
+ ; CHECK: Trying to reduce VL for %{{.+}}:vrm2 = PseudoVADD_VV_M2
; CHECK: Abort due to CommonVL == VLOp, no point in reducing.
%v = call <vscale x 4 x i32> @llvm.riscv.vadd.nxv4i32.nxv4i32(<vscale x 4 x i32> poison, <vscale x 4 x i32> %a, <vscale x 4 x i32> %b, i64 %vl)
%w = call <vscale x 4 x i32> @llvm.riscv.vadd.nxv4i32.nxv4i32(<vscale x 4 x i32> poison, <vscale x 4 x i32> %v, <vscale x 4 x i32> %a, i64 %vl)
>From 9f24fe778aec374b3f2765fb27df17ae8c13fc56 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 30 Jul 2025 16:07:00 +0800
Subject: [PATCH 3/5] Link to talk in header comment
---
llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index 3ed4448915e02..5ee64719a923c 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -10,9 +10,19 @@
// instructions are inserted.
//
// The purpose of this optimization is to make the VL argument, for instructions
-// that have a VL argument, as small as possible. This is implemented by
-// visiting each instruction in reverse order and checking that if it has a VL
-// argument, whether the VL can be reduced.
+// that have a VL argument, as small as possible.
+//
+// This is split into a sparse dataflow analysis where we determine what VL is
+// demanded by each instruction first, and then afterwards try to reduce the VL
+// of each instruction if it demands less than its VL operand.
+//
+// The analysis is explained in more detail in the 2025 EuroLLVM Developers'
+// Meeting talk "Accidental Dataflow Analysis: Extending the RISC-V VL
+// Optimizer", which is available on YouTube at
+// https://www.youtube.com/watch?v=Mfb5fRSdJAc
+//
+// The slides for the talk are available at
+// https://llvm.org/devmtg/2025-04/slides/technical_talk/lau_accidental_dataflow.pdf
//
//===---------------------------------------------------------------------===//
>From 9b61df60f3922e55703c607c057fb940a21c806e Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Fri, 22 Aug 2025 09:42:41 +0800
Subject: [PATCH 4/5] Fix test name
---
llvm/test/CodeGen/RISCV/rvv/vl-opt.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
index ecea4efa4e768..0b2fba25f0a80 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
@@ -226,8 +226,8 @@ exit:
ret void
}
-define <vscale x 4 x i32> @phi(<vscale x 4 x i32> %v, i1 %cond, iXLen %vl) {
-; CHECK-LABEL: phi:
+define <vscale x 4 x i32> @join(<vscale x 4 x i32> %v, i1 %cond, iXLen %vl) {
+; CHECK-LABEL: join:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: andi a0, a0, 1
; CHECK-NEXT: vsetivli zero, 2, e32, m2, ta, ma
>From 257ed3cfb2b68493c2935f7e92dd8e2c5ee19056 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 3 Sep 2025 15:11:04 +0800
Subject: [PATCH 5/5] Reword a comment to be more clear
---
llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index 75822c0ce3ac7..e431b15be78ca 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -1561,8 +1561,8 @@ bool RISCVVLOptimizer::runOnMachineFunction(MachineFunction &MF) {
assert(DemandedVLs.empty());
- // For each instruction that defines a vector, compute what VL its
- // upstream uses demand.
+ // For each instruction that defines a vector, propagate the VL it
+ // uses to its inputs.
for (MachineBasicBlock *MBB : post_order(&MF)) {
assert(MDT->isReachableFromEntry(MBB));
for (MachineInstr &MI : reverse(*MBB))
More information about the llvm-commits
mailing list