| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature |
| ; RUN: opt < %s -passes=instcombine -S | FileCheck %s |
| |
| declare i5 @llvm.umin.i5(i5, i5) |
| declare <2 x i8> @llvm.smin.v2i8(<2 x i8>, <2 x i8>) |
| declare <2 x i8> @llvm.smax.v2i8(<2 x i8>, <2 x i8>) |
| declare <2 x i8> @llvm.umin.v2i8(<2 x i8>, <2 x i8>) |
| declare <2 x i8> @llvm.umax.v2i8(<2 x i8>, <2 x i8>) |
| |
| declare i8 @llvm.smax.i8(i8, i8) |
| declare i8 @llvm.smin.i8(i8, i8) |
| declare i8 @llvm.umax.i8(i8, i8) |
| declare i8 @llvm.umin.i8(i8, i8) |
| |
| define i32 @max_na_b_minux_na(i32 %A, i32 %B) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_na_b_minux_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[NOT]], i32 [[B]]) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 0, [[TMP1]] |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, %B |
| %l1 = select i1 %l0, i32 %not, i32 %B |
| %x = sub i32 %l1, %not |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_na_b(i32 %A, i32 %B) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_na_b |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[NOT]], i32 [[B]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, %B |
| %l1 = select i1 %l0, i32 %not, i32 %B |
| %x = sub i32 %not, %l1 |
| ret i32 %x |
| } |
| |
| define i5 @sub_umin(i5 %a, i5 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_umin |
| ; CHECK-SAME: (i5 [[A:%.*]], i5 [[B:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call i5 @llvm.usub.sat.i5(i5 [[A]], i5 [[B]]) |
| ; CHECK-NEXT: ret i5 [[R]] |
| ; |
| %umin = call i5 @llvm.umin.i5(i5 %a, i5 %b) |
| %r = sub i5 %a, %umin |
| ret i5 %r |
| } |
| |
| define <2 x i8> @sub_umin_commute_vec(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_umin_commute_vec |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[B]], <2 x i8> [[A]]) |
| ; CHECK-NEXT: ret <2 x i8> [[R]] |
| ; |
| %umin = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %b, <2 x i8> %a) |
| %r = sub <2 x i8> %b, %umin |
| ret <2 x i8> %r |
| } |
| |
| define i5 @sub_umin_uses(i5 %a, i5 %b, ptr %p) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_umin_uses |
| ; CHECK-SAME: (i5 [[A:%.*]], i5 [[B:%.*]], ptr [[P:%.*]]) { |
| ; CHECK-NEXT: [[UMIN:%.*]] = call i5 @llvm.umin.i5(i5 [[A]], i5 [[B]]) |
| ; CHECK-NEXT: store i5 [[UMIN]], ptr [[P]], align 1 |
| ; CHECK-NEXT: [[R:%.*]] = sub i5 [[A]], [[UMIN]] |
| ; CHECK-NEXT: ret i5 [[R]] |
| ; |
| %umin = call i5 @llvm.umin.i5(i5 %a, i5 %b) |
| store i5 %umin, ptr %p |
| %r = sub i5 %a, %umin |
| ret i5 %r |
| } |
| |
| define i5 @sub_umin_no_common_op(i5 %a, i5 %b, i5 %c) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_umin_no_common_op |
| ; CHECK-SAME: (i5 [[A:%.*]], i5 [[B:%.*]], i5 [[C:%.*]]) { |
| ; CHECK-NEXT: [[UMIN:%.*]] = call i5 @llvm.umin.i5(i5 [[A]], i5 [[B]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i5 [[C]], [[UMIN]] |
| ; CHECK-NEXT: ret i5 [[R]] |
| ; |
| %umin = call i5 @llvm.umin.i5(i5 %a, i5 %b) |
| %r = sub i5 %c, %umin |
| ret i5 %r |
| } |
| |
| define i32 @max_b_na_minus_na(i32 %A, i32 %B) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_b_na_minus_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[NOT]], i32 [[B]]) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 0, [[TMP1]] |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ugt i32 %not, %B |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %l1, %not |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_b_na(i32 %A, i32 %B) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_b_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[NOT]], i32 [[B]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ugt i32 %not, %B |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %not, %l1 |
| ret i32 %x |
| } |
| |
| |
| define i32 @max_na_bi_minux_na(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_na_bi_minux_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 0, [[TMP1]] |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %B = xor i32 %Bi, -1 |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, %B |
| %l1 = select i1 %l0, i32 %not, i32 %B |
| %x = sub i32 %l1, %not |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_na_bi(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_na_bi |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %B = xor i32 %Bi, -1 |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, %B |
| %l1 = select i1 %l0, i32 %not, i32 %B |
| %x = sub i32 %not, %l1 |
| ret i32 %x |
| } |
| |
| define i32 @max_bi_na_minus_na(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_bi_na_minus_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 0, [[TMP1]] |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %B = xor i32 %Bi, -1 |
| %not = xor i32 %A, -1 |
| %l0 = icmp ugt i32 %not, %B |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %l1, %not |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_bi_na(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_bi_na |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %B = xor i32 %Bi, -1 |
| %not = xor i32 %A, -1 |
| %l0 = icmp ugt i32 %not, %B |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %not, %l1 |
| ret i32 %x |
| } |
| |
| |
| define i32 @max_na_bi_minux_na_use(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_na_bi_minux_na_use |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 -32) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[A]], [[TMP1]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, 31 |
| %l1 = select i1 %l0, i32 %not, i32 31 |
| %x = sub i32 %l1, %not |
| call void @use32(i32 %l1) |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_na_bi_use(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_na_bi_use |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 -32) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[TMP1]], [[A]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, 31 |
| %l1 = select i1 %l0, i32 %not, i32 31 |
| %x = sub i32 %not, %l1 |
| call void @use32(i32 %l1) |
| ret i32 %x |
| } |
| |
| define i32 @max_bi_na_minus_na_use(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_bi_na_minus_na_use |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[A]], [[TMP1]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %B = xor i32 %Bi, -1 |
| %l0 = icmp ult i32 %B, %not |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %l1, %not |
| call void @use32(i32 %l1) |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_bi_na_use(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_bi_na_use |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[TMP1]], [[A]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %B = xor i32 %Bi, -1 |
| %l0 = icmp ult i32 %B, %not |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %not, %l1 |
| call void @use32(i32 %l1) |
| ret i32 %x |
| } |
| |
| |
| define i32 @max_na_bi_minux_na_use2(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_na_bi_minux_na_use2 |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[L1:%.*]] = call i32 @llvm.umin.i32(i32 [[NOT]], i32 31) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[L1]], [[NOT]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: call void @use32(i32 [[NOT]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, 31 |
| %l1 = select i1 %l0, i32 %not, i32 31 |
| %x = sub i32 %l1, %not |
| call void @use32(i32 %l1) |
| call void @use32(i32 %not) |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_na_bi_use2(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_na_bi_use2 |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[L1:%.*]] = call i32 @llvm.umin.i32(i32 [[NOT]], i32 31) |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[NOT]], [[L1]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: call void @use32(i32 [[NOT]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %l0 = icmp ult i32 %not, 31 |
| %l1 = select i1 %l0, i32 %not, i32 31 |
| %x = sub i32 %not, %l1 |
| call void @use32(i32 %l1) |
| call void @use32(i32 %not) |
| ret i32 %x |
| } |
| |
| define i32 @max_bi_na_minus_na_use2(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@max_bi_na_minus_na_use2 |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[A]], [[TMP1]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: call void @use32(i32 [[NOT]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %B = xor i32 %Bi, -1 |
| %l0 = icmp ult i32 %B, %not |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %l1, %not |
| call void @use32(i32 %l1) |
| call void @use32(i32 %not) |
| ret i32 %x |
| } |
| |
| define i32 @na_minus_max_bi_na_use2(i32 %A, i32 %Bi) { |
| ; CHECK-LABEL: define {{[^@]+}}@na_minus_max_bi_na_use2 |
| ; CHECK-SAME: (i32 [[A:%.*]], i32 [[BI:%.*]]) { |
| ; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A]], -1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.umax.i32(i32 [[BI]], i32 [[A]]) |
| ; CHECK-NEXT: [[L1:%.*]] = xor i32 [[TMP1]], -1 |
| ; CHECK-NEXT: [[X:%.*]] = sub i32 [[TMP1]], [[A]] |
| ; CHECK-NEXT: call void @use32(i32 [[L1]]) |
| ; CHECK-NEXT: call void @use32(i32 [[NOT]]) |
| ; CHECK-NEXT: ret i32 [[X]] |
| ; |
| %not = xor i32 %A, -1 |
| %B = xor i32 %Bi, -1 |
| %l0 = icmp ult i32 %B, %not |
| %l1 = select i1 %l0, i32 %B, i32 %not |
| %x = sub i32 %not, %l1 |
| call void @use32(i32 %l1) |
| call void @use32(i32 %not) |
| ret i32 %x |
| } |
| |
| define i8 @umin_not_sub(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_not_sub |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[MINXY:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: [[SUBY:%.*]] = sub i8 [[TMP1]], [[Y]] |
| ; CHECK-NEXT: call void @use8(i8 [[SUBX]]) |
| ; CHECK-NEXT: call void @use8(i8 [[SUBY]]) |
| ; CHECK-NEXT: ret i8 [[MINXY]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| %cmpxy = icmp ult i8 %nx, %ny |
| %minxy = select i1 %cmpxy, i8 %nx, i8 %ny |
| %subx = sub i8 %nx, %minxy |
| %suby = sub i8 %ny, %minxy |
| call void @use8(i8 %subx) |
| call void @use8(i8 %suby) |
| ret i8 %minxy |
| } |
| |
| define i8 @umin_not_sub_rev(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_not_sub_rev |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[MINXY:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[X]], [[TMP1]] |
| ; CHECK-NEXT: [[SUBY:%.*]] = sub i8 [[Y]], [[TMP1]] |
| ; CHECK-NEXT: call void @use8(i8 [[SUBX]]) |
| ; CHECK-NEXT: call void @use8(i8 [[SUBY]]) |
| ; CHECK-NEXT: ret i8 [[MINXY]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| %cmpxy = icmp ult i8 %nx, %ny |
| %minxy = select i1 %cmpxy, i8 %nx, i8 %ny |
| %subx = sub i8 %minxy, %nx |
| %suby = sub i8 %minxy, %ny |
| call void @use8(i8 %subx) |
| call void @use8(i8 %suby) |
| ret i8 %minxy |
| } |
| |
| define void @umin3_not_all_ops_extra_uses_invert_subs(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin3_not_all_ops_extra_uses_invert_subs |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Z]]) |
| ; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.umax.i8(i8 [[Y]], i8 [[TMP1]]) |
| ; CHECK-NEXT: [[MINXYZ:%.*]] = xor i8 [[TMP2]], -1 |
| ; CHECK-NEXT: [[XMIN:%.*]] = sub i8 [[TMP2]], [[X]] |
| ; CHECK-NEXT: [[YMIN:%.*]] = sub i8 [[TMP2]], [[Y]] |
| ; CHECK-NEXT: [[ZMIN:%.*]] = sub i8 [[TMP2]], [[Z]] |
| ; CHECK-NEXT: call void @use8(i8 [[MINXYZ]]) |
| ; CHECK-NEXT: call void @use8(i8 [[XMIN]]) |
| ; CHECK-NEXT: call void @use8(i8 [[YMIN]]) |
| ; CHECK-NEXT: call void @use8(i8 [[ZMIN]]) |
| ; CHECK-NEXT: ret void |
| ; |
| %xn = xor i8 %x, -1 |
| %yn = xor i8 %y, -1 |
| %zn = xor i8 %z, -1 |
| %cmpxz = icmp ult i8 %xn, %zn |
| %minxz = select i1 %cmpxz, i8 %xn, i8 %zn |
| %cmpxyz = icmp ult i8 %minxz, %yn |
| %minxyz = select i1 %cmpxyz, i8 %minxz, i8 %yn |
| %xmin = sub i8 %xn, %minxyz |
| %ymin = sub i8 %yn, %minxyz |
| %zmin = sub i8 %zn, %minxyz |
| call void @use8(i8 %minxyz) |
| call void @use8(i8 %xmin) |
| call void @use8(i8 %ymin) |
| call void @use8(i8 %zmin) |
| ret void |
| } |
| |
| ; Handle this pattern with extra uses because it shows up in benchmarks. |
| ; ~X - Min/Max(~X, Y) -> ~Min/Max(X, ~Y) - X |
| ; ~X - Min/Max(Y, ~X) -> ~Min/Max(X, ~Y) - X |
| ; Min/Max(~X, Y) - ~X -> X - ~Min/Max(X, ~Y) |
| ; Min/Max(Y, ~X) - ~X -> X - ~Min/Max(X, ~Y) |
| |
| define i8 @umin_not_sub_intrinsic_commute0(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_not_sub_intrinsic_commute0 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NY]]) |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[M:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[SUBX]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| call void @use8(i8 %ny) |
| %m = call i8 @llvm.umin.i8(i8 %nx, i8 %ny) |
| call void @use8(i8 %m) |
| %subx = sub i8 %nx, %m |
| ret i8 %subx |
| } |
| |
| define i8 @umax_not_sub_intrinsic_commute1(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_not_sub_intrinsic_commute1 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NY]]) |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[M:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[SUBX]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| call void @use8(i8 %ny) |
| %m = call i8 @llvm.umax.i8(i8 %ny, i8 %nx) |
| call void @use8(i8 %m) |
| %subx = sub i8 %nx, %m |
| ret i8 %subx |
| } |
| |
| define i8 @smin_not_sub_intrinsic_commute2(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@smin_not_sub_intrinsic_commute2 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NY]]) |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[M:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[X]], [[TMP1]] |
| ; CHECK-NEXT: ret i8 [[SUBX]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| call void @use8(i8 %ny) |
| %m = call i8 @llvm.smin.i8(i8 %nx, i8 %ny) |
| call void @use8(i8 %m) |
| %subx = sub i8 %m, %nx |
| ret i8 %subx |
| } |
| |
| define i8 @smax_not_sub_intrinsic_commute3(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@smax_not_sub_intrinsic_commute3 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NY]]) |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[M:%.*]] = xor i8 [[TMP1]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[X]], [[TMP1]] |
| ; CHECK-NEXT: ret i8 [[SUBX]] |
| ; |
| %nx = xor i8 %x, -1 |
| %ny = xor i8 %y, -1 |
| call void @use8(i8 %ny) |
| %m = call i8 @llvm.smax.i8(i8 %ny, i8 %nx) |
| call void @use8(i8 %m) |
| %subx = sub i8 %m, %nx |
| ret i8 %subx |
| } |
| |
| ; negative test - don't increase instruction count |
| |
| define i8 @umin_not_sub_intrinsic_uses(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_not_sub_intrinsic_uses |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NX]]) |
| ; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y]], -1 |
| ; CHECK-NEXT: call void @use8(i8 [[NY]]) |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[NX]], i8 [[NY]]) |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[SUBX:%.*]] = sub i8 [[NX]], [[M]] |
| ; CHECK-NEXT: ret i8 [[SUBX]] |
| ; |
| %nx = xor i8 %x, -1 |
| call void @use8(i8 %nx) |
| %ny = xor i8 %y, -1 |
| call void @use8(i8 %ny) |
| %m = call i8 @llvm.umin.i8(i8 %nx, i8 %ny) |
| call void @use8(i8 %m) |
| %subx = sub i8 %nx, %m |
| ret i8 %subx |
| } |
| |
| define i8 @umax_sub_op0(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op0 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umax.i8(i8 %y, i8 %x) |
| %r = sub i8 %u, %y |
| ret i8 %r |
| } |
| |
| define <2 x i8> @umax_sub_op0_vec_commute(<2 x i8> %x, <2 x i8> %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op0_vec_commute |
| ; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[X]], <2 x i8> [[Y]]) |
| ; CHECK-NEXT: ret <2 x i8> [[R]] |
| ; |
| %u = call <2 x i8> @llvm.umax.v2i8(<2 x i8> %x, <2 x i8> %y) |
| %r = sub <2 x i8> %u, %y |
| ret <2 x i8> %r |
| } |
| |
| define i8 @umax_sub_op0_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op0_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: call void @use8(i8 [[U]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[U]], [[Y]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umax.i8(i8 %x, i8 %y) |
| call void @use8(i8 %u) |
| %r = sub i8 %u, %y |
| ret i8 %r |
| } |
| |
| define i8 @umax_sub_op1(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op1 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 0, [[TMP1]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umax.i8(i8 %x, i8 %y) |
| %r = sub i8 %y, %u |
| ret i8 %r |
| } |
| |
| define <2 x i8> @umax_sub_op1_vec_commute(<2 x i8> %x, <2 x i8> %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op1_vec_commute |
| ; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[X]], <2 x i8> [[Y]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub <2 x i8> zeroinitializer, [[TMP1]] |
| ; CHECK-NEXT: ret <2 x i8> [[R]] |
| ; |
| %u = call <2 x i8> @llvm.umax.v2i8(<2 x i8> %y, <2 x i8> %x) |
| %r = sub <2 x i8> %y, %u |
| ret <2 x i8> %r |
| } |
| |
| define i8 @umax_sub_op1_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umax_sub_op1_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: call void @use8(i8 [[U]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[Y]], [[U]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umax.i8(i8 %x, i8 %y) |
| call void @use8(i8 %u) |
| %r = sub i8 %y, %u |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op1(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op1 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %y, i8 %x) |
| %r = sub i8 %y, %u |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op1_commute(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op1_commute |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %x, i8 %y) |
| %r = sub i8 %y, %u |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op0(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op0 |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 0, [[TMP1]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %y, i8 %x) |
| %r = sub i8 %u, %y |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op0_commute(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op0_commute |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 0, [[TMP1]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %x, i8 %y) |
| %r = sub i8 %u, %y |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op1_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op1_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: call void @use8(i8 [[U]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[Y]], [[U]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %y, i8 %x) |
| call void @use8(i8 %u) |
| %r = sub i8 %y, %u |
| ret i8 %r |
| } |
| |
| define i8 @umin_sub_op0_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@umin_sub_op0_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: call void @use8(i8 [[U]]) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[U]], [[Y]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %u = call i8 @llvm.umin.i8(i8 %y, i8 %x) |
| call void @use8(i8 %u) |
| %r = sub i8 %u, %y |
| ret i8 %r |
| } |
| |
| ; |
| ; sub(add(X,Y), s/umin(X,Y)) --> s/umax(X,Y) |
| ; sub(add(X,Y), s/umax(X,Y)) --> s/umin(X,Y) |
| ; |
| |
| define i8 @diff_add_smin(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_smin |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.smin.i8(i8 %x, i8 %y) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_smax(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_smax |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.smin.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.smax.i8(i8 %y, i8 %x) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_umin(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_umin |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %x, i8 %y) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_umax(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_umax |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umax.i8(i8 %y, i8 %x) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_smin_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_smin_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.smin.i8(i8 %x, i8 %y) |
| %s = sub i8 %a, %m |
| call void @use8(i8 %m) |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_use_smax(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_use_smax |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[S:%.*]] = call i8 @llvm.smin.i8(i8 [[Y]], i8 [[X]]) |
| ; CHECK-NEXT: call void @use8(i8 [[A]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.smax.i8(i8 %y, i8 %x) |
| %s = sub i8 %a, %m |
| call void @use8(i8 %a) |
| ret i8 %s |
| } |
| |
| define i8 @diff_add_use_umin_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@diff_add_use_umin_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] |
| ; CHECK-NEXT: call void @use8(i8 [[A]]) |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %x, i8 %y) |
| %s = sub i8 %a, %m |
| call void @use8(i8 %a) |
| call void @use8(i8 %m) |
| ret i8 %s |
| } |
| |
| ; sub(add(X,Y),umin(Y,Z)) --> add(X,usubsat(Y,Z)) |
| |
| define i8 @sub_add_umin(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @sub_add_umin_commute_umin(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_commute_umin |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %z, i8 %y) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @sub_add_umin_commute_add(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_commute_add |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %y, %x |
| %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @sub_add_umin_commute_add_umin(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_commute_add_umin |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %y, %x |
| %m = call i8 @llvm.umin.i8(i8 %z, i8 %y) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define <2 x i8> @sub_add_umin_vec(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_vec |
| ; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) { |
| ; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[Y]], <2 x i8> [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = add <2 x i8> [[TMP1]], [[X]] |
| ; CHECK-NEXT: ret <2 x i8> [[S]] |
| ; |
| %a = add <2 x i8> %x, %y |
| %m = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %y, <2 x i8> %z) |
| %s = sub <2 x i8> %a, %m |
| ret <2 x i8> %s |
| } |
| |
| ; negative test |
| |
| define i8 @sub_add_umin_mismatch(i8 %x, i8 %y, i8 %z, i8 %t) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_mismatch |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]], i8 [[T:%.*]]) { |
| ; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[T]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %t, i8 %z) |
| %s = sub i8 %a, %m |
| ret i8 %s |
| } |
| |
| define i8 @sub_add_umin_use_a(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_use_a |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] |
| ; CHECK-NEXT: call void @use8(i8 [[A]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) |
| %s = sub i8 %a, %m |
| call void @use8(i8 %a) |
| ret i8 %s |
| } |
| |
| define i8 @sub_add_umin_use_m(i8 %x, i8 %y, i8 %z) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_add_umin_use_m |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { |
| ; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[Z]]) |
| ; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: ret i8 [[S]] |
| ; |
| %a = add i8 %x, %y |
| %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) |
| %s = sub i8 %a, %m |
| call void @use8(i8 %m) |
| ret i8 %s |
| } |
| |
| define <2 x i8> @sub_smax0_sub_nsw(<2 x i8> %x, <2 x i8> %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smax0_sub_nsw |
| ; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[X]], <2 x i8> [[Y]]) |
| ; CHECK-NEXT: ret <2 x i8> [[R]] |
| ; |
| %sub = sub nsw <2 x i8> %x, %y |
| %m = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %sub, <2 x i8> <i8 0, i8 poison>) |
| %r = sub <2 x i8> %x, %m |
| ret <2 x i8> %r |
| } |
| |
| define i8 @sub_smax0_sub_nsw_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smax0_sub_nsw_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[SUB]], i8 0) |
| ; CHECK-NEXT: call void @use8(i8 [[M]]) |
| ; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub nsw i8 %x, %y |
| %m = call i8 @llvm.smax.i8(i8 %sub, i8 0) |
| call void @use8(i8 %m) |
| %r = sub i8 %x, %m |
| ret i8 %r |
| } |
| |
| ; negative test - must have nsw |
| |
| define i8 @sub_smax0_sub(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smax0_sub |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[SUB]], i8 0) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[M]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub i8 %x, %y |
| %m = call i8 @llvm.smax.i8(i8 %sub, i8 0) |
| %r = sub i8 %x, %m |
| ret i8 %r |
| } |
| |
| ; negative test - wrong op |
| |
| define i8 @sub_smax0_sub_commute(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smax0_sub_commute |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[SUB]], i8 0) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[M]], [[X]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub nsw i8 %x, %y |
| %m = call i8 @llvm.smax.i8(i8 %sub, i8 0) |
| %r = sub i8 %m, %x |
| ret i8 %r |
| } |
| |
| define i8 @sub_smin0_sub_nsw_use(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smin0_sub_nsw_use |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[X]], [[Y]] |
| ; CHECK-NEXT: call void @use8(i8 [[SUB]]) |
| ; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]]) |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub nsw i8 %x, %y |
| call void @use8(i8 %sub) |
| %m = call i8 @llvm.smin.i8(i8 %sub, i8 0) |
| %r = sub i8 %x, %m |
| ret i8 %r |
| } |
| |
| define <2 x i8> @sub_smin0_sub_nsw(<2 x i8> %x, <2 x i8> %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smin0_sub_nsw |
| ; CHECK-SAME: (<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) { |
| ; CHECK-NEXT: [[R:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[X]], <2 x i8> [[Y]]) |
| ; CHECK-NEXT: ret <2 x i8> [[R]] |
| ; |
| %sub = sub nsw <2 x i8> %x, %y |
| %m = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %sub, <2 x i8> zeroinitializer) |
| %r = sub <2 x i8> %x, %m |
| ret <2 x i8> %r |
| } |
| |
| ; negative test - must have nsw |
| |
| define i8 @sub_smin0_sub(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smin0_sub |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[SUB]], i8 0) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[M]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub i8 %x, %y |
| %m = call i8 @llvm.smin.i8(i8 %sub, i8 0) |
| %r = sub i8 %x, %m |
| ret i8 %r |
| } |
| |
| ; negative test - wrong op |
| |
| define i8 @sub_smin0_sub_nsw_commute(i8 %x, i8 %y) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_smin0_sub_nsw_commute |
| ; CHECK-SAME: (i8 [[X:%.*]], i8 [[Y:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[Y]], [[X]] |
| ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[SUB]], i8 0) |
| ; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[M]] |
| ; CHECK-NEXT: ret i8 [[R]] |
| ; |
| %sub = sub nsw i8 %y, %x |
| %m = call i8 @llvm.smin.i8(i8 %sub, i8 0) |
| %r = sub i8 %x, %m |
| ret i8 %r |
| } |
| |
| define i8 @sub_max_min_nsw(i8 %a, i8 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nsw |
| ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) |
| ; CHECK-NEXT: ret i8 [[AB]] |
| ; |
| %min = call i8 @llvm.smin.i8(i8 %a, i8 %b) |
| %max = call i8 @llvm.smax.i8(i8 %a, i8 %b) |
| %ab = sub nsw i8 %max, %min |
| ret i8 %ab |
| } |
| |
| define i8 @sub_max_min_nuw(i8 %a, i8 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nuw |
| ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) |
| ; CHECK-NEXT: ret i8 [[AB]] |
| ; |
| %min = call i8 @llvm.smin.i8(i8 %a, i8 %b) |
| %max = call i8 @llvm.smax.i8(i8 %a, i8 %b) |
| %ab = sub nuw i8 %max, %min |
| ret i8 %ab |
| } |
| |
| define i8 @sub_max_min_nsw_commute(i8 %a, i8 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nsw_commute |
| ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) |
| ; CHECK-NEXT: ret i8 [[AB]] |
| ; |
| %min = call i8 @llvm.smin.i8(i8 %b, i8 %a) |
| %max = call i8 @llvm.smax.i8(i8 %a, i8 %b) |
| %ab = sub nsw i8 %max, %min |
| ret i8 %ab |
| } |
| |
| define i8 @sub_max_min_nuw_commute(i8 %a, i8 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nuw_commute |
| ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) |
| ; CHECK-NEXT: ret i8 [[AB]] |
| ; |
| %min = call i8 @llvm.smin.i8(i8 %b, i8 %a) |
| %max = call i8 @llvm.smax.i8(i8 %a, i8 %b) |
| %ab = sub nuw i8 %max, %min |
| ret i8 %ab |
| } |
| |
| define <2 x i8> @sub_max_min_vec_nsw(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nsw |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) |
| ; CHECK-NEXT: ret <2 x i8> [[AB]] |
| ; |
| %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %max = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %ab = sub nsw <2 x i8> %max, %min |
| ret <2 x i8> %ab |
| } |
| |
| define <2 x i8> @sub_max_min_vec_nuw(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nuw |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) |
| ; CHECK-NEXT: ret <2 x i8> [[AB]] |
| ; |
| %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %max = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %ab = sub nuw <2 x i8> %max, %min |
| ret <2 x i8> %ab |
| } |
| |
| define <2 x i8> @sub_max_min_vec_nsw_commute(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nsw_commute |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) |
| ; CHECK-NEXT: ret <2 x i8> [[AB]] |
| ; |
| %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %b, <2 x i8> %a) |
| %max = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %ab = sub nsw <2 x i8> %max, %min |
| ret <2 x i8> %ab |
| } |
| |
| define <2 x i8> @sub_max_min_vec_nuw_commute(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nuw_commute |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] |
| ; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) |
| ; CHECK-NEXT: ret <2 x i8> [[AB]] |
| ; |
| %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %b, <2 x i8> %a) |
| %max = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %a, <2 x i8> %b) |
| %ab = sub nuw <2 x i8> %max, %min |
| ret <2 x i8> %ab |
| } |
| |
| ; negative test - multiple use |
| |
| define i8 @sub_max_min_multi_use(i8 %a, i8 %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_multi_use |
| ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { |
| ; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[A]], i8 [[B]]) |
| ; CHECK-NEXT: call void @use8(i8 [[MIN]]) |
| ; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]]) |
| ; CHECK-NEXT: call void @use8(i8 [[MAX]]) |
| ; CHECK-NEXT: [[AB:%.*]] = sub nsw i8 [[MAX]], [[MIN]] |
| ; CHECK-NEXT: ret i8 [[AB]] |
| ; |
| %min = call i8 @llvm.smin.i8(i8 %a, i8 %b) |
| call void @use8(i8 %min) |
| %max = call i8 @llvm.smax.i8(i8 %a, i8 %b) |
| call void @use8(i8 %max) |
| %ab = sub nsw i8 %max, %min |
| ret i8 %ab |
| } |
| |
| define <2 x i8> @sub_max_min_vec_multi_use(<2 x i8> %a, <2 x i8> %b) { |
| ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_multi_use |
| ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { |
| ; CHECK-NEXT: [[MIN:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) |
| ; CHECK-NEXT: call void @use8v2(<2 x i8> [[MIN]]) |
| ; CHECK-NEXT: [[MAX:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) |
| ; CHECK-NEXT: call void @use8v2(<2 x i8> [[MAX]]) |
| ; CHECK-NEXT: [[AB:%.*]] = sub nsw <2 x i8> [[MAX]], [[MIN]] |
| ; CHECK-NEXT: ret <2 x i8> [[AB]] |
| ; |
| %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %a, <2 x i8> %b) |
| call void @use8v2(<2 x i8> %min) |
| %max = call <2 x i8> @llvm.smax.v2i8(<2 x i8> %a, <2 x i8> %b) |
| call void @use8v2(<2 x i8> %max) |
| %ab = sub nsw <2 x i8> %max, %min |
| ret <2 x i8> %ab |
| } |
| |
| declare void @use8(i8) |
| declare void @use32(i32 %u) |
| |
| declare void @use8v2(i8) |