Index: llvm/include/llvm/CodeGen/TargetRegisterInfo.h =================================================================== --- llvm/include/llvm/CodeGen/TargetRegisterInfo.h +++ llvm/include/llvm/CodeGen/TargetRegisterInfo.h @@ -449,6 +449,14 @@ return nullptr; } + /// Return true if unwinders may not preserve all callee-saved registers. + virtual bool needsCustomEHPadPreservedMask() const { return false; } + + /// Return a register mask for the registers preserved by the unwinder. + virtual const uint32_t *getCustomEHPadPreservedMask() const { + llvm_unreachable("target does not provide EH pad preserved mask"); + } + /// Return a register mask that clobbers everything. virtual const uint32_t *getNoPreservedMask() const { llvm_unreachable("target does not provide no preserved mask"); Index: llvm/lib/CodeGen/MachineBasicBlock.cpp =================================================================== --- llvm/lib/CodeGen/MachineBasicBlock.cpp +++ llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -1522,7 +1522,15 @@ const uint32_t * MachineBasicBlock::getBeginClobberMask(const TargetRegisterInfo *TRI) const { // EH funclet entry does not preserve any registers. - return isEHFuncletEntry() ? TRI->getNoPreservedMask() : nullptr; + if (isEHFuncletEntry()) + return TRI->getNoPreservedMask(); + + // Unwinders may not preserve all registers. + if (isEHPad() && TRI->needsCustomEHPadPreservedMask()) + if (auto *Mask = TRI->getCustomEHPadPreservedMask()) + return Mask; + + return nullptr; } const uint32_t * Index: llvm/lib/Target/AArch64/AArch64RegisterInfo.h =================================================================== --- llvm/lib/Target/AArch64/AArch64RegisterInfo.h +++ llvm/lib/Target/AArch64/AArch64RegisterInfo.h @@ -72,6 +72,10 @@ // Funclets on ARM64 Windows don't preserve any registers. const uint32_t *getNoPreservedMask() const override; + // Unwinders may not preserve all Neon and SVE registers. + bool needsCustomEHPadPreservedMask() const override; + const uint32_t *getCustomEHPadPreservedMask() const override; + /// getThisReturnPreservedMask - Returns a call preserved mask specific to the /// case that 'returned' is on an i64 first argument if the calling convention /// is one that can (partially) model this attribute with a preserved mask Index: llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -240,6 +240,14 @@ return SCS ? CSR_AArch64_AAPCS_SCS_RegMask : CSR_AArch64_AAPCS_RegMask; } +bool AArch64RegisterInfo::needsCustomEHPadPreservedMask() const { + return true; +} + +const uint32_t *AArch64RegisterInfo::getCustomEHPadPreservedMask() const { + return CSR_AArch64_AAPCS_RegMask; +} + const uint32_t *AArch64RegisterInfo::getTLSCallPreservedMask() const { if (TT.isOSDarwin()) return CSR_Darwin_AArch64_TLS_RegMask; Index: llvm/test/CodeGen/AArch64/unwind-preserved.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/unwind-preserved.ll @@ -0,0 +1,68 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve < %s | FileCheck %s + +; Test that z0 is saved/restored, as the unwinder may only retain the low 64bits (d0). +define @invoke_callee_may_throw_sve( %v) personality i8 0 { +; CHECK-LABEL: invoke_callee_may_throw_sve: +; CHECK: .Lfunc_begin0: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: stp x29, x30, [sp, #-16]! // 16-byte Folded Spill +; CHECK-NEXT: addvl sp, sp, #-1 +; CHECK-NEXT: .cfi_escape 0x0f, 0x0c, 0x8f, 0x00, 0x11, 0x10, 0x22, 0x11, 0x08, 0x92, 0x2e, 0x00, 0x1e, 0x22 // sp + 16 + 8 * VG +; CHECK-NEXT: .cfi_offset w30, -8 +; CHECK-NEXT: .cfi_offset w29, -16 +; CHECK-NEXT: .Ltmp0: +; CHECK-NEXT: str z0, [sp] // 16-byte Folded Spill +; CHECK-NEXT: bl may_throw_sve +; CHECK-NEXT: .Ltmp1: +; CHECK-NEXT: .LBB0_1: // %.Lcontinue +; CHECK-NEXT: addvl sp, sp, #1 +; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB0_2: // %.Lunwind +; CHECK-NEXT: .Ltmp2: +; CHECK-NEXT: ldr z0, [sp] // 16-byte Folded Reload +; CHECK-NEXT: b .LBB0_1 + %result = invoke @may_throw_sve( %v) to label %.Lcontinue unwind label %.Lunwind +.Lcontinue: + ret %result +.Lunwind: + %lp = landingpad { i8*, i32 } cleanup + ret %v; +} + +declare @may_throw_sve( %v); + + +; Test that q0 is saved/restored, as the unwinder may only retain the low 64bits (d0). +define aarch64_vector_pcs <4 x i32> @invoke_callee_may_throw_neon(<4 x i32> %v) personality i8 0 { +; CHECK-LABEL: invoke_callee_may_throw_neon: +; CHECK: .Lfunc_begin1: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: sub sp, sp, #32 // =32 +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset w30, -16 +; CHECK-NEXT: .Ltmp3: +; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill +; CHECK-NEXT: bl may_throw_neon +; CHECK-NEXT: .Ltmp4: +; CHECK-NEXT: .LBB1_1: // %.Lcontinue +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: add sp, sp, #32 // =32 +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB1_2: // %.Lunwind +; CHECK-NEXT: .Ltmp5: +; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload +; CHECK-NEXT: b .LBB1_1 + %result = invoke aarch64_vector_pcs <4 x i32> @may_throw_neon(<4 x i32> %v) to label %.Lcontinue unwind label %.Lunwind +.Lcontinue: + ret <4 x i32> %result +.Lunwind: + %lp = landingpad { i8*, i32 } cleanup + ret <4 x i32> %v; +} + +declare aarch64_vector_pcs <4 x i32> @may_throw_neon(<4 x i32> %v);