Ryan Buchner 1f1fd07c32
[InstCombine] Optimize (select %x, op(%x), 0) to op(%x) for operations where op(0) == 0 (#147605)
Currently this optimization only occurs for `mul`, but this generalizes
that for any operation that has a fixed point of `0`.

There is similar logic within `EarlyCSE` pass, but that is stricter in
terms of `poison` propagation so will not optimize for many operations.

Alive2 Proofs:
`and`:
https://alive2.llvm.org/ce/z/RraasX ; base-case
https://alive2.llvm.org/ce/z/gzfFTX ; commuted-case
https://alive2.llvm.org/ce/z/63XaoX ; compare against undef
https://alive2.llvm.org/ce/z/MVRVNd ; select undef
https://alive2.llvm.org/ce/z/2bsoYG ; vector
https://alive2.llvm.org/ce/z/xByeX- ; vector compare against undef
https://alive2.llvm.org/ce/z/zNdzmZ ; vector select undef

`fshl`:
https://alive2.llvm.org/ce/z/U3_PG3 ; base-case
https://alive2.llvm.org/ce/z/BWCnxT ; compare against undef
https://alive2.llvm.org/ce/z/8HGAE_ ; select undef
; vector times out

`fshr`:
https://alive2.llvm.org/ce/z/o6F47G ; base-case
https://alive2.llvm.org/ce/z/fVnBXy ; compare against undef
https://alive2.llvm.org/ce/z/suymYJ ; select undef
; vector times out

`umin`:
https://alive2.llvm.org/ce/z/GGMqf6 ; base-case
https://alive2.llvm.org/ce/z/6cx5-k ; commuted-case
https://alive2.llvm.org/ce/z/W5d9tz ; compare against undef
https://alive2.llvm.org/ce/z/nKbaUn ; select undef
https://alive2.llvm.org/ce/z/gxEGqc ; vector
https://alive2.llvm.org/ce/z/_SDpi_ ; vector compare against undef

`sdiv`:
https://alive2.llvm.org/ce/z/5XGs3q

`srem`:
https://alive2.llvm.org/ce/z/vXAnQM

`udiv`:
https://alive2.llvm.org/ce/z/e6_8Ug

`urem`:
https://alive2.llvm.org/ce/z/VmM2SL

`shl`:
https://alive2.llvm.org/ce/z/aCZr3u ; Argument with range
https://alive2.llvm.org/ce/z/YgDy8C ; Instruction with known bits
https://alive2.llvm.org/ce/z/6pIxR6 ; Constant

`lshr`:
https://alive2.llvm.org/ce/z/WCCBej

`ashr:
https://alive2.llvm.org/ce/z/egV4TR

---------

Co-authored-by: Ryan Buchner <rbuchner@ventanamicro.com>
Co-authored-by: Yingwei Zheng <dtcxzyw@qq.com>
2025-07-16 19:42:41 -07:00

838 lines
27 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
declare void @use(i8)
declare void @use.i1(i1)
declare i8 @llvm.umin.i8(i8, i8)
define i1 @icmp_select_const(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_const(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp eq i8 [[Y:%.*]], 0
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 0, i8 %y
%cmp2 = icmp eq i8 %sel, 0
ret i1 %cmp2
}
define i1 @icmp_select_var(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp eq i8 [[Y:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp eq i8 %sel, %z
ret i1 %cmp2
}
define i1 @icmp_select_var_commuted(i8 %x, i8 %y, i8 %_z) {
; CHECK-LABEL: @icmp_select_var_commuted(
; CHECK-NEXT: [[Z:%.*]] = udiv i8 42, [[_Z:%.*]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp eq i8 [[Y:%.*]], [[Z]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%z = udiv i8 42, %_z ; thwart complexity-based canonicalization
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp eq i8 %z, %sel
ret i1 %cmp2
}
define i1 @icmp_select_var_select(i8 %x, i8 %y, i1 %c) {
; CHECK-LABEL: @icmp_select_var_select(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP212:%.*]] = icmp eq i8 [[X]], [[Y:%.*]]
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP1]], i1 true, i1 [[NOT_C]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 [[CMP212]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%z = select i1 %c, i8 %x, i8 %y
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp eq i8 %z, %sel
ret i1 %cmp2
}
define i1 @icmp_select_var_both_fold(i8 %x, i8 %y, i8 %_z) {
; CHECK-LABEL: @icmp_select_var_both_fold(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
%z = or i8 %_z, 1
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 2
%cmp2 = icmp eq i8 %sel, %z
ret i1 %cmp2
}
define i1 @icmp_select_var_extra_use(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var_extra_use(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i8 [[Z:%.*]], i8 [[Y:%.*]]
; CHECK-NEXT: call void @use(i8 [[SEL]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[SEL]], [[Z]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
call void @use(i8 %sel)
%cmp2 = icmp eq i8 %sel, %z
ret i1 %cmp2
}
define i1 @icmp_select_var_both_fold_extra_use(i8 %x, i8 %y, i8 %_z) {
; CHECK-LABEL: @icmp_select_var_both_fold_extra_use(
; CHECK-NEXT: [[Z:%.*]] = or i8 [[_Z:%.*]], 1
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i8 [[Z]], i8 2
; CHECK-NEXT: call void @use(i8 [[SEL]])
; CHECK-NEXT: ret i1 [[CMP1]]
;
%z = or i8 %_z, 1
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 2
call void @use(i8 %sel)
%cmp2 = icmp eq i8 %sel, %z
ret i1 %cmp2
}
define i1 @icmp_select_var_pred_ne(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var_pred_ne(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp ne i8 [[Y:%.*]], [[Z:%.*]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 [[CMP21]], i1 false
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp ne i8 %sel, %z
ret i1 %cmp2
}
define i1 @icmp_select_var_pred_ult(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var_pred_ult(
; CHECK-NEXT: [[Z1:%.*]] = add nuw i8 [[Z:%.*]], 2
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp ult i8 [[Y:%.*]], [[Z1]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%z1 = add nuw i8 %z, 2
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp ult i8 %sel, %z1
ret i1 %cmp2
}
define i1 @icmp_select_var_pred_uge(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var_pred_uge(
; CHECK-NEXT: [[Z1:%.*]] = add nuw i8 [[Z:%.*]], 2
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp uge i8 [[Y:%.*]], [[Z1]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 [[CMP21]], i1 false
; CHECK-NEXT: ret i1 [[CMP2]]
;
%z1 = add nuw i8 %z, 2
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp uge i8 %sel, %z1
ret i1 %cmp2
}
define i1 @icmp_select_var_pred_uge_commuted(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @icmp_select_var_pred_uge_commuted(
; CHECK-NEXT: [[Z1:%.*]] = add nuw i8 [[Z:%.*]], 2
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp ule i8 [[Y:%.*]], [[Z1]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%z1 = add nuw i8 %z, 2
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %z, i8 %y
%cmp2 = icmp uge i8 %z1, %sel
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp eq i8 [[Y:%.*]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 0, i8 %y
%cmp2 = icmp eq i8 %sel, %x
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond_ne(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond_ne(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: [[CMP21:%.*]] = icmp ne i8 [[Y:%.*]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 [[CMP21]], i1 false
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 0, i8 %y
%cmp2 = icmp ne i8 %sel, %x
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond_swapped_select(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond_swapped_select(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[Y:%.*]], 0
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 [[TMP1]], i1 false
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 %y, i8 0
%cmp2 = icmp eq i8 %sel, %x
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond_swapped_select_with_inv_cond(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond_swapped_select_with_inv_cond(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: call void @use.i1(i1 [[CMP1]])
; CHECK-NEXT: [[CMP21:%.*]] = icmp eq i8 [[Y:%.*]], [[X]]
; CHECK-NEXT: [[NOT_CMP1:%.*]] = xor i1 [[CMP1]], true
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[NOT_CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp ne i8 %x, 0
call void @use.i1(i1 %cmp1)
%sel = select i1 %cmp1, i8 %y, i8 0
%cmp2 = icmp eq i8 %sel, %x
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond_relational(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond_relational(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[X:%.*]], 10
; CHECK-NEXT: [[CMP21:%.*]] = icmp ult i8 [[Y:%.*]], [[X]]
; CHECK-NEXT: [[CMP2:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP21]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp ugt i8 %x, 10
%sel = select i1 %cmp1, i8 10, i8 %y
%cmp2 = icmp ult i8 %sel, %x
ret i1 %cmp2
}
define i1 @icmp_select_implied_cond_relational_off_by_one(i8 %x, i8 %y) {
; CHECK-LABEL: @icmp_select_implied_cond_relational_off_by_one(
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[X:%.*]], 10
; CHECK-NEXT: call void @use.i1(i1 [[CMP1]])
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i8 11, i8 [[Y:%.*]]
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 [[SEL]], [[X]]
; CHECK-NEXT: ret i1 [[CMP2]]
;
%cmp1 = icmp ugt i8 %x, 10
call void @use.i1(i1 %cmp1)
%sel = select i1 %cmp1, i8 11, i8 %y
%cmp2 = icmp ult i8 %sel, %x
ret i1 %cmp2
}
define i1 @umin_seq_comparison(i8 %x, i8 %y) {
; CHECK-LABEL: @umin_seq_comparison(
; CHECK-NEXT: [[Y:%.*]] = freeze i8 [[Y1:%.*]]
; CHECK-NEXT: [[CMP21:%.*]] = icmp ule i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP21]]
;
%min = call i8 @llvm.umin.i8(i8 %x, i8 %y)
%cmp1 = icmp eq i8 %x, 0
%sel = select i1 %cmp1, i8 0, i8 %min
%cmp2 = icmp eq i8 %sel, %x
ret i1 %cmp2
}
; ((A ? TC : FC) & (B ? TC : FC)) == 0 --> xor A, B
define i1 @select_constants_and_icmp_eq0(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0(
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 2, i8 1
%s2 = select i1 %y, i8 2, i8 1
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; extra uses on all intermediates are ok
define i1 @select_constants_and_icmp_eq0_uses(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_uses(
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S1]])
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S2]])
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
; CHECK-NEXT: call void @use(i8 [[AND]])
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 2, i8 1
call void @use(i8 %s1)
%s2 = select i1 %y, i8 2, i8 1
call void @use(i8 %s2)
%and = and i8 %s1, %s2
call void @use(i8 %and)
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; vector splat constants are ok
define <2 x i1> @select_constants_and_icmp_eq0_vec_splat(<2 x i1> %x, <2 x i1> %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_vec_splat(
; CHECK-NEXT: [[CMP:%.*]] = xor <2 x i1> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%s1 = select <2 x i1> %x, <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
%s2 = select <2 x i1> %y, <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
%and = and <2 x i9> %s1, %s2
%cmp = icmp eq <2 x i9> %and, zeroinitializer
ret <2 x i1> %cmp
}
; common bit set - simplified via known bits
define i1 @select_constants_and_icmp_eq0_common_bit(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_common_bit(
; CHECK-NEXT: ret i1 false
;
%s1 = select i1 %x, i8 2, i8 3
%s2 = select i1 %y, i8 2, i8 3
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; negative test - need matching constants
define i1 @select_constants_and_icmp_eq0_no_common_op1(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_no_common_op1(
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 16, i8 3
%s2 = select i1 %y, i8 24, i8 3
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; negative test - need matching constants
define i1 @select_constants_and_icmp_eq0_no_common_op2(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_no_common_op2(
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 16, i8 3
%s2 = select i1 %y, i8 16, i8 7
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; reduces via FoldOpInstSelect, but this could be a simple 'or'
define i1 @select_constants_and_icmp_eq0_zero_tval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_zero_tval(
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]]
; CHECK-NEXT: ret i1 [[TMP1]]
;
%s1 = select i1 %x, i8 0, i8 12
%s2 = select i1 %y, i8 0, i8 12
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
; reduces via FoldOpInstSelect, but this could be a simple 'not-of-and'
define i1 @select_constants_and_icmp_eq0_zero_fval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq0_zero_fval(
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false
; CHECK-NEXT: [[NOT_:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[NOT_]]
;
%s1 = select i1 %x, i8 12, i8 0
%s2 = select i1 %y, i8 12, i8 0
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 0
ret i1 %cmp
}
define i1 @select_constants_and_icmp_eq_tval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq_tval(
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 6, i8 1
%s2 = select i1 %y, i8 6, i8 1
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 6
ret i1 %cmp
}
define i1 @select_constants_and_icmp_eq_fval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_eq_fval(
; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 12, i8 3
%s2 = select i1 %y, i8 12, i8 3
%and = and i8 %s1, %s2
%cmp = icmp eq i8 %and, 3
ret i1 %cmp
}
; ((A ? TC : FC) & (B ? TC : FC)) != 0 --> not(xor A, B)
define i1 @select_constants_and_icmp_ne0(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0(
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 2, i8 1
%s2 = select i1 %y, i8 2, i8 1
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; extra uses on select intermediates are ok
define i1 @select_constants_and_icmp_ne0_uses(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_uses(
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S1]])
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S2]])
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X]], [[Y]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 2, i8 1
call void @use(i8 %s1)
%s2 = select i1 %y, i8 2, i8 1
call void @use(i8 %s2)
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; negative test - don't create extra instructions
define i1 @select_constants_and_icmp_ne0_all_uses(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_all_uses(
; CHECK-NEXT: [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S1]])
; CHECK-NEXT: [[S2:%.*]] = select i1 [[Y:%.*]], i8 2, i8 1
; CHECK-NEXT: call void @use(i8 [[S2]])
; CHECK-NEXT: [[AND:%.*]] = and i8 [[S1]], [[S2]]
; CHECK-NEXT: call void @use(i8 [[AND]])
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[AND]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 2, i8 1
call void @use(i8 %s1)
%s2 = select i1 %y, i8 2, i8 1
call void @use(i8 %s2)
%and = and i8 %s1, %s2
call void @use(i8 %and)
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; vector splat constants are ok
define <2 x i1> @select_constants_and_icmp_ne0_vec_splat(<2 x i1> %x, <2 x i1> %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_vec_splat(
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i1> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor <2 x i1> [[TMP1]], splat (i1 true)
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%s1 = select <2 x i1> %x, <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
%s2 = select <2 x i1> %y, <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
%and = and <2 x i9> %s1, %s2
%cmp = icmp ne <2 x i9> %and, zeroinitializer
ret <2 x i1> %cmp
}
; common bit set - simplified via known bits
define i1 @select_constants_and_icmp_ne0_common_bit(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_common_bit(
; CHECK-NEXT: ret i1 true
;
%s1 = select i1 %x, i8 2, i8 3
%s2 = select i1 %y, i8 2, i8 3
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; negative test - need matching constants
define i1 @select_constants_and_icmp_ne0_no_common_op1(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op1(
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 16, i8 3
%s2 = select i1 %y, i8 24, i8 3
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; negative test - need matching constants
define i1 @select_constants_and_icmp_ne0_no_common_op2(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op2(
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 16, i8 3
%s2 = select i1 %y, i8 16, i8 7
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; reduces via FoldOpInstSelect, but this could be a simple 'nor'
define i1 @select_constants_and_icmp_ne0_zero_tval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_zero_tval(
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]]
; CHECK-NEXT: [[NOT_:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[NOT_]]
;
%s1 = select i1 %x, i8 0, i8 12
%s2 = select i1 %y, i8 0, i8 12
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
; reduces via FoldOpInstSelect, but this could be a simple 'and'
define i1 @select_constants_and_icmp_ne0_zero_fval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne0_zero_fval(
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false
; CHECK-NEXT: ret i1 [[TMP1]]
;
%s1 = select i1 %x, i8 12, i8 0
%s2 = select i1 %y, i8 12, i8 0
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 0
ret i1 %cmp
}
define i1 @select_constants_and_icmp_ne_tval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne_tval(
; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 6, i8 1
%s2 = select i1 %y, i8 6, i8 1
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 6
ret i1 %cmp
}
define i1 @select_constants_and_icmp_ne_fval(i1 %x, i1 %y) {
; CHECK-LABEL: @select_constants_and_icmp_ne_fval(
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%s1 = select i1 %x, i8 12, i8 3
%s2 = select i1 %y, i8 12, i8 3
%and = and i8 %s1, %s2
%cmp = icmp ne i8 %and, 3
ret i1 %cmp
}
define i1 @icmp_eq_select(i1 %cond, i32 %a, i32 %b) {
; CHECK-LABEL: @icmp_eq_select(
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i1 [[RES]]
;
%lhs = select i1 %cond, i32 %a, i32 %b
%rhs = select i1 %cond, i32 %b, i32 %a
%res = icmp eq i32 %lhs, %rhs
ret i1 %res
}
define i1 @icmp_slt_select(i1 %cond, i32 %a, i32 %b) {
; CHECK-LABEL: @icmp_slt_select(
; CHECK-NEXT: [[LHS:%.*]] = select i1 [[COND:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
; CHECK-NEXT: [[RHS:%.*]] = select i1 [[COND]], i32 [[B]], i32 [[A]]
; CHECK-NEXT: [[RES:%.*]] = icmp slt i32 [[LHS]], [[RHS]]
; CHECK-NEXT: ret i1 [[RES]]
;
%lhs = select i1 %cond, i32 %a, i32 %b
%rhs = select i1 %cond, i32 %b, i32 %a
%res = icmp slt i32 %lhs, %rhs
ret i1 %res
}
define i1 @discr_eq(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_eq(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, -2
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %add1, i8 1
%add2 = add i8 %b, -2
%cmp2 = icmp ugt i8 %b, 1
%sel2 = select i1 %cmp2, i8 %add2, i8 1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_ne(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_ne(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3
; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, -2
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %add1, i8 1
%add2 = add i8 %b, -2
%cmp2 = icmp ugt i8 %b, 1
%sel2 = select i1 %cmp2, i8 %add2, i8 1
%res = icmp ne i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_xor_eq(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_xor_eq(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 -4
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 -4
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%xor1 = xor i8 %a, -3
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %xor1, i8 1
%xor2 = xor i8 %b, -3
%cmp2 = icmp ugt i8 %b, 1
%sel2 = select i1 %cmp2, i8 %xor2, i8 1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_eq_simple(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_eq_simple(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[B:%.*]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, -2
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %add1, i8 1
%add2 = add i8 %b, -2
%res = icmp eq i8 %sel1, %add2
ret i1 %res
}
define i1 @discr_eq_add_commuted(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @discr_eq_add_commuted(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 [[B]]
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, %b
%sel1 = select i1 %cond1, i8 %add1, i8 %a
%add2 = add i8 %c, %a
%sel2 = select i1 %cond2, i8 %add2, i8 %add1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_eq_add_commuted_implies_poison(i8 %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @discr_eq_add_commuted_implies_poison(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 [[B]]
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, %b
%sel1 = select i1 %cond1, i8 %add1, i8 %a
%add2 = add i8 %c, %a
%sel2 = select i1 %cond2, i8 %add2, i8 %add1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_eq_sub(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @discr_eq_sub(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 0
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%neg = sub i8 0, %a
%sub1 = sub i8 %b, %a
%sel1 = select i1 %cond1, i8 %sub1, i8 %neg
%sub2 = sub i8 %c, %a
%sel2 = select i1 %cond2, i8 %sub2, i8 %neg
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
; Negative tests
define i1 @discr_eq_multi_use(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_eq_multi_use(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1
; CHECK-NEXT: call void @use(i8 [[SEL1]])
; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1
; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, -2
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %add1, i8 1
call void @use(i8 %sel1)
%add2 = add i8 %b, -2
%cmp2 = icmp ugt i8 %b, 1
%sel2 = select i1 %cmp2, i8 %add2, i8 1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define i1 @discr_eq_failed_to_simplify(i8 %a, i8 %b) {
; CHECK-LABEL: @discr_eq_failed_to_simplify(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -3
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1
; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1
; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%add1 = add i8 %a, -3
%cmp1 = icmp ugt i8 %a, 1
%sel1 = select i1 %cmp1, i8 %add1, i8 1
%add2 = add i8 %b, -2
%cmp2 = icmp ugt i8 %b, 1
%sel2 = select i1 %cmp2, i8 %add2, i8 1
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
define <2 x i1> @discr_eq_simple_vec(<2 x i8> %a, <2 x i8> %b, i1 %cond) {
; CHECK-LABEL: @discr_eq_simple_vec(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD1:%.*]] = add <2 x i8> [[A:%.*]], <i8 poison, i8 -2>
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[ADD1]], <2 x i8> splat (i8 1)
; CHECK-NEXT: [[ADD2:%.*]] = add <2 x i8> [[B:%.*]], <i8 -2, i8 poison>
; CHECK-NEXT: [[RES:%.*]] = icmp eq <2 x i8> [[SEL1]], [[ADD2]]
; CHECK-NEXT: ret <2 x i1> [[RES]]
;
entry:
%add1 = add <2 x i8> %a, <i8 poison, i8 -2>
%sel1 = select i1 %cond, <2 x i8> %add1, <2 x i8> splat(i8 1)
%add2 = add <2 x i8> %b, <i8 -2, i8 poison>
%res = icmp eq <2 x i8> %sel1, %add2
ret <2 x i1> %res
}
define i1 @discr_eq_sub_commuted(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @discr_eq_sub_commuted(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
; CHECK-NEXT: [[SUB1:%.*]] = sub i8 [[A]], [[B:%.*]]
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[COND1:%.*]], i8 [[SUB1]], i8 [[NEG]]
; CHECK-NEXT: [[SUB2:%.*]] = sub i8 [[A]], [[C:%.*]]
; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[COND2:%.*]], i8 [[SUB2]], i8 [[NEG]]
; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]]
; CHECK-NEXT: ret i1 [[RES]]
;
entry:
%neg = sub i8 0, %a
%sub1 = sub i8 %a, %b
%sel1 = select i1 %cond1, i8 %sub1, i8 %neg
%sub2 = sub i8 %a, %c
%sel2 = select i1 %cond2, i8 %sub2, i8 %neg
%res = icmp eq i8 %sel1, %sel2
ret i1 %res
}
@g = external global i8
; Do not introduce constant expressions.
define i1 @discr_eq_constantexpr(ptr %p) {
; CHECK-LABEL: @discr_eq_constantexpr(
; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[P:%.*]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[I]], ptrtoint (ptr @g to i64)
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB]], -1
; CHECK-NEXT: ret i1 [[CMP]]
;
%i = ptrtoint ptr %p to i64
%sub = sub i64 %i, ptrtoint (ptr @g to i64)
%cmp = icmp eq i64 %sub, -1
ret i1 %cmp
}