Skip to content

Commit 8ae1e3a

Browse files
committed
Aarch64: Emit a minimal SEH prologue when needed
In some cases, with very simple thunks, it is possible that the `.seh_endprologue` is not emitted. This causes issues in the assembler because the epilogue ends up starting before the prologue has ended.
1 parent cccc9d3 commit 8ae1e3a

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

llvm/lib/Target/AArch64/AArch64PrologueEpilogue.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ void AArch64PrologueEmitter::emitPrologue() {
541541
// to determine the end of the prologue.
542542
DebugLoc DL;
543543

544+
if (AFI->getArgumentStackToRestore())
545+
HasWinCFI = true;
546+
544547
if (AFI->shouldSignReturnAddress(MF)) {
545548
// If pac-ret+leaf is in effect, PAUTH_PROLOGUE pseudo instructions
546549
// are inserted by emitPacRetPlusLeafHardening().
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
; RUN: llc -mtriple=aarch64-windows %s -o - | FileCheck %s
2+
3+
; This test verifies that functions requiring Windows CFI that have minimal
4+
; or no prologue instructions still emit proper SEH directives, specifically
5+
; ensuring .seh_endprologue is emitted before .seh_startepilogue.
6+
;
7+
; This reproduces the issue where Swift async functions with swifttailcc
8+
; calling convention would fail with:
9+
; "error: starting epilogue (.seh_startepilogue) before prologue has ended (.seh_endprologue)"
10+
11+
; Test 1: Swift-style tail call function with minimal prologue
12+
define swifttailcc void @test_swifttailcc_minimal(ptr %async_ctx, ptr %arg1, ptr %arg2) {
13+
; CHECK-LABEL: test_swifttailcc_minimal:
14+
; CHECK-NOT: .seh_proc test_swifttailcc_minimal
15+
; CHECK-NOT: .seh_endprologue
16+
; CHECK-NOT: .seh_startepilogue
17+
; CHECK-NOT: .seh_endepilogue
18+
; CHECK-NOT: .seh_endproc
19+
entry:
20+
%ptr1 = getelementptr inbounds i8, ptr %async_ctx, i64 16
21+
%ptr2 = getelementptr inbounds i8, ptr %async_ctx, i64 24
22+
store ptr %arg1, ptr %ptr1, align 8
23+
store ptr %arg2, ptr %ptr2, align 8
24+
musttail call swifttailcc void @external_swift_function(ptr %async_ctx, ptr %arg1)
25+
ret void
26+
}
27+
28+
; Test 2: Regular function with no stack frame but needs epilogue
29+
define void @test_no_stack_frame() {
30+
; CHECK-LABEL: test_no_stack_frame:
31+
; CHECK-NEXT: .seh_proc test_no_stack_frame
32+
; CHECK: .seh_endprologue
33+
; CHECK: .seh_startepilogue
34+
; CHECK: .seh_endepilogue
35+
; CHECK: .seh_endproc
36+
entry:
37+
call void @external_function()
38+
ret void
39+
}
40+
41+
; Test 3: Function with minimal stack adjustment only in epilogue
42+
define void @test_minimal_stack_adjust(ptr %ptr) {
43+
; CHECK-LABEL: test_minimal_stack_adjust:
44+
; CHECK-NEXT: .seh_proc test_minimal_stack_adjust
45+
; CHECK: .seh_endprologue
46+
; CHECK: .seh_startepilogue
47+
; CHECK: add sp, sp, #16
48+
; CHECK: .seh_stackalloc 16
49+
; CHECK: .seh_endepilogue
50+
; CHECK: .seh_endproc
51+
entry:
52+
%local = alloca i64, align 8
53+
store i64 42, ptr %local, align 8
54+
%value = load i64, ptr %local, align 8
55+
store i64 %value, ptr %ptr, align 8
56+
ret void
57+
}
58+
59+
; Test 4: Function similar to the original failing case
60+
define linkonce_odr hidden swifttailcc void @test_linkonce_swifttailcc(ptr swiftasync %async_ctx, ptr %arg1, ptr noalias dereferenceable(40) %arg2, ptr %arg3, i64 %value, ptr %arg4, ptr %arg5, ptr %arg6, i1 %flag, ptr %arg7, ptr noalias dereferenceable(40) %arg8) {
61+
; CHECK-LABEL: test_linkonce_swifttailcc:
62+
; CHECK-NEXT: .seh_proc
63+
; CHECK: .seh_endprologue
64+
; CHECK: .seh_startepilogue
65+
; CHECK: .seh_endepilogue
66+
; CHECK: .seh_endproc
67+
entry:
68+
%frame_ptr = getelementptr inbounds nuw i8, ptr %async_ctx, i64 16
69+
%ctx1 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 400
70+
%ctx2 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 1168
71+
%spill1 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 2392
72+
store ptr %arg8, ptr %spill1, align 8
73+
%spill2 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 2384
74+
store ptr %arg7, ptr %spill2, align 8
75+
%spill3 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 2225
76+
store i1 %flag, ptr %spill3, align 1
77+
%spill4 = getelementptr inbounds nuw i8, ptr %async_ctx, i64 2376
78+
store ptr %arg6, ptr %spill4, align 8
79+
musttail call swifttailcc void @external_swift_continuation(ptr swiftasync %async_ctx, i64 0, i64 0)
80+
ret void
81+
}
82+
83+
declare swifttailcc void @external_swift_function(ptr, ptr)
84+
declare swifttailcc void @external_swift_continuation(ptr swiftasync, i64, i64)
85+
declare void @external_function()

llvm/test/CodeGen/AArch64/wincfi-seh-only-in-epilogue.ll renamed to llvm/test/CodeGen/AArch64/wincfi-minimal-seh-prologue.ll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ entry:
55
ret void
66
}
77

8-
; Check that there is no .seh_endprologue but there is seh_startepilogue/seh_endepilogue.
9-
; CHECK-NOT: .seh_endprologue
8+
; Check that there is a minimal SEH prologue with seh_startepilogue/seh_endepilogue.
9+
; CHECK: .seh_proc test
10+
; CHECK: .seh_endprologue
1011
; CHECK: .seh_startepilogue
1112
; CHECK: add sp, sp, #48
1213
; CHECK: .seh_stackalloc 48

0 commit comments

Comments
 (0)