diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index de52122ab9c82..f9c8c785ca2e9 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -4479,7 +4479,11 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( auto *AFI = MF.getInfo(); bool UsesWinAAPCS = isTargetWindows(MF); - if (UsesWinAAPCS && hasFP(MF) && AFI->hasSwiftAsyncContext()) { + // Only the WinCFI path reverses CSI above, which is what places the frame + // record at the top of the callee-save area and lets the async context slot + // be allocated directly below FP inside the loop. Without that reversal the + // slot has to be carved out up front. + if (UsesWinAAPCS && !NeedsWinCFI && hasFP(MF) && AFI->hasSwiftAsyncContext()) { int FrameIdx = MFI.CreateStackObject(8, Align(16), true); AFI->setSwiftAsyncContextFrameIdx(FrameIdx); if ((unsigned)FrameIdx < MinCSFrameIndex) @@ -4548,8 +4552,8 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( MaxCSFrameIndex = FrameIdx; // Grab 8 bytes below FP for the extended asynchronous frame info. - if (hasFP(MF) && AFI->hasSwiftAsyncContext() && !UsesWinAAPCS && - Reg == AArch64::FP) { + if (hasFP(MF) && AFI->hasSwiftAsyncContext() && + (!UsesWinAAPCS || NeedsWinCFI) && Reg == AArch64::FP) { FrameIdx = MFI.CreateStackObject(8, Alignment, true); AFI->setSwiftAsyncContextFrameIdx(FrameIdx); if ((unsigned)FrameIdx < MinCSFrameIndex) diff --git a/llvm/test/CodeGen/AArch64/swift-async-context-frame-record-win.ll b/llvm/test/CodeGen/AArch64/swift-async-context-frame-record-win.ll new file mode 100644 index 0000000000000..9b673f013c135 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/swift-async-context-frame-record-win.ll @@ -0,0 +1,103 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -mtriple=aarch64-unknown-windows-msvc -O2 < %s | FileCheck %s + +; The swift async context slot is allocated directly below the frame record. If +; it is allocated above it instead, MachineFrameInfo's view of the callee-save +; area disagrees with the prologue by 8 bytes, leaving a hole that PEI's stack +; slot scavenger hands to a live local, which then shares an address with the +; saved caller x29. +; +; The full frame layout is checked so that any object placed at or above the +; frame record shows up here as a diff: x29 is saved at sp+88, the async context +; sits below it at sp+80, and the spills land at sp+8 and sp+104. + +declare ptr @llvm.swift.async.context.addr() nounwind +declare swiftcc void @swift_task_dealloc() + +define swifttailcc void @test(ptr %ctx, ptr %vw0, ptr %vw1, ptr %vw2, ptr %vw3, ptr %obj0, ptr %obj1, ptr %obj2, ptr %obj3, ptr %obj4) { +; CHECK-LABEL: test: +; CHECK: .seh_proc test +; CHECK-NEXT: // %bb.0: // %entryresume.0 +; CHECK-NEXT: orr x29, x29, #0x1000000000000000 +; CHECK-NEXT: .seh_nop +; CHECK-NEXT: sub sp, sp, #112 +; CHECK-NEXT: .seh_stackalloc 112 +; CHECK-NEXT: str x19, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: .seh_save_reg x19, 16 +; CHECK-NEXT: str x21, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: .seh_save_reg x21, 24 +; CHECK-NEXT: stp x23, x24, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: .seh_save_regp x23, 32 +; CHECK-NEXT: stp x25, x26, [sp, #48] // 16-byte Folded Spill +; CHECK-NEXT: .seh_save_regp x25, 48 +; CHECK-NEXT: stp x27, x28, [sp, #64] // 16-byte Folded Spill +; CHECK-NEXT: .seh_save_regp x27, 64 +; CHECK-NEXT: stp x29, x30, [sp, #88] // 16-byte Folded Spill +; CHECK-NEXT: .seh_save_fplr 88 +; CHECK-NEXT: str xzr, [sp, #80] +; CHECK-NEXT: .seh_nop +; CHECK-NEXT: add x29, sp, #88 +; CHECK-NEXT: .seh_add_fp 88 +; CHECK-NEXT: .seh_endprologue +; CHECK-NEXT: str x7, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: mov x21, xzr +; CHECK-NEXT: mov x22, x6 +; CHECK-NEXT: ldp x20, x27, [x29, #24] +; CHECK-NEXT: ldr x28, [x21] +; CHECK-NEXT: str x5, [x29, #16] // 8-byte Folded Spill +; CHECK-NEXT: mov x23, x4 +; CHECK-NEXT: mov x24, x3 +; CHECK-NEXT: mov x25, x2 +; CHECK-NEXT: mov x26, x1 +; CHECK-NEXT: mov x19, x0 +; CHECK-NEXT: bl swift_task_dealloc +; CHECK-NEXT: ldr x8, [x19] +; CHECK-NEXT: mov x0, x28 +; CHECK-NEXT: mov x1, x27 +; CHECK-NEXT: blr x8 +; CHECK-NEXT: ldr x8, [x22] +; CHECK-NEXT: mov x0, x24 +; CHECK-NEXT: mov x1, xzr +; CHECK-NEXT: blr x8 +; CHECK-NEXT: ldr x8, [x20] +; CHECK-NEXT: ldr x1, [sp, #8] // 8-byte Folded Reload +; CHECK-NEXT: mov x0, x25 +; CHECK-NEXT: blr x8 +; CHECK-NEXT: ldr x2, [x23] +; CHECK-NEXT: ldr x1, [x29, #16] // 8-byte Folded Reload +; CHECK-NEXT: mov x0, x26 +; CHECK-NEXT: .seh_startepilogue +; CHECK-NEXT: ldp x29, x30, [sp, #88] // 16-byte Folded Reload +; CHECK-NEXT: .seh_save_fplr 88 +; CHECK-NEXT: ldp x27, x28, [sp, #64] // 16-byte Folded Reload +; CHECK-NEXT: .seh_save_regp x27, 64 +; CHECK-NEXT: ldp x25, x26, [sp, #48] // 16-byte Folded Reload +; CHECK-NEXT: .seh_save_regp x25, 48 +; CHECK-NEXT: ldp x23, x24, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: .seh_save_regp x23, 32 +; CHECK-NEXT: ldr x21, [sp, #24] // 8-byte Folded Reload +; CHECK-NEXT: .seh_save_reg x21, 24 +; CHECK-NEXT: ldr x19, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: .seh_save_reg x19, 16 +; CHECK-NEXT: and x29, x29, #0xefffffffffffffff +; CHECK-NEXT: .seh_nop +; CHECK-NEXT: add sp, sp, #112 +; CHECK-NEXT: .seh_stackalloc 112 +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: br x2 +; CHECK-NEXT: .seh_endfunclet +; CHECK-NEXT: .seh_endproc +entryresume.0: + %ctxaddr = tail call ptr @llvm.swift.async.context.addr() + %reloaded = load ptr, ptr null, align 8 + call swiftcc void @swift_task_dealloc() + %destroy0 = load ptr, ptr %ctx, align 8 + tail call void %destroy0(ptr %reloaded, ptr %obj4) + %destroy1 = load ptr, ptr %obj1, align 8 + tail call void %destroy1(ptr %vw2, ptr null) + %destroy2 = load ptr, ptr %obj3, align 8 + tail call void %destroy2(ptr %vw1, ptr %obj2) + %destroy3 = load ptr, ptr %vw3, align 8 + tail call void %destroy3(ptr %vw0, ptr %obj0) + ret void +} diff --git a/llvm/test/CodeGen/AArch64/swift-async-context-seh.ll b/llvm/test/CodeGen/AArch64/swift-async-context-seh.ll index 852c97743a10c..0d1cd593f4729 100644 --- a/llvm/test/CodeGen/AArch64/swift-async-context-seh.ll +++ b/llvm/test/CodeGen/AArch64/swift-async-context-seh.ll @@ -8,7 +8,7 @@ ; CHECK: orr x29, x29, #0x1000000000000000 ; CHECK-NEXT: .seh_nop -; CHECK: str x22, [sp, #16] +; CHECK: str x22, [sp] ; CHECK-NEXT: .seh_nop ; CHECK: and x29, x29, #0xefffffffffffffff ; CHECK-NEXT: .seh_nop diff --git a/llvm/test/CodeGen/AArch64/swift-async-context-slot-offset-win.ll b/llvm/test/CodeGen/AArch64/swift-async-context-slot-offset-win.ll index 86e459a4af717..4e9d0d6f65d6b 100644 --- a/llvm/test/CodeGen/AArch64/swift-async-context-slot-offset-win.ll +++ b/llvm/test/CodeGen/AArch64/swift-async-context-slot-offset-win.ll @@ -5,13 +5,16 @@ ; saving it won't overwrite the saved value of the callee-saved ; register. ; -; CHECK: sub sp, sp, #64 -; CHECK: str x19, [sp, #16] -; CHECK: str x21, [sp, #24] -; CHECK-NOT: stp x29, x30, [sp, #32] -; CHECK: stp x29, x30, [sp, #40] -; CHECK-NOT: str x22, [sp, #24] -; CHECK: str x22, [sp, #32] +; The async context slot sits directly below the frame record, so the callee +; saves below it stay clear of both. +; +; CHECK: str x19, [sp, #-48]! +; CHECK: str x21, [sp, #8] +; CHECK-NOT: stp x29, x30, [sp, #16] +; CHECK: stp x29, x30, [sp, #24] +; CHECK-NOT: str x22, [sp, #8] +; CHECK: str x22, [sp, #16] +; CHECK: add x29, sp, #24 declare ptr @llvm.swift.async.context.addr() declare swiftcc i64 @foo(i64 %0, i64 %1)