; 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, %v4 = shl nsw <2 x i64> %v3, %v5 = add nsw <2 x i64> %v4, %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:%.*]], ; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1:%.*]], [[V5]] ; CHECK-NEXT: ret <2 x i1> [[V6]] ; %v1 = shl nsw <2 x i64> %v0, %v4 = shl nsw <2 x i64> %v3, %v5 = add nsw <2 x i64> %v4, %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:%.*]], ; CHECK-NEXT: [[V4:%.*]] = shl nsw <2 x i64> [[V3:%.*]], ; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V4]], ; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1]], [[V5]] ; CHECK-NEXT: ret <2 x i1> [[V6]] ; %v1 = shl nsw <2 x i64> %v0, %v4 = shl nsw <2 x i64> %v3, %v5 = add nsw <2 x i64> %v4, %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 }