Skip to content

Commit 8a36594

Browse files
committed
[SCCP] Use constant ranges for binary operators.
If one of the operands of a binary operator is a constant range, we can use ConstantRange::binaryOp to approximate the result. We still handle single element constant ranges as we did previously, with ConstantExpr::get(), because ConstantRange::binaryOp still gives worse results in a few cases for single element ranges. Also note that we bail out early if any of the operands is still unknown. Reviewers: davide, efriedma, mssimpso Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D71936
1 parent d8fcdcd commit 8a36594

File tree

5 files changed

+165
-56
lines changed

5 files changed

+165
-56
lines changed

llvm/lib/Transforms/Scalar/SCCP.cpp

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -977,9 +977,18 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) {
977977
LatticeVal V2State = getValueState(I.getOperand(1));
978978

979979
LatticeVal &IV = ValueState[&I];
980-
if (isOverdefined(IV))
980+
if (IV.isOverdefined())
981+
return;
982+
983+
// If something is undef, wait for it to resolve.
984+
if (V1State.isUnknownOrUndef() || V2State.isUnknownOrUndef())
985+
return;
986+
987+
if (V1State.isOverdefined() && V2State.isOverdefined())
981988
return (void)markOverdefined(&I);
982989

990+
// Both operands are non-integer constants or constant expressions.
991+
// TODO: Use information from notconstant better.
983992
if (isConstant(V1State) && isConstant(V2State)) {
984993
Constant *C = ConstantExpr::get(I.getOpcode(), getConstant(V1State),
985994
getConstant(V2State));
@@ -989,50 +998,21 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) {
989998
return (void)markConstant(IV, &I, C);
990999
}
9911000

992-
// If something is undef, wait for it to resolve.
993-
if (V1State.isUnknownOrUndef() || V2State.isUnknownOrUndef())
994-
return;
995-
996-
// Otherwise, one of our operands is overdefined. Try to produce something
997-
// better than overdefined with some tricks.
998-
// If this is 0 / Y, it doesn't matter that the second operand is
999-
// overdefined, and we can replace it with zero.
1000-
if (I.getOpcode() == Instruction::UDiv || I.getOpcode() == Instruction::SDiv)
1001-
if (isConstant(V1State) && getConstant(V1State)->isNullValue())
1002-
return (void)markConstant(IV, &I, getConstant(V1State));
1003-
1004-
// If this is:
1005-
// -> AND/MUL with 0
1006-
// -> OR with -1
1007-
// it doesn't matter that the other operand is overdefined.
1008-
if (I.getOpcode() == Instruction::And || I.getOpcode() == Instruction::Mul ||
1009-
I.getOpcode() == Instruction::Or) {
1010-
LatticeVal *NonOverdefVal = nullptr;
1011-
if (!isOverdefined(V1State))
1012-
NonOverdefVal = &V1State;
1013-
1014-
else if (!isOverdefined(V2State))
1015-
NonOverdefVal = &V2State;
1016-
if (NonOverdefVal) {
1017-
if (!isConstant(*NonOverdefVal))
1018-
return;
1001+
// Operands are either constant ranges, notconstant, overdefined or one of the
1002+
// operands is a constant.
1003+
ConstantRange A = ConstantRange::getFull(I.getType()->getScalarSizeInBits());
1004+
ConstantRange B = ConstantRange::getFull(I.getType()->getScalarSizeInBits());
1005+
if (V1State.isConstantRange())
1006+
A = V1State.getConstantRange();
1007+
if (V2State.isConstantRange())
1008+
B = V2State.getConstantRange();
10191009

1020-
if (I.getOpcode() == Instruction::And ||
1021-
I.getOpcode() == Instruction::Mul) {
1022-
// X and 0 = 0
1023-
// X * 0 = 0
1024-
if (getConstant(*NonOverdefVal)->isNullValue())
1025-
return (void)markConstant(IV, &I, getConstant(*NonOverdefVal));
1026-
} else {
1027-
// X or -1 = -1
1028-
if (ConstantInt *CI = getConstantInt(*NonOverdefVal))
1029-
if (CI->isMinusOne())
1030-
return (void)markConstant(IV, &I, CI);
1031-
}
1032-
}
1033-
}
1010+
ConstantRange R = A.binaryOp(cast<BinaryOperator>(&I)->getOpcode(), B);
1011+
mergeInValue(&I, LatticeVal::getRange(R));
10341012

1035-
markOverdefined(&I);
1013+
// TODO: Currently we do not exploit special values that produce something
1014+
// better than overdefined with an overdefined operand for vector or floating
1015+
// point types, like and <4 x i32> overdefined, zeroinitializer.
10361016
}
10371017

10381018
// Handle ICmpInst instruction.

llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@ define void @sdiv1_cmp_constants(i32 %x) {
77
; CHECK-NEXT: [[D:%.*]] = sdiv i32 1, [[X:%.*]]
88
; CHECK-NEXT: [[C_0:%.*]] = icmp slt i32 0, [[D]]
99
; CHECK-NEXT: call void @use(i1 [[C_0]])
10-
; CHECK-NEXT: [[C_1:%.*]] = icmp slt i32 1, [[D]]
11-
; CHECK-NEXT: call void @use(i1 [[C_1]])
12-
; CHECK-NEXT: [[C_2:%.*]] = icmp slt i32 2, [[D]]
13-
; CHECK-NEXT: call void @use(i1 [[C_2]])
10+
; CHECK-NEXT: call void @use(i1 false)
11+
; CHECK-NEXT: call void @use(i1 false)
1412
; CHECK-NEXT: [[C_3:%.*]] = icmp eq i32 1, [[D]]
1513
; CHECK-NEXT: call void @use(i1 [[C_3]])
1614
; CHECK-NEXT: [[C_4:%.*]] = icmp eq i32 0, [[D]]
1715
; CHECK-NEXT: call void @use(i1 [[C_4]])
18-
; CHECK-NEXT: [[C_5:%.*]] = icmp eq i32 2, [[D]]
19-
; CHECK-NEXT: call void @use(i1 [[C_5]])
16+
; CHECK-NEXT: call void @use(i1 false)
2017
; CHECK-NEXT: ret void
2118
;
2219
%d = sdiv i32 1, %x
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
; RUN: opt < %s -ipsccp -S | FileCheck %s
2+
3+
; x = [10, 21), y = [100, 201)
4+
; x + y = [110, 221)
5+
define internal i1 @f.add(i32 %x, i32 %y) {
6+
; CHECK-LABEL: define internal i1 @f.add(i32 %x, i32 %y) {
7+
; CHECK-NEXT: %a.1 = add i32 %x, %y
8+
; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, 219
9+
; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, 111
10+
; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, 150
11+
; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, 150
12+
; CHECK-NEXT: %res.1 = add i1 false, %c.2
13+
; CHECK-NEXT: %res.2 = add i1 %res.1, false
14+
; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
15+
; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
16+
; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
17+
; CHECK-NEXT: ret i1 %res.5
18+
;
19+
%a.1 = add i32 %x, %y
20+
%c.1 = icmp sgt i32 %a.1, 220
21+
%c.2 = icmp sgt i32 %a.1, 219
22+
%c.3 = icmp slt i32 %a.1, 110
23+
%c.4 = icmp slt i32 %a.1, 111
24+
%c.5 = icmp eq i32 %a.1, 150
25+
%c.6 = icmp slt i32 %a.1, 150
26+
%res.1 = add i1 %c.1, %c.2
27+
%res.2 = add i1 %res.1, %c.3
28+
%res.3 = add i1 %res.2, %c.4
29+
%res.4 = add i1 %res.3, %c.5
30+
%res.5 = add i1 %res.4, %c.6
31+
ret i1 %res.5
32+
}
33+
34+
define i1 @caller.add() {
35+
; CHECK-LABEL: define i1 @caller.add() {
36+
; CHECK-NEXT: %call.1 = tail call i1 @f.add(i32 10, i32 100)
37+
; CHECK-NEXT: %call.2 = tail call i1 @f.add(i32 20, i32 200)
38+
; CHECK-NEXT: %res = and i1 %call.1, %call.2
39+
; CHECK-NEXT: ret i1 %res
40+
;
41+
%call.1 = tail call i1 @f.add(i32 10, i32 100)
42+
%call.2 = tail call i1 @f.add(i32 20, i32 200)
43+
%res = and i1 %call.1, %call.2
44+
ret i1 %res
45+
}
46+
47+
48+
; x = [10, 21), y = [100, 201)
49+
; x - y = [-190, -79)
50+
define internal i1 @f.sub(i32 %x, i32 %y) {
51+
; CHECK-LABEL: define internal i1 @f.sub(i32 %x, i32 %y) {
52+
; CHECK-NEXT: %a.1 = sub i32 %x, %y
53+
; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, -81
54+
; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, -189
55+
; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, -150
56+
; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, -150
57+
; CHECK-NEXT: %res.1 = add i1 false, %c.2
58+
; CHECK-NEXT: %res.2 = add i1 %res.1, false
59+
; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
60+
; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
61+
; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
62+
; CHECK-NEXT: ret i1 %res.5
63+
;
64+
%a.1 = sub i32 %x, %y
65+
%c.1 = icmp sgt i32 %a.1, -80
66+
%c.2 = icmp sgt i32 %a.1, -81
67+
%c.3 = icmp slt i32 %a.1, -190
68+
%c.4 = icmp slt i32 %a.1, -189
69+
%c.5 = icmp eq i32 %a.1, -150
70+
%c.6 = icmp slt i32 %a.1, -150
71+
%res.1 = add i1 %c.1, %c.2
72+
%res.2 = add i1 %res.1, %c.3
73+
%res.3 = add i1 %res.2, %c.4
74+
%res.4 = add i1 %res.3, %c.5
75+
%res.5 = add i1 %res.4, %c.6
76+
ret i1 %res.5
77+
}
78+
79+
define i1 @caller.sub() {
80+
; CHECK-LABEL: define i1 @caller.sub() {
81+
; CHECK-NEXT: %call.1 = tail call i1 @f.sub(i32 10, i32 100)
82+
; CHECK-NEXT: %call.2 = tail call i1 @f.sub(i32 20, i32 200)
83+
; CHECK-NEXT: %res = and i1 %call.1, %call.2
84+
; CHECK-NEXT: ret i1 %res
85+
;
86+
%call.1 = tail call i1 @f.sub(i32 10, i32 100)
87+
%call.2 = tail call i1 @f.sub(i32 20, i32 200)
88+
%res = and i1 %call.1, %call.2
89+
ret i1 %res
90+
}
91+
92+
; x = [10, 21), y = [100, 201)
93+
; x * y = [1000, 4001)
94+
define internal i1 @f.mul(i32 %x, i32 %y) {
95+
; CHECK-LABEL: define internal i1 @f.mul(i32 %x, i32 %y) {
96+
; CHECK-NEXT: %a.1 = mul i32 %x, %y
97+
; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, 3999
98+
; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, 1001
99+
; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, 1500
100+
; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, 1500
101+
; CHECK-NEXT: %res.1 = add i1 false, %c.2
102+
; CHECK-NEXT: %res.2 = add i1 %res.1, false
103+
; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
104+
; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
105+
; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
106+
; CHECK-NEXT: ret i1 %res.5
107+
;
108+
%a.1 = mul i32 %x, %y
109+
%c.1 = icmp sgt i32 %a.1, 4000
110+
%c.2 = icmp sgt i32 %a.1, 3999
111+
%c.3 = icmp slt i32 %a.1, 1000
112+
%c.4 = icmp slt i32 %a.1, 1001
113+
%c.5 = icmp eq i32 %a.1, 1500
114+
%c.6 = icmp slt i32 %a.1, 1500
115+
%res.1 = add i1 %c.1, %c.2
116+
%res.2 = add i1 %res.1, %c.3
117+
%res.3 = add i1 %res.2, %c.4
118+
%res.4 = add i1 %res.3, %c.5
119+
%res.5 = add i1 %res.4, %c.6
120+
ret i1 %res.5
121+
}
122+
123+
define i1 @caller.mul() {
124+
; CHECK-LABEL: define i1 @caller.mul() {
125+
; CHECK-NEXT: %call.1 = tail call i1 @f.mul(i32 10, i32 100)
126+
; CHECK-NEXT: %call.2 = tail call i1 @f.mul(i32 20, i32 200)
127+
; CHECK-NEXT: %res = and i1 %call.1, %call.2
128+
; CHECK-NEXT: ret i1 %res
129+
;
130+
%call.1 = tail call i1 @f.mul(i32 10, i32 100)
131+
%call.2 = tail call i1 @f.mul(i32 20, i32 200)
132+
%res = and i1 %call.1, %call.2
133+
ret i1 %res
134+
}

llvm/test/Transforms/SCCP/range-and.ll

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ define void @and_range_limit(i64 %a) {
88
; CHECK-NEXT: [[R:%.*]] = and i64 [[A:%.*]], 255
99
; CHECK-NEXT: [[C_0:%.*]] = icmp slt i64 [[R]], 15
1010
; CHECK-NEXT: call void @use(i1 [[C_0]])
11-
; CHECK-NEXT: [[C_1:%.*]] = icmp slt i64 [[R]], 256
12-
; CHECK-NEXT: call void @use(i1 [[C_1]])
11+
; CHECK-NEXT: call void @use(i1 true)
1312
; CHECK-NEXT: [[C_2:%.*]] = icmp eq i64 [[R]], 100
1413
; CHECK-NEXT: call void @use(i1 [[C_2]])
15-
; CHECK-NEXT: [[C_3:%.*]] = icmp eq i64 [[R]], 300
16-
; CHECK-NEXT: call void @use(i1 [[C_3]])
14+
; CHECK-NEXT: call void @use(i1 false)
1715
; CHECK-NEXT: [[C_4:%.*]] = icmp ne i64 [[R]], 100
1816
; CHECK-NEXT: call void @use(i1 [[C_4]])
19-
; CHECK-NEXT: [[C_5:%.*]] = icmp ne i64 [[R]], 300
20-
; CHECK-NEXT: call void @use(i1 [[C_5]])
17+
; CHECK-NEXT: call void @use(i1 true)
2118
; CHECK-NEXT: ret void
2219
;
2320
%r = and i64 %a, 255

llvm/test/Transforms/SCCP/vector-bitcast.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32-S128"
44

5-
; CHECK: store volatile <2 x i64> zeroinitializer, <2 x i64>* %p
5+
; FIXME: Add back support for handling special values of vector/fp types.
6+
; CHECK: store volatile <2 x i64> %and.i119.i, <2 x i64>* %p
67
; rdar://11324230
78

89
define void @foo(<2 x i64>* %p) nounwind {

0 commit comments

Comments
 (0)