Skip to content

Commit c0f2dad

Browse files
committed
X86: enable proper frame lowering for swift async on Win64
The Swift asynchronous calling convention requires some additional setup on the frame. In particular, the swift async register needs to be spilled relative to the frame pointer, which we now accommodate. This may require some further refinement due to frame setup restrictions on Windows, but this initial approach should be sufficient to move towards a working asynchronous support.
1 parent d517a45 commit c0f2dad

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

llvm/lib/Target/X86/X86FrameLowering.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,48 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
15181518
.setMIFlag(MachineInstr::FrameSetup);
15191519
}
15201520
}
1521+
1522+
if (IsWin64Prologue && !IsFunclet) {
1523+
if (X86FI->hasSwiftAsyncContext()) {
1524+
// Before we update the live frame pointer we have to ensure there's a
1525+
// valid (or null) asynchronous context in its slot just before FP in
1526+
// the frame record, so store it now.
1527+
const auto &Attrs = MF.getFunction().getAttributes();
1528+
1529+
if (Attrs.hasAttrSomewhere(Attribute::SwiftAsync)) {
1530+
// We have an initial context in r14, store it just before the frame
1531+
// pointer.
1532+
BuildMI(MBB, MBBI, DL, TII.get(X86::PUSH64r))
1533+
.addReg(X86::R14)
1534+
.setMIFlag(MachineInstr::FrameSetup);
1535+
} else {
1536+
// No initial context, store null so that there's no pointer that
1537+
// could be misused.
1538+
BuildMI(MBB, MBBI, DL, TII.get(X86::PUSH64i8))
1539+
.addImm(0)
1540+
.setMIFlag(MachineInstr::FrameSetup);
1541+
}
1542+
1543+
if (NeedsWinCFI) {
1544+
HasWinCFI = true;
1545+
BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg))
1546+
.addImm(X86::R14)
1547+
.setMIFlag(MachineInstr::FrameSetup);
1548+
}
1549+
1550+
BuildMI(MBB, MBBI, DL, TII.get(X86::LEA64r), FramePtr)
1551+
.addUse(X86::RSP)
1552+
.addImm(1)
1553+
.addUse(X86::NoRegister)
1554+
.addImm(8)
1555+
.addUse(X86::NoRegister)
1556+
.setMIFlag(MachineInstr::FrameSetup);
1557+
BuildMI(MBB, MBBI, DL, TII.get(X86::SUB64ri8), X86::RSP)
1558+
.addUse(X86::RSP)
1559+
.addImm(8)
1560+
.setMIFlag(MachineInstr::FrameSetup);
1561+
}
1562+
}
15211563
} else {
15221564
assert(!IsFunclet && "funclets without FPs not yet implemented");
15231565
NumBytes = StackSize - X86FI->getCalleeSavedFrameSize();
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
; RUN: llc -mtriple=x86_64-unknown-windows-msvc %s -o - | FileCheck %s
2+
; RUN: llc -mtriple=i686-windows-msvc %s -o - | FileCheck %s --check-prefix=CHECK-32
3+
4+
define void @simple(i8* swiftasync %ctx) "frame-pointer"="all" {
5+
; CHECK-LABEL: simple:
6+
; CHECK: btsq $60, %rbp
7+
; CHECK: pushq %rbp
8+
; CHECK: pushq %r14
9+
; CHECK: leaq 8(%rsp), %rbp
10+
11+
; [...]
12+
13+
; CHECK: addq $16, %rsp
14+
; CHECK: popq %rbp
15+
; CHECK: btrq $60, %rbp
16+
; CHECK: retq
17+
18+
; CHECK-32-LABEL: simple:
19+
; CHECK-32: movl 8(%ebp), [[TMP:%.*]]
20+
; CHECK-32: movl [[TMP]], {{.*}}(%ebp)
21+
22+
ret void
23+
}
24+
25+
define void @more_csrs(i8* swiftasync %ctx) "frame-pointer"="all" {
26+
; CHECK-LABEL: more_csrs:
27+
; CHECK: btsq $60, %rbp
28+
; CHECK: pushq %rbp
29+
; CHECK: .seh_pushreg %rbp
30+
; CHECK: pushq %r14
31+
; CHECK: .seh_pushreg %r14
32+
; CHECK: leaq 8(%rsp), %rbp
33+
; CHECK: subq $8, %rsp
34+
; CHECK: pushq %r15
35+
; CHECK: .seh_pushreg %r15
36+
37+
; [...]
38+
39+
; CHECK: popq %r15
40+
; CHECK: addq $16, %rsp
41+
; CHECK: popq %rbp
42+
; CHECK: btrq $60, %rbp
43+
; CHECK: retq
44+
call void asm sideeffect "", "~{r15}"()
45+
ret void
46+
}
47+
48+
define void @locals(i8* swiftasync %ctx) "frame-pointer"="all" {
49+
; CHECK-LABEL: locals:
50+
; CHECK: btsq $60, %rbp
51+
; CHECK: pushq %rbp
52+
; CHECK: .seh_pushreg %rbp
53+
; CHECK: pushq %r14
54+
; CHECK: .seh_pushreg %r14
55+
; CHECK: leaq 8(%rsp), %rbp
56+
; CHECK: subq $88, %rsp
57+
58+
; CHECK: leaq -48(%rbp), %rcx
59+
; CHECK: callq bar
60+
61+
; CHECK: addq $80, %rsp
62+
; CHECK: addq $16, %rsp
63+
; CHECK: popq %rbp
64+
; CHECK: btrq $60, %rbp
65+
; CHECK: retq
66+
67+
%var = alloca i32, i32 10
68+
call void @bar(i32* %var)
69+
ret void
70+
}
71+
72+
define void @use_input_context(i8* swiftasync %ctx, i8** %ptr) "frame-pointer"="all" {
73+
; CHECK-LABEL: use_input_context:
74+
; CHECK: movq %r14, (%rcx)
75+
76+
store i8* %ctx, i8** %ptr
77+
ret void
78+
}
79+
80+
define i8** @context_in_func() "frame-pointer"="non-leaf" {
81+
; CHECK-LABEL: context_in_func:
82+
; CHECK: leaq -8(%rbp), %rax
83+
84+
; CHECK-32-LABEL: context_in_func
85+
; CHECK-32: movl %esp, %eax
86+
87+
%ptr = call i8** @llvm.swift.async.context.addr()
88+
ret i8** %ptr
89+
}
90+
91+
define void @write_frame_context(i8* swiftasync %ctx, i8* %newctx) "frame-pointer"="non-leaf" {
92+
; CHECK-LABEL: write_frame_context:
93+
; CHECK: movq %rbp, [[TMP:%.*]]
94+
; CHECK: subq $8, [[TMP]]
95+
; CHECK: movq %rcx, ([[TMP]])
96+
97+
%ptr = call i8** @llvm.swift.async.context.addr()
98+
store i8* %newctx, i8** %ptr
99+
ret void
100+
}
101+
102+
define void @simple_fp_elim(i8* swiftasync %ctx) "frame-pointer"="non-leaf" {
103+
; CHECK-LABEL: simple_fp_elim:
104+
; CHECK-NOT: btsq
105+
106+
ret void
107+
}
108+
109+
declare void @bar(i32*)
110+
declare i8** @llvm.swift.async.context.addr()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; RUN: llc -mtriple x86_64-unknown-windows-msvc %s -o - | FileCheck %s
2+
3+
4+
declare swifttailcc void @swifttail_callee()
5+
define swifttailcc void @swifttail() {
6+
; CHECK-LABEL: swifttail:
7+
; CHECK-NOT: popq %r14
8+
call void asm "","~{r14}"()
9+
tail call swifttailcc void @swifttail_callee()
10+
ret void
11+
}
12+
13+
define void @has_swiftasync(i8* swiftasync %in) {
14+
; CHECK-LABEL: has_swiftasync:
15+
; CHECK: popq %r14
16+
call void asm "","~{r14}"()
17+
ret void
18+
}
19+
20+
; It's impossible to get a tail call from a function without a swiftasync
21+
; parameter to one with unless the CC is swifttailcc. So it doesn't matter
22+
; whether r14 is callee-saved in this case.
23+
define void @calls_swiftasync() {
24+
; CHECK-LABEL: calls_swiftasync:
25+
; CHECK-NOT: jmpq _has_swiftasync
26+
call void asm "","~{r14}"()
27+
tail call void @has_swiftasync(i8* swiftasync null)
28+
ret void
29+
}
30+
31+
define swifttailcc void @no_preserve_swiftself() {
32+
; CHECK-LABEL: no_preserve_swiftself:
33+
; CHECK-NOT: popq %r13
34+
call void asm "","~{r13}"()
35+
ret void
36+
}
37+
38+
declare swifttailcc i8* @SwiftSelf(i8 * swiftasync %context, i8* swiftself %closure)
39+
define swiftcc i8* @CallSwiftSelf(i8* swiftself %closure, i8* %context) {
40+
; CHECK-LABEL: CallSwiftSelf:
41+
; CHECK: pushq %r13
42+
;call void asm "","~{r13}"() ; We get a push r13 but why not with the call
43+
; below?
44+
%res = call swifttailcc i8* @SwiftSelf(i8 * swiftasync %context, i8* swiftself %closure)
45+
ret i8* %res
46+
}

0 commit comments

Comments
 (0)