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