From 3d66cabfb4774347d87998ec968a8e5018d99211 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 4 Nov 2025 13:40:42 -0800 Subject: [PATCH 1/3] AArch64: correct `preserve_most` and `preserve_all` on Windows (#166436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes register information handling for the `preserve_most` and `preserve_all` calling conventions on Windows ARM64. The root issue was cascading `if` statements whose behavior depended on their order. This patch makes the minimal, tactical change needed for Swift’s two calling conventions, unblocking current work. A broader refactor to remove the ordering dependency is still desired and will follow in a subsequent PR. (cherry picked from commit 8cd22447af239206daabb42fc63e2824a1f7fb6e) --- .../AArch64/AArch64CallingConvention.td | 6 +++ .../Target/AArch64/AArch64RegisterInfo.cpp | 14 +++++-- llvm/test/CodeGen/AArch64/preserve_mostcc.ll | 38 ++++++++++++------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index f43363d17694c..301262232d0bc 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -610,6 +610,12 @@ def CSR_Win_AArch64_AAPCS_SwiftError def CSR_Win_AArch64_AAPCS_SwiftTail : CalleeSavedRegs<(sub CSR_Win_AArch64_AAPCS, X20, X22)>; +def CSR_Win_AArch64_RT_MostRegs + : CalleeSavedRegs<(add CSR_Win_AArch64_AAPCS, (sequence "X%u", 9, 15))>; + +def CSR_Win_AArch64_RT_AllRegs + : CalleeSavedRegs<(add CSR_Win_AArch64_RT_MostRegs, (sequence "Q%u", 8, 31))>; + // The Control Flow Guard check call uses a custom calling convention that also // preserves X0-X8 and Q0-Q7. def CSR_Win_AArch64_CFGuard_Check : CalleeSavedRegs<(add CSR_Win_AArch64_AAPCS, diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 77dfab83a834a..d071eec7f5458 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -89,6 +89,16 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { if (MF->getSubtarget().isTargetDarwin()) return getDarwinCalleeSavedRegs(MF); + if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost) + return MF->getSubtarget().isTargetWindows() + ? CSR_Win_AArch64_RT_MostRegs_SaveList + : CSR_AArch64_RT_MostRegs_SaveList; + + if (MF->getFunction().getCallingConv() == CallingConv::PreserveAll) + return MF->getSubtarget().isTargetWindows() + ? CSR_Win_AArch64_RT_AllRegs_SaveList + : CSR_AArch64_RT_AllRegs_SaveList; + if (MF->getFunction().getCallingConv() == CallingConv::CFGuard_Check) return CSR_Win_AArch64_CFGuard_Check_SaveList; if (MF->getSubtarget().isTargetWindows()) { @@ -140,10 +150,6 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return CSR_AArch64_AAPCS_SwiftError_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::SwiftTail) return CSR_AArch64_AAPCS_SwiftTail_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost) - return CSR_AArch64_RT_MostRegs_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::PreserveAll) - return CSR_AArch64_RT_AllRegs_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::Win64) // This is for OSes other than Windows; Windows is a separate case further // above. diff --git a/llvm/test/CodeGen/AArch64/preserve_mostcc.ll b/llvm/test/CodeGen/AArch64/preserve_mostcc.ll index 7f0968c8eb339..75c8567e2095e 100644 --- a/llvm/test/CodeGen/AArch64/preserve_mostcc.ll +++ b/llvm/test/CodeGen/AArch64/preserve_mostcc.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -mtriple=arm64-apple-ios-8.0.0 | FileCheck %s +; RUN: llc < %s -mtriple=arm64-apple-ios-8.0.0 | FileCheck -check-prefix CHECK -check-prefix CHECK-DARWIN %s +; RUN: llc < %s -mtriple=aarch64-unknown-windiws-msvc | FileCheck -check-prefix CHECK -check-prefix CHECK-WIN %s declare void @standard_cc_func() declare preserve_mostcc void @preserve_mostcc_func() @@ -8,18 +9,26 @@ declare preserve_mostcc void @preserve_mostcc_func() define preserve_mostcc void @preserve_mostcc1() nounwind { entry: ;CHECK-LABEL: preserve_mostcc1 -;CHECK-NOT: stp -;CHECK-NOT: str -;CHECK: str x15 -;CHECK-NEXT: stp x14, x13, -;CHECK-NEXT: stp x12, x11, -;CHECK-NEXT: stp x10, x9, -;CHECK: bl _standard_cc_func +;CHECK-DARWIN-NOT: stp +;CHECK-DARWIN-NOT: str +;CHECK-DARWIN: str x15 +;CHECK-DARWIN-NEXT: stp x14, x13, +;CHECK-DARWIN-NEXT: stp x12, x11, +;CHECK-DARWIN-NEXT: stp x10, x9, +;CHECK-WIN: stp x15, x14 +;CHECK-WIN-NEXT: stp x13, x12, +;CHECK-WIN-NEXT: stp x11, x10, +;CHECK-WIN-NEXT: stp x9, x30 +;CHECK: bl {{_?}}standard_cc_func call void @standard_cc_func() -;CHECK: ldp x10, x9, -;CHECK-NEXT: ldp x12, x11, -;CHECK-NEXT: ldp x14, x13, -;CHECK-NEXT: ldr x15 +;CHECK-DARWIN: ldp x10, x9, +;CHECK-DARWIN-NEXT: ldp x12, x11, +;CHECK-DARWIN-NEXT: ldp x14, x13, +;CHECK-DARWIN-NEXT: ldr x15 +;CHECK-WIN: ldp x9, x30 +;CHECK-WIN-NEXT: ldp x11, x10, +;CHECK-WIN-NEXT: ldp x13, x12, +;CHECK-WIN-NEXT: ldp x15, x14, ret void } @@ -31,9 +40,10 @@ define preserve_mostcc void @preserve_mostcc2() nounwind { entry: ;CHECK-LABEL: preserve_mostcc2 ;CHECK-NOT: x14 -;CHECK: stp x29, x30, +;CHECK-DARWIN: stp x29, x30, +;CHECK-WIN: str x30 ;CHECK-NOT: x14 -;CHECK: bl _preserve_mostcc_func +;CHECK: bl {{_?}}preserve_mostcc_func call preserve_mostcc void @preserve_mostcc_func() ret void } From 8c510743df336358674d5ee6105e26fc6ed402de Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 4 Nov 2025 23:22:47 -0800 Subject: [PATCH 2/3] test: correct typo in RUN line (#166511) Correct a typo in the triple that is used for the test. Because the OS was not recognised, it would fall to the non-Windows code generation. (cherry picked from commit 66f52ca9a09c6e265385046f36ac51a863078948) --- llvm/test/CodeGen/AArch64/preserve_mostcc.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/CodeGen/AArch64/preserve_mostcc.ll b/llvm/test/CodeGen/AArch64/preserve_mostcc.ll index 75c8567e2095e..f77ada4eae022 100644 --- a/llvm/test/CodeGen/AArch64/preserve_mostcc.ll +++ b/llvm/test/CodeGen/AArch64/preserve_mostcc.ll @@ -1,5 +1,5 @@ ; RUN: llc < %s -mtriple=arm64-apple-ios-8.0.0 | FileCheck -check-prefix CHECK -check-prefix CHECK-DARWIN %s -; RUN: llc < %s -mtriple=aarch64-unknown-windiws-msvc | FileCheck -check-prefix CHECK -check-prefix CHECK-WIN %s +; RUN: llc < %s -mtriple=aarch64-unknown-windows-msvc | FileCheck -check-prefix CHECK -check-prefix CHECK-WIN %s declare void @standard_cc_func() declare preserve_mostcc void @preserve_mostcc_func() From dcc82b53dca726317d7ab1303a2877025d2a817b Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 6 Nov 2025 22:12:27 -0800 Subject: [PATCH 3/3] AArch64: support extended spills in SEH on WoS (#166849) When lowering code for Windows, we might be using a non-standard calling convention (e.g. `preserve_most`). In such a case, we might be spilling registers which are unexpected (i.e. x9-x15). Use the ARM64EC opcodes to indicate such spills. This adds support for the handling for these spills but is insufficient on its own. The encoded results are incorrect due to the expectation that the pair wise spills are always 16-byte aligned which we currently do not enforce. Fixing that is beyond the scope of emitting the SEH directives for the spill. (cherry picked from commit 77b9301ad9043af854b93a9184962324389b8643) --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 16 +++++++++ .../Target/AArch64/AArch64FrameLowering.cpp | 32 +++++++++++++---- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 2 ++ llvm/lib/Target/AArch64/AArch64InstrInfo.td | 2 ++ .../CodeGen/AArch64/seh-extended-spills.ll | 34 +++++++++++++++++++ 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/seh-extended-spills.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 12a4c955a3a06..f06fb52ee5741 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -3406,6 +3406,22 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { TS->emitARM64WinCFIPACSignLR(); return; + case AArch64::SEH_SaveAnyRegI: + assert(MI->getOperand(1).getImm() <= 1008 && + "SaveAnyRegQP SEH opcode offset must fit into 6 bits"); + TS->emitARM64WinCFISaveAnyRegI(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveAnyRegIP: + assert(MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1 && + "Non-consecutive registers not allowed for save_any_reg"); + assert(MI->getOperand(2).getImm() <= 1008 && + "SaveAnyRegQP SEH opcode offset must fit into 6 bits"); + TS->emitARM64WinCFISaveAnyRegIP(MI->getOperand(0).getImm(), + MI->getOperand(2).getImm()); + return; + case AArch64::SEH_SaveAnyRegQP: assert(MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1 && "Non-consecutive registers not allowed for save_any_reg"); diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 66a9901cc06a9..b8a1b78c61ac5 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1407,14 +1407,24 @@ static MachineBasicBlock::iterator InsertSEH(MachineBasicBlock::iterator MBBI, case AArch64::LDPXi: { Register Reg0 = MBBI->getOperand(0).getReg(); Register Reg1 = MBBI->getOperand(1).getReg(); + + int SEHReg0 = RegInfo->getSEHRegNum(Reg0); + int SEHReg1 = RegInfo->getSEHRegNum(Reg1); + if (Reg0 == AArch64::FP && Reg1 == AArch64::LR) MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFPLR)) .addImm(Imm * 8) .setMIFlag(Flag); - else + else if (SEHReg0 >= 19 && SEHReg1 >= 19) MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveRegP)) - .addImm(RegInfo->getSEHRegNum(Reg0)) - .addImm(RegInfo->getSEHRegNum(Reg1)) + .addImm(SEHReg0) + .addImm(SEHReg1) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveAnyRegIP)) + .addImm(SEHReg0) + .addImm(SEHReg1) .addImm(Imm * 8) .setMIFlag(Flag); break; @@ -1422,10 +1432,16 @@ static MachineBasicBlock::iterator InsertSEH(MachineBasicBlock::iterator MBBI, case AArch64::STRXui: case AArch64::LDRXui: { int Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); - MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg)) - .addImm(Reg) - .addImm(Imm * 8) - .setMIFlag(Flag); + if (Reg >= 19) + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveAnyRegI)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); break; } case AArch64::STRDui: @@ -1479,6 +1495,8 @@ static void fixupSEHOpcode(MachineBasicBlock::iterator MBBI, case AArch64::SEH_SaveReg: case AArch64::SEH_SaveFRegP: case AArch64::SEH_SaveFReg: + case AArch64::SEH_SaveAnyRegI: + case AArch64::SEH_SaveAnyRegIP: case AArch64::SEH_SaveAnyRegQP: case AArch64::SEH_SaveAnyRegQPX: ImmOpnd = &MBBI->getOperand(ImmIdx); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 2d504317dd374..d346378517545 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -1174,6 +1174,8 @@ bool AArch64InstrInfo::isSEHInstruction(const MachineInstr &MI) { case AArch64::SEH_EpilogStart: case AArch64::SEH_EpilogEnd: case AArch64::SEH_PACSignLR: + case AArch64::SEH_SaveAnyRegI: + case AArch64::SEH_SaveAnyRegIP: case AArch64::SEH_SaveAnyRegQP: case AArch64::SEH_SaveAnyRegQPX: case AArch64::SEH_AllocZ: diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 43212f21a2c19..3c2944c1104f8 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -5634,6 +5634,8 @@ let isPseudo = 1 in { def SEH_EpilogStart : Pseudo<(outs), (ins), []>, Sched<[]>; def SEH_EpilogEnd : Pseudo<(outs), (ins), []>, Sched<[]>; def SEH_PACSignLR : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_SaveAnyRegI : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveAnyRegIP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; def SEH_SaveAnyRegQP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; def SEH_SaveAnyRegQPX : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; def SEH_AllocZ : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; diff --git a/llvm/test/CodeGen/AArch64/seh-extended-spills.ll b/llvm/test/CodeGen/AArch64/seh-extended-spills.ll new file mode 100644 index 0000000000000..ecc22703ef584 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/seh-extended-spills.ll @@ -0,0 +1,34 @@ +; RUN: llc -mtriple aarch64-unknown-windows-msvc -filetype asm -o - %s | FileCheck %s + +declare dso_local void @g(ptr noundef) +define dso_local preserve_mostcc void @f(ptr noundef %p) #0 { +entry: + %p.addr = alloca ptr, align 8 + store ptr %p, ptr %p.addr, align 8 + %0 = load ptr, ptr %p.addr, align 8 + call void @g(ptr noundef %0) + ret void +} + +attributes #0 = { nounwind uwtable(sync) } + +; CHECK: stp x9, x10, [sp, #[[OFFSET_0:[0-9]+]]] +; CHECK-NEXT: .seh_save_any_reg_p x9, [[OFFSET_0]] +; CHECK: stp x11, x12, [sp, #[[OFFSET_1:[0-9]+]]] +; CHECK-NEXT: .seh_save_any_reg_p x11, [[OFFSET_1]] +; CHECK: stp x13, x14, [sp, #[[OFFSET_2:[0-9]+]]] +; CHECK-NEXT: .seh_save_any_reg_p x13, [[OFFSET_2]] +; CHECK: str x15, [sp, #[[OFFSET_3:[0-9]+]]] +; CHECK-NEXT: .seh_save_any_reg x15, [[OFFSET_3]] +; CHECK: .seh_endprologue + +; CHECK: .seh_startepilogue +; CHECK: ldr x15, [sp, #[[OFFSET_3]]] +; CHECK-NEXT: .seh_save_any_reg x15, [[OFFSET_3]] +; CHECK: ldp x13, x14, [sp, #[[OFFSET_2]]] +; CHECK-NEXT: .seh_save_any_reg_p x13, [[OFFSET_2]] +; CHECK: ldp x11, x12, [sp, #[[OFFSET_1]]] +; CHECK-NEXT: .seh_save_any_reg_p x11, [[OFFSET_1]] +; CHECK: ldp x9, x10, [sp, #[[OFFSET_0]]] +; CHECK-NEXT: .seh_save_any_reg_p x9, [[OFFSET_0]] +; CHECK: .seh_endepilogue