[llvm] 8a36594 - [SCCP] Use constant ranges for binary operators.
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 19 02:36:09 PDT 2020
Author: Florian Hahn
Date: 2020-03-19T09:35:48Z
New Revision: 8a36594a7ec1dba26686d1fb8bd6b56500bc4717
URL: https://github.com/llvm/llvm-project/commit/8a36594a7ec1dba26686d1fb8bd6b56500bc4717
DIFF: https://github.com/llvm/llvm-project/commit/8a36594a7ec1dba26686d1fb8bd6b56500bc4717.diff
LOG: [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
Added:
llvm/test/Transforms/SCCP/ip-ranges-binaryops.ll
Modified:
llvm/lib/Transforms/Scalar/SCCP.cpp
llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll
llvm/test/Transforms/SCCP/range-and.ll
llvm/test/Transforms/SCCP/vector-bitcast.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp
index c17c35b7e323..5df422e8e4b5 100644
--- a/llvm/lib/Transforms/Scalar/SCCP.cpp
+++ b/llvm/lib/Transforms/Scalar/SCCP.cpp
@@ -977,9 +977,18 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) {
LatticeVal V2State = getValueState(I.getOperand(1));
LatticeVal &IV = ValueState[&I];
- if (isOverdefined(IV))
+ if (IV.isOverdefined())
+ return;
+
+ // If something is undef, wait for it to resolve.
+ if (V1State.isUnknownOrUndef() || V2State.isUnknownOrUndef())
+ return;
+
+ if (V1State.isOverdefined() && V2State.isOverdefined())
return (void)markOverdefined(&I);
+ // Both operands are non-integer constants or constant expressions.
+ // TODO: Use information from notconstant better.
if (isConstant(V1State) && isConstant(V2State)) {
Constant *C = ConstantExpr::get(I.getOpcode(), getConstant(V1State),
getConstant(V2State));
@@ -989,50 +998,21 @@ void SCCPSolver::visitBinaryOperator(Instruction &I) {
return (void)markConstant(IV, &I, C);
}
- // If something is undef, wait for it to resolve.
- if (V1State.isUnknownOrUndef() || V2State.isUnknownOrUndef())
- return;
-
- // Otherwise, one of our operands is overdefined. Try to produce something
- // better than overdefined with some tricks.
- // If this is 0 / Y, it doesn't matter that the second operand is
- // overdefined, and we can replace it with zero.
- if (I.getOpcode() == Instruction::UDiv || I.getOpcode() == Instruction::SDiv)
- if (isConstant(V1State) && getConstant(V1State)->isNullValue())
- return (void)markConstant(IV, &I, getConstant(V1State));
-
- // If this is:
- // -> AND/MUL with 0
- // -> OR with -1
- // it doesn't matter that the other operand is overdefined.
- if (I.getOpcode() == Instruction::And || I.getOpcode() == Instruction::Mul ||
- I.getOpcode() == Instruction::Or) {
- LatticeVal *NonOverdefVal = nullptr;
- if (!isOverdefined(V1State))
- NonOverdefVal = &V1State;
-
- else if (!isOverdefined(V2State))
- NonOverdefVal = &V2State;
- if (NonOverdefVal) {
- if (!isConstant(*NonOverdefVal))
- return;
+ // Operands are either constant ranges, notconstant, overdefined or one of the
+ // operands is a constant.
+ ConstantRange A = ConstantRange::getFull(I.getType()->getScalarSizeInBits());
+ ConstantRange B = ConstantRange::getFull(I.getType()->getScalarSizeInBits());
+ if (V1State.isConstantRange())
+ A = V1State.getConstantRange();
+ if (V2State.isConstantRange())
+ B = V2State.getConstantRange();
- if (I.getOpcode() == Instruction::And ||
- I.getOpcode() == Instruction::Mul) {
- // X and 0 = 0
- // X * 0 = 0
- if (getConstant(*NonOverdefVal)->isNullValue())
- return (void)markConstant(IV, &I, getConstant(*NonOverdefVal));
- } else {
- // X or -1 = -1
- if (ConstantInt *CI = getConstantInt(*NonOverdefVal))
- if (CI->isMinusOne())
- return (void)markConstant(IV, &I, CI);
- }
- }
- }
+ ConstantRange R = A.binaryOp(cast<BinaryOperator>(&I)->getOpcode(), B);
+ mergeInValue(&I, LatticeVal::getRange(R));
- markOverdefined(&I);
+ // TODO: Currently we do not exploit special values that produce something
+ // better than overdefined with an overdefined operand for vector or floating
+ // point types, like and <4 x i32> overdefined, zeroinitializer.
}
// Handle ICmpInst instruction.
diff --git a/llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll b/llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll
index a354ae0d4d5d..f2b036f982b8 100644
--- a/llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll
+++ b/llvm/test/Transforms/SCCP/binaryops-range-special-cases.ll
@@ -7,16 +7,13 @@ define void @sdiv1_cmp_constants(i32 %x) {
; CHECK-NEXT: [[D:%.*]] = sdiv i32 1, [[X:%.*]]
; CHECK-NEXT: [[C_0:%.*]] = icmp slt i32 0, [[D]]
; CHECK-NEXT: call void @use(i1 [[C_0]])
-; CHECK-NEXT: [[C_1:%.*]] = icmp slt i32 1, [[D]]
-; CHECK-NEXT: call void @use(i1 [[C_1]])
-; CHECK-NEXT: [[C_2:%.*]] = icmp slt i32 2, [[D]]
-; CHECK-NEXT: call void @use(i1 [[C_2]])
+; CHECK-NEXT: call void @use(i1 false)
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C_3:%.*]] = icmp eq i32 1, [[D]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
; CHECK-NEXT: [[C_4:%.*]] = icmp eq i32 0, [[D]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[C_5:%.*]] = icmp eq i32 2, [[D]]
-; CHECK-NEXT: call void @use(i1 [[C_5]])
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: ret void
;
%d = sdiv i32 1, %x
diff --git a/llvm/test/Transforms/SCCP/ip-ranges-binaryops.ll b/llvm/test/Transforms/SCCP/ip-ranges-binaryops.ll
new file mode 100644
index 000000000000..cef41bbdb584
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/ip-ranges-binaryops.ll
@@ -0,0 +1,134 @@
+; RUN: opt < %s -ipsccp -S | FileCheck %s
+
+; x = [10, 21), y = [100, 201)
+; x + y = [110, 221)
+define internal i1 @f.add(i32 %x, i32 %y) {
+; CHECK-LABEL: define internal i1 @f.add(i32 %x, i32 %y) {
+; CHECK-NEXT: %a.1 = add i32 %x, %y
+; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, 219
+; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, 111
+; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, 150
+; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, 150
+; CHECK-NEXT: %res.1 = add i1 false, %c.2
+; CHECK-NEXT: %res.2 = add i1 %res.1, false
+; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
+; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
+; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
+; CHECK-NEXT: ret i1 %res.5
+;
+ %a.1 = add i32 %x, %y
+ %c.1 = icmp sgt i32 %a.1, 220
+ %c.2 = icmp sgt i32 %a.1, 219
+ %c.3 = icmp slt i32 %a.1, 110
+ %c.4 = icmp slt i32 %a.1, 111
+ %c.5 = icmp eq i32 %a.1, 150
+ %c.6 = icmp slt i32 %a.1, 150
+ %res.1 = add i1 %c.1, %c.2
+ %res.2 = add i1 %res.1, %c.3
+ %res.3 = add i1 %res.2, %c.4
+ %res.4 = add i1 %res.3, %c.5
+ %res.5 = add i1 %res.4, %c.6
+ ret i1 %res.5
+}
+
+define i1 @caller.add() {
+; CHECK-LABEL: define i1 @caller.add() {
+; CHECK-NEXT: %call.1 = tail call i1 @f.add(i32 10, i32 100)
+; CHECK-NEXT: %call.2 = tail call i1 @f.add(i32 20, i32 200)
+; CHECK-NEXT: %res = and i1 %call.1, %call.2
+; CHECK-NEXT: ret i1 %res
+;
+ %call.1 = tail call i1 @f.add(i32 10, i32 100)
+ %call.2 = tail call i1 @f.add(i32 20, i32 200)
+ %res = and i1 %call.1, %call.2
+ ret i1 %res
+}
+
+
+; x = [10, 21), y = [100, 201)
+; x - y = [-190, -79)
+define internal i1 @f.sub(i32 %x, i32 %y) {
+; CHECK-LABEL: define internal i1 @f.sub(i32 %x, i32 %y) {
+; CHECK-NEXT: %a.1 = sub i32 %x, %y
+; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, -81
+; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, -189
+; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, -150
+; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, -150
+; CHECK-NEXT: %res.1 = add i1 false, %c.2
+; CHECK-NEXT: %res.2 = add i1 %res.1, false
+; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
+; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
+; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
+; CHECK-NEXT: ret i1 %res.5
+;
+ %a.1 = sub i32 %x, %y
+ %c.1 = icmp sgt i32 %a.1, -80
+ %c.2 = icmp sgt i32 %a.1, -81
+ %c.3 = icmp slt i32 %a.1, -190
+ %c.4 = icmp slt i32 %a.1, -189
+ %c.5 = icmp eq i32 %a.1, -150
+ %c.6 = icmp slt i32 %a.1, -150
+ %res.1 = add i1 %c.1, %c.2
+ %res.2 = add i1 %res.1, %c.3
+ %res.3 = add i1 %res.2, %c.4
+ %res.4 = add i1 %res.3, %c.5
+ %res.5 = add i1 %res.4, %c.6
+ ret i1 %res.5
+}
+
+define i1 @caller.sub() {
+; CHECK-LABEL: define i1 @caller.sub() {
+; CHECK-NEXT: %call.1 = tail call i1 @f.sub(i32 10, i32 100)
+; CHECK-NEXT: %call.2 = tail call i1 @f.sub(i32 20, i32 200)
+; CHECK-NEXT: %res = and i1 %call.1, %call.2
+; CHECK-NEXT: ret i1 %res
+;
+ %call.1 = tail call i1 @f.sub(i32 10, i32 100)
+ %call.2 = tail call i1 @f.sub(i32 20, i32 200)
+ %res = and i1 %call.1, %call.2
+ ret i1 %res
+}
+
+; x = [10, 21), y = [100, 201)
+; x * y = [1000, 4001)
+define internal i1 @f.mul(i32 %x, i32 %y) {
+; CHECK-LABEL: define internal i1 @f.mul(i32 %x, i32 %y) {
+; CHECK-NEXT: %a.1 = mul i32 %x, %y
+; CHECK-NEXT: %c.2 = icmp sgt i32 %a.1, 3999
+; CHECK-NEXT: %c.4 = icmp slt i32 %a.1, 1001
+; CHECK-NEXT: %c.5 = icmp eq i32 %a.1, 1500
+; CHECK-NEXT: %c.6 = icmp slt i32 %a.1, 1500
+; CHECK-NEXT: %res.1 = add i1 false, %c.2
+; CHECK-NEXT: %res.2 = add i1 %res.1, false
+; CHECK-NEXT: %res.3 = add i1 %res.2, %c.4
+; CHECK-NEXT: %res.4 = add i1 %res.3, %c.5
+; CHECK-NEXT: %res.5 = add i1 %res.4, %c.6
+; CHECK-NEXT: ret i1 %res.5
+;
+ %a.1 = mul i32 %x, %y
+ %c.1 = icmp sgt i32 %a.1, 4000
+ %c.2 = icmp sgt i32 %a.1, 3999
+ %c.3 = icmp slt i32 %a.1, 1000
+ %c.4 = icmp slt i32 %a.1, 1001
+ %c.5 = icmp eq i32 %a.1, 1500
+ %c.6 = icmp slt i32 %a.1, 1500
+ %res.1 = add i1 %c.1, %c.2
+ %res.2 = add i1 %res.1, %c.3
+ %res.3 = add i1 %res.2, %c.4
+ %res.4 = add i1 %res.3, %c.5
+ %res.5 = add i1 %res.4, %c.6
+ ret i1 %res.5
+}
+
+define i1 @caller.mul() {
+; CHECK-LABEL: define i1 @caller.mul() {
+; CHECK-NEXT: %call.1 = tail call i1 @f.mul(i32 10, i32 100)
+; CHECK-NEXT: %call.2 = tail call i1 @f.mul(i32 20, i32 200)
+; CHECK-NEXT: %res = and i1 %call.1, %call.2
+; CHECK-NEXT: ret i1 %res
+;
+ %call.1 = tail call i1 @f.mul(i32 10, i32 100)
+ %call.2 = tail call i1 @f.mul(i32 20, i32 200)
+ %res = and i1 %call.1, %call.2
+ ret i1 %res
+}
diff --git a/llvm/test/Transforms/SCCP/range-and.ll b/llvm/test/Transforms/SCCP/range-and.ll
index e948274dd8f1..84296b94f463 100644
--- a/llvm/test/Transforms/SCCP/range-and.ll
+++ b/llvm/test/Transforms/SCCP/range-and.ll
@@ -8,16 +8,13 @@ define void @and_range_limit(i64 %a) {
; CHECK-NEXT: [[R:%.*]] = and i64 [[A:%.*]], 255
; CHECK-NEXT: [[C_0:%.*]] = icmp slt i64 [[R]], 15
; CHECK-NEXT: call void @use(i1 [[C_0]])
-; CHECK-NEXT: [[C_1:%.*]] = icmp slt i64 [[R]], 256
-; CHECK-NEXT: call void @use(i1 [[C_1]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C_2:%.*]] = icmp eq i64 [[R]], 100
; CHECK-NEXT: call void @use(i1 [[C_2]])
-; CHECK-NEXT: [[C_3:%.*]] = icmp eq i64 [[R]], 300
-; CHECK-NEXT: call void @use(i1 [[C_3]])
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C_4:%.*]] = icmp ne i64 [[R]], 100
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[C_5:%.*]] = icmp ne i64 [[R]], 300
-; CHECK-NEXT: call void @use(i1 [[C_5]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
;
%r = and i64 %a, 255
diff --git a/llvm/test/Transforms/SCCP/vector-bitcast.ll b/llvm/test/Transforms/SCCP/vector-bitcast.ll
index b032085083c6..35312034c65b 100644
--- a/llvm/test/Transforms/SCCP/vector-bitcast.ll
+++ b/llvm/test/Transforms/SCCP/vector-bitcast.ll
@@ -2,7 +2,8 @@
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"
-; CHECK: store volatile <2 x i64> zeroinitializer, <2 x i64>* %p
+; FIXME: Add back support for handling special values of vector/fp types.
+; CHECK: store volatile <2 x i64> %and.i119.i, <2 x i64>* %p
; rdar://11324230
define void @foo(<2 x i64>* %p) nounwind {
More information about the llvm-commits
mailing list