[llvm] [Arm64] Make tail calls work properly for Arm64 Windows (PR #230429)

Saleem Abdulrasool via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 9 20:53:32 PDT 2026


================
@@ -0,0 +1,583 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=aarch64-windows -verify-machineinstrs | FileCheck %s --check-prefix=HINT
+; RUN: llc < %s -mtriple=aarch64-windows -mattr=+pauth -verify-machineinstrs | FileCheck %s --check-prefix=PAUTH
+; RUN: llc < %s -mtriple=aarch64-windows -filetype=obj -o %t.obj
+; RUN: llvm-readobj --unwind %t.obj | FileCheck %s --check-prefix=UNWIND
+
+; When a tail call, or a return from a function with callee-popped stack
+; arguments, changes the stack argument size, the epilogue doesn't leave SP at
+; its value on entry. The Windows unwinder applies the pac_sign_lr unwind code
+; using whatever SP it has at that point, which wouldn't be the SP that the
+; return address was signed with, so the return address is authenticated before
+; the epilogue starts, where the prologue's unwind codes still apply, and the
+; authenticated return address stays in LR rather than being restored by
+; the epilogue. Its unwind codes then have no pac_sign_lr.
+
+declare swifttailcc void @callee12(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
+declare swifttailcc void @callee1(i64)
+declare void @use(ptr)
+declare void @clobber()
+
+define swifttailcc void @pac_grow(i64 %a) "sign-return-address"="all" {
+;
+; HINT-LABEL: pac_grow:
+; HINT:       .seh_proc pac_grow
+; HINT-NEXT:  // %bb.0:
+; HINT-NEXT:    hint #27
+; HINT-NEXT:    .seh_pac_sign_lr
+; HINT-NEXT:    str x30, [sp, #-48]! // 8-byte Folded Spill
+; HINT-NEXT:    .seh_save_reg_x x30, 48
+; HINT-NEXT:    .seh_endprologue
+; HINT-NEXT:    mov x20, x0
+; HINT-NEXT:    bl clobber
+; HINT-NEXT:    ldr x17, [sp] // 8-byte Reload
+; HINT-NEXT:    stp x20, x20, [sp, #32]
+; HINT-NEXT:    mov x0, x20
+; HINT-NEXT:    stp x20, x20, [sp, #16]
+; HINT-NEXT:    mov x1, x20
+; HINT-NEXT:    mov x2, x20
+; HINT-NEXT:    mov x3, x20
+; HINT-NEXT:    mov x4, x20
+; HINT-NEXT:    mov x5, x20
+; HINT-NEXT:    mov x6, x20
+; HINT-NEXT:    mov x7, x20
+; HINT-NEXT:    add x16, sp, #48
+; HINT-NEXT:    hint #14
+; HINT-NEXT:    mov x30, x17
+; HINT-NEXT:    .seh_startepilogue
+; HINT-NEXT:    add sp, sp, #16
+; HINT-NEXT:    .seh_stackalloc 16
+; HINT-NEXT:    .seh_endepilogue
+; HINT-NEXT:    b callee12
+; HINT-NEXT:    .seh_endfunclet
+; HINT-NEXT:    .seh_endproc
+;
+; PAUTH-LABEL: pac_grow:
+; PAUTH:       .seh_proc pac_grow
+; PAUTH-NEXT:  // %bb.0:
+; PAUTH-NEXT:    pacibsp
+; PAUTH-NEXT:    .seh_pac_sign_lr
+; PAUTH-NEXT:    str x30, [sp, #-48]! // 8-byte Folded Spill
+; PAUTH-NEXT:    .seh_save_reg_x x30, 48
+; PAUTH-NEXT:    .seh_endprologue
+; PAUTH-NEXT:    mov x20, x0
+; PAUTH-NEXT:    bl clobber
+; PAUTH-NEXT:    ldr x30, [sp] // 8-byte Reload
+; PAUTH-NEXT:    stp x20, x20, [sp, #32]
+; PAUTH-NEXT:    mov x0, x20
+; PAUTH-NEXT:    stp x20, x20, [sp, #16]
+; PAUTH-NEXT:    mov x1, x20
+; PAUTH-NEXT:    mov x2, x20
+; PAUTH-NEXT:    mov x3, x20
+; PAUTH-NEXT:    mov x4, x20
+; PAUTH-NEXT:    mov x5, x20
+; PAUTH-NEXT:    mov x6, x20
+; PAUTH-NEXT:    mov x7, x20
+; PAUTH-NEXT:    add x16, sp, #48
+; PAUTH-NEXT:    autib x30, x16
+; PAUTH-NEXT:    .seh_startepilogue
+; PAUTH-NEXT:    add sp, sp, #16
+; PAUTH-NEXT:    .seh_stackalloc 16
+; PAUTH-NEXT:    .seh_endepilogue
+; PAUTH-NEXT:    b callee12
+; PAUTH-NEXT:    .seh_endfunclet
+; PAUTH-NEXT:    .seh_endproc
----------------
compnerd wrote:

I'm not convinced that the full instruction matching helps understand the checked invariants. Simplifying the checks would help.

https://github.com/llvm/llvm-project/pull/230429


More information about the llvm-commits mailing list