https://github.com/atrosinenko created https://github.com/llvm/llvm-project/pull/220191
With shrink-wrapping, it is possible for PAUTH_EPILOGUE to be inserted in the middle of the function where X15/X16/X17 may be alive and must not be clobbered. This commit implements ad-hoc spilling of the scratch registers used by PAUTH_EPILOGUE to other GPRs. If no such registers is available at the insertion point, an explicit compiler error is triggered. >From 5205dc55cb8844e602104f83b2af4e6ceef056f7 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Mon, 27 Jul 2026 21:43:29 +0300 Subject: [PATCH] [AArch64][PAC] Prevent PAUTH_EPILOGUE from overwriting live registers With shrink-wrapping, it is possible for PAUTH_EPILOGUE to be inserted in the middle of the function where X15/X16/X17 may be alive and must not be clobbered. This commit implements ad-hoc spilling of the scratch registers used by PAUTH_EPILOGUE to other GPRs. If no such registers is available at the insertion point, an explicit compiler error is triggered. --- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 77 ++- .../sign-return-address-epilogue-regs.ll | 541 ++++++++++++++++++ 2 files changed, 609 insertions(+), 9 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/sign-return-address-epilogue-regs.ll diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 92b4d21454048..bcff007a00a90 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -11771,23 +11771,82 @@ unsigned llvm::getBLRCallOpcode(const MachineFunction &MF) { void AArch64InstrInfo::createPauthEpilogueInstr(MachineBasicBlock &MBB, DebugLoc DL) const { MachineBasicBlock::iterator InsertPt = MBB.getFirstTerminator(); - auto Builder = BuildMI(MBB, InsertPt, DL, get(AArch64::PAUTH_EPILOGUE)) - .setMIFlag(MachineInstr::FrameDestroy); - MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); const auto *AFI = MF.getInfo<AArch64FunctionInfo>(); auto &AFL = *static_cast<const AArch64FrameLowering *>( MF.getSubtarget().getFrameLowering()); + + SmallVector<Register, 3> ImplicitDefs; if (AFL.getArgumentStackToRestore(MF, MBB)) { - Builder.addReg(AArch64::X17, RegState::ImplicitDefine); - Builder.addReg(AArch64::X16, RegState::ImplicitDefine); + ImplicitDefs.push_back(AArch64::X17); + ImplicitDefs.push_back(AArch64::X16); if (AFI->branchProtectionPAuthLR()) - Builder.addReg(AArch64::X15, RegState::ImplicitDefine); - return; + ImplicitDefs.push_back(AArch64::X15); + } else if (AFI->branchProtectionPAuthLR() && !Subtarget.hasPAuthLR()) { + ImplicitDefs.push_back(AArch64::X16); + } + + // If the scratch registers we plan using are alive at this point, + // try spilling them to other registers. + + assert(MF.getProperties().hasTracksLiveness()); + LivePhysRegs LiveRegs(TRI); + LiveRegs.addLiveOuts(MBB); + for (auto &MI : llvm::reverse(llvm::make_range(InsertPt, MBB.end()))) + LiveRegs.stepBackward(MI); + + auto FindAvailableRegister = [&LiveRegs, &MRI]() { + for (Register Reg : AArch64::GPR64RegClass) { + if (LiveRegs.available(MRI, Reg)) + return Reg; + } + reportFatalUsageError("Cannot insert PAUTH_EPILOGUE: ran out of registers"); + }; + + // Find out which scratch registers have to be spilled to other GPRs, + // mark the rest as unavailable to be spilled-to. + SmallVector<std::pair<Register, Register>, 3> Spills; + for (Register ScratchReg : ImplicitDefs) { + // With Speculative Load Hardening, X16 is reported as reserved by MRI, + // but a fallback to DSB+ISB is actually supported as long as implicit-defs + // are set appropriately. + if (ScratchReg == AArch64::X16 && + MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening)) + continue; + + assert(!MRI.isReserved(ScratchReg)); + + if (LiveRegs.available(MRI, ScratchReg)) + LiveRegs.addReg(ScratchReg); + else + Spills.emplace_back(ScratchReg, AArch64::NoRegister); } - if (AFI->branchProtectionPAuthLR() && !Subtarget.hasPAuthLR()) - Builder.addReg(AArch64::X16, RegState::ImplicitDefine); + for (auto &[ScratchReg, SpillReg] : Spills) { + (void)ScratchReg; + SpillReg = FindAvailableRegister(); + LiveRegs.addReg(SpillReg); + } + + auto EmitMOV = [&](Register DstReg, Register SrcReg) { + BuildMI(MBB, InsertPt, DL, get(AArch64::ORRXrs), DstReg) + .addReg(AArch64::XZR) + .addReg(SrcReg) + .addImm(0) + .setMIFlag(MachineInstr::FrameDestroy); + }; + + for (auto [ScratchReg, SpillReg] : Spills) + EmitMOV(SpillReg, ScratchReg); + + auto Builder = BuildMI(MBB, InsertPt, DL, get(AArch64::PAUTH_EPILOGUE)) + .setMIFlag(MachineInstr::FrameDestroy); + for (auto ScratchReg : ImplicitDefs) + Builder.addReg(ScratchReg, RegState::ImplicitDefine); + + for (auto [ScratchReg, SpillReg] : llvm::reverse(Spills)) + EmitMOV(ScratchReg, SpillReg); } MachineBasicBlock::iterator diff --git a/llvm/test/CodeGen/AArch64/sign-return-address-epilogue-regs.ll b/llvm/test/CodeGen/AArch64/sign-return-address-epilogue-regs.ll new file mode 100644 index 0000000000000..a389219e06dae --- /dev/null +++ b/llvm/test/CodeGen/AArch64/sign-return-address-epilogue-regs.ll @@ -0,0 +1,541 @@ +; RUN: rm -rf %t && split-file %s %t + +;--- ok.ll + +; RUN: llc -mtriple=aarch64 -asm-verbose=0 < %t/ok.ll | FileCheck --check-prefixes=COMPAT %s +; RUN: llc -mtriple=aarch64 -asm-verbose=0 -mattr=v8.3a < %t/ok.ll | FileCheck --check-prefixes=V83A %s +; RUN: llc -mtriple=aarch64 -asm-verbose=0 -mattr=v9a -mattr=pauth-lr < %t/ok.ll | FileCheck --check-prefixes=PAUTHLR %s + +define i64 @test_x16_available(i64 %arg) "branch-protection-pauth-lr" "sign-return-address"="non-leaf" { +; COMPAT-LABEL: test_x16_available: +; COMPAT: cbz x0, .LBB0_2 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: .Ltmp0: +; COMPAT-NEXT: hint #25 +; COMPAT-NEXT: .cfi_set_ra_state 2, .Ltmp0 +; COMPAT-NEXT: str x30, [sp, #-16]! +; COMPAT-NEXT: .cfi_def_cfa_offset 16 +; COMPAT-NEXT: .cfi_offset w30, -16 +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x30, #12345 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ldr x30, [sp], #16 +; COMPAT-NEXT: adrp x16, .Ltmp0 +; COMPAT-NEXT: add x16, x16, :lo12:.Ltmp0 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: hint #29 +; COMPAT-NEXT: .LBB0_2: +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x0, #42 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ret +; +; V83A-LABEL: test_x16_available: +; V83A: cbz x0, .LBB0_2 +; V83A-NEXT: hint #39 +; V83A-NEXT: .Ltmp0: +; V83A-NEXT: paciasp +; V83A-NEXT: .cfi_set_ra_state 2, .Ltmp0 +; V83A-NEXT: str x30, [sp, #-16]! +; V83A-NEXT: .cfi_def_cfa_offset 16 +; V83A-NEXT: .cfi_offset w30, -16 +; V83A-NEXT: //APP +; V83A-NEXT: mov x30, #12345 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ldr x30, [sp], #16 +; V83A-NEXT: adrp x16, .Ltmp0 +; V83A-NEXT: add x16, x16, :lo12:.Ltmp0 +; V83A-NEXT: hint #39 +; V83A-NEXT: autiasp +; V83A-NEXT: .LBB0_2: +; V83A-NEXT: //APP +; V83A-NEXT: mov x0, #42 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ret +; +; PAUTHLR-LABEL: test_x16_available: +; PAUTHLR: cbz x0, .LBB0_2 +; PAUTHLR-NEXT: .Ltmp0: +; PAUTHLR-NEXT: paciasppc +; PAUTHLR-NEXT: .cfi_set_ra_state 2, .Ltmp0 +; PAUTHLR-NEXT: str x30, [sp, #-16]! +; PAUTHLR-NEXT: .cfi_def_cfa_offset 16 +; PAUTHLR-NEXT: .cfi_offset w30, -16 +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x30, #12345 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ldr x30, [sp], #16 +; PAUTHLR-NEXT: autiasppc .Ltmp0 +; PAUTHLR-NEXT: .LBB0_2: +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x0, #42 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ret + %cond = icmp eq i64 %arg, 0 + br i1 %cond, label %if.end, label %if.then + +if.then: + tail call void asm sideeffect "mov x30, 12345", "~{lr}"() + br label %if.end + +if.end: + %result = tail call i64 asm sideeffect "mov $0, 42", "=r"() + ret i64 %result +} + +; Make sure the scratch registers that are alive at the insertion point of +; PAUTH_EPILOGUE are spilled to the free registers. +; +; The LLVM IR below roughly corresponds to the following C source: +; +; void callee(); +; int64_t test(int64_t arg) { +; register int64_t live_x16 asm("x16"); +; register int64_t live_x17 asm("x17"); +; // Put some values into x16 and x17. +; asm volatile("mov %0, 42\n\tmov %1, 123" : "=r"(live_x16), "=r"(live_x17)); +; +; // Force conditional call frame creation. +; // Check that the shrink-wrapped epilogue keeps the contents of x16 and x17 +; // intact if these registers are alive. +; if (arg) { +; // --- prologue goes here --- +; asm volatile("" : : : "lr"); +; // --- epilogue goes here --- +; } +; +; // Use the values stored in x16 and x17 at the beginning of the function. +; int64_t res; +; asm volatile("add %0, %1, %2" : "=r"(res) : "r"(live_x16), "r"(live_x17)); +; return res; +; } +; +; If FEAT_PAuth_LR is not available, PAUTH_EPILOGUE has to use X16 as a scratch +; register, thus X16 has to be spilled to another GPR. On the other hand, X17 +; is known not to be clobbered by PAUTH_EPILOGUE as SP adjustment is not needed, +; thus spilling X17 is not required. + +define i64 @test_x16_alive(i64 %arg) "branch-protection-pauth-lr" "sign-return-address"="non-leaf" { +; COMPAT-LABEL: test_x16_alive: +; COMPAT: //APP +; COMPAT-NEXT: mov x16, #42 +; COMPAT-NEXT: mov x17, #123 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: cbz x0, .LBB1_2 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: .Ltmp1: +; COMPAT-NEXT: hint #25 +; COMPAT-NEXT: .cfi_set_ra_state 2, .Ltmp1 +; COMPAT-NEXT: str x30, [sp, #-16]! +; COMPAT-NEXT: .cfi_def_cfa_offset 16 +; COMPAT-NEXT: .cfi_offset w30, -16 +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x30, #12345 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ldr x30, [sp], #16 +; COMPAT-NEXT: mov x0, x16 +; COMPAT-NEXT: adrp x16, .Ltmp1 +; COMPAT-NEXT: add x16, x16, :lo12:.Ltmp1 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: hint #29 +; COMPAT-NEXT: mov x16, x0 +; COMPAT-NEXT: .LBB1_2: +; COMPAT-NEXT: //APP +; COMPAT-NEXT: add x0, x16, x17 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ret +; +; V83A-LABEL: test_x16_alive: +; V83A: //APP +; V83A-NEXT: mov x16, #42 +; V83A-NEXT: mov x17, #123 +; V83A-NEXT: //NO_APP +; V83A-NEXT: cbz x0, .LBB1_2 +; V83A-NEXT: hint #39 +; V83A-NEXT: .Ltmp1: +; V83A-NEXT: paciasp +; V83A-NEXT: .cfi_set_ra_state 2, .Ltmp1 +; V83A-NEXT: str x30, [sp, #-16]! +; V83A-NEXT: .cfi_def_cfa_offset 16 +; V83A-NEXT: .cfi_offset w30, -16 +; V83A-NEXT: //APP +; V83A-NEXT: mov x30, #12345 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ldr x30, [sp], #16 +; V83A-NEXT: mov x0, x16 +; V83A-NEXT: adrp x16, .Ltmp1 +; V83A-NEXT: add x16, x16, :lo12:.Ltmp1 +; V83A-NEXT: hint #39 +; V83A-NEXT: autiasp +; V83A-NEXT: mov x16, x0 +; V83A-NEXT: .LBB1_2: +; V83A-NEXT: //APP +; V83A-NEXT: add x0, x16, x17 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ret +; +; PAUTHLR-LABEL: test_x16_alive: +; PAUTHLR: //APP +; PAUTHLR-NEXT: mov x16, #42 +; PAUTHLR-NEXT: mov x17, #123 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: cbz x0, .LBB1_2 +; PAUTHLR-NEXT: .Ltmp1: +; PAUTHLR-NEXT: paciasppc +; PAUTHLR-NEXT: .cfi_set_ra_state 2, .Ltmp1 +; PAUTHLR-NEXT: str x30, [sp, #-16]! +; PAUTHLR-NEXT: .cfi_def_cfa_offset 16 +; PAUTHLR-NEXT: .cfi_offset w30, -16 +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x30, #12345 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ldr x30, [sp], #16 +; PAUTHLR-NEXT: autiasppc .Ltmp1 +; PAUTHLR-NEXT: .LBB1_2: +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: add x0, x16, x17 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ret + %regs = tail call { i64, i64 } asm sideeffect "mov $0, 42\0A\09mov $1, 123", "={x16},={x17}"() + %live_x16 = extractvalue { i64, i64 } %regs, 0 + %live_x17 = extractvalue { i64, i64 } %regs, 1 + %cond = icmp eq i64 %arg, 0 + br i1 %cond, label %if.end, label %if.then + +if.then: + tail call void asm sideeffect "mov x30, 12345", "~{lr}"() + br label %if.end + +if.end: + %result = tail call i64 asm sideeffect "add $0, $1, $2", "=r,{x16},{x17}"(i64 %live_x16, i64 %live_x17) + ret i64 %result +} + +define i32 @test_w16_alive(i64 %arg) "branch-protection-pauth-lr" "sign-return-address"="non-leaf" { +; COMPAT-LABEL: test_w16_alive: +; COMPAT: //APP +; COMPAT-NEXT: mov w16, #42 +; COMPAT-NEXT: mov w17, #123 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: cbz x0, .LBB2_2 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: .Ltmp2: +; COMPAT-NEXT: hint #25 +; COMPAT-NEXT: .cfi_set_ra_state 2, .Ltmp2 +; COMPAT-NEXT: str x30, [sp, #-16]! +; COMPAT-NEXT: .cfi_def_cfa_offset 16 +; COMPAT-NEXT: .cfi_offset w30, -16 +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x30, #12345 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ldr x30, [sp], #16 +; COMPAT-NEXT: mov x0, x16 +; COMPAT-NEXT: adrp x16, .Ltmp2 +; COMPAT-NEXT: add x16, x16, :lo12:.Ltmp2 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: hint #29 +; COMPAT-NEXT: mov x16, x0 +; COMPAT-NEXT: .LBB2_2: +; COMPAT-NEXT: //APP +; COMPAT-NEXT: add w0, w16, w17 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ret +; +; V83A-LABEL: test_w16_alive: +; V83A: //APP +; V83A-NEXT: mov w16, #42 +; V83A-NEXT: mov w17, #123 +; V83A-NEXT: //NO_APP +; V83A-NEXT: cbz x0, .LBB2_2 +; V83A-NEXT: hint #39 +; V83A-NEXT: .Ltmp2: +; V83A-NEXT: paciasp +; V83A-NEXT: .cfi_set_ra_state 2, .Ltmp2 +; V83A-NEXT: str x30, [sp, #-16]! +; V83A-NEXT: .cfi_def_cfa_offset 16 +; V83A-NEXT: .cfi_offset w30, -16 +; V83A-NEXT: //APP +; V83A-NEXT: mov x30, #12345 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ldr x30, [sp], #16 +; V83A-NEXT: mov x0, x16 +; V83A-NEXT: adrp x16, .Ltmp2 +; V83A-NEXT: add x16, x16, :lo12:.Ltmp2 +; V83A-NEXT: hint #39 +; V83A-NEXT: autiasp +; V83A-NEXT: mov x16, x0 +; V83A-NEXT: .LBB2_2: +; V83A-NEXT: //APP +; V83A-NEXT: add w0, w16, w17 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ret +; +; PAUTHLR-LABEL: test_w16_alive: +; PAUTHLR: //APP +; PAUTHLR-NEXT: mov w16, #42 +; PAUTHLR-NEXT: mov w17, #123 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: cbz x0, .LBB2_2 +; PAUTHLR-NEXT: .Ltmp2: +; PAUTHLR-NEXT: paciasppc +; PAUTHLR-NEXT: .cfi_set_ra_state 2, .Ltmp2 +; PAUTHLR-NEXT: str x30, [sp, #-16]! +; PAUTHLR-NEXT: .cfi_def_cfa_offset 16 +; PAUTHLR-NEXT: .cfi_offset w30, -16 +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x30, #12345 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ldr x30, [sp], #16 +; PAUTHLR-NEXT: autiasppc .Ltmp2 +; PAUTHLR-NEXT: .LBB2_2: +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: add w0, w16, w17 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ret + %regs = tail call { i32, i32 } asm sideeffect "mov ${0:w}, 42\0A\09mov ${1:w}, 123", "={w16},={w17}"() + %live_w16 = extractvalue { i32, i32 } %regs, 0 + %live_w17 = extractvalue { i32, i32 } %regs, 1 + %cond = icmp eq i64 %arg, 0 + br i1 %cond, label %if.end, label %if.then + +if.then: + tail call void asm sideeffect "mov x30, 12345", "~{lr}"() + br label %if.end + +if.end: + %result = tail call i32 asm sideeffect "add ${0:w}, ${1:w}, ${2:w}", "=r,{w16},{w17}"(i32 %live_w16, i32 %live_w17) + ret i32 %result +} + +; Request multiple scratch registers to check that both implicit-defs and "spill +; slot" registers are marked as used appropriately when searching for available +; registers. +; +; SwiftTail has the same set of CSRs as the C calling convention except for +; X20 and X22. At the insertion point of PAUTH_EPILOGUE, +; * keep X15 available to be used as a scratch register +; * make X16 and X17 require spilling to other registers +; (same as in @test_no_clobber_x16) +; * make sure X0-X14 and X18 are alive, so that they cannot be spilled-to +; +; This way, X20 and X22 are the only available registers left. +; See also @test_multiple_scratch_regs_fail. + +%large_struct = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } + +%regs_t = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } + +define swifttailcc i64 @test_multiple_scratch_regs(i64 %arg, %large_struct %s) "branch-protection-pauth-lr" "sign-return-address"="non-leaf" { +; COMPAT-LABEL: test_multiple_scratch_regs: +; COMPAT: mov x15, x0 +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x16, #42 +; COMPAT-NEXT: mov x17, #123 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: //APP +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: cbz x15, .LBB3_2 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: .Ltmp3: +; COMPAT-NEXT: hint #25 +; COMPAT-NEXT: .cfi_set_ra_state 2, .Ltmp3 +; COMPAT-NEXT: sub sp, sp, #80 +; COMPAT-NEXT: str x30, [sp, #64] +; COMPAT-NEXT: .cfi_def_cfa_offset 80 +; COMPAT-NEXT: .cfi_offset w30, -16 +; COMPAT-NEXT: //APP +; COMPAT-NEXT: mov x30, #12345 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ldr x30, [sp, #64] +; COMPAT-NEXT: mov x20, x17 +; COMPAT-NEXT: mov x22, x16 +; COMPAT-NEXT: add x16, sp, #80 +; COMPAT-NEXT: mov x17, x30 +; COMPAT-NEXT: adrp x15, .Ltmp3 +; COMPAT-NEXT: add x15, x15, :lo12:.Ltmp3 +; COMPAT-NEXT: hint #39 +; COMPAT-NEXT: hint #12 +; COMPAT-NEXT: mov x30, x17 +; COMPAT-NEXT: add sp, sp, #160 +; COMPAT-NEXT: mov x16, x22 +; COMPAT-NEXT: mov x17, x20 +; COMPAT-NEXT: .LBB3_2: +; COMPAT-NEXT: //APP +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: //APP +; COMPAT-NEXT: add x0, x16, x17 +; COMPAT-NEXT: //NO_APP +; COMPAT-NEXT: ret +; +; V83A-LABEL: test_multiple_scratch_regs: +; V83A: mov x15, x0 +; V83A-NEXT: //APP +; V83A-NEXT: mov x16, #42 +; V83A-NEXT: mov x17, #123 +; V83A-NEXT: //NO_APP +; V83A-NEXT: //APP +; V83A-NEXT: //NO_APP +; V83A-NEXT: cbz x15, .LBB3_2 +; V83A-NEXT: hint #39 +; V83A-NEXT: .Ltmp3: +; V83A-NEXT: paciasp +; V83A-NEXT: .cfi_set_ra_state 2, .Ltmp3 +; V83A-NEXT: sub sp, sp, #80 +; V83A-NEXT: str x30, [sp, #64] +; V83A-NEXT: .cfi_def_cfa_offset 80 +; V83A-NEXT: .cfi_offset w30, -16 +; V83A-NEXT: //APP +; V83A-NEXT: mov x30, #12345 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ldr x30, [sp, #64] +; V83A-NEXT: mov x20, x17 +; V83A-NEXT: mov x22, x16 +; V83A-NEXT: add x16, sp, #80 +; V83A-NEXT: mov x17, x30 +; V83A-NEXT: adrp x15, .Ltmp3 +; V83A-NEXT: add x15, x15, :lo12:.Ltmp3 +; V83A-NEXT: hint #39 +; V83A-NEXT: autia1716 +; V83A-NEXT: mov x30, x17 +; V83A-NEXT: add sp, sp, #160 +; V83A-NEXT: mov x16, x22 +; V83A-NEXT: mov x17, x20 +; V83A-NEXT: .LBB3_2: +; V83A-NEXT: //APP +; V83A-NEXT: //NO_APP +; V83A-NEXT: //APP +; V83A-NEXT: add x0, x16, x17 +; V83A-NEXT: //NO_APP +; V83A-NEXT: ret +; +; PAUTHLR-LABEL: test_multiple_scratch_regs: +; PAUTHLR: mov x15, x0 +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x16, #42 +; PAUTHLR-NEXT: mov x17, #123 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: cbz x15, .LBB3_2 +; PAUTHLR-NEXT: .Ltmp3: +; PAUTHLR-NEXT: paciasppc +; PAUTHLR-NEXT: .cfi_set_ra_state 2, .Ltmp3 +; PAUTHLR-NEXT: sub sp, sp, #80 +; PAUTHLR-NEXT: str x30, [sp, #64] +; PAUTHLR-NEXT: .cfi_def_cfa_offset 80 +; PAUTHLR-NEXT: .cfi_offset w30, -16 +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: mov x30, #12345 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ldr x30, [sp, #64] +; PAUTHLR-NEXT: mov x20, x17 +; PAUTHLR-NEXT: mov x22, x16 +; PAUTHLR-NEXT: add x16, sp, #80 +; PAUTHLR-NEXT: mov x17, x30 +; PAUTHLR-NEXT: adrp x15, .Ltmp3 +; PAUTHLR-NEXT: add x15, x15, :lo12:.Ltmp3 +; PAUTHLR-NEXT: autia171615 +; PAUTHLR-NEXT: mov x30, x17 +; PAUTHLR-NEXT: add sp, sp, #160 +; PAUTHLR-NEXT: mov x16, x22 +; PAUTHLR-NEXT: mov x17, x20 +; PAUTHLR-NEXT: .LBB3_2: +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: //APP +; PAUTHLR-NEXT: add x0, x16, x17 +; PAUTHLR-NEXT: //NO_APP +; PAUTHLR-NEXT: ret + %local = alloca [64 x i8], align 8 + %regs = tail call { i64, i64 } asm sideeffect "mov $0, 42\0A\09mov $1, 123", "={x16},={x17}"() + %live_x16 = extractvalue { i64, i64 } %regs, 0 + %live_x17 = extractvalue { i64, i64 } %regs, 1 + + %other.regs.def = tail call %regs_t asm sideeffect "", "={x0},={x1},={x2},={x3},={x4},={x5},={x6},={x7},={x8},={x9},={x10},={x11},={x12},={x13},={x14},={x18}"() + %live_x0 = extractvalue %regs_t %other.regs.def, 0 + %live_x1 = extractvalue %regs_t %other.regs.def, 1 + %live_x2 = extractvalue %regs_t %other.regs.def, 2 + %live_x3 = extractvalue %regs_t %other.regs.def, 3 + %live_x4 = extractvalue %regs_t %other.regs.def, 4 + %live_x5 = extractvalue %regs_t %other.regs.def, 5 + %live_x6 = extractvalue %regs_t %other.regs.def, 6 + %live_x7 = extractvalue %regs_t %other.regs.def, 7 + %live_x8 = extractvalue %regs_t %other.regs.def, 8 + %live_x9 = extractvalue %regs_t %other.regs.def, 9 + %live_x10 = extractvalue %regs_t %other.regs.def, 10 + %live_x11 = extractvalue %regs_t %other.regs.def, 11 + %live_x12 = extractvalue %regs_t %other.regs.def, 12 + %live_x13 = extractvalue %regs_t %other.regs.def, 13 + %live_x14 = extractvalue %regs_t %other.regs.def, 14 + + %live_x18 = extractvalue %regs_t %other.regs.def, 15 + + %cond = icmp eq i64 %arg, 0 + br i1 %cond, label %if.end, label %if.then + +if.then: + call void @llvm.lifetime.start.p0(i64 64, ptr %local) + tail call void asm sideeffect "mov x30, 12345", "~{lr}"() + call void @llvm.lifetime.end.p0(i64 64, ptr %local) + br label %if.end + +if.end: + tail call void asm sideeffect "", "{x0},{x1},{x2},{x3},{x4},{x5},{x6},{x7},{x8},{x9},{x10},{x11},{x12},{x13},{x14},{x18}" (i64 %live_x0, i64 %live_x1, i64 %live_x2, i64 %live_x3, i64 %live_x4, i64 %live_x5, i64 %live_x6, i64 %live_x7, i64 %live_x8, i64 %live_x9, i64 %live_x10, i64 %live_x11, i64 %live_x12, i64 %live_x13, i64 %live_x14, i64 %live_x18) + %result = tail call i64 asm sideeffect "add $0, $1, $2", "=r,{x16},{x17}"(i64 %live_x16, i64 %live_x17) + ret i64 %result +} + +;--- not-enough-regs.ll + +; RUN: not llc -mtriple=aarch64 -asm-verbose=0 < %t/not-enough-regs.ll 2>&1 | FileCheck --check-prefixes=NOT-ENOUGH-REGS %s +; RUN: not llc -mtriple=aarch64 -asm-verbose=0 -mattr=v8.3a < %t/not-enough-regs.ll 2>&1 | FileCheck --check-prefixes=NOT-ENOUGH-REGS %s + +; Compared to @test_multiple_scratch_regs, this test case keeps one more register +; alive (X20) at the insertion point of PAUTH_EPILOGUE. + +; NOT-ENOUGH-REGS: LLVM ERROR: Cannot insert PAUTH_EPILOGUE: ran out of registers + +%large_struct = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } + +%regs_t = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 } + +define swifttailcc i64 @test_multiple_scratch_regs_fail(i64 %arg, %large_struct %s) "branch-protection-pauth-lr" "sign-return-address"="non-leaf" { + %local = alloca [64 x i8], align 8 + %regs = tail call { i64, i64 } asm sideeffect "mov $0, 42\0A\09mov $1, 123", "={x16},={x17}"() + %live_x16 = extractvalue { i64, i64 } %regs, 0 + %live_x17 = extractvalue { i64, i64 } %regs, 1 + + %other.regs.def = tail call %regs_t asm sideeffect "", "={x0},={x1},={x2},={x3},={x4},={x5},={x6},={x7},={x8},={x9},={x10},={x11},={x12},={x13},={x14},={x18},={x20}"() + %live_x0 = extractvalue %regs_t %other.regs.def, 0 + %live_x1 = extractvalue %regs_t %other.regs.def, 1 + %live_x2 = extractvalue %regs_t %other.regs.def, 2 + %live_x3 = extractvalue %regs_t %other.regs.def, 3 + %live_x4 = extractvalue %regs_t %other.regs.def, 4 + %live_x5 = extractvalue %regs_t %other.regs.def, 5 + %live_x6 = extractvalue %regs_t %other.regs.def, 6 + %live_x7 = extractvalue %regs_t %other.regs.def, 7 + %live_x8 = extractvalue %regs_t %other.regs.def, 8 + %live_x9 = extractvalue %regs_t %other.regs.def, 9 + %live_x10 = extractvalue %regs_t %other.regs.def, 10 + %live_x11 = extractvalue %regs_t %other.regs.def, 11 + %live_x12 = extractvalue %regs_t %other.regs.def, 12 + %live_x13 = extractvalue %regs_t %other.regs.def, 13 + %live_x14 = extractvalue %regs_t %other.regs.def, 14 + + %live_x18 = extractvalue %regs_t %other.regs.def, 15 + %live_x20 = extractvalue %regs_t %other.regs.def, 16 + + %cond = icmp eq i64 %arg, 0 + br i1 %cond, label %if.end, label %if.then + +if.then: + call void @llvm.lifetime.start.p0(i64 64, ptr %local) + tail call void asm sideeffect "mov x30, 12345", "~{lr}"() + call void @llvm.lifetime.end.p0(i64 64, ptr %local) + br label %if.end + +if.end: + tail call void asm sideeffect "", "{x0},{x1},{x2},{x3},{x4},{x5},{x6},{x7},{x8},{x9},{x10},{x11},{x12},{x13},{x14},{x18},{x20}" (i64 %live_x0, i64 %live_x1, i64 %live_x2, i64 %live_x3, i64 %live_x4, i64 %live_x5, i64 %live_x6, i64 %live_x7, i64 %live_x8, i64 %live_x9, i64 %live_x10, i64 %live_x11, i64 %live_x12, i64 %live_x13, i64 %live_x14, i64 %live_x18, i64 %live_x20) + %result = tail call i64 asm sideeffect "add $0, $1, $2", "=r,{x16},{x17}"(i64 %live_x16, i64 %live_x17) + ret i64 %result +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
