blob: f58ac4ab3837db47e1615b98f636ec3bf34623ce [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
; Test case: Fold (X << 5) == ((Y << 5) + 32) into X == (Y + 1).
; This corresponds to the provided alive2 proof.
declare void @use_i64(i64)
define i1 @shl_add_const_eq_base(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_base(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: icmp ne
define i1 @shl_add_const_ne(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_ne(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp ne i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp ne i64 %v1, %v5 ; Note: icmp ne
ret i1 %v6
}
; Test: shl amounts do not match (5 vs 4).
define i1 @shl_add_const_eq_mismatch_shl_amt(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_mismatch_shl_amt(
; CHECK-NEXT: [[V1:%.*]] = shl nsw i64 [[V0:%.*]], 5
; CHECK-NEXT: [[V4:%.*]] = shl nsw i64 [[V3:%.*]], 4
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V4]], 16
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 4 ; Shift amount mismatch
%v5 = add nsw i64 %v4, 16
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Constant K is a multiple of 2^L (64 vs 32). Should simplify to K/2^L = 2.
define i1 @shl_add_const_eq_k_multiple_of_pow2(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_k_multiple_of_pow2(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 2
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 64 ; Constant mismatch
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Missing NSW flag on one of the shl instructions.
define i1 @shl_add_const_eq_no_nsw_on_v1(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_no_nsw_on_v1(
; CHECK-NEXT: [[V1:%.*]] = shl i64 [[V0:%.*]], 5
; CHECK-NEXT: [[V4:%.*]] = shl nsw i64 [[V3:%.*]], 5
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V4]], 32
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl i64 %v0, 5 ; Missing nsw
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Lower bit width (i8) and different shift amount (3). Constant is 8.
define i1 @shl_add_const_eq_i8(i8 %v0, i8 %v3) {
; CHECK-LABEL: @shl_add_const_eq_i8(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i8 [[V0:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i8 %v0, 3
%v4 = shl nsw i8 %v3, 3
%v5 = add nsw i8 %v4, 8 ; 2^3 = 8
%v6 = icmp eq i8 %v1, %v5
ret i1 %v6
}
; Test: i32 bit width and larger shift amount (10). Constant is 1024.
define i1 @shl_add_const_eq_i32(i32 %v0, i32 %v3) {
; CHECK-LABEL: @shl_add_const_eq_i32(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i32 [[V0:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i32 %v0, 10
%v4 = shl nsw i32 %v3, 10
%v5 = add nsw i32 %v4, 1024 ; 2^10 = 1024
%v6 = icmp eq i32 %v1, %v5
ret i1 %v6
}
; Test: Multi-use case. The optimization should still occur if applicable,
; but the extraneous call must be preserved.
define i1 @shl_add_const_eq_multi_use(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_multi_use(
; CHECK-NEXT: [[V1:%.*]] = shl nsw i64 [[V0:%.*]], 5
; CHECK-NEXT: call void @use_i64(i64 [[V1]])
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V0]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
call void @use_i64(i64 %v1) ; Additional use of v1
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Vector splat. Should fold once optimization is applied.
define <2 x i1> @shl_add_const_eq_vec_splat(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_splat(
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V3:%.*]], splat (i64 1)
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 5>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 5>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 32>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Vector splat with poison. Should fold once optimization is applied.
define <2 x i1> @shl_add_const_eq_vec_splat_poison(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_splat_poison(
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V3:%.*]], <i64 1, i64 poison>
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 5>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 5>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 poison>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Vector non-splat (should not fold).
define <2 x i1> @shl_add_const_eq_vec_non_splat(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_non_splat(
; CHECK-NEXT: [[V1:%.*]] = shl nsw <2 x i64> [[V0:%.*]], <i64 5, i64 6>
; CHECK-NEXT: [[V4:%.*]] = shl nsw <2 x i64> [[V3:%.*]], <i64 5, i64 6>
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V4]], <i64 32, i64 64>
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 6>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 6>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 64>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Commutative (shl on the right side).
define i1 @shl_add_const_eq_commutative(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_commutative(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V0:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v5, %v1
ret i1 %v6
}
; Test: Variable shift amount with nuw (Logical)
define i1 @icmp_shl_var_amount_nuw(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_var_amount_nuw(
; CHECK-NEXT: [[Y:%.*]] = add nuw i32 [[Y1:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp eq i32 %shlx, %add
ret i1 %cmp
}
; Test: Variable shift amount with nsw (Arithmetic)
define i1 @icmp_shl_var_amount_nsw(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_var_amount_nsw(
; CHECK-NEXT: [[Y:%.*]] = add nsw i32 [[Y1:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp eq i32 %shlx, %add
ret i1 %cmp
}
; Test: ult (Unsigned Less Than) with nuw
define i1 @icmp_shl_nuw_ult(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nuw_ult(
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp ult i32 %shlx, %add
ret i1 %cmp
}
; Test: ugt (Unsigned Greater Than) with nuw
define i1 @icmp_shl_nuw_ugt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nuw_ugt(
; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp ugt i32 %shlx, %add
ret i1 %cmp
}
; Test: slt (Signed Less Than) with nsw
define i1 @icmp_shl_nsw_slt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_slt(
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp slt i32 %shlx, %add
ret i1 %cmp
}
; Test: sgt (Signed Greater Than) with nsw
define i1 @icmp_shl_nsw_sgt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_sgt(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp sgt i32 %shlx, %add
ret i1 %cmp
}
; Test: sle (Signed Less or Equal) with nsw
define i1 @icmp_shl_nsw_sle(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_sle(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp sle i32 %shlx, %add
ret i1 %cmp
}
; NOTE: This is a regression test for a miscompile discovered by fuzzing.
define i1 @icmp_slt_shl_nsw_add_nsw_fuzz(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_slt_shl_nsw_add_nsw_fuzz(
; CHECK-NEXT: [[SHLX:%.*]] = shl nsw i32 [[X:%.*]], 5
; CHECK-NEXT: [[SHLY:%.*]] = shl nuw i32 [[Y:%.*]], 5
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[SHLX]], [[SHLY]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nsw i32 32, %shly
%cmp = icmp slt i32 %shlx, %add
ret i1 %cmp
}
; NOTE: This is a regression test for a miscompile discovered by fuzzing (unsigned comparison).
define i1 @icmp_ugt_shl_nuw_add_nsw_fuzz(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_ugt_shl_nuw_add_nsw_fuzz(
; CHECK-NEXT: [[SHLX:%.*]] = shl nuw i32 [[X:%.*]], 5
; CHECK-NEXT: [[SHLY:%.*]] = shl nsw i32 [[Y:%.*]], 5
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SHLY]], 32
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SHLX]], [[ADD]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp ugt i32 %shlx, %add
ret i1 %cmp
}
; Regression test: (shl nuw X, C1) `icmp` (lshr Y, C2) must NOT fold into
; X `icmp` (lshr Y, C1+C2), because lshr is lossy and the low C1 bits of
; (lshr Y, C2) may be non-zero.
;
; Counterexample: X=1, Y=34, C1=4, C2=1
; Correct: (1 << 4) uge (34 >> 1) = 16 uge 17 = false
; Buggy: 1 uge (34 >> 5) = 1 uge 1 = true <-- WRONG
define i1 @test_uge_shl_nuw_lshr(i64 %x, i64 %y) {
; CHECK-LABEL: @test_uge_shl_nuw_lshr(
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X:%.*]], 4
; CHECK-NEXT: [[LSHR:%.*]] = lshr i64 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i64 [[SHL]], [[LSHR]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl = shl nuw i64 %x, 4
%lshr = lshr i64 %y, 1
%cmp = icmp uge i64 %shl, %lshr
ret i1 %cmp
}
define i1 @test_ult_shl_nuw_lshr(i64 %x, i64 %y) {
; CHECK-LABEL: @test_ult_shl_nuw_lshr(
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X:%.*]], 4
; CHECK-NEXT: [[LSHR:%.*]] = lshr i64 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[SHL]], [[LSHR]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl = shl nuw i64 %x, 4
%lshr = lshr i64 %y, 1
%cmp = icmp ult i64 %shl, %lshr
ret i1 %cmp
}
; Same pattern with eq predicate: also must not fold.
define i1 @test_eq_shl_nuw_lshr(i64 %x, i64 %y) {
; CHECK-LABEL: @test_eq_shl_nuw_lshr(
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X:%.*]], 4
; CHECK-NEXT: [[LSHR:%.*]] = lshr i64 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SHL]], [[LSHR]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl = shl nuw i64 %x, 4
%lshr = lshr i64 %y, 1
%cmp = icmp eq i64 %shl, %lshr
ret i1 %cmp
}