; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s ; Test that b - smin(b, a) is recognized as non-negative define i64 @func1(i32 %a, i32 %b) { ; CHECK-LABEL: define i64 @func1( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[SPEC_SELECT:%.*]] = tail call i32 @llvm.smin.i32(i32 [[B]], i32 [[A]]) ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[SPEC_SELECT]] ; CHECK-NEXT: [[CONV:%.*]] = zext nneg i32 [[SUB]] to i64 ; CHECK-NEXT: ret i64 [[CONV]] ; entry: %spec.select = tail call i32 @llvm.smin.i32(i32 %b, i32 %a) %sub = sub nsw i32 %b, %spec.select %conv = sext i32 %sub to i64 ret i64 %conv } ; Test commutative smin pattern: b - smin(a, b) should also optimize define i64 @smin_commutative(i32 %a, i32 %b) { ; CHECK-LABEL: define i64 @smin_commutative( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.smin.i32(i32 [[A]], i32 [[B]]) ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[MIN]] ; CHECK-NEXT: [[EXT:%.*]] = zext nneg i32 [[SUB]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) %sub = sub nsw i32 %b, %min %ext = sext i32 %sub to i64 ret i64 %ext } ; NEGATIVE TEST: mismatched operands should not optimize define i64 @smin_mismatch(i32 %a, i32 %b, i32 %c) { ; CHECK-LABEL: define i64 @smin_mismatch( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) { ; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.smin.i32(i32 [[B]], i32 [[C]]) ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], [[MIN]] ; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %min = call i32 @llvm.smin.i32(i32 %b, i32 %c) %sub = sub nsw i32 %a, %min %ext = sext i32 %sub to i64 ret i64 %ext } ; NEGATIVE TEST: unguarded subtraction should NOT optimize define i64 @neg_unguarded_sub(i32 %a, i32 %b) { ; CHECK-LABEL: define i64 @neg_unguarded_sub( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[A]] ; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %sub = sub nsw i32 %b, %a %ext = sext i32 %sub to i64 ret i64 %ext } ; Test that select i1 (X < Y) ? 0 : X - Y is recognized as non-negative, converting sext to zext define i64 @select_nonnegative_slt(i32 noundef %x, i32 %y) { ; CHECK-LABEL: define i64 @select_nonnegative_slt( ; CHECK-SAME: i32 noundef [[X:%.*]], i32 [[Y:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smin.i32(i32 [[Y]], i32 [[X]]) ; CHECK-NEXT: [[COND:%.*]] = sub nsw i32 [[X]], [[TMP1]] ; CHECK-NEXT: [[CONV:%.*]] = zext nneg i32 [[COND]] to i64 ; CHECK-NEXT: ret i64 [[CONV]] ; %cmp = icmp slt i32 %x, %y ; b < a %sub = sub nsw i32 %x, %y ; b - a %cond = select i1 %cmp, i32 0, i32 %sub ; (X < Y) ? 0 : X - Y %conv = sext i32 %cond to i64 ret i64 %conv } ; Test that select <4 x i1> (X < Y) ? 0 : X - Y is recognized as non-negative, converting sext to zext define <4 x i64> @select_nonnegative_slt_vec(<4 x i32> noundef %x, <4 x i32> %y) { ; CHECK-LABEL: define <4 x i64> @select_nonnegative_slt_vec( ; CHECK-SAME: <4 x i32> noundef [[X:%.*]], <4 x i32> [[Y:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call <4 x i32> @llvm.smin.v4i32(<4 x i32> [[Y]], <4 x i32> [[X]]) ; CHECK-NEXT: [[COND:%.*]] = sub nsw <4 x i32> [[X]], [[TMP1]] ; CHECK-NEXT: [[CONV:%.*]] = zext nneg <4 x i32> [[COND]] to <4 x i64> ; CHECK-NEXT: ret <4 x i64> [[CONV]] ; %cmp = icmp slt <4 x i32> %x, %y %sub = sub nsw <4 x i32> %x, %y %cond = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %sub %conv = sext <4 x i32> %cond to <4 x i64> ret <4 x i64> %conv } ; Scalable vector should transform define @select_nonnegative_slt_scalable( noundef %x, %y) { ; CHECK-LABEL: define @select_nonnegative_slt_scalable( ; CHECK-SAME: noundef [[X:%.*]], [[Y:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call @llvm.smin.nxv4i32( [[Y]], [[X]]) ; CHECK-NEXT: [[COND:%.*]] = sub nsw [[X]], [[TMP1]] ; CHECK-NEXT: [[CONV:%.*]] = zext nneg [[COND]] to ; CHECK-NEXT: ret [[CONV]] ; %cmp = icmp slt %x, %y %sub = sub nsw %x, %y %cond = select %cmp, zeroinitializer, %sub %conv = sext %cond to ret %conv } define i64 @slt_commuted(i32 %x, i32 noundef %y) { ; CHECK-LABEL: define i64 @slt_commuted( ; CHECK-SAME: i32 [[X:%.*]], i32 noundef [[Y:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smin.i32(i32 [[X]], i32 [[Y]]) ; CHECK-NEXT: [[SEL:%.*]] = sub nsw i32 [[Y]], [[TMP1]] ; CHECK-NEXT: [[EXT:%.*]] = zext nneg i32 [[SEL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %cmp = icmp sgt i32 %y, %x %sub = sub nsw i32 %y, %x %sel = select i1 %cmp, i32 %sub, i32 0 ; (X < Y) ? Y - X : 0 %ext = sext i32 %sel to i64 ret i64 %ext } define i64 @sgt_commuted(i32 noundef %x, i32 %y) { ; CHECK-LABEL: define i64 @sgt_commuted( ; CHECK-SAME: i32 noundef [[X:%.*]], i32 [[Y:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.smin.i32(i32 [[Y]], i32 [[X]]) ; CHECK-NEXT: [[SEL:%.*]] = sub nsw i32 [[X]], [[TMP1]] ; CHECK-NEXT: [[EXT:%.*]] = zext nneg i32 [[SEL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %cmp = icmp slt i32 %y, %x ; %sub = sub nsw i32 %x, %y ; %sel = select i1 %cmp, i32 %sub, i32 0 ; (X > Y) ? X - Y : 0 %ext = sext i32 %sel to i64 ret i64 %ext } ; NEGATIVE TEST: FVal is negative define i64 @sgt_negative_fval(i32 noundef %x, i32 %y) { ; CHECK-LABEL: define i64 @sgt_negative_fval( ; CHECK-SAME: i32 noundef [[X:%.*]], i32 [[Y:%.*]]) { ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[Y]], [[X]] ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]] ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 -1 ; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SEL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %cmp = icmp slt i32 %y, %x ; %sub = sub nsw i32 %x, %y ; %sel = select i1 %cmp, i32 %sub, i32 -1 ; (X > Y) ? X - Y : -1 %ext = sext i32 %sel to i64 ret i64 %ext } ; NEGATIVE TEST: Wrong operand in select arm define i64 @slt_wrong_sub_order(i32 noundef %x, i32 %y) { ; CHECK-LABEL: define i64 @slt_wrong_sub_order( ; CHECK-SAME: i32 noundef [[X:%.*]], i32 [[Y:%.*]]) { ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], [[Y]] ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]] ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 0 ; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SEL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %cmp = icmp slt i32 %x, %y %sub = sub nsw i32 %x, %y %sel = select i1 %cmp, i32 %sub, i32 0 ; (X < Y) ? X - Y : 0 %ext = sext i32 %sel to i64 ret i64 %ext } ; NEGATIVE TEST: TVal is negative define i64 @slt_negative_tval(i32 %x, i32 noundef %y) { ; ; CHECK-LABEL: define i64 @slt_negative_tval( ; CHECK-SAME: i32 [[X:%.*]], i32 noundef [[Y:%.*]]) { ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], [[Y]] ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[Y]], [[X]] ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 -1, i32 [[SUB]] ; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SEL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; %cmp = icmp slt i32 %x, %y %sub = sub nsw i32 %y, %x %sel = select i1 %cmp, i32 -1, i32 %sub ; (X < Y) ? -1 : Y - X %ext = sext i32 %sel to i64 ret i64 %ext }