Skip to content

Commit 5fa59ed

Browse files
authored
[ConstraintElim] Add support for trunc nsw/nuw (#118745)
Proof for `trunc nsw nneg X -> trunc nuw X`: https://alive2.llvm.org/ce/z/ooP6Mt
1 parent bb3eb0c commit 5fa59ed

File tree

2 files changed

+225
-3
lines changed

2 files changed

+225
-3
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,9 @@ static Decomposition decompose(Value *V,
521521
else if (match(V, m_NNegZExt(m_Value(Op0)))) {
522522
V = Op0;
523523
IsKnownNonNegative = true;
524+
} else if (match(V, m_NSWTrunc(m_Value(Op0)))) {
525+
if (Op0->getType()->getScalarSizeInBits() <= 64)
526+
V = Op0;
524527
}
525528

526529
if (match(V, m_NSWAdd(m_Value(Op0), m_Value(Op1))))
@@ -558,12 +561,19 @@ static Decomposition decompose(Value *V,
558561
if (match(V, m_ZExt(m_Value(Op0)))) {
559562
IsKnownNonNegative = true;
560563
V = Op0;
561-
}
562-
563-
if (match(V, m_SExt(m_Value(Op0)))) {
564+
} else if (match(V, m_SExt(m_Value(Op0)))) {
564565
V = Op0;
565566
Preconditions.emplace_back(CmpInst::ICMP_SGE, Op0,
566567
ConstantInt::get(Op0->getType(), 0));
568+
} else if (auto *Trunc = dyn_cast<TruncInst>(V)) {
569+
if (Trunc->getSrcTy()->getScalarSizeInBits() <= 64) {
570+
if (Trunc->hasNoUnsignedWrap() || Trunc->hasNoSignedWrap()) {
571+
V = Trunc->getOperand(0);
572+
if (!Trunc->hasNoUnsignedWrap())
573+
Preconditions.emplace_back(CmpInst::ICMP_SGE, V,
574+
ConstantInt::get(V->getType(), 0));
575+
}
576+
}
567577
}
568578

569579
Value *Op1;
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
3+
4+
define i1 @test_icmp_ult_zext_icmp_trunc_nuw(i16 %x, i32 %y) {
5+
; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc_nuw(
6+
; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
7+
; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[X]] to i32
8+
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
9+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
10+
; CHECK: [[IF_THEN]]:
11+
; CHECK-NEXT: [[CONV:%.*]] = trunc nuw i32 [[Y]] to i16
12+
; CHECK-NEXT: ret i1 false
13+
; CHECK: [[IF_ELSE]]:
14+
; CHECK-NEXT: ret i1 false
15+
;
16+
%ext = zext i16 %x to i32
17+
%cond = icmp ult i32 %y, %ext
18+
br i1 %cond, label %if.then, label %if.else
19+
20+
if.then:
21+
%conv = trunc nuw i32 %y to i16
22+
%cmp = icmp eq i16 %x, %conv
23+
ret i1 %cmp
24+
25+
if.else:
26+
ret i1 false
27+
}
28+
29+
define i1 @test_icmp_slt_sext_icmp_trunc_nsw(i16 %x, i32 %y) {
30+
; CHECK-LABEL: define i1 @test_icmp_slt_sext_icmp_trunc_nsw(
31+
; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
32+
; CHECK-NEXT: [[EXT:%.*]] = sext i16 [[X]] to i32
33+
; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[Y]], [[EXT]]
34+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
35+
; CHECK: [[IF_THEN]]:
36+
; CHECK-NEXT: [[CONV:%.*]] = trunc nsw i32 [[Y]] to i16
37+
; CHECK-NEXT: ret i1 false
38+
; CHECK: [[IF_ELSE]]:
39+
; CHECK-NEXT: ret i1 false
40+
;
41+
%ext = sext i16 %x to i32
42+
%cond = icmp slt i32 %y, %ext
43+
br i1 %cond, label %if.then, label %if.else
44+
45+
if.then:
46+
%conv = trunc nsw i32 %y to i16
47+
%cmp = icmp slt i16 %x, %conv
48+
ret i1 %cmp
49+
50+
if.else:
51+
ret i1 false
52+
}
53+
54+
define i1 @test_icmp_ult_trunc_nsw_nneg_icmp_trunc_nuw(i64 %x, i32 %y) {
55+
; CHECK-LABEL: define i1 @test_icmp_ult_trunc_nsw_nneg_icmp_trunc_nuw(
56+
; CHECK-SAME: i64 [[X:%.*]], i32 [[Y:%.*]]) {
57+
; CHECK-NEXT: [[EXT:%.*]] = trunc nsw i64 [[X]] to i32
58+
; CHECK-NEXT: [[NNEG:%.*]] = icmp sgt i64 [[X]], -1
59+
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
60+
; CHECK-NEXT: [[AND:%.*]] = and i1 [[NNEG]], [[COND]]
61+
; CHECK-NEXT: br i1 [[AND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
62+
; CHECK: [[IF_THEN]]:
63+
; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[Y]] to i64
64+
; CHECK-NEXT: ret i1 false
65+
; CHECK: [[IF_ELSE]]:
66+
; CHECK-NEXT: ret i1 false
67+
;
68+
%ext = trunc nsw i64 %x to i32
69+
%nneg = icmp sgt i64 %x, -1
70+
%cond = icmp ult i32 %y, %ext
71+
%and = and i1 %nneg, %cond
72+
br i1 %and, label %if.then, label %if.else
73+
74+
if.then:
75+
%conv = zext i32 %y to i64
76+
%cmp = icmp eq i64 %x, %conv
77+
ret i1 %cmp
78+
79+
if.else:
80+
ret i1 false
81+
}
82+
83+
define i1 @test2(i32 %n) {
84+
; CHECK-LABEL: define i1 @test2(
85+
; CHECK-SAME: i32 [[N:%.*]]) {
86+
; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[N]], 0
87+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
88+
; CHECK: [[IF_THEN]]:
89+
; CHECK-NEXT: [[EXT:%.*]] = zext nneg i32 [[N]] to i64
90+
; CHECK-NEXT: [[END:%.*]] = add nsw i64 [[EXT]], -1
91+
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
92+
; CHECK: [[FOR_BODY]]:
93+
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, %[[IF_THEN]] ], [ [[INDVAR_NEXT:%.*]], %[[FOR_NEXT:.*]] ]
94+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[INDVAR]], [[END]]
95+
; CHECK-NEXT: br i1 [[CMP]], label %[[IF_ELSE]], label %[[FOR_NEXT]]
96+
; CHECK: [[FOR_NEXT]]:
97+
; CHECK-NEXT: [[INDVAR_NEXT]] = add nuw nsw i64 [[INDVAR]], 1
98+
; CHECK-NEXT: [[COND2:%.*]] = call i1 @cond()
99+
; CHECK-NEXT: br i1 [[COND2]], label %[[FOR_BODY]], label %[[FOR_END:.*]]
100+
; CHECK: [[FOR_END]]:
101+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nsw i64 [[INDVAR_NEXT]] to i32
102+
; CHECK-NEXT: ret i1 true
103+
; CHECK: [[IF_ELSE]]:
104+
; CHECK-NEXT: ret i1 false
105+
;
106+
%cond = icmp sgt i32 %n, 0
107+
br i1 %cond, label %if.then, label %if.else
108+
109+
if.then:
110+
%ext = zext nneg i32 %n to i64
111+
%end = add nsw i64 %ext, -1
112+
br label %for.body
113+
114+
for.body:
115+
%indvar = phi i64 [ 0, %if.then ], [ %indvar.next, %for.next ]
116+
%cmp = icmp eq i64 %indvar, %end
117+
br i1 %cmp, label %if.else, label %for.next
118+
119+
for.next:
120+
%indvar.next = add nuw nsw i64 %indvar, 1
121+
%cond2 = call i1 @cond()
122+
br i1 %cond2, label %for.body, label %for.end
123+
124+
for.end:
125+
%trunc = trunc nsw i64 %indvar.next to i32
126+
%res = icmp sgt i32 %n, %trunc
127+
ret i1 %res
128+
129+
if.else:
130+
ret i1 false
131+
}
132+
133+
define i1 @test_icmp_ult_zext_icmp_trunc(i16 %x, i32 %y) {
134+
; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc(
135+
; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
136+
; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[X]] to i32
137+
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
138+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
139+
; CHECK: [[IF_THEN]]:
140+
; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[Y]] to i16
141+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i16 [[X]], [[CONV]]
142+
; CHECK-NEXT: ret i1 [[CMP]]
143+
; CHECK: [[IF_ELSE]]:
144+
; CHECK-NEXT: ret i1 false
145+
;
146+
%ext = zext i16 %x to i32
147+
%cond = icmp ult i32 %y, %ext
148+
br i1 %cond, label %if.then, label %if.else
149+
150+
if.then:
151+
%conv = trunc i32 %y to i16
152+
%cmp = icmp eq i16 %x, %conv
153+
ret i1 %cmp
154+
155+
if.else:
156+
ret i1 false
157+
}
158+
159+
define i1 @test_icmp_ult_zext_icmp_trunc_nuw_i128(i16 %x, i128 %y) {
160+
; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc_nuw_i128(
161+
; CHECK-SAME: i16 [[X:%.*]], i128 [[Y:%.*]]) {
162+
; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[X]] to i128
163+
; CHECK-NEXT: [[COND:%.*]] = icmp ult i128 [[Y]], [[EXT]]
164+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
165+
; CHECK: [[IF_THEN]]:
166+
; CHECK-NEXT: [[CONV:%.*]] = trunc nuw i128 [[Y]] to i16
167+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i16 [[X]], [[CONV]]
168+
; CHECK-NEXT: ret i1 [[CMP]]
169+
; CHECK: [[IF_ELSE]]:
170+
; CHECK-NEXT: ret i1 false
171+
;
172+
%ext = zext i16 %x to i128
173+
%cond = icmp ult i128 %y, %ext
174+
br i1 %cond, label %if.then, label %if.else
175+
176+
if.then:
177+
%conv = trunc nuw i128 %y to i16
178+
%cmp = icmp eq i16 %x, %conv
179+
ret i1 %cmp
180+
181+
if.else:
182+
ret i1 false
183+
}
184+
185+
; We do not know the sign of %x, so we cannot infer nuw for %ext.
186+
define i1 @test_icmp_ult_trunc_nsw_icmp_trunc_nuw(i64 %x, i32 %y) {
187+
; CHECK-LABEL: define i1 @test_icmp_ult_trunc_nsw_icmp_trunc_nuw(
188+
; CHECK-SAME: i64 [[X:%.*]], i32 [[Y:%.*]]) {
189+
; CHECK-NEXT: [[EXT:%.*]] = trunc nsw i64 [[X]] to i32
190+
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
191+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
192+
; CHECK: [[IF_THEN]]:
193+
; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[Y]] to i64
194+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[X]], [[CONV]]
195+
; CHECK-NEXT: ret i1 [[CMP]]
196+
; CHECK: [[IF_ELSE]]:
197+
; CHECK-NEXT: ret i1 false
198+
;
199+
%ext = trunc nsw i64 %x to i32
200+
%cond = icmp ult i32 %y, %ext
201+
br i1 %cond, label %if.then, label %if.else
202+
203+
if.then:
204+
%conv = zext i32 %y to i64
205+
%cmp = icmp eq i64 %x, %conv
206+
ret i1 %cmp
207+
208+
if.else:
209+
ret i1 false
210+
}
211+
212+
declare void @cond()

0 commit comments

Comments
 (0)