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