[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