Skip to content

Commit 5073b5f

Browse files
[CVP] Infer nuw/nsw flags for TruncInst (#130504)
Proof: https://alive2.llvm.org/ce/z/U-G7yV Helps: rust-lang/rust#72646 and rust-lang/rust#122734 Rust compiler's current output: https://godbolt.org/z/7E3fET6Md IPSCCP can do this transform but it does not help the motivating issue since it runs only once early in the optimization pipeline. Reimplementing this in CVP folds the motivating issue into a simple `icmp eq` instruction. Fixes #130100
1 parent 059ada4 commit 5073b5f

File tree

7 files changed

+151
-12
lines changed

7 files changed

+151
-12
lines changed

clang/test/CodeGen/attr-counted-by.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ size_t test3_bdos(struct annotated *p) {
333333
// SANITIZE-WITH-ATTR-NEXT: [[COUNT50:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD44]] to i64
334334
// SANITIZE-WITH-ATTR-NEXT: [[TMP10:%.*]] = sub nsw i64 [[COUNT50]], [[IDXPROM42]]
335335
// SANITIZE-WITH-ATTR-NEXT: [[TMP11:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP10]], i64 0)
336-
// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw i64 [[TMP11]] to i32
336+
// SANITIZE-WITH-ATTR-NEXT: [[DOTTR:%.*]] = trunc nuw nsw i64 [[TMP11]] to i32
337337
// SANITIZE-WITH-ATTR-NEXT: [[CONV54:%.*]] = shl i32 [[DOTTR]], 2
338338
// SANITIZE-WITH-ATTR-NEXT: [[CONV55:%.*]] = and i32 [[CONV54]], 252
339339
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV55]], ptr [[ARRAYIDX65]], align 4, !tbaa [[TBAA4]]

llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,34 @@ static bool processAnd(BinaryOperator *BinOp, LazyValueInfo *LVI) {
12121212
return true;
12131213
}
12141214

1215+
static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
1216+
if (TI->hasNoSignedWrap() && TI->hasNoUnsignedWrap())
1217+
return false;
1218+
1219+
ConstantRange Range =
1220+
LVI->getConstantRangeAtUse(TI->getOperandUse(0), /*UndefAllowed=*/false);
1221+
uint64_t DestWidth = TI->getDestTy()->getScalarSizeInBits();
1222+
bool Changed = false;
1223+
1224+
if (!TI->hasNoUnsignedWrap()) {
1225+
if (Range.getActiveBits() <= DestWidth) {
1226+
TI->setHasNoUnsignedWrap(true);
1227+
++NumNUW;
1228+
Changed = true;
1229+
}
1230+
}
1231+
1232+
if (!TI->hasNoSignedWrap()) {
1233+
if (Range.getMinSignedBits() <= DestWidth) {
1234+
TI->setHasNoSignedWrap(true);
1235+
++NumNSW;
1236+
Changed = true;
1237+
}
1238+
}
1239+
1240+
return Changed;
1241+
}
1242+
12151243
static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
12161244
const SimplifyQuery &SQ) {
12171245
bool FnChanged = false;
@@ -1275,6 +1303,9 @@ static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
12751303
case Instruction::And:
12761304
BBChanged |= processAnd(cast<BinaryOperator>(&II), LVI);
12771305
break;
1306+
case Instruction::Trunc:
1307+
BBChanged |= processTrunc(cast<TruncInst>(&II), LVI);
1308+
break;
12781309
}
12791310
}
12801311

llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ define i1 @test12(i32 %x) {
339339
; CHECK-NEXT: [[ZEXT:%.*]] = zext i32 [[X:%.*]] to i64
340340
; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i64 [[ZEXT]], 7
341341
; CHECK-NEXT: [[SHR:%.*]] = lshr i64 [[MUL]], 32
342-
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i64 [[SHR]] to i32
342+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i64 [[SHR]] to i32
343343
; CHECK-NEXT: ret i1 true
344344
;
345345
%zext = zext i32 %x to i64

llvm/test/Transforms/CorrelatedValuePropagation/range.ll

+2-2
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ entry:
937937
define i1 @ctlz_with_range_metadata(i16 %x) {
938938
; CHECK-LABEL: @ctlz_with_range_metadata(
939939
; CHECK-NEXT: [[CTLZ:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false), !range [[RNG5:![0-9]+]]
940-
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[CTLZ]] to i8
940+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[CTLZ]] to i8
941941
; CHECK-NEXT: ret i1 true
942942
;
943943
%ctlz = call i16 @llvm.ctlz.i16(i16 %x, i1 false), !range !{i16 0, i16 8}
@@ -949,7 +949,7 @@ define i1 @ctlz_with_range_metadata(i16 %x) {
949949
define i1 @abs_with_range_metadata(i16 %x) {
950950
; CHECK-LABEL: @abs_with_range_metadata(
951951
; CHECK-NEXT: [[ABS:%.*]] = call i16 @llvm.abs.i16(i16 [[X:%.*]], i1 false), !range [[RNG5]]
952-
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[ABS]] to i8
952+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[ABS]] to i8
953953
; CHECK-NEXT: ret i1 true
954954
;
955955
%abs = call i16 @llvm.abs.i16(i16 %x, i1 false), !range !{i16 0, i16 8}

llvm/test/Transforms/CorrelatedValuePropagation/sext.ll

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ define void @test1(i32 %n) {
2020
; CHECK: for.body:
2121
; CHECK-NEXT: [[EXT_WIDE1:%.*]] = zext nneg i32 [[A]] to i64
2222
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE1]])
23-
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE1]] to i32
23+
; CHECK-NEXT: [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
2424
; CHECK-NEXT: br label [[FOR_COND]]
2525
; CHECK: for.end:
2626
; CHECK-NEXT: ret void
@@ -55,7 +55,7 @@ define void @test2(i32 %n) {
5555
; CHECK: for.body:
5656
; CHECK-NEXT: [[EXT_WIDE:%.*]] = sext i32 [[A]] to i64
5757
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
58-
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
58+
; CHECK-NEXT: [[EXT]] = trunc nsw i64 [[EXT_WIDE]] to i32
5959
; CHECK-NEXT: br label [[FOR_COND]]
6060
; CHECK: for.end:
6161
; CHECK-NEXT: ret void
@@ -87,7 +87,7 @@ define void @test3(i32 %n) {
8787
; CHECK: bb:
8888
; CHECK-NEXT: [[EXT_WIDE1:%.*]] = zext nneg i32 [[N]] to i64
8989
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE1]])
90-
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE1]] to i32
90+
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
9191
; CHECK-NEXT: br label [[EXIT]]
9292
; CHECK: exit:
9393
; CHECK-NEXT: ret void
@@ -115,7 +115,7 @@ define void @test4(i32 %n) {
115115
; CHECK: bb:
116116
; CHECK-NEXT: [[EXT_WIDE:%.*]] = sext i32 [[N]] to i64
117117
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
118-
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
118+
; CHECK-NEXT: [[EXT:%.*]] = trunc nsw i64 [[EXT_WIDE]] to i32
119119
; CHECK-NEXT: br label [[EXIT]]
120120
; CHECK: exit:
121121
; CHECK-NEXT: ret void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
3+
4+
5+
define i1 @infer_nuw(i8 range(i8 0, 2) %A, i8 range(i8 0, 2) %B) {
6+
; CHECK-LABEL: define i1 @infer_nuw(
7+
; CHECK-SAME: i8 range(i8 0, 2) [[A:%.*]], i8 range(i8 0, 2) [[B:%.*]]) {
8+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[B]], [[A]]
9+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
10+
; CHECK-NEXT: ret i1 [[TRUNC]]
11+
;
12+
%xor = xor i8 %B, %A
13+
%trunc = trunc i8 %xor to i1
14+
ret i1 %trunc
15+
}
16+
17+
18+
define i4 @infer_nsw(i8 %A) {
19+
; CHECK-LABEL: define range(i4 -4, 4) i4 @infer_nsw(
20+
; CHECK-SAME: i8 [[A:%.*]]) {
21+
; CHECK-NEXT: [[ASHR:%.*]] = ashr i8 [[A]], 5
22+
; CHECK-NEXT: [[B:%.*]] = trunc nsw i8 [[ASHR]] to i4
23+
; CHECK-NEXT: ret i4 [[B]]
24+
;
25+
%ashr = ashr i8 %A, 5
26+
%result = trunc i8 %ashr to i4
27+
ret i4 %result
28+
}
29+
30+
31+
define i8 @infer_nuw_nsw(i16 range(i16 -5, -3) %A, i16 range(i16 -5, -3) %B) {
32+
; CHECK-LABEL: define range(i8 0, 8) i8 @infer_nuw_nsw(
33+
; CHECK-SAME: i16 range(i16 -5, -3) [[A:%.*]], i16 range(i16 -5, -3) [[B:%.*]]) {
34+
; CHECK-NEXT: [[XOR:%.*]] = xor i16 [[B]], [[A]]
35+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw nsw i16 [[XOR]] to i8
36+
; CHECK-NEXT: ret i8 [[TRUNC]]
37+
;
38+
%xor = xor i16 %B, %A
39+
%trunc = trunc i16 %xor to i8
40+
ret i8 %trunc
41+
}
42+
43+
44+
define i8 @infer_nsw_from_assume(i16 %x) {
45+
; CHECK-LABEL: define i8 @infer_nsw_from_assume(
46+
; CHECK-SAME: i16 [[X:%.*]]) {
47+
; CHECK-NEXT: [[ADD:%.*]] = add i16 [[X]], 128
48+
; CHECK-NEXT: [[OR_COND_I:%.*]] = icmp ult i16 [[ADD]], 256
49+
; CHECK-NEXT: tail call void @llvm.assume(i1 [[OR_COND_I]])
50+
; CHECK-NEXT: [[CONV1:%.*]] = trunc nsw i16 [[X]] to i8
51+
; CHECK-NEXT: ret i8 [[CONV1]]
52+
;
53+
%add = add i16 %x, 128
54+
%or.cond.i = icmp ult i16 %add, 256
55+
tail call void @llvm.assume(i1 %or.cond.i)
56+
%conv1 = trunc i16 %x to i8
57+
ret i8 %conv1
58+
}
59+
60+
61+
define i1 @rust_issue_122734(i8 range(i8 0, 3) %A, i8 range(i8 0, 3) %B) {
62+
; CHECK-LABEL: define i1 @rust_issue_122734(
63+
; CHECK-SAME: i8 range(i8 0, 3) [[A:%.*]], i8 range(i8 0, 3) [[B:%.*]]) {
64+
; CHECK-NEXT: [[START:.*]]:
65+
; CHECK-NEXT: [[LHS:%.*]] = icmp eq i8 [[A]], 2
66+
; CHECK-NEXT: [[RHS:%.*]] = icmp eq i8 [[B]], 2
67+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[LHS]], [[RHS]]
68+
; CHECK-NEXT: [[AND:%.*]] = and i1 [[LHS]], [[RHS]]
69+
; CHECK-NEXT: br i1 [[OR]], label %[[IFTRUE:.*]], label %[[IFFALSE:.*]]
70+
; CHECK: [[IFTRUE]]:
71+
; CHECK-NEXT: [[PHI:%.*]] = phi i1 [ [[XOR2:%.*]], %[[IFFALSE]] ], [ [[AND]], %[[START]] ]
72+
; CHECK-NEXT: ret i1 [[PHI]]
73+
; CHECK: [[IFFALSE]]:
74+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[A]], [[B]]
75+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
76+
; CHECK-NEXT: [[XOR2]] = xor i1 [[TRUNC]], true
77+
; CHECK-NEXT: br label %[[IFTRUE]]
78+
;
79+
start:
80+
%lhs = icmp eq i8 %A, 2
81+
%rhs = icmp eq i8 %B, 2
82+
%or = or i1 %lhs, %rhs
83+
%and = and i1 %lhs, %rhs
84+
br i1 %or, label %iftrue, label %iffalse
85+
86+
iftrue:
87+
%phi = phi i1 [ %xor2, %iffalse], [ %and, %start ]
88+
ret i1 %phi
89+
90+
iffalse:
91+
%xor = xor i8 %A, %B
92+
%trunc = trunc i8 %xor to i1
93+
%xor2 = xor i1 %trunc, true
94+
br label %iftrue
95+
}
96+
97+
98+
define i1 @overdefined_range_negative(i8 %A, i8 %B) {
99+
; CHECK-LABEL: define i1 @overdefined_range_negative(
100+
; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
101+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[A]], [[B]]
102+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i8 [[XOR]] to i1
103+
; CHECK-NEXT: ret i1 [[TRUNC]]
104+
;
105+
%xor = xor i8 %A, %B
106+
%trunc = trunc i8 %xor to i1
107+
ret i1 %trunc
108+
}

llvm/test/Transforms/CorrelatedValuePropagation/zext.ll

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ define void @test1(i32 %n) {
1414
; CHECK: for.body:
1515
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext nneg i32 [[A]] to i64
1616
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
17-
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
17+
; CHECK-NEXT: [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
1818
; CHECK-NEXT: br label [[FOR_COND]]
1919
; CHECK: for.end:
2020
; CHECK-NEXT: ret void
@@ -49,7 +49,7 @@ define void @test2(i32 %n) {
4949
; CHECK: for.body:
5050
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext i32 [[A]] to i64
5151
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
52-
; CHECK-NEXT: [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
52+
; CHECK-NEXT: [[EXT]] = trunc nuw i64 [[EXT_WIDE]] to i32
5353
; CHECK-NEXT: br label [[FOR_COND]]
5454
; CHECK: for.end:
5555
; CHECK-NEXT: ret void
@@ -81,7 +81,7 @@ define void @test3(i32 %n) {
8181
; CHECK: bb:
8282
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext nneg i32 [[N]] to i64
8383
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
84-
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
84+
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
8585
; CHECK-NEXT: br label [[EXIT]]
8686
; CHECK: exit:
8787
; CHECK-NEXT: ret void
@@ -109,7 +109,7 @@ define void @test4(i32 %n) {
109109
; CHECK: bb:
110110
; CHECK-NEXT: [[EXT_WIDE:%.*]] = zext i32 [[N]] to i64
111111
; CHECK-NEXT: call void @use64(i64 [[EXT_WIDE]])
112-
; CHECK-NEXT: [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
112+
; CHECK-NEXT: [[EXT:%.*]] = trunc nuw i64 [[EXT_WIDE]] to i32
113113
; CHECK-NEXT: br label [[EXIT]]
114114
; CHECK: exit:
115115
; CHECK-NEXT: ret void

0 commit comments

Comments
 (0)