65816-llvm-mos/src/llvm/test/CodeGen/W65816/i32-compare-chain.ll
2026-09-29 17:22:52 -05:00

632 lines
13 KiB
LLVM

; Pin: an i32 compare lowers to the 65xx multi-precision compare, not to
; materialised per-half booleans.
;
; Ordered predicates: lda lhs.lo ; cmp rhs.lo ; lda lhs.hi ; sbc rhs.hi
; then ONE bcc/bcs (or one SELECT_CC diamond for a value). The CMP leaves
; C = no-borrow of the low halves, the SBC folds it into the high halves.
; Signed predicates first bias both high halves by 0x8000 (a constant's
; bias folds: 0x1234 -> sbc #0x9234). x <= K / x > K become x < K+1 /
; x >= K+1; reg/reg <= and > swap operands. A constant whose low half is
; 0 reduces to an i16 compare of the high halves (x < 0x10000 is hi < 1,
; signed x < 0 is hi < 0, which is just `lda hi ; bmi`).
;
; eq / ne feeding a branch (W65816CmpPrepare): the low halves, bne, then
; the high halves, bne. Against 0 (or a constant with a zero half) it stays
; the branch-free lo | hi, (lo ^ K) | hi, ... tested against 0.
;
; The old lowering built (hiLT | (hiEQ & loLT)) from three i16 setcc
; diamonds and branched on the OR: every `i < n` in a 32-bit loop paid for
; three materialised booleans and a PHP/PLP. CHECK-NOT php pins that.
;
; RUN: llc -mtriple=w65816 -O2 < %s | FileCheck %s
target datalayout = "e-m:e-p:32:16-i16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16"
@sink = global i16 0
; a.hi arrives in X: it must be loaded into A for the second compare.
define void @br_eq(i32 %a, i32 %b) {
; CHECK-LABEL: br_eq:
; CHECK-NOT: php
; CHECK: stx 0xd0
; CHECK-NEXT: cmp 0x4, s
; CHECK-NEXT: bne [[F:.LBB[0-9_]+]]
; CHECK: lda 0xd0
; CHECK-NEXT: cmp 0x6, s
; CHECK-NEXT: bne [[F]]
; CHECK: rtl
entry:
%c = icmp eq i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_ne(i32 %a, i32 %b) {
; CHECK-LABEL: br_ne:
; CHECK-NOT: php
; CHECK: stx 0xd0
; CHECK-NEXT: cmp 0x4, s
; CHECK-NEXT: bne [[T:.LBB[0-9_]+]]
; CHECK: lda 0xd0
; CHECK-NEXT: cmp 0x6, s
; CHECK-NEXT: bne [[T]]
; CHECK: rtl
entry:
%c = icmp ne i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; a.lo arrives in A, a.hi in X, b on the stack.
define void @br_ult(i32 %a, i32 %b) {
; CHECK-LABEL: br_ult:
; CHECK-NOT: php
; CHECK: stx 0xd0
; CHECK-NEXT: cmp 0x4, s
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc 0x6, s
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp ult i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; a <= b is b >= a: b is loaded first.
define void @br_ule(i32 %a, i32 %b) {
; CHECK-LABEL: br_ule:
; CHECK-NOT: php
; CHECK: lda 0x8, s
; CHECK-NEXT: cmp 0x1, s
; CHECK: lda 0xa, s
; CHECK-NEXT: sbc 0x3, s
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp ule i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_ugt(i32 %a, i32 %b) {
; CHECK-LABEL: br_ugt:
; CHECK-NOT: php
; CHECK: lda 0x8, s
; CHECK-NEXT: cmp 0x1, s
; CHECK: lda 0xa, s
; CHECK-NEXT: sbc 0x3, s
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp ugt i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_uge(i32 %a, i32 %b) {
; CHECK-LABEL: br_uge:
; CHECK-NOT: php
; CHECK: stx 0xd0
; CHECK-NEXT: cmp 0x4, s
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc 0x6, s
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp uge i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; Signed: both high halves biased by 0x8000, then the unsigned chain.
define void @br_slt(i32 %a, i32 %b) {
; CHECK-LABEL: br_slt:
; CHECK-NOT: php
; CHECK: eor
; CHECK: eor #0x8000
; CHECK: cmp 0xa, s
; CHECK-NEXT: lda
; CHECK-NEXT: sbc
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp slt i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_sle(i32 %a, i32 %b) {
; CHECK-LABEL: br_sle:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: eor
; CHECK: lda 0xa, s
; CHECK-NEXT: cmp
; CHECK-NEXT: lda
; CHECK-NEXT: sbc
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp sle i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_sgt(i32 %a, i32 %b) {
; CHECK-LABEL: br_sgt:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: eor
; CHECK: lda 0xa, s
; CHECK-NEXT: cmp
; CHECK-NEXT: lda
; CHECK-NEXT: sbc
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp sgt i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @br_sge(i32 %a, i32 %b) {
; CHECK-LABEL: br_sge:
; CHECK-NOT: php
; CHECK: eor
; CHECK: eor #0x8000
; CHECK: cmp 0xa, s
; CHECK-NEXT: lda
; CHECK-NEXT: sbc
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp sge i32 %a, %b
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; Against a constant (0x12345678): immediate operands, x <= K as x < K+1.
define void @brk_eq(i32 %a) {
; CHECK-LABEL: brk_eq:
; CHECK-NOT: php
; CHECK: cmp #0x5678
; CHECK-NEXT: bne [[F:.LBB[0-9_]+]]
; CHECK: lda 0xd0
; CHECK-NEXT: cmp #0x1234
; CHECK-NEXT: bne [[F]]
; CHECK: rtl
entry:
%c = icmp eq i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_ne(i32 %a) {
; CHECK-LABEL: brk_ne:
; CHECK-NOT: php
; CHECK: cmp #0x5678
; CHECK-NEXT: bne [[T:.LBB[0-9_]+]]
; CHECK: lda 0xd0
; CHECK-NEXT: cmp #0x1234
; CHECK-NEXT: bne [[T]]
; CHECK: rtl
entry:
%c = icmp ne i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_ult(i32 %a) {
; CHECK-LABEL: brk_ult:
; CHECK-NOT: php
; CHECK: cmp #0x5678
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc #0x1234
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp ult i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_ule(i32 %a) {
; CHECK-LABEL: brk_ule:
; CHECK-NOT: php
; CHECK: cmp #0x5679
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc #0x1234
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp ule i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_ugt(i32 %a) {
; CHECK-LABEL: brk_ugt:
; CHECK-NOT: php
; CHECK: cmp #0x5679
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc #0x1234
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp ugt i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_uge(i32 %a) {
; CHECK-LABEL: brk_uge:
; CHECK-NOT: php
; CHECK: cmp #0x5678
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc #0x1234
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp uge i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_slt(i32 %a) {
; CHECK-LABEL: brk_slt:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: cmp #0x5678
; CHECK-NEXT: lda
; CHECK-NEXT: sbc #0x9234
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp slt i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_sle(i32 %a) {
; CHECK-LABEL: brk_sle:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: cmp #0x5679
; CHECK-NEXT: lda
; CHECK-NEXT: sbc #0x9234
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp sle i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_sgt(i32 %a) {
; CHECK-LABEL: brk_sgt:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: cmp #0x5679
; CHECK-NEXT: lda
; CHECK-NEXT: sbc #0x9234
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp sgt i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_sge(i32 %a) {
; CHECK-LABEL: brk_sge:
; CHECK-NOT: php
; CHECK: eor #0x8000
; CHECK: cmp #0x5678
; CHECK-NEXT: lda
; CHECK-NEXT: sbc #0x9234
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp sge i32 %a, 305419896
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; Against 0 and -1, and constants whose low half is 0: no chain at all.
define void @brk_eq0(i32 %a) {
; CHECK-LABEL: brk_eq0:
; CHECK-NOT: eor
; CHECK: lda 0xd0
; CHECK-NEXT: ora 0x1, s
; CHECK-NEXT: beq
; CHECK: rtl
entry:
%c = icmp eq i32 %a, 0
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_slt0(i32 %a) {
; CHECK-LABEL: brk_slt0:
; CHECK-NOT: sbc
; CHECK: lda 0xd0
; CHECK-NEXT: bmi
; CHECK: rtl
entry:
%c = icmp slt i32 %a, 0
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; x > -1 is x >= 0: the high half's sign alone.
define void @brk_sgtm1(i32 %a) {
; CHECK-LABEL: brk_sgtm1:
; CHECK-NOT: sbc
; CHECK: lda 0xd0
; CHECK-NEXT: bmi
; CHECK: rtl
entry:
%c = icmp sgt i32 %a, -1
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
define void @brk_ult64k(i32 %a) {
; CHECK-LABEL: brk_ult64k:
; CHECK-NOT: sbc
; CHECK: lda 0xd0
; CHECK-NEXT: cmp #0x1
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp ult i32 %a, 65536
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; x > 0x1FFFF is x >= 0x20000 is hi >= 2.
define void @brk_ugt1ffff(i32 %a) {
; CHECK-LABEL: brk_ugt1ffff:
; CHECK-NOT: sbc
; CHECK: lda 0xd0
; CHECK-NEXT: cmp #0x2
; CHECK-NEXT: bcc
; CHECK: rtl
entry:
%c = icmp ugt i32 %a, 131071
br i1 %c, label %t, label %f
t:
store volatile i16 1, ptr @sink
ret void
f:
store volatile i16 2, ptr @sink
ret void
}
; A value: one diamond on the chain's C, not three.
define i16 @set_ult(i32 %a, i32 %b) {
; CHECK-LABEL: set_ult:
; CHECK-NOT: php
; CHECK: cmp 0x4, s
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc 0x6, s
; CHECK-NEXT: lda #0x1
; CHECK-NEXT: bcc
; CHECK: lda #0x0
; CHECK: rtl
entry:
%c = icmp ult i32 %a, %b
%z = zext i1 %c to i16
ret i16 %z
}
define i16 @set_sge(i32 %a, i32 %b) {
; CHECK-LABEL: set_sge:
; CHECK-NOT: php
; CHECK: cmp
; CHECK: sbc
; CHECK-NEXT: bcs
; CHECK: rtl
entry:
%c = icmp sge i32 %a, %b
%z = zext i1 %c to i16
ret i16 %z
}
; The combiner folds the zext into an i32-typed SETCC: the i16 boolean is
; widened with a zero high half (this used to be "Cannot select").
define i32 @set32_ult(i32 %a, i32 %b) {
; CHECK-LABEL: set32_ult:
; CHECK: cmp 0x8, s
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc 0xa, s
; CHECK-NEXT: bcc
; CHECK: ldx #0x0
; CHECK: rtl
%c = icmp ult i32 %a, %b
%z = zext i1 %c to i32
ret i32 %z
}
define i16 @sel16_slt(i32 %a, i32 %b, i16 %x, i16 %y) {
; CHECK-LABEL: sel16_slt:
; CHECK-NOT: php
; CHECK: cmp 0xc, s
; CHECK-NEXT: lda
; CHECK-NEXT: sbc
; CHECK-NEXT: bcc
; CHECK: rtl
%c = icmp slt i32 %a, %b
%r = select i1 %c, i16 %x, i16 %y
ret i16 %r
}
define i32 @sel32_uge(i32 %a, i32 %b, i32 %x, i32 %y) {
; CHECK-LABEL: sel32_uge:
; CHECK: cmp 0xa, s
; CHECK-NEXT: lda 0xd0
; CHECK-NEXT: sbc 0xc, s
; CHECK-NEXT: bcs
; CHECK: rtl
%c = icmp uge i32 %a, %b
%r = select i1 %c, i32 %x, i32 %y
ret i32 %r
}
; The loop shape from JoeyLib's old sfxCopyUnsigned: the latch's i < n is
; the chain and one bcc back to the top, and every i32 += 1 keeps the
; carry into its high half (the bne skips over it).
define void @loop_ult(ptr %dst, i32 %n) {
; CHECK-LABEL: loop_ult:
; CHECK: [[TOP:.LBB[0-9_]+]]: ; %body
; CHECK-NOT: php
; CHECK: inc 0xd2
; CHECK-NEXT: bne
; CHECK-NEXT: lda 0x3, s
; CHECK-NEXT: inc a
; CHECK-NEXT: sta 0x3, s
; CHECK: inc 0xd4
; CHECK-NEXT: bne
; CHECK-NEXT: lda 0x5, s
; CHECK-NEXT: inc a
; CHECK-NEXT: sta 0x5, s
; CHECK: lda 0xd4
; CHECK-NEXT: cmp 0xc, s
; CHECK-NEXT: lda 0x5, s
; CHECK-NEXT: sbc 0xe, s
; CHECK-NEXT: bcc [[TOP]]
; CHECK: rtl
entry:
%z = icmp eq i32 %n, 0
br i1 %z, label %done, label %body
body:
%i = phi i32 [ 0, %entry ], [ %inc, %body ]
%p = getelementptr inbounds i8, ptr %dst, i32 %i
store i8 1, ptr %p
%inc = add nuw i32 %i, 1
%c = icmp ult i32 %inc, %n
br i1 %c, label %body, label %done
done:
ret void
}