[llvm] [BOLT][AArch64] Support FEAT_CMPBR branch instructions. (PR #174972)
Alexandros Lamprineas via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 8 05:08:41 PST 2026
https://github.com/labrinea created https://github.com/llvm/llvm-project/pull/174972
The Armv9.6-A compare-and-branch instructions use a short range 9-bit immediate value. They do not have a corresponding relocation type in the ABI. That means they can only be used either in compact code model, or in non-relocation mode. With this patch I am adding support for them in BOLT.
>From 9ae12a45f96d2e2fac8439dfb838331492a41336 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Fri, 14 Nov 2025 11:43:47 +0000
Subject: [PATCH] [BOLT][AArch64] Support FEAT_CMPBR branch instructions.
The Armv9.6-A compare-and-branch instructions use a short range 9-bit
immediate value. They do not have a corresponding relocation type in
the ABI. That means they can only be used either in compact code model,
or in non-relocation mode. With this patch I am adding support for
them in BOLT.
---
.../Target/AArch64/AArch64MCPlusBuilder.cpp | 155 ++++++++++++++++--
bolt/test/AArch64/compare-and-branch.S | 127 ++++++++++++++
2 files changed, 271 insertions(+), 11 deletions(-)
create mode 100644 bolt/test/AArch64/compare-and-branch.S
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 03fb4ddc2f238..1d9e4cbce118a 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -716,6 +716,51 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
return Insts;
}
+ bool isCompAndBranch(const MCInst &Inst) const {
+ const unsigned opcode = Inst.getOpcode();
+ switch (opcode) {
+ case AArch64::CBBEQWrr:
+ case AArch64::CBBGEWrr:
+ case AArch64::CBBGTWrr:
+ case AArch64::CBBHIWrr:
+ case AArch64::CBBHSWrr:
+ case AArch64::CBBNEWrr:
+ case AArch64::CBHEQWrr:
+ case AArch64::CBHGEWrr:
+ case AArch64::CBHGTWrr:
+ case AArch64::CBHHIWrr:
+ case AArch64::CBHHSWrr:
+ case AArch64::CBHNEWrr:
+ case AArch64::CBHIWrr:
+ case AArch64::CBHIXrr:
+ case AArch64::CBHSWrr:
+ case AArch64::CBHSXrr:
+ case AArch64::CBNEWrr:
+ case AArch64::CBNEXrr:
+ case AArch64::CBEQWrr:
+ case AArch64::CBEQXrr:
+ case AArch64::CBGEWrr:
+ case AArch64::CBGEXrr:
+ case AArch64::CBGTWrr:
+ case AArch64::CBGTXrr:
+ case AArch64::CBEQWri:
+ case AArch64::CBEQXri:
+ case AArch64::CBGTWri:
+ case AArch64::CBGTXri:
+ case AArch64::CBHIWri:
+ case AArch64::CBHIXri:
+ case AArch64::CBLOWri:
+ case AArch64::CBLOXri:
+ case AArch64::CBLTWri:
+ case AArch64::CBLTXri:
+ case AArch64::CBNEWri:
+ case AArch64::CBNEXri:
+ return true;
+ default:
+ return false;
+ }
+ }
+
bool isTB(const MCInst &Inst) const {
return (Inst.getOpcode() == AArch64::TBNZW ||
Inst.getOpcode() == AArch64::TBNZX ||
@@ -1260,7 +1305,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
if (isConditionalBranch(Inst) || isADR(Inst) || isADRP(Inst) ||
isMOVW(Inst))
OpNum = 1;
- if (isTB(Inst) || isAddXri(Inst))
+ if (isTB(Inst) || isAddXri(Inst) || isCompAndBranch(Inst))
OpNum = 2;
}
@@ -1329,7 +1374,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
++OI;
}
- if (isTB(Inst)) {
+ if (isTB(Inst) || isCompAndBranch(Inst)) {
assert(MCPlus::getNumPrimeOperands(Inst) >= 3 &&
"Invalid number of operands");
OI = Inst.begin() + 2;
@@ -1682,6 +1727,42 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
case AArch64::CBZX: return AArch64::CBNZX;
case AArch64::CBNZW: return AArch64::CBZW;
case AArch64::CBNZX: return AArch64::CBZX;
+ case AArch64::CBGTWrr: return AArch64::CBGEWrr;
+ case AArch64::CBGTXrr: return AArch64::CBGEXrr;
+ case AArch64::CBGEWrr: return AArch64::CBGTWrr;
+ case AArch64::CBGEXrr: return AArch64::CBGTXrr;
+ case AArch64::CBHIWrr: return AArch64::CBHSWrr;
+ case AArch64::CBHIXrr: return AArch64::CBHSXrr;
+ case AArch64::CBHSWrr: return AArch64::CBHIWrr;
+ case AArch64::CBHSXrr: return AArch64::CBHIXrr;
+ case AArch64::CBEQWrr: return AArch64::CBNEWrr;
+ case AArch64::CBEQXrr: return AArch64::CBNEXrr;
+ case AArch64::CBNEWrr: return AArch64::CBEQWrr;
+ case AArch64::CBNEXrr: return AArch64::CBEQXrr;
+ case AArch64::CBHGTWrr: return AArch64::CBHGEWrr;
+ case AArch64::CBHGEWrr: return AArch64::CBHGTWrr;
+ case AArch64::CBHHIWrr: return AArch64::CBHHSWrr;
+ case AArch64::CBHHSWrr: return AArch64::CBHHIWrr;
+ case AArch64::CBHEQWrr: return AArch64::CBHNEWrr;
+ case AArch64::CBHNEWrr: return AArch64::CBHEQWrr;
+ case AArch64::CBBGTWrr: return AArch64::CBBGEWrr;
+ case AArch64::CBBGEWrr: return AArch64::CBBGTWrr;
+ case AArch64::CBBHIWrr: return AArch64::CBBHSWrr;
+ case AArch64::CBBHSWrr: return AArch64::CBBHIWrr;
+ case AArch64::CBBEQWrr: return AArch64::CBBNEWrr;
+ case AArch64::CBBNEWrr: return AArch64::CBBEQWrr;
+ case AArch64::CBGTWri: return AArch64::CBLTWri;
+ case AArch64::CBGTXri: return AArch64::CBLTXri;
+ case AArch64::CBLTWri: return AArch64::CBGTWri;
+ case AArch64::CBLTXri: return AArch64::CBGTXri;
+ case AArch64::CBHIWri: return AArch64::CBLOWri;
+ case AArch64::CBHIXri: return AArch64::CBLOXri;
+ case AArch64::CBLOWri: return AArch64::CBHIWri;
+ case AArch64::CBLOXri: return AArch64::CBHIXri;
+ case AArch64::CBEQWri: return AArch64::CBNEWri;
+ case AArch64::CBEQXri: return AArch64::CBNEXri;
+ case AArch64::CBNEWri: return AArch64::CBEQWri;
+ case AArch64::CBNEXri: return AArch64::CBEQXri;
}
}
@@ -1702,9 +1783,63 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
}
}
+ bool needsRegSwap(unsigned Opcode) const {
+ switch (Opcode) {
+ default:
+ return false;
+ case AArch64::CBGTWrr:
+ case AArch64::CBGTXrr:
+ case AArch64::CBGEWrr:
+ case AArch64::CBGEXrr:
+ case AArch64::CBHIWrr:
+ case AArch64::CBHIXrr:
+ case AArch64::CBHSWrr:
+ case AArch64::CBHSXrr:
+ case AArch64::CBHGTWrr:
+ case AArch64::CBHGEWrr:
+ case AArch64::CBHHIWrr:
+ case AArch64::CBHHSWrr:
+ case AArch64::CBBGTWrr:
+ case AArch64::CBBGEWrr:
+ case AArch64::CBBHIWrr:
+ case AArch64::CBBHSWrr:
+ return true;
+ }
+ }
+
+ bool needsImmDec(unsigned Opcode) const {
+ switch (Opcode) {
+ default:
+ return false;
+ case AArch64::CBLTWri:
+ case AArch64::CBLTXri:
+ case AArch64::CBLOWri:
+ case AArch64::CBLOXri:
+ return true;
+ }
+ }
+
+ bool needsImmInc(unsigned Opcode) const {
+ switch (Opcode) {
+ default:
+ return false;
+ case AArch64::CBGTWri:
+ case AArch64::CBGTXri:
+ case AArch64::CBHIWri:
+ case AArch64::CBHIXri:
+ return true;
+ }
+ }
+
void reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
- if (isTB(Inst) || isCB(Inst)) {
+ if (isTB(Inst) || isCB(Inst) || isCompAndBranch(Inst)) {
+ if (needsRegSwap(Inst.getOpcode()))
+ std::swap(Inst.getOperand(0), Inst.getOperand(1));
+ else if (needsImmDec(Inst.getOpcode()))
+ Inst.getOperand(1).setImm(Inst.getOperand(1).getImm() - 1);
+ else if (needsImmInc(Inst.getOpcode()))
+ Inst.getOperand(1).setImm(Inst.getOperand(1).getImm() + 1);
Inst.setOpcode(getInvertedBranchOpcode(Inst.getOpcode()));
assert(Inst.getOpcode() != 0 && "Invalid branch instruction");
} else if (Inst.getOpcode() == AArch64::Bcc) {
@@ -1721,18 +1856,16 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
}
int getPCRelEncodingSize(const MCInst &Inst) const override {
+ if (isCompAndBranch(Inst))
+ return 11;
+ if (isTB(Inst))
+ return 16;
+ if (isCB(Inst))
+ return 21;
switch (Inst.getOpcode()) {
default:
llvm_unreachable("Failed to get pcrel encoding size");
return 0;
- case AArch64::TBZW: return 16;
- case AArch64::TBZX: return 16;
- case AArch64::TBNZW: return 16;
- case AArch64::TBNZX: return 16;
- case AArch64::CBZW: return 21;
- case AArch64::CBZX: return 21;
- case AArch64::CBNZW: return 21;
- case AArch64::CBNZX: return 21;
case AArch64::B: return 28;
case AArch64::BL: return 28;
case AArch64::Bcc: return 21;
diff --git a/bolt/test/AArch64/compare-and-branch.S b/bolt/test/AArch64/compare-and-branch.S
new file mode 100644
index 0000000000000..374541c9bd6a0
--- /dev/null
+++ b/bolt/test/AArch64/compare-and-branch.S
@@ -0,0 +1,127 @@
+# This test checks that splitting functions which contain short range
+# conditional branches works in compact code model without relying on
+# relocations. Also checks that splitting works in non-relocation mode,
+# in order to test the branch inversion on those instructions.
+
+# REQUIRES: system-linux, asserts
+
+# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DRELOCATION_MODE=1
+# RUN: link_fdata --no-lbr %s %t %t.fdata
+# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions --compact-code-model
+# RUN: llvm-objdump -r %t | FileCheck %s --check-prefix=CHECK-RELOCS
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s --check-prefix=RELOC-MODE
+
+# RUN: %clang %cflags -march=armv9-a+cmpbr -Wl,-q %s -o %t -DRELOCATION_MODE=0
+# RUN: link_fdata --no-lbr %s %t %t.fdata
+# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions
+# RUN: llvm-objdump -r %t | FileCheck %s --check-prefix=CHECK-NO-RELOCS
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s --check-prefix=NON-RELOC-MODE
+
+ .globl foo
+ .type foo, %function
+foo:
+# FDATA: 1 foo #.entry_foo# 10
+.entry_foo:
+## Test immediate increment when inverting the branch.
+ cbgt x0, #0, .Lcold_foo
+ mov x0, #1
+.Lcold_foo:
+ ret
+
+ .globl bar
+ .type bar, %function
+bar:
+# FDATA: 1 bar #.entry_bar# 10
+.entry_bar:
+## Test immediate decrement when inverting the branch.
+ cblo x0, #1, .Lcold_bar
+ mov x0, #2
+.Lcold_bar:
+ ret
+
+ .globl baz
+ .type baz, %function
+baz:
+# FDATA: 1 baz #.entry_baz# 10
+.entry_baz:
+## Test register swap when inverting the branch.
+ cbge x0, x1, .Lcold_baz
+ mov x0, #3
+.Lcold_baz:
+ ret
+
+## Force relocation mode.
+.if RELOCATION_MODE
+.reloc 0, R_AARCH64_NONE
+.endif
+
+
+# CHECK-RELOCS: R_AARCH64_NONE *ABS*
+# CHECK-RELOCS-NOT: R_AARCH64_
+
+
+# CHECK-NO-RELOCS-NOT: R_AARCH64_
+
+
+# RELOC-MODE: Disassembly of section .text:
+
+# RELOC-MODE: <foo>:
+# RELOC-MODE-NEXT: {{.*}} cbgt x0, #0x0, 0x[[ADDR0:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR1:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: [[ADDR0]]: {{.*}} b 0x[[ADDR2:[0-9a-f]+]] <{{.*}}>
+
+# RELOC-MODE: <bar>:
+# RELOC-MODE-NEXT: {{.*}} cblo x0, #0x1, 0x[[ADDR3:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR4:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: [[ADDR3]]: {{.*}} b 0x[[ADDR5:[0-9a-f]+]] <{{.*}}>
+
+# RELOC-MODE: <baz>:
+# RELOC-MODE-NEXT: {{.*}} cbge x0, x1, 0x[[ADDR6:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR7:[0-9a-f]+]] <{{.*}}>
+# RELOC-MODE-NEXT: [[ADDR6]]: {{.*}} b 0x[[ADDR8:[0-9a-f]+]] <{{.*}}>
+
+# RELOC-MODE: Disassembly of section .text.cold:
+
+# RELOC-MODE: <foo.cold.0>:
+# RELOC-MODE-NEXT: [[ADDR1]]: {{.*}} mov x0, #0x1 // =1
+# RELOC-MODE-NEXT: [[ADDR2]]: {{.*}} ret
+
+# RELOC-MODE: <bar.cold.0>:
+# RELOC-MODE-NEXT: [[ADDR4]]: {{.*}} mov x0, #0x2 // =2
+# RELOC-MODE-NEXT: [[ADDR5]]: {{.*}} ret
+
+# RELOC-MODE: <baz.cold.0>:
+# RELOC-MODE-NEXT: [[ADDR7]]: {{.*}} mov x0, #0x3 // =3
+# RELOC-MODE-NEXT: [[ADDR8]]: {{.*}} ret
+
+
+# NON-RELOC-MODE: Disassembly of section .text:
+
+# NON-RELOC-MODE: <foo>:
+# NON-RELOC-MODE-NEXT: {{.*}} cblt x0, #0x1, 0x[[ADDR0:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR1:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: [[ADDR0]]: {{.*}} b 0x[[ADDR2:[0-9a-f]+]] <{{.*}}>
+
+# NON-RELOC-MODE: <bar>:
+# NON-RELOC-MODE-NEXT: {{.*}} cbhi x0, #0x0, 0x[[ADDR3:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR4:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: [[ADDR3]]: {{.*}} b 0x[[ADDR5:[0-9a-f]+]] <{{.*}}>
+
+# NON-RELOC-MODE: <baz>:
+# NON-RELOC-MODE-NEXT: {{.*}} cbgt x1, x0, 0x[[ADDR6:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: {{.*}} b 0x[[ADDR7:[0-9a-f]+]] <{{.*}}>
+# NON-RELOC-MODE-NEXT: [[ADDR6]]: {{.*}} b 0x[[ADDR8:[0-9a-f]+]] <{{.*}}>
+
+# NON-RELOC-MODE: Disassembly of section .bolt.text:
+
+# NON-RELOC-MODE: <foo.cold.0>:
+# NON-RELOC-MODE-NEXT: [[ADDR2]]: {{.*}} mov x0, #0x1 // =1
+# NON-RELOC-MODE-NEXT: [[ADDR1]]: {{.*}} ret
+
+# NON-RELOC-MODE: <bar.cold.0>:
+# NON-RELOC-MODE-NEXT: [[ADDR5]]: {{.*}} mov x0, #0x2 // =2
+# NON-RELOC-MODE-NEXT: [[ADDR4]]: {{.*}} ret
+
+# NON-RELOC-MODE: <baz.cold.0>:
+# NON-RELOC-MODE-NEXT: [[ADDR8]]: {{.*}} mov x0, #0x3 // =3
+# NON-RELOC-MODE-NEXT: [[ADDR7]]: {{.*}} ret
More information about the llvm-commits
mailing list