diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.h b/llvm/lib/Target/AArch64/AArch64FrameLowering.h --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -30,6 +30,13 @@ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const override; + static void signLR(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, bool NeedsWinCFI, + bool *HasWinCFI); + + static void authenticateLR(MachineFunction &MF, MachineBasicBlock &MBB, + bool NeedsWinCFI, bool *HasWinCFI); + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into /// the function. void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1385,6 +1385,42 @@ .setMIFlags(MachineInstr::FrameSetup); } +void AArch64FrameLowering::signLR(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + bool NeedsWinCFI, bool *HasWinCFI) { + const auto &MFnI = *MF.getInfo(); + const AArch64Subtarget &Subtarget = MF.getSubtarget(); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + bool EmitCFI = MFnI.needsDwarfUnwindInfo(MF); + + // Debug location must be unknown, see emitPrologue(). + DebugLoc DL; + + if (MFnI.shouldSignWithBKey()) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY)) + .setMIFlag(MachineInstr::FrameSetup); + } + + // No SEH opcode for this one; it doesn't materialize into an + // instruction on Windows. + BuildMI( + MBB, MBBI, DL, + TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP : AArch64::PACIASP)) + .setMIFlag(MachineInstr::FrameSetup); + + if (EmitCFI) { + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } else if (NeedsWinCFI) { + *HasWinCFI = true; + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR)) + .setMIFlag(MachineInstr::FrameSetup); + } +} + void AArch64FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.begin(); @@ -1418,31 +1454,9 @@ emitShadowCallStackPrologue(*TII, MF, MBB, MBBI, DL, NeedsWinCFI, MFnI.needsDwarfUnwindInfo(MF)); - if (MFnI.shouldSignReturnAddress(MF)) { - if (MFnI.shouldSignWithBKey()) { - BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY)) - .setMIFlag(MachineInstr::FrameSetup); - } - - // No SEH opcode for this one; it doesn't materialize into an - // instruction on Windows. - BuildMI(MBB, MBBI, DL, - TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP - : AArch64::PACIASP)) - .setMIFlag(MachineInstr::FrameSetup); + if (MFnI.shouldSignReturnAddress(MF)) + signLR(MF, MBB, MBBI, NeedsWinCFI, &HasWinCFI); - if (EmitCFI) { - unsigned CFIIndex = - MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); - BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) - .addCFIIndex(CFIIndex) - .setMIFlags(MachineInstr::FrameSetup); - } else if (NeedsWinCFI) { - HasWinCFI = true; - BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR)) - .setMIFlag(MachineInstr::FrameSetup); - } - } if (EmitCFI && MFnI.isMTETagged()) { BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITMTETAGGED)) .setMIFlag(MachineInstr::FrameSetup); @@ -1901,11 +1915,10 @@ } } -static void InsertReturnAddressAuth(MachineFunction &MF, MachineBasicBlock &MBB, - bool NeedsWinCFI, bool *HasWinCFI) { +void AArch64FrameLowering::authenticateLR(MachineFunction &MF, + MachineBasicBlock &MBB, + bool NeedsWinCFI, bool *HasWinCFI) { const auto &MFI = *MF.getInfo(); - if (!MFI.shouldSignReturnAddress(MF)) - return; const AArch64Subtarget &Subtarget = MF.getSubtarget(); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); bool EmitAsyncCFI = MFI.needsAsyncDwarfUnwindInfo(MF); @@ -1920,10 +1933,11 @@ // From v8.3a onwards there are optimised authenticate LR and return // instructions, namely RETA{A,B}, that can be used instead. In this case the // DW_CFA_AARCH64_negate_ra_state can't be emitted. - if (Subtarget.hasPAuth() && - !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack) && - MBBI != MBB.end() && MBBI->getOpcode() == AArch64::RET_ReallyLR && - !NeedsWinCFI) { + bool TerminatorIsCombinable = + MBBI != MBB.end() && (MBBI->getOpcode() == AArch64::RET_ReallyLR || + MBBI->getOpcode() == AArch64::RET); + if (Subtarget.hasPAuth() && TerminatorIsCombinable && !NeedsWinCFI && + !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) { BuildMI(MBB, MBBI, DL, TII->get(MFI.shouldSignWithBKey() ? AArch64::RETAB : AArch64::RETAA)) .copyImplicitOps(*MBBI); @@ -1963,12 +1977,12 @@ MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); MachineFrameInfo &MFI = MF.getFrameInfo(); + AArch64FunctionInfo *AFI = MF.getInfo(); const AArch64Subtarget &Subtarget = MF.getSubtarget(); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL; bool NeedsWinCFI = needsWinCFI(MF); - bool EmitCFI = - MF.getInfo()->needsAsyncDwarfUnwindInfo(MF); + bool EmitCFI = AFI->needsAsyncDwarfUnwindInfo(MF); bool HasWinCFI = false; bool IsFunclet = false; auto WinCFI = make_scope_exit([&]() { assert(HasWinCFI == MF.hasWinCFI()); }); @@ -1979,7 +1993,8 @@ } auto FinishingTouches = make_scope_exit([&]() { - InsertReturnAddressAuth(MF, MBB, NeedsWinCFI, &HasWinCFI); + if (AFI->shouldSignReturnAddress(MF)) + authenticateLR(MF, MBB, NeedsWinCFI, &HasWinCFI); if (needsShadowCallStackPrologueEpilogue(MF)) emitShadowCallStackEpilogue(*TII, MF, MBB, MBB.getFirstTerminator(), DL); if (EmitCFI) @@ -1992,7 +2007,6 @@ int64_t NumBytes = IsFunclet ? getWinEHFuncletFrameSize(MF) : MFI.getStackSize(); - AArch64FunctionInfo *AFI = MF.getInfo(); // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -295,6 +295,8 @@ bool OutlineFromLinkOnceODRs) const override; std::optional getOutliningCandidateInfo( std::vector &RepeatedSequenceLocs) const override; + void mergeOutliningCandidateAttributes( + Function &F, std::vector &Candidates) const override; outliner::InstrType getOutliningTypeImpl(MachineBasicBlock::iterator &MIT, unsigned Flags) const override; SmallVector< diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "AArch64InstrInfo.h" +#include "AArch64FrameLowering.h" #include "AArch64MachineFunctionInfo.h" #include "AArch64Subtarget.h" #include "MCTargetDesc/AArch64AddressingModes.h" @@ -7679,6 +7680,23 @@ NumBytesToCreateFrame, FrameID); } +void AArch64InstrInfo::mergeOutliningCandidateAttributes( + Function &F, std::vector &Candidates) const { + // If a bunch of candidates reach this point they must agree on their return + // address signing. It is therefore enough to just consider the signing + // behaviour of one of them + const auto &CFn = Candidates.front().getMF()->getFunction(); + + // Since all candidates belong to the same module, just copy the + // function-level attributes of an arbitrary function. + if (CFn.hasFnAttribute("sign-return-address")) + F.addFnAttr(CFn.getFnAttribute("sign-return-address")); + if (CFn.hasFnAttribute("sign-return-address-key")) + F.addFnAttr(CFn.getFnAttribute("sign-return-address-key")); + + AArch64GenInstrInfo::mergeOutliningCandidateAttributes(F, Candidates); +} + bool AArch64InstrInfo::isFunctionSafeToOutlineFrom( MachineFunction &MF, bool OutlineFromLinkOnceODRs) const { const Function &F = MF.getFunction(); @@ -7991,65 +8009,15 @@ } static void signOutlinedFunction(MachineFunction &MF, MachineBasicBlock &MBB, - bool ShouldSignReturnAddr, - bool ShouldSignReturnAddrWithBKey) { - if (ShouldSignReturnAddr) { - MachineBasicBlock::iterator MBBPAC = MBB.begin(); - MachineBasicBlock::iterator MBBAUT = MBB.getFirstTerminator(); - const AArch64Subtarget &Subtarget = MF.getSubtarget(); - const TargetInstrInfo *TII = Subtarget.getInstrInfo(); - DebugLoc DL; - - if (MBBAUT != MBB.end()) - DL = MBBAUT->getDebugLoc(); - - // At the very beginning of the basic block we insert the following - // depending on the key type - // - // a_key: b_key: - // PACIASP EMITBKEY - // CFI_INSTRUCTION PACIBSP - // CFI_INSTRUCTION - if (ShouldSignReturnAddrWithBKey) { - BuildMI(MBB, MBBPAC, DebugLoc(), TII->get(AArch64::EMITBKEY)) - .setMIFlag(MachineInstr::FrameSetup); - } - - BuildMI(MBB, MBBPAC, DebugLoc(), - TII->get(ShouldSignReturnAddrWithBKey ? AArch64::PACIBSP - : AArch64::PACIASP)) - .setMIFlag(MachineInstr::FrameSetup); + bool ShouldSignReturnAddr) { + if (!ShouldSignReturnAddr) + return; - if (MF.getInfo()->needsDwarfUnwindInfo(MF)) { - unsigned CFIIndex = - MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); - BuildMI(MBB, MBBPAC, DebugLoc(), TII->get(AArch64::CFI_INSTRUCTION)) - .addCFIIndex(CFIIndex) - .setMIFlags(MachineInstr::FrameSetup); - } + AArch64FrameLowering::signLR(MF, MBB, MBB.begin(), + /*NeedsWinCFI=*/false, /*HasWinCFI=*/nullptr); - // If v8.3a features are available we can replace a RET instruction by - // RETAA or RETAB and omit the AUT instructions. In this case the - // DW_CFA_AARCH64_negate_ra_state can't be emitted. - if (Subtarget.hasPAuth() && MBBAUT != MBB.end() && - MBBAUT->getOpcode() == AArch64::RET) { - BuildMI(MBB, MBBAUT, DL, - TII->get(ShouldSignReturnAddrWithBKey ? AArch64::RETAB - : AArch64::RETAA)) - .copyImplicitOps(*MBBAUT); - MBB.erase(MBBAUT); - } else { - BuildMI(MBB, MBBAUT, DL, - TII->get(ShouldSignReturnAddrWithBKey ? AArch64::AUTIBSP - : AArch64::AUTIASP)) - .setMIFlag(MachineInstr::FrameDestroy); - unsigned CFIIndexAuth = - MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); - BuildMI(MBB, MBBAUT, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) - .addCFIIndex(CFIIndexAuth) - .setMIFlags(MachineInstr::FrameDestroy); - } - } + AArch64FrameLowering::authenticateLR(MF, MBB, /*NeedsWinCFI=*/false, + /*HasWinCFI=*/nullptr); } void AArch64InstrInfo::buildOutlinedFrame( @@ -8149,20 +8117,12 @@ Et = MBB.insert(Et, LDRXpost); } - // If a bunch of candidates reach this point they must agree on their return - // address signing. It is therefore enough to just consider the signing - // behaviour of one of them - const auto &MFI = *OF.Candidates.front().getMF()->getInfo(); - bool ShouldSignReturnAddr = MFI.shouldSignReturnAddress(!IsLeafFunction); - - // a_key is the default - bool ShouldSignReturnAddrWithBKey = MFI.shouldSignWithBKey(); + bool ShouldSignReturnAddr = FI->shouldSignReturnAddress(!IsLeafFunction); // If this is a tail call outlined function, then there's already a return. if (OF.FrameConstructionID == MachineOutlinerTailCall || OF.FrameConstructionID == MachineOutlinerThunk) { - signOutlinedFunction(MF, MBB, ShouldSignReturnAddr, - ShouldSignReturnAddrWithBKey); + signOutlinedFunction(MF, MBB, ShouldSignReturnAddr); return; } @@ -8176,8 +8136,7 @@ .addReg(AArch64::LR); MBB.insert(MBB.end(), ret); - signOutlinedFunction(MF, MBB, ShouldSignReturnAddr, - ShouldSignReturnAddrWithBKey); + signOutlinedFunction(MF, MBB, ShouldSignReturnAddr); FI->setOutliningStyle("Function"); diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir --- a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir +++ b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir @@ -211,5 +211,4 @@ # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 # CHECK: $sp = frame-destroy ADDXri $sp, 16, 0 # CHECK-NEXT: frame-destroy AUTIASP implicit-def $lr, implicit $lr, implicit $sp -# CHECK-NEXT: frame-destroy CFI_INSTRUCTION negate_ra_sign_state # CHECK-NEXT: RET $lr