[llvm] [X86] Fix missing CFI after the Swift async context push (PR #202570)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 09:21:39 PDT 2026
https://github.com/adalal-2441 updated https://github.com/llvm/llvm-project/pull/202570
>From cb6296de6e817f9b280f8f29c0de171938601a49 Mon Sep 17 00:00:00 2001
From: Akshat <adalal at amd.com>
Date: Tue, 9 Jun 2026 15:42:29 +0530
Subject: [PATCH] [X86] Fix missing CFI after the Swift async context push
The Swift async prologue pushes the context slot (pushq %r14 / $0) but
doesn't touch the CFA until the later .cfi_def_cfa_register %rbp. So the
CFA still describes the stack from before the push and stays stale all
the way through the leaq and the subq. If something unwinds in that
window (debugger, profiler, signal) it reads the wrong slot. Normal
execution is fine.
FIX: account for the push with .cfi_adjust_cfa_offset 8, then switch to
an %rbp-relative CFA (.cfi_def_cfa %rbp, 16) right after the leaq and
before the subq, so the rule is correct before rsp moves again.
Adds swift-async-cfi-prologue.ll (directives + .eh_frame rows, plus a
locals case for the subq) and updates swift-async.ll.
Found via @jlebar's X86 LLVM bug hunt / FuzzX effort:
https://github.com/SemiAnalysisAI/FuzzX/tree/master/x86/bugs/036-frame-swift-async-cfi-missing
cc @jlebar
---
llvm/lib/Target/X86/X86FrameLowering.cpp | 22 +++++++++-
.../CodeGen/X86/swift-async-cfi-prologue.ll | 41 +++++++++++++++++++
llvm/test/CodeGen/X86/swift-async.ll | 13 +++++-
3 files changed, 74 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/swift-async-cfi-prologue.ll
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 8fe09bea456c8..59705247ec288 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -1888,6 +1888,15 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
.addImm(0)
.setMIFlag(MachineInstr::FrameSetup);
}
+
+ // Update CFA offset for the async-context push.
+ if (NeedsDwarfCFI && !ArgBaseReg.isValid()) {
+ BuildCFI(
+ MBB, MBBI, DL,
+ MCCFIInstruction::createAdjustCfaOffset(nullptr, -stackGrowth),
+ MachineInstr::FrameSetup);
+ }
+
EmitSEHAfter(EmitSEHPushR14);
BuildMI(MBB, MBBI, DL, TII.get(X86::LEA64r), FramePtr)
@@ -1897,6 +1906,17 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
.addImm(8)
.addUse(X86::NoRegister)
.setMIFlag(MachineInstr::FrameSetup);
+
+ // Switch to an FP-relative CFA before adjusting RSP below.
+ if (NeedsDwarfCFI && !ArgBaseReg.isValid()) {
+ unsigned DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true);
+ BuildCFI(MBB, MBBI, DL,
+ MCCFIInstruction::cfiDefCfa(nullptr, DwarfFramePtr,
+ -2 * stackGrowth +
+ (int)TailCallArgReserveSize),
+ MachineInstr::FrameSetup);
+ }
+
BuildMI(MBB, MBBI, DL, TII.get(X86::SUB64ri32), X86::RSP)
.addUse(X86::RSP)
.addImm(8)
@@ -1926,7 +1946,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::createEscape(nullptr, CfaExpr.str()),
MachineInstr::FrameSetup);
- } else {
+ } else if (!X86FI->hasSwiftAsyncContext()) {
// Mark effective beginning of when frame pointer becomes valid.
// Define the current CFA to use the EBP/RBP register.
unsigned DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true);
diff --git a/llvm/test/CodeGen/X86/swift-async-cfi-prologue.ll b/llvm/test/CodeGen/X86/swift-async-cfi-prologue.ll
new file mode 100644
index 0000000000000..f572595ad2118
--- /dev/null
+++ b/llvm/test/CodeGen/X86/swift-async-cfi-prologue.ll
@@ -0,0 +1,41 @@
+; RUN: llc -mtriple=x86_64-apple-macosx -O0 %s -o - | FileCheck %s --check-prefix=ASM
+; RUN: llc -mtriple=x86_64-apple-macosx -O0 -filetype=obj %s -o - | \
+; RUN: llvm-dwarfdump --eh-frame - | FileCheck %s --check-prefix=UNWIND
+
+; Verify Swift async prologue CFA updates after the context push.
+
+; ASM-LABEL: foo:
+; ASM: pushq %rbp
+; ASM-NEXT: .cfi_def_cfa_offset 16
+; ASM-NEXT: .cfi_offset %rbp, -16
+; ASM-NEXT: pushq %r14
+; ASM-NEXT: .cfi_adjust_cfa_offset 8
+; ASM-NEXT: leaq 8(%rsp), %rbp
+; ASM-NEXT: .cfi_def_cfa %rbp, 16
+; ASM-NOT: .cfi_def_cfa_register
+
+; UNWIND: 0x0: CFA=RSP+8:
+; UNWIND-NEXT: 0x6: CFA=RSP+16:
+; UNWIND-NEXT: 0x8: CFA=RSP+24:
+; UNWIND-NEXT: 0xd: CFA=RBP+16:
+
+define void @foo(ptr swiftasync %ctx) "frame-pointer"="all" {
+ call void asm sideeffect "int3", ""()
+ ret void
+}
+
+; A frame with locals emits a real stack adjustment after the FP is set up.
+; The CFA must already be %rbp-relative before that subq, so no CFI directive
+; appears between the .cfi_def_cfa and the subq.
+; ASM-LABEL: with_locals:
+; ASM: pushq %r14
+; ASM-NEXT: .cfi_adjust_cfa_offset 8
+; ASM-NEXT: leaq 8(%rsp), %rbp
+; ASM-NEXT: .cfi_def_cfa %rbp, 16
+; ASM-NEXT: subq ${{[0-9]+}}, %rsp
+
+define void @with_locals(ptr swiftasync %ctx) "frame-pointer"="all" {
+ %a = alloca [128 x i8]
+ call void asm sideeffect "int3", "r"(ptr %a)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/swift-async.ll b/llvm/test/CodeGen/X86/swift-async.ll
index 0017d2af85878..c31cceeb88f17 100644
--- a/llvm/test/CodeGen/X86/swift-async.ll
+++ b/llvm/test/CodeGen/X86/swift-async.ll
@@ -27,9 +27,12 @@ define void @more_csrs(ptr swiftasync %ctx) "frame-pointer"="all" {
; CHECK-LABEL: more_csrs:
; CHECK: btsq $60, %rbp
; CHECK: pushq %rbp
+; CHECK: .cfi_def_cfa_offset 16
; CHECK: .cfi_offset %rbp, -16
; CHECK: pushq %r14
+; CHECK: .cfi_adjust_cfa_offset 8
; CHECK: leaq 8(%rsp), %rbp
+; CHECK: .cfi_def_cfa %rbp, 16
; CHECK: subq $8, %rsp
; CHECK: pushq %r15
; CHECK: .cfi_offset %r15, -40
@@ -52,8 +55,9 @@ define void @locals(ptr swiftasync %ctx) "frame-pointer"="all" {
; CHECK: .cfi_def_cfa_offset 16
; CHECK: .cfi_offset %rbp, -16
; CHECK: pushq %r14
+; CHECK: .cfi_adjust_cfa_offset 8
; CHECK: leaq 8(%rsp), %rbp
-; CHECK: .cfi_def_cfa_register %rbp
+; CHECK: .cfi_def_cfa %rbp, 16
; CHECK: subq $56, %rsp
; CHECK: leaq -48(%rbp), %rdi
@@ -80,6 +84,13 @@ define void @use_input_context(ptr swiftasync %ctx, ptr %ptr) "frame-pointer"="a
define ptr @context_in_func() "frame-pointer"="non-leaf" {
; CHECK-LABEL: context_in_func:
+; CHECK: pushq %rbp
+; CHECK: .cfi_def_cfa_offset 16
+; CHECK: .cfi_offset %rbp, -16
+; CHECK: pushq $0
+; CHECK: .cfi_adjust_cfa_offset 8
+; CHECK: leaq 8(%rsp), %rbp
+; CHECK: .cfi_def_cfa %rbp, 16
; CHECK: leaq -8(%rbp), %rax
; CHECK-32-LABEL: context_in_func
More information about the llvm-commits
mailing list