[llvm] [AArch64] Lower an equality branch against a logical immediate to EOR + CBZ/CBNZ (PR #223123)

Andrew Gaul via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 22:54:34 PDT 2026


https://github.com/gaul updated https://github.com/llvm/llvm-project/pull/223123

>From a1d11b01b359951354bdf5e8bc50df7ff5b1ee1e Mon Sep 17 00:00:00 2001
From: Andrew Gaul <andrew at gaul.org>
Date: Fri, 11 Sep 2026 21:52:07 -0700
Subject: [PATCH 1/2] [AArch64] Add tests for equality branches against logical
 immediates (NFC)

Branches on x == C and x != C where C is not a legal CMP/CMN immediate:
constants that are logical immediates (INT64_MIN, the i32 sign bit,
0xffffff, 0x7fffffff), the same constant reused across a call, and the
shapes that must keep their current lowering -- a CMP or CMN immediate,
a constant that is neither kind of immediate, an ordered condition, a
value-producing compare, and a function under speculative load
hardening. The checks record the current codegen, which materializes
the constant for every compare.

Co-Authored-By: Claude Fable 5.1 <noreply at anthropic.com>
Claude-Session: https://claude.ai/code/session_01KZMjy58X6Z2sH8fd4SSRi4
---
 .../CodeGen/AArch64/branch-eq-logical-imm.ll  | 333 ++++++++++++++++++
 1 file changed, 333 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll

diff --git a/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll b/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll
new file mode 100644
index 0000000000000..9844d4a650744
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll
@@ -0,0 +1,333 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-linux-gnu < %s | FileCheck %s
+
+; Equality branches against constants that no CMP/CMN immediate encodes.
+; Those that are logical immediates can be tested as (x ^ C) == 0.
+
+declare void @g()
+declare void @h()
+
+define void @br_eq_int64_min(i64 %x) {
+; CHECK-LABEL: br_eq_int64_min:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.ne .LBB0_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB0_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, -9223372036854775808
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+define void @br_ne_int64_min(i64 %x) {
+; CHECK-LABEL: br_ne_int64_min:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.eq .LBB1_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB1_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp ne i64 %x, -9223372036854775808
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+define void @br_eq_i32_signbit(i32 %x) {
+; CHECK-LABEL: br_eq_i32_signbit:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #-2147483648 // =0x80000000
+; CHECK-NEXT:    cmp w0, w8
+; CHECK-NEXT:    b.ne .LBB2_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB2_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i32 %x, -2147483648
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+define void @br_ne_i32_ffffff(i32 %x) {
+; CHECK-LABEL: br_ne_i32_ffffff:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #16777215 // =0xffffff
+; CHECK-NEXT:    cmp w0, w8
+; CHECK-NEXT:    b.eq .LBB3_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB3_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp ne i32 %x, 16777215
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+define void @br_eq_i64_int32_max(i64 %x) {
+; CHECK-LABEL: br_eq_i64_int32_max:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #2147483647 // =0x7fffffff
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.ne .LBB4_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB4_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, 2147483647
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+; The constant is used twice, across a call.
+define void @br_two_uses(i64 %x, i64 %y) {
+; CHECK-LABEL: br_two_uses:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    str x30, [sp, #-32]! // 8-byte Folded Spill
+; CHECK-NEXT:    stp x20, x19, [sp, #16] // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    .cfi_offset w19, -8
+; CHECK-NEXT:    .cfi_offset w20, -16
+; CHECK-NEXT:    .cfi_offset w30, -32
+; CHECK-NEXT:    mov x20, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    mov x19, x1
+; CHECK-NEXT:    cmp x0, x20
+; CHECK-NEXT:    b.ne .LBB5_2
+; CHECK-NEXT:  // %bb.1: // %callg
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:  .LBB5_2: // %second
+; CHECK-NEXT:    cmp x19, x20
+; CHECK-NEXT:    b.ne .LBB5_4
+; CHECK-NEXT:  // %bb.3: // %callh
+; CHECK-NEXT:    bl h
+; CHECK-NEXT:  .LBB5_4: // %exit
+; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
+; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
+entry:
+  %cx = icmp eq i64 %x, -9223372036854775808
+  br i1 %cx, label %callg, label %second
+callg:
+  call void @g()
+  br label %second
+second:
+  %cy = icmp eq i64 %y, -9223372036854775808
+  br i1 %cy, label %callh, label %exit
+callh:
+  call void @h()
+  br label %exit
+exit:
+  ret void
+}
+
+; A CMP immediate keeps the compare.
+define void @br_eq_imm12(i64 %x) {
+; CHECK-LABEL: br_eq_imm12:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    cmp x0, #100
+; CHECK-NEXT:    b.ne .LBB6_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB6_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, 100
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+; A negative constant whose magnitude encodes keeps CMN.
+define void @br_eq_neg5(i64 %x) {
+; CHECK-LABEL: br_eq_neg5:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    cmn x0, #5
+; CHECK-NEXT:    b.ne .LBB7_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB7_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, -5
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+; Not a logical immediate: the constant is materialized.
+define void @br_eq_0x1234(i64 %x) {
+; CHECK-LABEL: br_eq_0x1234:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.ne .LBB8_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB8_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, 4660
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+; An ordered condition needs the subtraction.
+define void @br_sgt_int32_max(i64 %x) {
+; CHECK-LABEL: br_sgt_int32_max:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #-2147483648 // =0x80000000
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.lt .LBB9_2
+; CHECK-NEXT:  // %bb.1: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:  .LBB9_2: // %exit
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp sgt i64 %x, 2147483647
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}
+
+; Value-producing compares.
+define i1 @setcc_int64_min(i64 %x) {
+; CHECK-LABEL: setcc_int64_min:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
+  %c = icmp eq i64 %x, -9223372036854775808
+  ret i1 %c
+}
+
+define i64 @select_int64_min(i64 %x, i64 %a, i64 %b) {
+; CHECK-LABEL: select_int64_min:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    csel x0, x1, x2, eq
+; CHECK-NEXT:    ret
+  %c = icmp eq i64 %x, -9223372036854775808
+  %r = select i1 %c, i64 %a, i64 %b
+  ret i64 %r
+}
+
+; Speculative load hardening needs a flag-setting branch.
+define void @br_eq_int64_min_slh(i64 %x) speculative_load_hardening {
+; CHECK-LABEL: br_eq_int64_min_slh:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    cmp sp, #0
+; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    csetm x16, ne
+; CHECK-NEXT:    cmp x0, x8
+; CHECK-NEXT:    b.ne .LBB12_3
+; CHECK-NEXT:  // %bb.1:
+; CHECK-NEXT:    csel x16, x16, xzr, eq
+; CHECK-NEXT:  // %bb.2: // %then
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    mov x0, sp
+; CHECK-NEXT:    and x0, x0, x16
+; CHECK-NEXT:    mov sp, x0
+; CHECK-NEXT:    bl g
+; CHECK-NEXT:    cmp sp, #0
+; CHECK-NEXT:    csetm x16, ne
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    and x30, x30, x16
+; CHECK-NEXT:    csdb
+; CHECK-NEXT:    b .LBB12_4
+; CHECK-NEXT:  .LBB12_3:
+; CHECK-NEXT:    csel x16, x16, xzr, ne
+; CHECK-NEXT:  .LBB12_4: // %exit
+; CHECK-NEXT:    mov x0, sp
+; CHECK-NEXT:    and x0, x0, x16
+; CHECK-NEXT:    mov sp, x0
+; CHECK-NEXT:    ret
+entry:
+  %c = icmp eq i64 %x, -9223372036854775808
+  br i1 %c, label %then, label %exit
+then:
+  call void @g()
+  br label %exit
+exit:
+  ret void
+}

>From 03bdc94a4266cb8fa52d55cc15e54f14b78b0219 Mon Sep 17 00:00:00 2001
From: Andrew Gaul <andrew at gaul.org>
Date: Fri, 11 Sep 2026 21:53:04 -0700
Subject: [PATCH 2/2] [AArch64] Lower an equality branch against a logical
 immediate to EOR + CBZ/CBNZ

For (x == C) or (x != C) feeding a branch, when C is not a legal
CMP/CMN immediate but is a logical immediate, branch on (x ^ C) == 0
instead of materializing C for the compare:

  mov  x8, #-9223372036854775808
  cmp  x0, x8
  b.ne .LBB0_2

becomes

  eor  x8, x0, #0x8000000000000000
  cbnz x8, .LBB0_2

One instruction fewer per site, no flag dependency, and no register
holding the constant: when the same constant is compared on both
sides of a call, or inside a loop, the constant is currently parked
in a callee-saved register and its spill paid for.

The shape is common. rustc places the niches of an enum that has two
or more dataless variants beside a Vec or String payload at
isize::MAX + 1 upward, so every test for such a variant compares
against 0x8000000000000000; Gecko's TimeDuration keeps INT64_MIN as
a sentinel; 0x7fffffff and 0xffffff are frequent limits. Firefox
155's libxul.so contains 13,019 branch compares of this form, 11,262
of them against INT64_MIN.

Only branches change. SETCC and SELECT_CC gain nothing from the
rewrite, the ordered conditions still need the subtraction, and the
speculative-load-hardening guard that keeps flag-setting branches is
respected. GlobalISel's selectCompareBranch does not get the
equivalent here.

Found via armlint.

Co-Authored-By: Claude Fable 5.1 <noreply at anthropic.com>
Claude-Session: https://claude.ai/code/session_01KZMjy58X6Z2sH8fd4SSRi4
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 15 +++++
 .../CodeGen/AArch64/branch-eq-logical-imm.ll  | 63 +++++++++----------
 2 files changed, 45 insertions(+), 33 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 99bcb5f50c134..336b4a882e14a 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -12257,6 +12257,21 @@ SDValue AArch64TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const {
       }
     }
 
+    // An equality test against a constant that no CMP/CMN immediate takes
+    // but a logical immediate does: branch on (LHS ^ C) == 0 rather than
+    // materializing C for the compare, so mov + cmp + b.cond becomes
+    // eor + cb(n)z. INT64_MIN, 0x7fffffff and 0xffffff are the common
+    // cases (enum niches and sentinels).
+    if (RHSC && (CC == ISD::SETEQ || CC == ISD::SETNE) &&
+        ProduceNonFlagSettingCondBr &&
+        !AArch64_AM::isLegalCmpImmed(RHSC->getAPIntValue()) &&
+        AArch64_AM::isLogicalImmediate(RHSC->getZExtValue(),
+                                       LHS.getValueSizeInBits())) {
+      LHS = DAG.getNode(ISD::XOR, DL, LHS.getValueType(), LHS, RHS);
+      RHS = DAG.getConstant(0, DL, LHS.getValueType());
+      RHSC = cast<ConstantSDNode>(RHS);
+    }
+
     // If the RHS of the comparison is zero, we can potentially fold this
     // to a specialized branch.
     if (RHSC && RHSC->getZExtValue() == 0 && ProduceNonFlagSettingCondBr) {
diff --git a/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll b/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll
index 9844d4a650744..d1a93b5c058a8 100644
--- a/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll
+++ b/llvm/test/CodeGen/AArch64/branch-eq-logical-imm.ll
@@ -2,7 +2,8 @@
 ; RUN: llc -mtriple=aarch64-linux-gnu < %s | FileCheck %s
 
 ; Equality branches against constants that no CMP/CMN immediate encodes.
-; Those that are logical immediates can be tested as (x ^ C) == 0.
+; Those that are logical immediates are lowered as EOR + CBZ/CBNZ, testing
+; (x ^ C) == 0 instead of materializing the constant for a CMP.
 
 declare void @g()
 declare void @h()
@@ -10,9 +11,8 @@ declare void @h()
 define void @br_eq_int64_min(i64 %x) {
 ; CHECK-LABEL: br_eq_int64_min:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
-; CHECK-NEXT:    cmp x0, x8
-; CHECK-NEXT:    b.ne .LBB0_2
+; CHECK-NEXT:    eor x8, x0, #0x8000000000000000
+; CHECK-NEXT:    cbnz x8, .LBB0_2
 ; CHECK-NEXT:  // %bb.1: // %then
 ; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
@@ -34,9 +34,8 @@ exit:
 define void @br_ne_int64_min(i64 %x) {
 ; CHECK-LABEL: br_ne_int64_min:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov x8, #-9223372036854775808 // =0x8000000000000000
-; CHECK-NEXT:    cmp x0, x8
-; CHECK-NEXT:    b.eq .LBB1_2
+; CHECK-NEXT:    eor x8, x0, #0x8000000000000000
+; CHECK-NEXT:    cbz x8, .LBB1_2
 ; CHECK-NEXT:  // %bb.1: // %then
 ; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
@@ -58,9 +57,8 @@ exit:
 define void @br_eq_i32_signbit(i32 %x) {
 ; CHECK-LABEL: br_eq_i32_signbit:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov w8, #-2147483648 // =0x80000000
-; CHECK-NEXT:    cmp w0, w8
-; CHECK-NEXT:    b.ne .LBB2_2
+; CHECK-NEXT:    eor w8, w0, #0x80000000
+; CHECK-NEXT:    cbnz w8, .LBB2_2
 ; CHECK-NEXT:  // %bb.1: // %then
 ; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
@@ -82,9 +80,8 @@ exit:
 define void @br_ne_i32_ffffff(i32 %x) {
 ; CHECK-LABEL: br_ne_i32_ffffff:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov w8, #16777215 // =0xffffff
-; CHECK-NEXT:    cmp w0, w8
-; CHECK-NEXT:    b.eq .LBB3_2
+; CHECK-NEXT:    eor w8, w0, #0xffffff
+; CHECK-NEXT:    cbz w8, .LBB3_2
 ; CHECK-NEXT:  // %bb.1: // %then
 ; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
@@ -106,9 +103,8 @@ exit:
 define void @br_eq_i64_int32_max(i64 %x) {
 ; CHECK-LABEL: br_eq_i64_int32_max:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov w8, #2147483647 // =0x7fffffff
-; CHECK-NEXT:    cmp x0, x8
-; CHECK-NEXT:    b.ne .LBB4_2
+; CHECK-NEXT:    eor x8, x0, #0x7fffffff
+; CHECK-NEXT:    cbnz x8, .LBB4_2
 ; CHECK-NEXT:  // %bb.1: // %then
 ; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
@@ -127,30 +123,31 @@ exit:
   ret void
 }
 
-; The constant is used twice, across a call.
+; The constant is used twice, across a call: neither use needs it in a
+; register, so nothing is pinned in a callee-saved register.
 define void @br_two_uses(i64 %x, i64 %y) {
 ; CHECK-LABEL: br_two_uses:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    str x30, [sp, #-32]! // 8-byte Folded Spill
-; CHECK-NEXT:    stp x20, x19, [sp, #16] // 16-byte Folded Spill
-; CHECK-NEXT:    .cfi_def_cfa_offset 32
+; CHECK-NEXT:    stp x30, x19, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    .cfi_offset w19, -8
-; CHECK-NEXT:    .cfi_offset w20, -16
-; CHECK-NEXT:    .cfi_offset w30, -32
-; CHECK-NEXT:    mov x20, #-9223372036854775808 // =0x8000000000000000
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    eor x8, x0, #0x8000000000000000
 ; CHECK-NEXT:    mov x19, x1
-; CHECK-NEXT:    cmp x0, x20
-; CHECK-NEXT:    b.ne .LBB5_2
-; CHECK-NEXT:  // %bb.1: // %callg
+; CHECK-NEXT:    cbz x8, .LBB5_3
+; CHECK-NEXT:  // %bb.1: // %second
+; CHECK-NEXT:    eor x8, x19, #0x8000000000000000
+; CHECK-NEXT:    cbz x8, .LBB5_4
+; CHECK-NEXT:  .LBB5_2: // %exit
+; CHECK-NEXT:    ldp x30, x19, [sp], #16 // 16-byte Folded Reload
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB5_3: // %callg
 ; CHECK-NEXT:    bl g
-; CHECK-NEXT:  .LBB5_2: // %second
-; CHECK-NEXT:    cmp x19, x20
-; CHECK-NEXT:    b.ne .LBB5_4
-; CHECK-NEXT:  // %bb.3: // %callh
+; CHECK-NEXT:    eor x8, x19, #0x8000000000000000
+; CHECK-NEXT:    cbnz x8, .LBB5_2
+; CHECK-NEXT:  .LBB5_4: // %callh
 ; CHECK-NEXT:    bl h
-; CHECK-NEXT:  .LBB5_4: // %exit
-; CHECK-NEXT:    ldp x20, x19, [sp, #16] // 16-byte Folded Reload
-; CHECK-NEXT:    ldr x30, [sp], #32 // 8-byte Folded Reload
+; CHECK-NEXT:    ldp x30, x19, [sp], #16 // 16-byte Folded Reload
 ; CHECK-NEXT:    ret
 entry:
   %cx = icmp eq i64 %x, -9223372036854775808



More information about the llvm-commits mailing list