diff --git a/llvm/test/Transforms/InstCombine/icmp-usub-sat.ll b/llvm/test/Transforms/InstCombine/icmp-usub-sat.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/icmp-usub-sat.ll @@ -0,0 +1,742 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +; Tests for InstCombineCompares.cpp::foldICmpUSubSatWithConstant +; https://github.com/llvm/llvm-project/issues/58342 + +; ============================================================================== +; Basic tests with one user and positive arguments +; ============================================================================== +define i1 @icmp_eq_basic_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_eq_basic_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 2) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SUB]], 5 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 2) + %cmp = icmp eq i8 %sub, 5 + ret i1 %cmp +} + +define i1 @icmp_ne_basic_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ne_basic_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 8) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i16 [[SUB]], 9 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 8) + %cmp = icmp ne i16 %sub, 9 + ret i1 %cmp +} + +define i1 @icmp_ule_basic_positive(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_ule_basic_positive +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 6) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 4 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 6) + %cmp = icmp ule i32 %sub, 3 + ret i1 %cmp +} + +define i1 @icmp_ult_basic_positive(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_ult_basic_positive +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[ARG]], 6 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 5) + %cmp = icmp ult i64 %sub, 1 + ret i1 %cmp +} + +define i1 @icmp_uge_basic_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_uge_basic_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 4) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[SUB]], 3 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 4) + %cmp = icmp uge i8 %sub, 4 + ret i1 %cmp +} + +define i1 @icmp_ugt_basic_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ugt_basic_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 1) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i16 [[SUB]], 3 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 1) + %cmp = icmp ugt i16 %sub, 3 + ret i1 %cmp +} + +define i1 @icmp_sle_basic_positive(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_sle_basic_positive +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 10) +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[SUB]], 9 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 10) + %cmp = icmp sle i32 %sub, 8 + ret i1 %cmp +} + +define i1 @icmp_slt_basic_positive(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_slt_basic_positive +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 24) +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[SUB]], 5 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 24) + %cmp = icmp slt i64 %sub, 5 + ret i1 %cmp +} + +define i1 @icmp_sge_basic_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_sge_basic_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 1) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SUB]], 3 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 1) + %cmp = icmp sge i8 %sub, 4 + ret i1 %cmp +} + +define i1 @icmp_sgt_basic_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_sgt_basic_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 2) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i16 [[SUB]], 5 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 2) + %cmp = icmp sgt i16 %sub, 5 + ret i1 %cmp +} + +; ============================================================================== +; Basic tests with one user and negative arguments +; ============================================================================== +define i1 @icmp_eq_basic_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_eq_basic_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -2) + %cmp = icmp eq i8 %sub, 5 + ret i1 %cmp +} + +define i1 @icmp_ne_basic_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ne_basic_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -8) + %cmp = icmp ne i16 %sub, 9 + ret i1 %cmp +} + +define i1 @icmp_ule_basic_negative(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_ule_basic_negative +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 -6) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 4 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 -6) + %cmp = icmp ule i32 %sub, 3 + ret i1 %cmp +} + +define i1 @icmp_ult_basic_negative(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_ult_basic_negative +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[ARG]], -4 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 -5) + %cmp = icmp ult i64 %sub, 1 + ret i1 %cmp +} + +define i1 @icmp_uge_basic_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_uge_basic_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -4) + %cmp = icmp uge i8 %sub, 4 + ret i1 %cmp +} + +define i1 @icmp_ugt_basic_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ugt_basic_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -1) + %cmp = icmp ugt i16 %sub, 3 + ret i1 %cmp +} + +define i1 @icmp_sle_basic_negative(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_sle_basic_negative +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 -10) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 9 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 -10) + %cmp = icmp sle i32 %sub, 8 + ret i1 %cmp +} + +define i1 @icmp_slt_basic_negative(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_slt_basic_negative +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 -24) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[SUB]], 5 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 -24) + %cmp = icmp slt i64 %sub, 5 + ret i1 %cmp +} + +define i1 @icmp_sge_basic_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_sge_basic_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -1) + %cmp = icmp sge i8 %sub, 4 + ret i1 %cmp +} + +define i1 @icmp_sgt_basic_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_sgt_basic_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -2) + %cmp = icmp sgt i16 %sub, 5 + ret i1 %cmp +} + +; ============================================================================== +; Tests with more than user and positive arguments +; ============================================================================== +define i1 @icmp_eq_multiuse_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_eq_multiuse_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 2) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SUB]], 5 +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i8 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 2) + %cmp = icmp eq i8 %sub, 5 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ne_multiuse_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ne_multiuse_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 8) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i16 [[SUB]], 9 +; CHECK-NEXT: [[TMP1:%.*]] = and i16 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i16 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 8) + %cmp = icmp ne i16 %sub, 9 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ule_multiuse_positive(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_ule_multiuse_positive +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 6) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 4 +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i32 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 6) + %cmp = icmp ule i32 %sub, 3 + %ext = zext i1 %cmp to i32 + %add = add i32 %sub, %ext + %ret = trunc i32 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ult_multiuse_positive(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_ult_multiuse_positive +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 5) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[ARG]], 6 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i64 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 5) + %cmp = icmp ult i64 %sub, 1 + %ext = zext i1 %cmp to i64 + %add = add i64 %sub, %ext + %ret = trunc i64 %add to i1 + ret i1 %ret +} + +define i1 @icmp_uge_multiuse_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_uge_multiuse_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 4) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[SUB]], 3 +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i8 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 4) + %cmp = icmp uge i8 %sub, 4 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ugt_multiuse_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ugt_multiuse_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 1) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i16 [[SUB]], 3 +; CHECK-NEXT: [[TMP1:%.*]] = and i16 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i16 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 1) + %cmp = icmp ugt i16 %sub, 3 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sle_multiuse_positive(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_sle_multiuse_positive +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 10) +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[SUB]], 9 +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i32 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 10) + %cmp = icmp sle i32 %sub, 8 + %ext = zext i1 %cmp to i32 + %add = add i32 %sub, %ext + %ret = trunc i32 %add to i1 + ret i1 %ret +} + +define i1 @icmp_slt_multiuse_positive(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_slt_multiuse_positive +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 24) +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[SUB]], 5 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i64 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 24) + %cmp = icmp slt i64 %sub, 5 + %ext = zext i1 %cmp to i64 + %add = add i64 %sub, %ext + %ret = trunc i64 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sge_multiuse_positive(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_sge_multiuse_positive +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 1) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SUB]], 3 +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i8 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 1) + %cmp = icmp sge i8 %sub, 4 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sgt_multiuse_positive(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_sgt_multiuse_positive +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 2) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i16 [[SUB]], 5 +; CHECK-NEXT: [[TMP1:%.*]] = and i16 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i16 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 2) + %cmp = icmp sgt i16 %sub, 5 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +; ============================================================================== +; Tests with more than one user and negative arguments +; ============================================================================== +define i1 @icmp_eq_multiuse_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_eq_multiuse_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[ARG]], -1 +; CHECK-NEXT: ret i1 [[RET]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -2) + %cmp = icmp eq i8 %sub, 5 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ne_multiuse_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ne_multiuse_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[ARG]], i16 -8) +; CHECK-NEXT: [[TMP1:%.*]] = and i16 [[SUB]], 1 +; CHECK-NEXT: [[RET:%.*]] = icmp eq i16 [[TMP1]], 0 +; CHECK-NEXT: ret i1 [[RET]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -8) + %cmp = icmp ne i16 %sub, 9 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ule_multiuse_negative(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_ule_multiuse_negative +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 -6) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 4 +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i32 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 -6) + %cmp = icmp ule i32 %sub, 3 + %ext = zext i1 %cmp to i32 + %add = add i32 %sub, %ext + %ret = trunc i32 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ult_multiuse_negative(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_ult_multiuse_negative +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 -5) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[ARG]], -4 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i64 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 -5) + %cmp = icmp ult i64 %sub, 1 + %ext = zext i1 %cmp to i64 + %add = add i64 %sub, %ext + %ret = trunc i64 %add to i1 + ret i1 %ret +} + +define i1 @icmp_uge_multiuse_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_uge_multiuse_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[ARG]], i8 -4) +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[SUB]], 1 +; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[TMP1]], 0 +; CHECK-NEXT: ret i1 [[RET]] +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -4) + %cmp = icmp uge i8 %sub, 4 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_ugt_multiuse_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_ugt_multiuse_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -1) + %cmp = icmp ugt i16 %sub, 3 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sle_multiuse_negative(i32 %arg) { +; CHECK-LABEL: define i1 @icmp_sle_multiuse_negative +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG]], i32 -10) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 9 +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i32 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i32 @llvm.usub.sat.i32(i32 %arg, i32 -10) + %cmp = icmp sle i32 %sub, 8 + %ext = zext i1 %cmp to i32 + %add = add i32 %sub, %ext + %ret = trunc i32 %add to i1 + ret i1 %ret +} + +define i1 @icmp_slt_multiuse_negative(i64 %arg) { +; CHECK-LABEL: define i1 @icmp_slt_multiuse_negative +; CHECK-SAME: (i64 [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call i64 @llvm.usub.sat.i64(i64 [[ARG]], i64 -24) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[SUB]], 5 +; CHECK-NEXT: [[TMP1:%.*]] = and i64 [[SUB]], 1 +; CHECK-NEXT: [[SUB_TR:%.*]] = icmp ne i64 [[TMP1]], 0 +; CHECK-NEXT: [[ADD_NARROW:%.*]] = xor i1 [[SUB_TR]], [[CMP]] +; CHECK-NEXT: ret i1 [[ADD_NARROW]] +; + %sub = call i64 @llvm.usub.sat.i64(i64 %arg, i64 -24) + %cmp = icmp slt i64 %sub, 5 + %ext = zext i1 %cmp to i64 + %add = add i64 %sub, %ext + %ret = trunc i64 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sge_multiuse_negative(i8 %arg) { +; CHECK-LABEL: define i1 @icmp_sge_multiuse_negative +; CHECK-SAME: (i8 [[ARG:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %sub = call i8 @llvm.usub.sat.i8(i8 %arg, i8 -1) + %cmp = icmp sge i8 %sub, 4 + %ext = zext i1 %cmp to i8 + %add = add i8 %sub, %ext + %ret = trunc i8 %add to i1 + ret i1 %ret +} + +define i1 @icmp_sgt_multiuse_negative(i16 %arg) { +; CHECK-LABEL: define i1 @icmp_sgt_multiuse_negative +; CHECK-SAME: (i16 [[ARG:%.*]]) { +; CHECK-NEXT: [[RET:%.*]] = icmp eq i16 [[ARG]], -1 +; CHECK-NEXT: ret i1 [[RET]] +; + %sub = call i16 @llvm.usub.sat.i16(i16 %arg, i16 -2) + %cmp = icmp sgt i16 %sub, 5 + %ext = zext i1 %cmp to i16 + %add = add i16 %sub, %ext + %ret = trunc i16 %add to i1 + ret i1 %ret +} + +; ============================================================================== +; Tests with vector types +; ============================================================================== +define <2 x i1> @icmp_eq_vector_positive_equal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_positive_equal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[ARG]], <2 x i8> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_eq_vector_negative_equal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_negative_equal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: ret <2 x i1> zeroinitializer +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_eq_vector_multiuse_positive_equal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_multiuse_positive_equal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[ARG]], <2 x i8> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[SUB]], +; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i8> [[SUB]] to <2 x i1> +; CHECK-NEXT: [[RET:%.*]] = xor <2 x i1> [[CMP]], [[TMP1]] +; CHECK-NEXT: ret <2 x i1> [[RET]] +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + %ext = zext <2 x i1> %cmp to <2 x i8> + %add = add <2 x i8> %sub, %ext + %ret = trunc <2 x i8> %add to <2 x i1> + ret <2 x i1> %ret +} + +define <2 x i1> @icmp_eq_vector_multiuse_negative_equal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_multiuse_negative_equal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[ARG]], <2 x i8> ) +; CHECK-NEXT: [[RET:%.*]] = trunc <2 x i8> [[SUB]] to <2 x i1> +; CHECK-NEXT: ret <2 x i1> [[RET]] +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + %ext = zext <2 x i1> %cmp to <2 x i8> + %add = add <2 x i8> %sub, %ext + %ret = trunc <2 x i8> %add to <2 x i1> + ret <2 x i1> %ret +} + +define <2 x i1> @icmp_eq_vector_positive_unequal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_positive_unequal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[ARG]], <2 x i8> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_eq_vector_negative_unequal(<2 x i8> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_eq_vector_negative_unequal +; CHECK-SAME: (<2 x i8> [[ARG:%.*]]) { +; CHECK-NEXT: ret <2 x i1> zeroinitializer +; + %sub = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %arg, <2 x i8> ) + %cmp = icmp eq <2 x i8> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_ne_vector_positive_equal(<2 x i16> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_ne_vector_positive_equal +; CHECK-SAME: (<2 x i16> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> [[ARG]], <2 x i16> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i16> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> %arg, <2 x i16> ) + %cmp = icmp ne <2 x i16> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_ne_vector_positive_unequal(<2 x i16> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_ne_vector_positive_unequal +; CHECK-SAME: (<2 x i16> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> [[ARG]], <2 x i16> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i16> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> %arg, <2 x i16> ) + %cmp = icmp ne <2 x i16> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_ule_vector_positive_equal(<2 x i32> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_ule_vector_positive_equal +; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> [[ARG]], <2 x i32> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult <2 x i32> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> %arg, <2 x i32> ) + %cmp = icmp ult <2 x i32> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_ule_vector_positive_unequal(<2 x i32> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_ule_vector_positive_unequal +; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> [[ARG]], <2 x i32> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult <2 x i32> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> %arg, <2 x i32> ) + %cmp = icmp ult <2 x i32> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_sgt_vector_positive_equal(<2 x i64> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_sgt_vector_positive_equal +; CHECK-SAME: (<2 x i64> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> [[ARG]], <2 x i64> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt <2 x i64> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> %arg, <2 x i64> ) + %cmp = icmp sgt <2 x i64> %sub, + ret <2 x i1> %cmp +} + +define <2 x i1> @icmp_sgt_vector_positive_unequal(<2 x i64> %arg) { +; CHECK-LABEL: define <2 x i1> @icmp_sgt_vector_positive_unequal +; CHECK-SAME: (<2 x i64> [[ARG:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> [[ARG]], <2 x i64> ) +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt <2 x i64> [[SUB]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %sub = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> %arg, <2 x i64> ) + %cmp = icmp sgt <2 x i64> %sub, + ret <2 x i1> %cmp +} + +declare i8 @llvm.usub.sat.i8(i8, i8) +declare i16 @llvm.usub.sat.i16(i16, i16) +declare i32 @llvm.usub.sat.i32(i32, i32) +declare i64 @llvm.usub.sat.i64(i64, i64) + +declare <2 x i64> @llvm.usub.sat.v2i64(<2 x i64>, <2 x i64>) +declare <2 x i32> @llvm.usub.sat.v2i32(<2 x i32>, <2 x i32>) +declare <2 x i16> @llvm.usub.sat.v2i16(<2 x i16>, <2 x i16>) +declare <2 x i8> @llvm.usub.sat.v2i8(<2 x i8>, <2 x i8>)