Skip to content

Commit 6ed64df

Browse files
authored
[SCEVDivision] Add SCEVDivisionPrinterPass with corresponding tests (llvm#155832)
This patch introduces `SCEVDivisionPrinterPass` and registers it under the name `print<scev-division>`, primarily for testing purposes. This pass invokes `SCEVDivision::divide` upon encountering `sdiv`, and prints the numerator, denominator, quotient, and remainder. It also adds several test cases, some of which are currently incorrect and require fixing. Along with that, this patch added some comments to clarify the behavior of `SCEVDivision::divide`, as follows: - This function does NOT actually perform the division - Given the `Numerator` and `Denominator`, find a pair `(Quotient, Remainder)` s.t. `Numerator = Quotient * Denominator + Remainder` - The common condition `Remainder < Denominator` is NOT necessarily required - There may be multiple solutions for `(Quotient, Remainder)`, and this function finds one of them - Especially, there is always a trivial solution `(0, Numerator)` - The following computations may wrap - The multiplication of `Quotient` and `Denominator` - The addition of `Quotient * Denominator` and `Remainder` Related discussion: llvm#154745
1 parent d3517b6 commit 6ed64df

File tree

6 files changed

+240
-2
lines changed

6 files changed

+240
-2
lines changed

llvm/include/llvm/Analysis/ScalarEvolutionDivision.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@ struct SCEVCouldNotCompute;
2525

2626
struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
2727
public:
28-
// Computes the Quotient and Remainder of the division of Numerator by
29-
// Denominator.
28+
/// Computes the Quotient and Remainder of the division of Numerator by
29+
/// Denominator. We are not actually performing the division here. Instead, we
30+
/// are trying to find SCEV expressions Quotient and Remainder that satisfy:
31+
///
32+
/// Numerator = Denominator * Quotient + Remainder
33+
///
34+
/// There may be multiple valid answers for Quotient and Remainder. This
35+
/// function finds one of them. Especially, there is always a trivial
36+
/// solution: (Quotient, Remainder) = (0, Numerator).
37+
///
38+
/// Note the following:
39+
/// * The condition Remainder < Denominator is NOT necessarily required.
40+
/// * Division of constants is performed as signed.
41+
/// * The multiplication of Quotient and Denominator may wrap.
42+
/// * The addition of Quotient*Denominator and Remainder may wrap.
3043
static void divide(ScalarEvolution &SE, const SCEV *Numerator,
3144
const SCEV *Denominator, const SCEV **Quotient,
3245
const SCEV **Remainder);
@@ -68,6 +81,15 @@ struct SCEVDivision : public SCEVVisitor<SCEVDivision, void> {
6881
const SCEV *Denominator, *Quotient, *Remainder, *Zero, *One;
6982
};
7083

84+
class SCEVDivisionPrinterPass : public PassInfoMixin<SCEVDivisionPrinterPass> {
85+
raw_ostream &OS;
86+
void runImpl(Function &F, ScalarEvolution &SE);
87+
88+
public:
89+
explicit SCEVDivisionPrinterPass(raw_ostream &OS) : OS(OS) {}
90+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
91+
};
92+
7193
} // end namespace llvm
7294

7395
#endif // LLVM_ANALYSIS_SCALAREVOLUTIONDIVISION_H

llvm/lib/Analysis/ScalarEvolutionDivision.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
#include "llvm/ADT/DenseMap.h"
1616
#include "llvm/ADT/SmallVector.h"
1717
#include "llvm/Analysis/ScalarEvolution.h"
18+
#include "llvm/IR/InstIterator.h"
19+
#include "llvm/IR/Instructions.h"
1820
#include "llvm/Support/Casting.h"
1921
#include <cassert>
2022
#include <cstdint>
2123

24+
#define DEBUG_TYPE "scev-division"
25+
2226
namespace llvm {
2327
class Type;
2428
} // namespace llvm
@@ -257,3 +261,31 @@ void SCEVDivision::cannotDivide(const SCEV *Numerator) {
257261
Quotient = Zero;
258262
Remainder = Numerator;
259263
}
264+
265+
void SCEVDivisionPrinterPass::runImpl(Function &F, ScalarEvolution &SE) {
266+
OS << "Printing analysis 'Scalar Evolution Division' for function '"
267+
<< F.getName() << "':\n";
268+
for (Instruction &Inst : instructions(F)) {
269+
BinaryOperator *Div = dyn_cast<BinaryOperator>(&Inst);
270+
if (!Div || Div->getOpcode() != Instruction::SDiv)
271+
continue;
272+
273+
const SCEV *Numerator = SE.getSCEV(Div->getOperand(0));
274+
const SCEV *Denominator = SE.getSCEV(Div->getOperand(1));
275+
const SCEV *Quotient, *Remainder;
276+
SCEVDivision::divide(SE, Numerator, Denominator, &Quotient, &Remainder);
277+
278+
OS << "Instruction: " << *Div << "\n";
279+
OS.indent(2) << "Numerator: " << *Numerator << "\n";
280+
OS.indent(2) << "Denominator: " << *Denominator << "\n";
281+
OS.indent(2) << "Quotient: " << *Quotient << "\n";
282+
OS.indent(2) << "Remainder: " << *Remainder << "\n";
283+
}
284+
}
285+
286+
PreservedAnalyses SCEVDivisionPrinterPass::run(Function &F,
287+
FunctionAnalysisManager &AM) {
288+
ScalarEvolution &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
289+
runImpl(F, SE);
290+
return PreservedAnalyses::all();
291+
}

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#include "llvm/Analysis/RegionInfo.h"
7070
#include "llvm/Analysis/ScalarEvolution.h"
7171
#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
72+
#include "llvm/Analysis/ScalarEvolutionDivision.h"
7273
#include "llvm/Analysis/ScopedNoAliasAA.h"
7374
#include "llvm/Analysis/StackLifetime.h"
7475
#include "llvm/Analysis/StackSafetyAnalysis.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ FUNCTION_PASS("print<phi-values>", PhiValuesPrinterPass(errs()))
518518
FUNCTION_PASS("print<postdomtree>", PostDominatorTreePrinterPass(errs()))
519519
FUNCTION_PASS("print<regions>", RegionInfoPrinterPass(errs()))
520520
FUNCTION_PASS("print<scalar-evolution>", ScalarEvolutionPrinterPass(errs()))
521+
FUNCTION_PASS("print<scev-division>", SCEVDivisionPrinterPass(errs()))
521522
FUNCTION_PASS("print<stack-safety-local>", StackSafetyPrinterPass(errs()))
522523
FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(errs()))
523524
FUNCTION_PASS("prof-inject", ProfileInjectorPass())
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
2+
; RUN: opt < %s "-passes=print<scev-division>" -disable-output 2>&1 | FileCheck %s
3+
4+
define noundef i8 @add(i8 %x, i8 %y) {
5+
; CHECK-LABEL: 'add'
6+
; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %y
7+
; CHECK-NEXT: Numerator: (%x + %y)
8+
; CHECK-NEXT: Denominator: %y
9+
; CHECK-NEXT: Quotient: 1
10+
; CHECK-NEXT: Remainder: %x
11+
;
12+
%add = add i8 %x, %y
13+
%div = sdiv i8 %add, %y
14+
ret i8 %div
15+
}
16+
17+
define noundef i8 @mul_add_mul(i8 %a, i8 %b, i8 %c) {
18+
; CHECK-LABEL: 'mul_add_mul'
19+
; CHECK-NEXT: Instruction: %div = sdiv i8 %add, %a
20+
; CHECK-NEXT: Numerator: ((2 * %c)<nuw><nsw> + (%a * %b)<nuw><nsw>)<nuw><nsw>
21+
; CHECK-NEXT: Denominator: %a
22+
; CHECK-NEXT: Quotient: %b
23+
; CHECK-NEXT: Remainder: (2 * %c)<nuw><nsw>
24+
;
25+
%mul0 = mul nsw nuw i8 %a, %b
26+
%mul1 = mul nsw nuw i8 %c, 2
27+
%add = add nsw nuw i8 %mul0, %mul1
28+
%div = sdiv i8 %add, %a
29+
ret i8 %div
30+
}
31+
32+
define noundef i8 @mul(i8 %x, i8 %y) {
33+
; CHECK-LABEL: 'mul'
34+
; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %y
35+
; CHECK-NEXT: Numerator: (%x * %y)
36+
; CHECK-NEXT: Denominator: %y
37+
; CHECK-NEXT: Quotient: %x
38+
; CHECK-NEXT: Remainder: 0
39+
;
40+
%mul = mul i8 %x, %y
41+
%div = sdiv i8 %mul, %y
42+
ret i8 %div
43+
}
44+
45+
define noundef i8 @add_mul_add(i8 %a, i8 %b, i8 %c) {
46+
; CHECK-LABEL: 'add_mul_add'
47+
; CHECK-NEXT: Instruction: %div = sdiv i8 %mul, %a
48+
; CHECK-NEXT: Numerator: ((%a + %b)<nuw><nsw> * (%a + %c)<nuw><nsw>)<nuw><nsw>
49+
; CHECK-NEXT: Denominator: %a
50+
; CHECK-NEXT: Quotient: 0
51+
; CHECK-NEXT: Remainder: ((%a + %b)<nuw><nsw> * (%a + %c)<nuw><nsw>)<nuw><nsw>
52+
;
53+
%add0 = add nsw nuw i8 %a, %b
54+
%add1 = add nsw nuw i8 %a, %c
55+
%mul = mul nsw nuw i8 %add0, %add1
56+
%div = sdiv i8 %mul, %a
57+
ret i8 %div
58+
}
59+
60+
; for (i = 0; i < n; i++)
61+
; div = i / den;
62+
;
63+
define void @addrec_iv(i8 %n, i8 %den) {
64+
; CHECK-LABEL: 'addrec_iv'
65+
; CHECK-NEXT: Instruction: %div = sdiv i8 %i, %den
66+
; CHECK-NEXT: Numerator: {0,+,1}<nuw><nsw><%loop>
67+
; CHECK-NEXT: Denominator: %den
68+
; CHECK-NEXT: Quotient: 0
69+
; CHECK-NEXT: Remainder: {0,+,1}<nuw><nsw><%loop>
70+
;
71+
entry:
72+
%guard = icmp sgt i8 %n, 0
73+
br i1 %guard, label %loop, label %exit
74+
75+
loop:
76+
%i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
77+
%div = sdiv i8 %i, %den
78+
%i.inc = add nsw i8 %i, 1
79+
%exitcond = icmp eq i8 %i.inc, %n
80+
br i1 %exitcond, label %exit, label %loop
81+
82+
exit:
83+
ret void
84+
}
85+
86+
; for (i = 0; i < n; i++)
87+
; div = (step * i) / step;
88+
;
89+
define void @addrec_step0(i8 %n, i8 %step) {
90+
; CHECK-LABEL: 'addrec_step0'
91+
; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
92+
; CHECK-NEXT: Numerator: {0,+,%step}<nuw><nsw><%loop>
93+
; CHECK-NEXT: Denominator: %step
94+
; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop>
95+
; CHECK-NEXT: Remainder: 0
96+
;
97+
entry:
98+
%guard = icmp sgt i8 %n, 0
99+
br i1 %guard, label %loop, label %exit
100+
101+
loop:
102+
%i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
103+
%num = phi i8 [ 0, %entry ], [ %num.next, %loop ]
104+
%div = sdiv i8 %num, %step
105+
%i.inc = add nsw i8 %i, 1
106+
%num.next = add nsw nuw i8 %num, %step
107+
%exitcond = icmp eq i8 %i.inc, %n
108+
br i1 %exitcond, label %exit, label %loop
109+
110+
exit:
111+
ret void
112+
}
113+
114+
; for (unsigned char i = 0; i < n; i++)
115+
; if (cond)
116+
; div = (step * i) / step;
117+
;
118+
; FIXME: The quotient can cause signed wrap, e.g., when %step is 0 and %n is
119+
; larger than 127.
120+
define void @addrec_step1(i8 %n, i8 %step) {
121+
; CHECK-LABEL: 'addrec_step1'
122+
; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
123+
; CHECK-NEXT: Numerator: {0,+,%step}<nuw><nsw><%loop.header>
124+
; CHECK-NEXT: Denominator: %step
125+
; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop.header>
126+
; CHECK-NEXT: Remainder: 0
127+
;
128+
entry:
129+
%guard = icmp ne i8 %n, 0
130+
br i1 %guard, label %loop.header, label %exit
131+
132+
loop.header:
133+
%i = phi i8 [ 0, %entry ], [ %i.inc, %loop.latch ]
134+
%num = phi i8 [ 0, %entry ], [ %num.next, %loop.latch ]
135+
%cond = freeze i1 poison
136+
br i1 %cond, label %division, label %loop.latch
137+
138+
division:
139+
%div = sdiv i8 %num, %step
140+
br label %loop.latch
141+
142+
loop.latch:
143+
%i.inc = add nuw i8 %i, 1
144+
%num.next = add nsw nuw i8 %num, %step
145+
%exitcond = icmp eq i8 %i.inc, %n
146+
br i1 %exitcond, label %exit, label %loop.header
147+
148+
exit:
149+
ret void
150+
}
151+
152+
; for (i = 0; i < n; i++)
153+
; div = (a + b) * i / a;
154+
;
155+
; FIXME: Both the quotient and the remainder can cause signed/unsigned wrap,
156+
; e.g., when %a + %b = 0 && %a != 0.
157+
define void @addrec_a_b(i8 %n, i8 %a, i8 %b) {
158+
; CHECK-LABEL: 'addrec_a_b'
159+
; CHECK-NEXT: Instruction: %div = sdiv i8 %num, %step
160+
; CHECK-NEXT: Numerator: {0,+,(%a + %b)}<nuw><nsw><%loop>
161+
; CHECK-NEXT: Denominator: (%a + %b)
162+
; CHECK-NEXT: Quotient: {0,+,1}<nuw><nsw><%loop>
163+
; CHECK-NEXT: Remainder: 0
164+
;
165+
entry:
166+
%guard = icmp sgt i8 %n, 0
167+
%step = add nsw nuw i8 %a, %b
168+
br i1 %guard, label %loop, label %exit
169+
170+
loop:
171+
%i = phi i8 [ 0, %entry ], [ %i.inc, %loop ]
172+
%num = phi i8 [ 0, %entry ], [ %num.next, %loop ]
173+
%div = sdiv i8 %num, %step
174+
%i.inc = add nsw i8 %i, 1
175+
%num.next = add nsw nuw i8 %num, %step
176+
%exitcond = icmp eq i8 %i.inc, %n
177+
br i1 %exitcond, label %exit, label %loop
178+
179+
exit:
180+
ret void
181+
}

llvm/utils/UpdateTestChecks/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"Delinearization",
4040
"Loop Access Analysis",
4141
"Scalar Evolution Analysis",
42+
"Scalar Evolution Division",
4243
}
4344

4445

0 commit comments

Comments
 (0)