blob: a63001767719caed77198872926168141bbc29c0 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s
; Unsigned comparisons.
define i64 @conditional_subtract(i64 %x, i64 %q) {
; CHECK-LABEL: conditional_subtract:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i32 @ge32(i32 %x, i32 %q) {
; CHECK-LABEL: ge32:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w8, w0, hs
; CHECK-NEXT: ret
%c = icmp uge i32 %x, %q
%s = select i1 %c, i32 %q, i32 0
%r = sub i32 %x, %s
ret i32 %r
}
define i64 @lt64(i64 %x, i64 %q) {
; CHECK-LABEL: lt64:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x0, x8, lo
; CHECK-NEXT: ret
%c = icmp ult i64 %x, %q
%s = select i1 %c, i64 0, i64 %q
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @gt64(i64 %x, i64 %q) {
; CHECK-LABEL: gt64:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hi
; CHECK-NEXT: ret
%c = icmp ugt i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @le64(i64 %x, i64 %q) {
; CHECK-LABEL: le64:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, ls
; CHECK-NEXT: ret
%c = icmp ule i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @below64(i64 %x, i64 %q) {
; CHECK-LABEL: below64:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, lo
; CHECK-NEXT: ret
%c = icmp ult i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
; Signed comparisons with either select arm zero.
define i32 @slt32_zero_true(i32 %x, i32 %q) {
; CHECK-LABEL: slt32_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w0, w8, lt
; CHECK-NEXT: ret
%c = icmp slt i32 %x, %q
%s = select i1 %c, i32 0, i32 %q
%r = sub i32 %x, %s
ret i32 %r
}
define i32 @sle32_zero_false(i32 %x, i32 %q) {
; CHECK-LABEL: sle32_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w8, w0, le
; CHECK-NEXT: ret
%c = icmp sle i32 %x, %q
%s = select i1 %c, i32 %q, i32 0
%r = sub i32 %x, %s
ret i32 %r
}
define i64 @sgt64_zero_true(i64 %x, i64 %q) {
; CHECK-LABEL: sgt64_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x0, x8, gt
; CHECK-NEXT: ret
%c = icmp sgt i64 %x, %q
%s = select i1 %c, i64 0, i64 %q
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @sge64_zero_false(i64 %x, i64 %q) {
; CHECK-LABEL: sge64_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, ge
; CHECK-NEXT: ret
%c = icmp sge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
; Equality comparisons also reuse the original flags.
define i32 @eq32_zero_false(i32 %x, i32 %q) {
; CHECK-LABEL: eq32_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w8, w0, eq
; CHECK-NEXT: ret
%c = icmp eq i32 %x, %q
%s = select i1 %c, i32 %q, i32 0
%r = sub i32 %x, %s
ret i32 %r
}
define i64 @eq64_zero_true(i64 %x, i64 %q) {
; CHECK-LABEL: eq64_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x0, x8, eq
; CHECK-NEXT: ret
%c = icmp eq i64 %x, %q
%s = select i1 %c, i64 0, i64 %q
%r = sub i64 %x, %s
ret i64 %r
}
define i32 @ne32_zero_true(i32 %x, i32 %q) {
; CHECK-LABEL: ne32_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w0, w8, ne
; CHECK-NEXT: ret
%c = icmp ne i32 %x, %q
%s = select i1 %c, i32 0, i32 %q
%r = sub i32 %x, %s
ret i32 %r
}
define i64 @ne64_zero_false(i64 %x, i64 %q) {
; CHECK-LABEL: ne64_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, ne
; CHECK-NEXT: ret
%c = icmp ne i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
; Overflow controls the select; the wrapping difference is valid in either arm.
define i32 @overflow32_zero_false(i32 %x, i32 %q) {
; CHECK-LABEL: overflow32_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w8, w0, vs
; CHECK-NEXT: ret
%pair = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %x, i32 %q)
%c = extractvalue { i32, i1 } %pair, 1
%s = select i1 %c, i32 %q, i32 0
%r = sub i32 %x, %s
ret i32 %r
}
define i32 @overflow32_zero_true(i32 %x, i32 %q) {
; CHECK-LABEL: overflow32_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs w8, w0, w1
; CHECK-NEXT: csel w0, w0, w8, vs
; CHECK-NEXT: ret
%pair = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %x, i32 %q)
%c = extractvalue { i32, i1 } %pair, 1
%s = select i1 %c, i32 0, i32 %q
%r = sub i32 %x, %s
ret i32 %r
}
define i64 @overflow64_zero_false(i64 %x, i64 %q) {
; CHECK-LABEL: overflow64_zero_false:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, vs
; CHECK-NEXT: ret
%pair = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %x, i64 %q)
%c = extractvalue { i64, i1 } %pair, 1
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @overflow64_zero_true(i64 %x, i64 %q) {
; CHECK-LABEL: overflow64_zero_true:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x0, x8, vs
; CHECK-NEXT: ret
%pair = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %x, i64 %q)
%c = extractvalue { i64, i1 } %pair, 1
%s = select i1 %c, i64 0, i64 %q
%r = sub i64 %x, %s
ret i64 %r
}
; Reusing SUBS must preserve other arithmetic-result and flag consumers.
define i64 @shared_flags(i64 %x, i64 %q, ptr %out) {
; CHECK-LABEL: shared_flags:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: cset w8, hs
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%flag = zext i1 %c to i64
store i64 %flag, ptr %out
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @shared_difference(i64 %x, i64 %q, ptr %out) {
; CHECK-LABEL: shared_difference:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%diff = sub i64 %x, %q
store i64 %diff, ptr %out
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @shared_signed_flags(i64 %x, i64 %q, ptr %out) {
; CHECK-LABEL: shared_signed_flags:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: cset w8, lt
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%signed = icmp slt i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%flag = zext i1 %signed to i64
store i64 %flag, ptr %out
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @shared_overflow(i64 %x, i64 %q, ptr %out) {
; CHECK-LABEL: shared_overflow:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: cset w8, vs
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%pair = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %x, i64 %q)
%overflow = extractvalue { i64, i1 } %pair, 1
%flag = zext i1 %overflow to i64
store i64 %flag, ptr %out
%r = sub i64 %x, %s
ret i64 %r
}
; Keep both SUBS results live while overflow controls the conditional subtraction.
define i64 @overflow_shared_results64(i64 %x, i64 %q, ptr %diff_out, ptr %overflow_out) {
; CHECK-LABEL: overflow_shared_results64:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, vs
; CHECK-NEXT: cset w9, vs
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: strb w9, [x3]
; CHECK-NEXT: ret
%pair = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %x, i64 %q)
%diff = extractvalue { i64, i1 } %pair, 0
%overflow = extractvalue { i64, i1 } %pair, 1
store i64 %diff, ptr %diff_out
store i1 %overflow, ptr %overflow_out
%s = select i1 %overflow, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
; An outer nuw subtraction can also reuse the wrapping SUBS result.
define i64 @nuw_sub(i64 %x, i64 %q) {
; CHECK-LABEL: nuw_sub:
; CHECK: // %bb.0:
; CHECK-NEXT: subs x8, x0, x1
; CHECK-NEXT: csel x0, x8, x0, hs
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub nuw i64 %x, %s
ret i64 %r
}
; Negative matches: different operands, nonzero alternatives, or shared selects.
define i64 @wrong_lhs(i64 %x, i64 %q, i64 %a) {
; CHECK-LABEL: wrong_lhs:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x2, x1
; CHECK-NEXT: csel x8, x1, xzr, hs
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: ret
%c = icmp uge i64 %a, %q
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @wrong_rhs(i64 %x, i64 %q, i64 %a) {
; CHECK-LABEL: wrong_rhs:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x0, x2
; CHECK-NEXT: csel x8, x1, xzr, hs
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %a
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @nonzero_arm(i64 %x, i64 %q) {
; CHECK-LABEL: nonzero_arm:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #42 // =0x2a
; CHECK-NEXT: cmp x0, x1
; CHECK-NEXT: csel x8, x1, x8, hs
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 42
%r = sub i64 %x, %s
ret i64 %r
}
define i64 @shared_select(i64 %x, i64 %q, ptr %out) {
; CHECK-LABEL: shared_select:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp x0, x1
; CHECK-NEXT: csel x8, x1, xzr, hs
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: str x8, [x2]
; CHECK-NEXT: ret
%c = icmp uge i64 %x, %q
%s = select i1 %c, i64 %q, i64 0
store i64 %s, ptr %out
%r = sub i64 %x, %s
ret i64 %r
}
; Narrow comparisons have different DAG operands from the outer subtraction.
define i64 @narrow_compare(i64 %x, i64 %q) {
; CHECK-LABEL: narrow_compare:
; CHECK: // %bb.0:
; CHECK-NEXT: cmp w0, w1
; CHECK-NEXT: csel x8, x1, xzr, hs
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: ret
%xx = trunc i64 %x to i32
%qq = trunc i64 %q to i32
%c = icmp uge i32 %xx, %qq
%s = select i1 %c, i64 %q, i64 0
%r = sub i64 %x, %s
ret i64 %r
}
define i32 @signed_narrow_compare32(i32 %x, i32 %q) {
; CHECK-LABEL: signed_narrow_compare32:
; CHECK: // %bb.0:
; CHECK-NEXT: sxth w8, w0
; CHECK-NEXT: cmp w8, w1, sxth
; CHECK-NEXT: csel w8, wzr, w1, lt
; CHECK-NEXT: sub w0, w0, w8
; CHECK-NEXT: ret
%xx = trunc i32 %x to i16
%qq = trunc i32 %q to i16
%c = icmp slt i16 %xx, %qq
%s = select i1 %c, i32 0, i32 %q
%r = sub i32 %x, %s
ret i32 %r
}
; Immediate comparisons can be canonicalized to different operands.
define i64 @constant_q(i64 %x) {
; CHECK-LABEL: constant_q:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #17 // =0x11
; CHECK-NEXT: cmp x0, #16
; CHECK-NEXT: csel x8, x8, xzr, hi
; CHECK-NEXT: sub x0, x0, x8
; CHECK-NEXT: ret
%c = icmp uge i64 %x, 17
%s = select i1 %c, i64 17, i64 0
%r = sub i64 %x, %s
ret i64 %r
}