blob: 027e0257f1372ad12647e59c392ccd62f8bbb180 [file]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=vector-combine -mtriple=aarch64 < %s | FileCheck %s
define i1 @nn_zext_eq_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_zext_eq_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
define i1 @np_sext_eq_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @np_sext_eq_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
define i1 @np_sext_ne_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @np_sext_ne_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp ne i32 %red, 0
ret i1 %cmp
}
define i1 @nn_ule_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_ule_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp ule i32 %red, 0
ret i1 %cmp
}
define i1 @nn_ugt_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_ugt_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp ugt i32 %red, 0
ret i1 %cmp
}
define i1 @nn_sle_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_sle_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp sle i32 %red, 0
ret i1 %cmp
}
define i1 @np_sge_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @np_sge_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp sge i32 %red, 0
ret i1 %cmp
}
define i1 @np_slt_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @np_slt_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp slt i32 %red, 0
ret i1 %cmp
}
define i1 @nn_sgt_0(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_sgt_0(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne <4 x i32> [[ADD]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp sgt i32 %red, 0
ret i1 %cmp
}
; Tautological predicates in the matching sign class: the fold must not
; rewrite these (the surviving icmp would imply a different predicate).
define i1 @nn_sge_0_tautological(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @nn_sge_0_tautological(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[ZA:%.*]] = zext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nuw <4 x i32> [[ZA]], [[ZB]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[ADD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp sge i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%za = zext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add nuw <4 x i32> %za, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp sge i32 %red, 0
ret i1 %cmp
}
define i1 @np_sle_0_tautological(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @np_sle_0_tautological(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[ADD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp sle i32 %red, 0
ret i1 %cmp
}
; ashr X, BW-1 produces {0, -1} per lane. ComputeNumSignBits == BitWidth
; recognizes this as non-positive even without a sext-of-i1 leaf.
define i1 @np_ashr_sign_bit_eq_0(<4 x i32> %x, <4 x i32> %y) {
; CHECK-LABEL: define i1 @np_ashr_sign_bit_eq_0(
; CHECK-SAME: <4 x i32> [[X:%.*]], <4 x i32> [[Y:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = or <4 x i32> [[Y]], [[X]]
; CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> [[TMP1]])
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[TMP2]], -1
; CHECK-NEXT: ret i1 [[CMP]]
;
%sx = ashr <4 x i32> %x, splat (i32 31)
%sy = ashr <4 x i32> %y, splat (i32 31)
%add = add nsw <4 x i32> %sx, %sy
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; Mixed sign classes: one operand is a sext (non-positive), one is a zext
; (non-negative). Sum can cancel, so the fold must bail.
define i1 @mixed_sign_bail(<4 x i1> %a, <4 x i1> %b) {
; CHECK-LABEL: define i1 @mixed_sign_bail(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[ZB:%.*]] = zext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add <4 x i32> [[SA]], [[ZB]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[ADD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%zb = zext <4 x i1> %b to <4 x i32>
%add = add <4 x i32> %sa, %zb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; reduce.add has another use, don't fold.
define i32 @multi_use_reduce_bail(<4 x i1> %a, <4 x i1> %b, ptr %p) {
; CHECK-LABEL: define i32 @multi_use_reduce_bail(
; CHECK-SAME: <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <4 x i1> [[A]] to <4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <4 x i1> [[B]] to <4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[ADD]])
; CHECK-NEXT: store i32 [[RED]], ptr [[P]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 0, i32 [[RED]]
; CHECK-NEXT: ret i32 [[SEL]]
;
%sa = sext <4 x i1> %a to <4 x i32>
%sb = sext <4 x i1> %b to <4 x i32>
%add = add nsw <4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %add)
store i32 %red, ptr %p
%cmp = icmp eq i32 %red, 0
%sel = select i1 %cmp, i32 0, i32 %red
ret i32 %sel
}
; Element count too large relative to sign bits of leaves, summation could
; overflow.
define i1 @overflow_bail(<16 x i32> %x) {
; CHECK-LABEL: define i1 @overflow_bail(
; CHECK-SAME: <16 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[ADD:%.*]] = add <16 x i32> [[X]], [[X]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v16i32(<16 x i32> [[ADD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%add = add <16 x i32> %x, %x
%red = call i32 @llvm.vector.reduce.add.v16i32(<16 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; Scalable vectors are not handled.
define i1 @scalable_bail(<vscale x 4 x i1> %a, <vscale x 4 x i1> %b) {
; CHECK-LABEL: define i1 @scalable_bail(
; CHECK-SAME: <vscale x 4 x i1> [[A:%.*]], <vscale x 4 x i1> [[B:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <vscale x 4 x i1> [[A]] to <vscale x 4 x i32>
; CHECK-NEXT: [[SB:%.*]] = sext <vscale x 4 x i1> [[B]] to <vscale x 4 x i32>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <vscale x 4 x i32> [[SA]], [[SB]]
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> [[ADD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <vscale x 4 x i1> %a to <vscale x 4 x i32>
%sb = sext <vscale x 4 x i1> %b to <vscale x 4 x i32>
%add = add nsw <vscale x 4 x i32> %sa, %sb
%red = call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %add)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; Single-lane vectors: reduce.add is trivial, don't bother.
define i1 @single_lane_bail(<1 x i1> %a) {
; CHECK-LABEL: define i1 @single_lane_bail(
; CHECK-SAME: <1 x i1> [[A:%.*]]) {
; CHECK-NEXT: [[SA:%.*]] = sext <1 x i1> [[A]] to <1 x i32>
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v1i32(<1 x i32> [[SA]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%sa = sext <1 x i1> %a to <1 x i32>
%red = call i32 @llvm.vector.reduce.add.v1i32(<1 x i32> %sa)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; Boundary: lanes with 3 sign bits are non-negative with magnitude < 2^29.
; With 4 lanes: Log2_32(4)=2 < 3 sign bits -> fold applies.
define i1 @overflow_boundary_ok(<4 x i32> %x) {
; CHECK-LABEL: define i1 @overflow_boundary_ok(
; CHECK-SAME: <4 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[MASKED:%.*]] = and <4 x i32> [[X]], splat (i32 536870911)
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[MASKED]], zeroinitializer
; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.vector.reduce.and.v4i1(<4 x i1> [[TMP1]])
; CHECK-NEXT: ret i1 [[CMP]]
;
%masked = and <4 x i32> %x, splat (i32 536870911)
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %masked)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; With 8 lanes: Log2_32(8)=3 >= 3 sign bits -> must bail.
define i1 @overflow_boundary_bail(<8 x i32> %x) {
; CHECK-LABEL: define i1 @overflow_boundary_bail(
; CHECK-SAME: <8 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[MASKED:%.*]] = and <8 x i32> [[X]], splat (i32 536870911)
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v8i32(<8 x i32> [[MASKED]])
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%masked = and <8 x i32> %x, splat (i32 536870911)
%red = call i32 @llvm.vector.reduce.add.v8i32(<8 x i32> %masked)
%cmp = icmp eq i32 %red, 0
ret i1 %cmp
}
; Signed-inequality boundary: 3 sign bits on a non-negative tree with 4 lanes
; gives Log2_32(4)=2 >= 3-1 sign bits -> the tighter signed margin bails,
; even though the eq/ne margin (2 < 3) would allow the fold.
define i1 @signed_boundary_bail(<4 x i32> %x) {
; CHECK-LABEL: define i1 @signed_boundary_bail(
; CHECK-SAME: <4 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[MASKED:%.*]] = and <4 x i32> [[X]], splat (i32 536870911)
; CHECK-NEXT: [[RED:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[MASKED]])
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[RED]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%masked = and <4 x i32> %x, splat (i32 536870911)
%red = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %masked)
%cmp = icmp sgt i32 %red, 0
ret i1 %cmp
}