[llvm] [SCEVDivision] Add SCEVDivisionPrinterPass with corresponding tests (PR #155832)
Ryotaro Kasuga via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 29 02:49:50 PDT 2025
https://github.com/kasuga-fj updated https://github.com/llvm/llvm-project/pull/155832
>From acdb7dcd259f24a517397686f6726eca8748c378 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Tue, 26 Aug 2025 06:57:03 +0000
Subject: [PATCH 1/3] [SCEVDivision] Add printer pass
---
.../llvm/Analysis/ScalarEvolutionDivision.h | 9 +
llvm/lib/Analysis/ScalarEvolutionDivision.cpp | 32 +++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassRegistry.def | 1 +
.../Analysis/ScalarEvolutionDivision/sdiv.ll | 210 ++++++++++++++++++
llvm/utils/UpdateTestChecks/common.py | 1 +
6 files changed, 254 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
index 3283d438ccb51..cf6db50fddbe3 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
@@ -68,6 +68,15 @@ struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
const SCEV *Denominator, *Quotient, *Remainder, *Zero, *One;
};
+class SCEVDivisionPrinterPass : public PassInfoMixin<SCEVDivisionPrinterPass> {
+ raw_ostream &OS;
+ void runImpl(Function &F, ScalarEvolution &SE);
+
+public:
+ explicit SCEVDivisionPrinterPass(raw_ostream &OS) : OS(OS) {}
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
} // end namespace llvm
#endif // LLVM_ANALYSIS_SCALAREVOLUTIONDIVISION_H
diff --git a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
index d03930d9e2d99..bce41f9f5329e 100644
--- a/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
+++ b/llvm/lib/Analysis/ScalarEvolutionDivision.cpp
@@ -15,10 +15,14 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <cstdint>
+#define DEBUG_TYPE "scev-division"
+
namespace llvm {
class Type;
} // namespace llvm
@@ -257,3 +261,31 @@ void SCEVDivision::cannotDivide(const SCEV *Numerator) {
Quotient = Zero;
Remainder = Numerator;
}
+
+void SCEVDivisionPrinterPass::runImpl(Function &F, ScalarEvolution &SE) {
+ OS << "Printing analysis 'Scalar Evolution Division' for function '"
+ << F.getName() << "':\n";
+ for (Instruction &Inst : instructions(F)) {
+ BinaryOperator *Div = dyn_cast<BinaryOperator>(&Inst);
+ if (!Div || Div->getOpcode() != Instruction::SDiv)
+ continue;
+
+ const SCEV *Numerator = SE.getSCEV(Div->getOperand(0));
+ const SCEV *Denominator = SE.getSCEV(Div->getOperand(1));
+ const SCEV *Quotient, *Remainder;
+ SCEVDivision::divide(SE, Numerator, Denominator, &Quotient, &Remainder);
+
+ OS << "Instruction: " << *Div << "\n";
+ OS.indent(2) << "Numerator: " << *Numerator << "\n";
+ OS.indent(2) << "Denominator: " << *Denominator << "\n";
+ OS.indent(2) << "Quotient: " << *Quotient << "\n";
+ OS.indent(2) << "Remainder: " << *Remainder << "\n";
+ }
+}
+
+PreservedAnalyses SCEVDivisionPrinterPass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ ScalarEvolution &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
+ runImpl(F, SE);
+ return PreservedAnalyses::all();
+}
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index b7edeea082762..d75304b5e11f6 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -69,6 +69,7 @@
#include "llvm/Analysis/RegionInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
+#include "llvm/Analysis/ScalarEvolutionDivision.h"
#include "llvm/Analysis/ScopedNoAliasAA.h"
#include "llvm/Analysis/StackLifetime.h"
#include "llvm/Analysis/StackSafetyAnalysis.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 1b111dc20d35c..4b462b9c6845c 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -518,6 +518,7 @@ FUNCTION_PASS("print<phi-values>", PhiValuesPrinterPass(errs()))
FUNCTION_PASS("print<postdomtree>", PostDominatorTreePrinterPass(errs()))
FUNCTION_PASS("print<regions>", RegionInfoPrinterPass(errs()))
FUNCTION_PASS("print<scalar-evolution>", ScalarEvolutionPrinterPass(errs()))
+FUNCTION_PASS("print<scev-division>", SCEVDivisionPrinterPass(errs()))
FUNCTION_PASS("print<stack-safety-local>", StackSafetyPrinterPass(errs()))
FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(errs()))
FUNCTION_PASS("prof-inject", ProfileInjectorPass())
diff --git a/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
new file mode 100644
index 0000000000000..02db29eee6a85
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
@@ -0,0 +1,210 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt < %s "-passes=print<scev-division>" -disable-output 2>&1 | FileCheck %s
+
+define i8 @add(i8 %x, i8 %y) {
+; CHECK-LABEL: 'add'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %y
+; CHECK-NEXT: Numerator: (%x + %y)
+; CHECK-NEXT: Denominator: %y
+; CHECK-NEXT: Quotient: 1
+; CHECK-NEXT: Remainder: %x
+;
+ %add = add i8 %x, %y
+ %div = sdiv i8 %add, %y
+ ret i8 %div
+}
+
+define i8 @mul_add_mul(i8 %a, i8 %b, i8 %c) {
+; CHECK-LABEL: 'mul_add_mul'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %a
+; CHECK-NEXT: Numerator: ((2 * %c) + (%a * %b))
+; CHECK-NEXT: Denominator: %a
+; CHECK-NEXT: Quotient: %b
+; CHECK-NEXT: Remainder: (2 * %c)
+;
+ %mul0 = mul nsw nuw i8 %a, %b
+ %tmp = add nsw nuw i8 %mul0, %c
+ %mul1 = mul nsw nuw i8 %a, %c
+ %add = add nsw nuw i8 %tmp, %c
+ %div = sdiv i8 %add, %a
+ ret i8 %div
+}
+
+; FIXME: We need nsw on mul.
+define i8 @mul(i8 %x, i8 %y) {
+; CHECK-LABEL: 'mul'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
+; CHECK-NEXT: Numerator: (%x * %y)
+; CHECK-NEXT: Denominator: %y
+; CHECK-NEXT: Quotient: %x
+; CHECK-NEXT: Remainder: 0
+;
+ %mul = mul i8 %x, %y
+ %div = sdiv i8 %mul, %y
+ ret i8 %div
+}
+
+define i8 @mul_nsw(i8 %x, i8 %y) {
+; CHECK-LABEL: 'mul_nsw'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
+; CHECK-NEXT: Numerator: (%x * %y)
+; CHECK-NEXT: Denominator: %y
+; CHECK-NEXT: Quotient: %x
+; CHECK-NEXT: Remainder: 0
+;
+ %mul = mul nsw i8 %x, %y
+ %div = sdiv i8 %mul, %y
+ ret i8 %div
+}
+
+; FIXME: We need nsw on mul.
+define i8 @mul_nuw(i8 %x, i8 %y) {
+; CHECK-LABEL: 'mul_nuw'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
+; CHECK-NEXT: Numerator: (%x * %y)
+; CHECK-NEXT: Denominator: %y
+; CHECK-NEXT: Quotient: %x
+; CHECK-NEXT: Remainder: 0
+;
+ %mul = mul nuw i8 %x, %y
+ %div = sdiv i8 %mul, %y
+ ret i8 %div
+}
+
+define i8 @add_mul_add(i8 %a, i8 %b, i8 %c) {
+; CHECK-LABEL: 'add_mul_add'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %a
+; CHECK-NEXT: Numerator: ((%a + %b) * (%a + %c))
+; CHECK-NEXT: Denominator: %a
+; CHECK-NEXT: Quotient: 0
+; CHECK-NEXT: Remainder: ((%a + %b) * (%a + %c))
+;
+ %add0 = add nsw nuw i8 %a, %b
+ %add1 = add nsw nuw i8 %a, %c
+ %mul = mul nsw nuw i8 %add0, %add1
+ %div = sdiv i8 %mul, %a
+ ret i8 %div
+}
+
+; for (i = 0; i < n; i++)
+; div = i / den;
+;
+define void @addrec_iv(i8 %n, i8 %den) {
+; CHECK-LABEL: 'addrec_iv'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %i, %den
+; CHECK-NEXT: Numerator: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT: Denominator: %den
+; CHECK-NEXT: Quotient: 0
+; CHECK-NEXT: Remainder: {0,+,1}<nuw><nsw><%loop>
+;
+entry:
+ %guard = icmp sgt i8 %n, 0
+ br i1 %guard, label %loop, label %exit
+
+loop:
+ %i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
+ %div = sdiv i8 %i, %den
+ %i.inc = add nsw i8 %i, 1
+ %exitcond = icmp eq i8 %i.inc, %n
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; for (i = 0; i < n; i++)
+; div = (step * i) / step;
+;
+define void @addrec_step0(i8 %n, i8 %step) {
+; CHECK-LABEL: 'addrec_step0'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
+; CHECK-NEXT: Numerator: {0,+,%step}<nuw><nsw><%loop>
+; CHECK-NEXT: Denominator: %step
+; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT: Remainder: 0
+;
+entry:
+ %guard = icmp sgt i8 %n, 0
+ br i1 %guard, label %loop, label %exit
+
+loop:
+ %i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
+ %num = phi i8 [ 0, %entry ], [ %num.next, %loop ]
+ %div = sdiv i8 %num, %step
+ %i.inc = add nsw i8 %i, 1
+ %num.next = add nsw nuw i8 %num, %step
+ %exitcond = icmp eq i8 %i.inc, %n
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+; for (unsigned char i = 0; i < n; i++)
+; if (cond)
+; div = (step * i) / step;
+;
+; FIXME: The quotient can cause signed wrap, e.g., when %step is 0 and %n is
+; larger than 127.
+define void @addrec_step1(i8 %n, i8 %step) {
+; CHECK-LABEL: 'addrec_step1'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
+; CHECK-NEXT: Numerator: {0,+,%step}<nuw><nsw><%loop.header>
+; CHECK-NEXT: Denominator: %step
+; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop.header>
+; CHECK-NEXT: Remainder: 0
+;
+entry:
+ %guard = icmp ne i8 %n, 0
+ br i1 %guard, label %loop.header, label %exit
+
+loop.header:
+ %i = phi i8 [ 0, %entry ], [ %i.inc, %loop.latch ]
+ %num = phi i8 [ 0, %entry ], [ %num.next, %loop.latch ]
+ %cond = freeze i1 poison
+ br i1 %cond, label %division, label %loop.latch
+
+division:
+ %div = sdiv i8 %num, %step
+ br label %loop.latch
+
+loop.latch:
+ %i.inc = add nuw i8 %i, 1
+ %num.next = add nsw nuw i8 %num, %step
+ %exitcond = icmp eq i8 %i.inc, %n
+ br i1 %exitcond, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+; for (i = 0; i < n; i++)
+; div = (a + b) * i / a;
+;
+; FIXME: Both the quotient and the remainder can cause signed/unsigned wrap,
+; e.g., when %a + %b = 0 && %a != 0.
+define void @addrec_a_b(i8 %n, i8 %a, i8 %b) {
+; CHECK-LABEL: 'addrec_a_b'
+; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
+; CHECK-NEXT: Numerator: {0,+,(%a + %b)}<nuw><nsw><%loop>
+; CHECK-NEXT: Denominator: (%a + %b)
+; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop>
+; CHECK-NEXT: Remainder: 0
+;
+entry:
+ %guard = icmp sgt i8 %n, 0
+ %step = add nsw nuw i8 %a, %b
+ br i1 %guard, label %loop, label %exit
+
+loop:
+ %i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
+ %num = phi i8 [ 0, %entry ], [ %num.next, %loop ]
+ %div = sdiv i8 %num, %step
+ %i.inc = add nsw i8 %i, 1
+ %num.next = add nsw nuw i8 %num, %step
+ %exitcond = icmp eq i8 %i.inc, %n
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index 835092c468695..a1f188fbf8d69 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -39,6 +39,7 @@
"Delinearization",
"Loop Access Analysis",
"Scalar Evolution Analysis",
+ "Scalar Evolution Division",
}
>From 0cba51e420109a52e7af8a6716ed53183dfc4409 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Fri, 29 Aug 2025 00:32:44 +0900
Subject: [PATCH 2/3] Address review comments
---
.../llvm/Analysis/ScalarEvolutionDivision.h | 15 +++++-
.../Analysis/ScalarEvolutionDivision/sdiv.ll | 49 ++++---------------
2 files changed, 23 insertions(+), 41 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
index cf6db50fddbe3..edd307dd6da13 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
@@ -25,8 +25,19 @@ struct SCEVCouldNotCompute;
struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
public:
- // Computes the Quotient and Remainder of the division of Numerator by
- // Denominator.
+ /// Computes the Quotient and Remainder of the division of Numerator by
+ /// Denominator. We are not actually performing the division here. Instead, we
+ /// are trying to find SCEV expressions Quotient and Remainder that satisfy:
+ ///
+ /// Numerator = Denominator * Quotient + Remainder
+ ///
+ /// There may be multiple valid answers for Quotient and Remainder. This
+ /// function finds one of them. Especially, there is always a trivial
+ /// solution: (Quotient, Remainder) = (0, Numerator).
+ ///
+ /// Note the following:
+ /// * The condition Remainder < Denominator is NOT necessarily required.
+ /// * Division of constants is performed as signed.
static void divide(ScalarEvolution &SE, const SCEV *Numerator,
const SCEV *Denominator, const SCEV **Quotient,
const SCEV **Remainder);
diff --git a/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
index 02db29eee6a85..e3cf15eb63534 100644
--- a/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
+++ b/llvm/test/Analysis/ScalarEvolutionDivision/sdiv.ll
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
; RUN: opt < %s "-passes=print<scev-division>" -disable-output 2>&1 | FileCheck %s
-define i8 @add(i8 %x, i8 %y) {
+define noundef i8 @add(i8 %x, i8 %y) {
; CHECK-LABEL: 'add'
; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %y
; CHECK-NEXT: Numerator: (%x + %y)
@@ -14,24 +14,22 @@ define i8 @add(i8 %x, i8 %y) {
ret i8 %div
}
-define i8 @mul_add_mul(i8 %a, i8 %b, i8 %c) {
+define noundef i8 @mul_add_mul(i8 %a, i8 %b, i8 %c) {
; CHECK-LABEL: 'mul_add_mul'
; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %a
-; CHECK-NEXT: Numerator: ((2 * %c) + (%a * %b))
+; CHECK-NEXT: Numerator: ((2 * %c)<nuw><nsw> + (%a * %b)<nuw><nsw>)<nuw><nsw>
; CHECK-NEXT: Denominator: %a
; CHECK-NEXT: Quotient: %b
-; CHECK-NEXT: Remainder: (2 * %c)
+; CHECK-NEXT: Remainder: (2 * %c)<nuw><nsw>
;
%mul0 = mul nsw nuw i8 %a, %b
- %tmp = add nsw nuw i8 %mul0, %c
- %mul1 = mul nsw nuw i8 %a, %c
- %add = add nsw nuw i8 %tmp, %c
+ %mul1 = mul nsw nuw i8 %c, 2
+ %add = add nsw nuw i8 %mul0, %mul1
%div = sdiv i8 %add, %a
ret i8 %div
}
-; FIXME: We need nsw on mul.
-define i8 @mul(i8 %x, i8 %y) {
+define noundef i8 @mul(i8 %x, i8 %y) {
; CHECK-LABEL: 'mul'
; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
; CHECK-NEXT: Numerator: (%x * %y)
@@ -44,40 +42,13 @@ define i8 @mul(i8 %x, i8 %y) {
ret i8 %div
}
-define i8 @mul_nsw(i8 %x, i8 %y) {
-; CHECK-LABEL: 'mul_nsw'
-; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
-; CHECK-NEXT: Numerator: (%x * %y)
-; CHECK-NEXT: Denominator: %y
-; CHECK-NEXT: Quotient: %x
-; CHECK-NEXT: Remainder: 0
-;
- %mul = mul nsw i8 %x, %y
- %div = sdiv i8 %mul, %y
- ret i8 %div
-}
-
-; FIXME: We need nsw on mul.
-define i8 @mul_nuw(i8 %x, i8 %y) {
-; CHECK-LABEL: 'mul_nuw'
-; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
-; CHECK-NEXT: Numerator: (%x * %y)
-; CHECK-NEXT: Denominator: %y
-; CHECK-NEXT: Quotient: %x
-; CHECK-NEXT: Remainder: 0
-;
- %mul = mul nuw i8 %x, %y
- %div = sdiv i8 %mul, %y
- ret i8 %div
-}
-
-define i8 @add_mul_add(i8 %a, i8 %b, i8 %c) {
+define noundef i8 @add_mul_add(i8 %a, i8 %b, i8 %c) {
; CHECK-LABEL: 'add_mul_add'
; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %a
-; CHECK-NEXT: Numerator: ((%a + %b) * (%a + %c))
+; CHECK-NEXT: Numerator: ((%a + %b)<nuw><nsw> * (%a + %c)<nuw><nsw>)<nuw><nsw>
; CHECK-NEXT: Denominator: %a
; CHECK-NEXT: Quotient: 0
-; CHECK-NEXT: Remainder: ((%a + %b) * (%a + %c))
+; CHECK-NEXT: Remainder: ((%a + %b)<nuw><nsw> * (%a + %c)<nuw><nsw>)<nuw><nsw>
;
%add0 = add nsw nuw i8 %a, %b
%add1 = add nsw nuw i8 %a, %c
>From d278b986cbd2b25793c9597c7f2d840d3889ea56 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Fri, 29 Aug 2025 09:48:36 +0000
Subject: [PATCH 3/3] Revise comment
---
llvm/include/llvm/Analysis/ScalarEvolutionDivision.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
index edd307dd6da13..7c78487fea8c0 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionDivision.h
@@ -38,6 +38,8 @@ struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
/// Note the following:
/// * The condition Remainder < Denominator is NOT necessarily required.
/// * Division of constants is performed as signed.
+ /// * The multiplication of Quotient and Denominator may wrap.
+ /// * The addition of Quotient*Denominator and Remainder may wrap.
static void divide(ScalarEvolution &SE, const SCEV *Numerator,
const SCEV *Denominator, const SCEV **Quotient,
const SCEV **Remainder);
More information about the llvm-commits
mailing list