| ; 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 |
| } |