Files
clang-p2996/llvm/test/Transforms/InstSimplify/and-or-implied-cond.ll
Nikita Popov 060de415af Reapply [InstCombine] Simplify and/or of icmp eq with op replacement (#70335)
Relative to the first attempt, this contains two changes:

First, we only handle the case where one side simplifies to true or
false, instead of calling simplification recursively. The previous
approach would return poison if one operand simplified to poison
(under the equality assumption), which is incorrect.

Second, we do not fold llvm.is.constant in simplifyWithOpReplaced().
We may be assuming that a value is constant, if the equality holds,
but it may not actually be constant. This is nominally just a QoI
issue, but the std::list implementation in libstdc++ relies on the
precise behavior in a way that causes miscompiles.

-----

and/or in logical (select) form benefit from generic simplifications via
simplifyWithOpReplaced(). However, the corresponding fold for plain
and/or currently does not exist.

Similar to selects, there are two general cases for this fold
(illustrated with `and`, but there are `or` conjugates).

The basic case is something like `(a == b) & c`, where the replacement
of a with b or b with a inside c allows it to fold to true or false.
Then the whole operation will fold to either false or `a == b`.

The second case is something like `(a != b) & c`, where the replacement
inside c allows it to fold to false. In that case, the operand can be
replaced with c, because in the case where a == b (and thus the icmp is
false), c itself will already be false.

As the test diffs show, this catches quite a lot of patterns in existing
test coverage. This also obsoletes quite a few existing special-case
and/or of icmp folds we have (e.g. simplifyAndOrOfICmpsWithLimitConst),
but I haven't removed anything as part of this patch in the interest of
risk mitigation.

Fixes #69050.
Fixes #69091.
2023-11-03 10:16:15 +01:00

335 lines
9.0 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
define i1 @or_implied(i8 %x, i1 %c) {
; CHECK-LABEL: @or_implied(
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp eq i8 %x, 0
%cmp2 = icmp ne i8 %x, 1
%and = and i1 %cmp, %c
%or = or i1 %and, %cmp2
ret i1 %or
}
define i1 @or_implied_comm1(i8 %x, i1 %c) {
; CHECK-LABEL: @or_implied_comm1(
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp eq i8 %x, 0
%cmp2 = icmp ne i8 %x, 1
%and = and i1 %cmp, %c
%or = or i1 %cmp2, %and
ret i1 %or
}
define i1 @or_implied_comm2(i8 %x, i1 %c) {
; CHECK-LABEL: @or_implied_comm2(
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp eq i8 %x, 0
%cmp2 = icmp ne i8 %x, 1
%and = and i1 %c, %cmp
%or = or i1 %and, %cmp2
ret i1 %or
}
define i1 @or_implied_comm3(i8 %x, i1 %c) {
; CHECK-LABEL: @or_implied_comm3(
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp eq i8 %x, 0
%cmp2 = icmp ne i8 %x, 1
%and = and i1 %c, %cmp
%or = or i1 %cmp2, %and
ret i1 %or
}
define i1 @or_not_implied(i8 %x, i1 %c) {
; CHECK-LABEL: @or_not_implied(
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP]], [[C:%.*]]
; CHECK-NEXT: [[OR:%.*]] = or i1 [[AND]], [[CMP2]]
; CHECK-NEXT: ret i1 [[OR]]
;
%cmp = icmp eq i8 %x, 0
%cmp2 = icmp ne i8 %x, 0
%and = and i1 %cmp, %c
%or = or i1 %and, %cmp2
ret i1 %or
}
define i1 @and_implied(i8 %x, i1 %c) {
; CHECK-LABEL: @and_implied(
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp ne i8 %x, 0
%cmp2 = icmp eq i8 %x, 1
%or = or i1 %cmp, %c
%and = and i1 %or, %cmp2
ret i1 %and
}
define i1 @and_implied_comm1(i8 %x, i1 %c) {
; CHECK-LABEL: @and_implied_comm1(
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp ne i8 %x, 0
%cmp2 = icmp eq i8 %x, 1
%or = or i1 %cmp, %c
%and = and i1 %cmp2, %or
ret i1 %and
}
define i1 @and_implied_comm2(i8 %x, i1 %c) {
; CHECK-LABEL: @and_implied_comm2(
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp ne i8 %x, 0
%cmp2 = icmp eq i8 %x, 1
%or = or i1 %c, %cmp
%and = and i1 %or, %cmp2
ret i1 %and
}
define i1 @and_implied_comm3(i8 %x, i1 %c) {
; CHECK-LABEL: @and_implied_comm3(
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp = icmp ne i8 %x, 0
%cmp2 = icmp eq i8 %x, 1
%or = or i1 %c, %cmp
%and = and i1 %cmp2, %or
ret i1 %and
}
define i1 @and_not_implied(i8 %x, i1 %c) {
; CHECK-LABEL: @and_not_implied(
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[X]], 0
; CHECK-NEXT: [[OR:%.*]] = or i1 [[CMP]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i1 [[OR]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
%cmp = icmp ne i8 %x, 0
%cmp2 = icmp eq i8 %x, 0
%or = or i1 %cmp, %c
%and = and i1 %or, %cmp2
ret i1 %and
}
define i1 @uaddo_and(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_and(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp uge i64 [[S]], [[A]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp uge i64 %s, %a
%cond_b = icmp uge i64 %s, %b
%cond = and i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_and_commuted1(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_and_commuted1(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ule i64 [[A]], [[S]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ule i64 %a, %s
%cond_b = icmp uge i64 %s, %b
%cond = and i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_and_commuted2(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_and_commuted2(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp uge i64 [[S]], [[A]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp uge i64 %s, %a
%cond_b = icmp ule i64 %b, %s
%cond = and i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_and_commuted3(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_and_commuted3(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ule i64 [[A]], [[S]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ule i64 %a, %s
%cond_b = icmp ule i64 %b, %s
%cond = and i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_or(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_or(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ult i64 [[S]], [[A]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ult i64 %s, %a
%cond_b = icmp ult i64 %s, %b
%cond = or i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_or_commuted1(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_or_commuted1(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ugt i64 [[A]], [[S]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ugt i64 %a, %s
%cond_b = icmp ult i64 %s, %b
%cond = or i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_or_commuted2(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_or_commuted2(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ult i64 [[S]], [[A]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ult i64 %s, %a
%cond_b = icmp ugt i64 %b, %s
%cond = or i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @uaddo_or_commuted3(i64 %a, i64 %b){
; CHECK-LABEL: @uaddo_or_commuted3(
; CHECK-NEXT: [[S:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[COND_A:%.*]] = icmp ugt i64 [[A]], [[S]]
; CHECK-NEXT: ret i1 [[COND_A]]
;
%s = add i64 %a, %b
%cond_a = icmp ugt i64 %a, %s
%cond_b = icmp ugt i64 %b, %s
%cond = or i1 %cond_a, %cond_b
ret i1 %cond
}
define i1 @pr69050(i32 %arg, i32 %arg1) {
; CHECK-LABEL: @pr69050(
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG:%.*]], -1
; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[ARG1:%.*]]
; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[AND]], 0
; CHECK-NEXT: ret i1 [[ICMP]]
;
%xor = xor i32 %arg, -1
%and = and i32 %xor, %arg1
%icmp = icmp ne i32 %and, 0
%icmp2 = icmp ne i32 %arg, -1
%and3 = and i1 %icmp2, %icmp
ret i1 %and3
}
define i1 @pr69091(i32 %arg, i32 %arg1) {
; CHECK-LABEL: @pr69091(
; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32 [[ARG:%.*]], -1
; CHECK-NEXT: ret i1 [[ICMP]]
;
%icmp = icmp ne i32 %arg, -1
%add = add i32 %arg, 1
%mul = mul i32 %add, %arg1
%icmp2 = icmp ne i32 %mul, 0
%or = or i1 %icmp, %icmp2
ret i1 %or
}
declare void @barrier()
define i1 @or_icmp_implies_ub(i32 %x) {
; CHECK-LABEL: @or_icmp_implies_ub(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @barrier()
; CHECK-NEXT: ret i1 [[CMP1]]
;
%cmp1 = icmp ne i32 %x, 0
call void @barrier()
%div = udiv i32 2147483647, %x
%cmp2 = icmp ugt i32 %x, %div
%or = or i1 %cmp1, %cmp2
ret i1 %or
}
define i1 @and_icmp_implies_ub(i32 %x) {
; CHECK-LABEL: @and_icmp_implies_ub(
; CHECK-NEXT: call void @barrier()
; CHECK-NEXT: ret i1 false
;
%cmp1 = icmp eq i32 %x, 0
call void @barrier()
%div = udiv i32 2147483647, %x
%cmp2 = icmp ugt i32 %x, %div
%and = and i1 %cmp1, %cmp2
ret i1 %and
}
define i1 @or_icmp_implies_poison(i32 %x) {
; CHECK-LABEL: @or_icmp_implies_poison(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[X:%.*]], 32
; CHECK-NEXT: [[SHL:%.*]] = shl i32 1, [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[X]], [[SHL]]
; CHECK-NEXT: [[OR:%.*]] = or i1 [[CMP1]], [[CMP2]]
; CHECK-NEXT: ret i1 [[OR]]
;
%cmp1 = icmp ne i32 %x, 32
%shl = shl i32 1, %x
%cmp2 = icmp ugt i32 %x, %shl
%or = or i1 %cmp1, %cmp2
ret i1 %or
}
define i1 @and_icmp_implies_poison(i32 %x) {
; CHECK-LABEL: @and_icmp_implies_poison(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[X:%.*]], 32
; CHECK-NEXT: [[SHL:%.*]] = shl i32 1, [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[X]], [[SHL]]
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND]]
;
%cmp1 = icmp eq i32 %x, 32
%shl = shl i32 1, %x
%cmp2 = icmp ugt i32 %x, %shl
%and = and i1 %cmp1, %cmp2
ret i1 %and
}
define i1 @and_is_constant(ptr %arg, ptr %arg2) {
; CHECK-LABEL: @and_is_constant(
; CHECK-NEXT: [[ICMP:%.*]] = icmp eq ptr [[ARG:%.*]], [[ARG2:%.*]]
; CHECK-NEXT: [[CALL:%.*]] = call i1 @llvm.is.constant.i1(i1 [[ICMP]])
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CALL]], [[ICMP]]
; CHECK-NEXT: ret i1 [[AND]]
;
%icmp = icmp eq ptr %arg, %arg2
%call = call i1 @llvm.is.constant.i1(i1 %icmp)
%and = and i1 %call, %icmp
ret i1 %and
}
declare i1 @llvm.is.constant.i1(i1)