[llvm] [AArch64] Fix missing :lo12: on the catchret address pair (PR #219200)
Jonas Heinle via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 07:06:45 PDT 2026
https://github.com/Kataglyphis updated https://github.com/llvm/llvm-project/pull/219200
>From 5ffe34a01054ada90d609f35d671ab8548ab2d21 Mon Sep 17 00:00:00 2001
From: Jonas Heinle <jonasheinle at googlemail.com>
Date: Thu, 27 Aug 2026 14:32:13 +0200
Subject: [PATCH] [AArch64] Fix missing :lo12: on the catchret address pair
The CATCHRET expansion builds the ADRP/ADD pair for the continuation block
without relocation specifiers, so the ADD prints as
add x0, x0, .LBB0_1
and LLVM's own assembler then rejects LLVM's own output:
error: expected compatible register, symbol or integer in range [0, 4095]
Any function with a catchret hits this. Set MO_PAGE and MO_PAGEOFF|MO_NC on the
two block operands and let lowerOperand() honour them, the way every other
symbol operand is already handled.
Two things a reviewer will want to know, both learned the hard way:
* The flags have to go on the MBB operands. Rewriting the pair with .addSym()
fixes the printing too, but it drops the block reference and shifts
basic-block numbering in wineh-catchret-label-generation.ll.
* LowerSymbolOperand() reads MO.getOffset(), which an MBB operand does not
have. It needs the same guard the neighbouring line already carries for
jump-table indices, in all three of the ELF/MachO/COFF paths.
Objects are unaffected -- the relocations were always correct (PAGEBASE_REL21 +
PAGEOFFSET_12A, verified by assembling the fixed output and diffing against
-filetype=obj). This is -S and clang-cl /FA only. That is still the path you
take when a codegen error arrives without a source location, and on
Windows-on-ARM with C++ exceptions it was closed.
Verified on a local AArch64 build with assertions, by running the patched and
the unpatched llc over all 2869 tests in test/CodeGen/AArch64: of the 2081 both
could compile, 14 differ, every one of them a Windows-EH test, and the whole
diff is 14 lines -- each the added :lo12:. Seven of those tests check that line
and are updated here; the other seven pass unchanged. The new test fails
against unpatched llc.
Found with clang-cl 23.1.0 building OpenCV for aarch64-pc-windows-msvc.
Assisted-by: Claude (Anthropic)
---
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 4 +--
.../lib/Target/AArch64/AArch64MCInstLower.cpp | 17 +++++++++----
llvm/test/CodeGen/AArch64/pr58516.ll | 2 +-
.../test/CodeGen/AArch64/wineh-bti-funclet.ll | 2 +-
.../wineh-catchret-label-generation.ll | 2 +-
.../CodeGen/AArch64/wineh-catchret-lo12.ll | 25 +++++++++++++++++++
.../CodeGen/AArch64/wineh-try-catch-nobase.ll | 2 +-
.../AArch64/wineh-try-catch-realign.ll | 2 +-
.../CodeGen/AArch64/wineh-try-catch-vla.ll | 2 +-
llvm/test/CodeGen/AArch64/wineh-try-catch.ll | 2 +-
10 files changed, 46 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/wineh-catchret-lo12.ll
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index 43aab1400e81f..2a3b699b2ce87 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -2592,11 +2592,11 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
FirstEpilogSEH = std::next(FirstEpilogSEH);
BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADRP))
.addReg(AArch64::X0, RegState::Define)
- .addMBB(TargetMBB);
+ .addMBB(TargetMBB, AArch64II::MO_PAGE);
BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADDXri))
.addReg(AArch64::X0, RegState::Define)
.addReg(AArch64::X0)
- .addMBB(TargetMBB)
+ .addMBB(TargetMBB, AArch64II::MO_PAGEOFF | AArch64II::MO_NC)
.addImm(0);
TargetMBB->setMachineBlockAddressTaken();
return true;
diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
index d3a38624488e8..2ac934540e3f5 100644
--- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
@@ -173,7 +173,7 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandMachO(const MachineOperand &MO,
}
// TODO: Migrate to MCSpecifierExpr::create like ELF.
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Spec, Ctx);
- if (!MO.isJTI() && MO.getOffset())
+ if (!MO.isJTI() && !MO.isMBB() && MO.getOffset())
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
return MCOperand::createExpr(Expr);
@@ -259,7 +259,7 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO,
RefFlags |= AArch64::S_NC;
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
- if (!MO.isJTI() && MO.getOffset())
+ if (!MO.isJTI() && !MO.isMBB() && MO.getOffset())
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
@@ -310,7 +310,7 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandCOFF(const MachineOperand &MO,
}
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
- if (!MO.isJTI() && MO.getOffset())
+ if (!MO.isJTI() && !MO.isMBB() && MO.getOffset())
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
@@ -347,8 +347,15 @@ bool AArch64MCInstLower::lowerOperand(const MachineOperand &MO,
MCOp = MCOperand::createImm(MO.getImm());
break;
case MachineOperand::MO_MachineBasicBlock:
- MCOp = MCOperand::createExpr(
- MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx));
+ // A block operand can carry a fragment flag -- the ADRP/ADD pair a Windows
+ // catch funclet uses to materialise its catchret continuation block does.
+ // Those need their relocation specifier; a plain branch target has no
+ // flags and keeps the bare symbol reference.
+ if (MO.getTargetFlags() & AArch64II::MO_FRAGMENT)
+ MCOp = LowerSymbolOperand(MO, MO.getMBB()->getSymbol());
+ else
+ MCOp = MCOperand::createExpr(
+ MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx));
break;
case MachineOperand::MO_GlobalAddress:
MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
diff --git a/llvm/test/CodeGen/AArch64/pr58516.ll b/llvm/test/CodeGen/AArch64/pr58516.ll
index 5554826a6ac76..66f56cf5485d6 100644
--- a/llvm/test/CodeGen/AArch64/pr58516.ll
+++ b/llvm/test/CodeGen/AArch64/pr58516.ll
@@ -76,7 +76,7 @@ define void @osfx(ptr %this) comdat personality ptr @__CxxFrameHandler3 {
; CHECK-NEXT: .seh_save_fplr 24
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: adrp x0, .LBB0_2
-; CHECK-NEXT: add x0, x0, .LBB0_2
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_2
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp, #24] // 16-byte Folded Reload
; CHECK-NEXT: .seh_save_fplr 24
diff --git a/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
index 4f4f984b8974b..9bb6504c6985f 100644
--- a/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
@@ -54,7 +54,7 @@ define dso_local void @wineh_funclet() #0 personality ptr @__CxxFrameHandler3 {
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: bl may_throw
; CHECK-NEXT: adrp x0, .LBB0_1
-; CHECK-NEXT: add x0, x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_1
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload
; CHECK-NEXT: .seh_save_fplr_x 16
diff --git a/llvm/test/CodeGen/AArch64/wineh-catchret-label-generation.ll b/llvm/test/CodeGen/AArch64/wineh-catchret-label-generation.ll
index 3f7df585c52b4..9bc7f0704a275 100644
--- a/llvm/test/CodeGen/AArch64/wineh-catchret-label-generation.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-catchret-label-generation.ll
@@ -51,7 +51,7 @@ define fastcc ptr @test_function(i1 %0, ptr %_Fmtfl.i.i, i1 %1) personality ptr
; CHECK-NEXT: .seh_save_fplr_x 16
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: adrp x0, .LBB0_2
-; CHECK-NEXT: add x0, x0, .LBB0_2
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_2
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload
; CHECK-NEXT: .seh_save_fplr_x 16
diff --git a/llvm/test/CodeGen/AArch64/wineh-catchret-lo12.ll b/llvm/test/CodeGen/AArch64/wineh-catchret-lo12.ll
new file mode 100644
index 0000000000000..7039b1b666004
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/wineh-catchret-lo12.ll
@@ -0,0 +1,25 @@
+; RUN: llc -mtriple=aarch64-pc-windows-msvc < %s | FileCheck %s
+; The output has to assemble as well as compile:
+; RUN: llc -mtriple=aarch64-pc-windows-msvc < %s | llvm-mc -triple=aarch64-pc-windows-msvc -filetype=obj -o /dev/null
+
+declare void @g()
+declare i32 @__CxxFrameHandler3(...)
+
+define void @f() personality ptr @__CxxFrameHandler3 {
+entry:
+ invoke void @g()
+ to label %cont unwind label %catch.dispatch
+
+catch.dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %cp = catchpad within %cs [ptr null, i32 64, ptr null]
+ catchret from %cp to label %cont
+
+cont:
+ ret void
+}
+
+; CHECK: adrp x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_1
diff --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
index 361dc8c61e161..9a3c4f18e220d 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
@@ -13,7 +13,7 @@
; CHECK-NEXT: mov x1, xzr
; CHECK-NEXT: bl "?bb@@YAXPEAHH at Z"
; CHECK-NEXT: adrp x0, .LBB0_1
-; CHECK-NEXT: add x0, x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_1
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload
; CHECK-NEXT: .seh_save_fplr_x 16
diff --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
index 484e1879549c4..c70049fae7599 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
@@ -20,7 +20,7 @@
; CHECK-NEXT: mov w1, wzr
; CHECK-NEXT: bl "?bb@@YAXPEAHH at Z"
; CHECK-NEXT: adrp x0, .LBB0_1
-; CHECK-NEXT: add x0, x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_1
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp, #16]
; CHECK-NEXT: .seh_save_fplr 16
diff --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
index 46e5f2403f2ae..44f1c44fc19f8 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
@@ -15,7 +15,7 @@
; CHECK-NEXT: mov x1, x0
; CHECK-NEXT: bl "?bb@@YAXPEAHH at Z"
; CHECK-NEXT: adrp x0, .LBB0_1
-; CHECK-NEXT: add x0, x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, :lo12:.LBB0_1
; CHECK-NEXT: .seh_startepilogue
; CHECK-NEXT: ldp x29, x30, [sp], #16
; CHECK-NEXT: .seh_save_fplr_x 16
diff --git a/llvm/test/CodeGen/AArch64/wineh-try-catch.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch.ll
index 6bb22fca24591..874c0dc28c3b7 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch.ll
@@ -67,7 +67,7 @@
; Check that the funclet branches back to the catchret destination
; CHECK: adrp x0, .LBB0_2
-; CHECK-NEXT: add x0, x0, [[CATCHRETDEST]]
+; CHECK-NEXT: add x0, x0, :lo12:[[CATCHRETDEST]]
; Now check that the offset of the unwind help object from the stack pointer on
More information about the llvm-commits
mailing list