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 @@ -37,6 +37,13 @@ static void authenticateLR(MachineFunction &MF, MachineBasicBlock &MBB, bool NeedsWinCFI, bool *HasWinCFI); + /// Checks authenticated LR just before the TCRETURN* instruction. + /// + /// This function may split MBB - in that case, the returned basic block + /// is the new block containing the return instruction. + static MachineBasicBlock &checkAuthenticatedLR(MachineFunction &MF, + MachineBasicBlock &MBB); + /// 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 @@ -193,6 +193,7 @@ #include "AArch64TargetMachine.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" +#include "Utils/AArch64PointerAuth.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -258,6 +259,13 @@ cl::desc("Emit homogeneous prologue and epilogue for the size " "optimization (default = off)")); +// TODO Not default to DummyLoad method if execute-only segments are needed. +cl::opt AuthenticatedLRCheckMethod( + "aarch64-authenticated-lr-check-method", cl::Hidden, + cl::init(AuthCheckMethod::DummyLoad), + cl::desc("Variant of check applied to authenticated LR during tail call"), + cl::values(AUTH_CHECK_METHOD_CL_VALUES_LR)); + STATISTIC(NumRedZoneFunctions, "Number of functions using red zone"); /// Returns how much of the incoming argument stack area (in bytes) we should @@ -269,14 +277,10 @@ static int64_t getArgumentStackToRestore(MachineFunction &MF, MachineBasicBlock &MBB) { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); - bool IsTailCallReturn = false; - if (MBB.end() != MBBI) { - unsigned RetOpcode = MBBI->getOpcode(); - IsTailCallReturn = RetOpcode == AArch64::TCRETURNdi || - RetOpcode == AArch64::TCRETURNri || - RetOpcode == AArch64::TCRETURNriBTI; - } AArch64FunctionInfo *AFI = MF.getInfo(); + bool IsTailCallReturn = (MBB.end() != MBBI) + ? AArch64InstrInfo::isTailCallReturnInst(*MBBI) + : false; int64_t ArgumentPopSize = 0; if (IsTailCallReturn) { @@ -1963,6 +1967,51 @@ } } +MachineBasicBlock & +AArch64FrameLowering::checkAuthenticatedLR(MachineFunction &MF, + MachineBasicBlock &MBB) { + const auto &Subtarget = MF.getSubtarget(); + const auto *TRI = Subtarget.getRegisterInfo(); + + auto TI = MBB.getFirstTerminator(); + if (TI == MBB.end()) + return MBB; + + // Only explicitly check LR if we are performing tail call. + if (!AArch64InstrInfo::isTailCallReturnInst(*TI)) + return MBB; + + Register TmpReg = + TI->readsRegister(AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16; + assert(!TI->readsRegister(TmpReg, TRI) && + "More than a single register is used by TCRETURN"); + + // The following code may create a signing oracle: + // + // + // TCRETURN ; the callee may sign and spill the LR in its prologue + // + // To avoid generating a signing oracle, check the authenticated value + // before possibly re-signing it in the callee, as follows: + // + // + // + // b. break_block + // ret_block: + // TCRETURN + // break_block: + // brk 0xc471 + // + // or just + // + // + // ldr tmp, [lr] + // TCRETURN + + return checkAuthenticatedRegister(TI, AuthenticatedLRCheckMethod, AArch64::LR, + TmpReg, /*UseIKey=*/true, 0xc471); +} + static bool isFuncletReturnInstr(const MachineInstr &MI) { switch (MI.getOpcode()) { default: @@ -1993,14 +2042,22 @@ } auto FinishingTouches = make_scope_exit([&]() { + MachineBasicBlock *RetMBB = &MBB; + if (AFI->shouldSignReturnAddress(MF)) authenticateLR(MF, MBB, NeedsWinCFI, &HasWinCFI); + if (needsShadowCallStackPrologueEpilogue(MF)) emitShadowCallStackEpilogue(*TII, MF, MBB, MBB.getFirstTerminator(), DL); + else if (AFI->shouldSignReturnAddress(MF)) + RetMBB = &checkAuthenticatedLR(MF, MBB); + + // checkAuthenticatedLR() may have split MBB at this point. + if (EmitCFI) - emitCalleeSavedGPRRestores(MBB, MBB.getFirstTerminator()); + emitCalleeSavedGPRRestores(*RetMBB, RetMBB->getFirstTerminator()); if (HasWinCFI) - BuildMI(MBB, MBB.getFirstTerminator(), DL, + BuildMI(*RetMBB, RetMBB->getFirstTerminator(), DL, TII->get(AArch64::SEH_EpilogEnd)) .setMIFlag(MachineInstr::FrameDestroy); }); 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 @@ -126,6 +126,9 @@ /// Return true if pairing the given load or store may be paired with another. static bool isPairableLdStInst(const MachineInstr &MI); + /// Returns true if MI is one of the TCRETURN* instructions. + static bool isTailCallReturnInst(const MachineInstr &MI); + /// Return the opcode that set flags when possible. The caller is /// responsible for ensuring the opc has a flag setting equivalent. static unsigned convertToFlagSettingOpc(unsigned Opc); 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 @@ -2454,6 +2454,20 @@ } } +bool AArch64InstrInfo::isTailCallReturnInst(const MachineInstr &MI) { + switch (MI.getOpcode()) { + default: + assert((!MI.isCall() || !MI.isReturn()) && + "Unexpected instruction - was a new tail call opcode introduced?"); + return false; + case AArch64::TCRETURNdi: + case AArch64::TCRETURNri: + case AArch64::TCRETURNriBTI: + case AArch64::TCRETURNriALL: + return true; + } +} + unsigned AArch64InstrInfo::convertToFlagSettingOpc(unsigned Opc) { switch (Opc) { default: diff --git a/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.h b/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.h @@ -0,0 +1,110 @@ +//===-- AArch64PointerAuth.h - PtrAuth helpers ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AARCH64_UTILS_AARCH64POINTERAUTH_H +#define LLVM_LIB_TARGET_AARCH64_UTILS_AARCH64POINTERAUTH_H + +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/Register.h" + +namespace llvm { + +/// Variants of check performed on an authenticated pointer. +/// +/// In cases such as authenticating the LR value when performing a tail call +/// or when re-signing a signed pointer with a different signing schema, +/// a failed authentication may not generate an exception on its own and may +/// create an authentication or signing oracle if not checked explicitly. +/// +/// A number of check methods modify control flow in a similar way by +/// rewriting the code +/// +/// ``` +/// +/// +/// ``` +/// +/// as follows: +/// +/// ``` +/// +/// +/// ret_block: +/// +/// ... +/// +/// break_block: +/// brk +/// ``` +enum class AuthCheckMethod { + /// Do not check the value at all + None, + /// Perform a load to a temporary register + DummyLoad, + /// Check by comparing bits 62 and 61 of the authenticated address. + /// + /// This method modifies control flow and inserts the following checker: + /// + /// ``` + /// eor Xtmp, Xn, Xn, lsl #1 + /// tbnz Xtmp, #62, break_block + /// ``` + HighBitsNoTBI, + /// Check by comparing the authenticated value with an XPAC-ed one without + /// using PAuth instructions not encoded as HINT. Can only be applied to LR. + /// + /// This method modifies control flow and inserts the following checker: + /// + /// ``` + /// mov Xtmp, LR + /// xpaclri ; encoded as "hint #7" + /// ; Note: at this point, the LR register contains the address as if + /// ; the authentication succeeded and the temporary register contains the + /// ; *real* result of authentication. + /// cmp Xtmp, LR + /// b.ne break_block + /// ``` + XPACHint, +}; + +#define AUTH_CHECK_METHOD_CL_VALUES_COMMON \ + clEnumValN(AuthCheckMethod::None, "none", \ + "Do not check authenticated address"), \ + clEnumValN(AuthCheckMethod::DummyLoad, "load", \ + "Perform dummy load from authenticated address"), \ + clEnumValN(AuthCheckMethod::HighBitsNoTBI, "high-bits-notbi", \ + "Compare bits 62 and 61 of address (TBI should be disabled)") + +#define AUTH_CHECK_METHOD_CL_VALUES_LR \ + AUTH_CHECK_METHOD_CL_VALUES_COMMON, \ + clEnumValN(AuthCheckMethod::XPACHint, "xpac-hint", \ + "Compare with the result of XPACLRI (pre-v8.3 compatible)") + +/// Explicitly checks that pointer authentication succeeded. +/// +/// Assuming AuthenticatedReg contains a value returned by one of the AUT* +/// instructions, check the value using Method just before the instruction +/// pointed to by MBBI. If the check succeedes, execution proceeds to the +/// instruction pointed to by MBBI, otherwise a CPU exception is generated. +/// +/// Some of the methods may need to know if the pointer was authenticated +/// using an I-key or D-key and which register can be used as temporary. +/// If an explicit BRK instruction is used to generate an exception, BrkImm +/// specifies its immediate operand. +/// +/// \returns The machine basic block containing the code that is executed +/// after the check succeeds. +MachineBasicBlock &checkAuthenticatedRegister(MachineBasicBlock::iterator MBBI, + AuthCheckMethod Method, + Register AuthenticatedReg, + Register TmpReg, bool UseIKey, + unsigned BrkImm); + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AARCH64_UTILS_AARCH64POINTERAUTH_H diff --git a/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.cpp b/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AArch64/Utils/AArch64PointerAuth.cpp @@ -0,0 +1,128 @@ +//===-- AArch64PointerAuth.cpp - PtrAuth helpers --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "AArch64PointerAuth.h" + +#include "AArch64BaseInfo.h" +#include "AArch64InstrInfo.h" +#include "AArch64Subtarget.h" + +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/PseudoSourceValue.h" + +using namespace llvm; + +namespace { + +class AddressCheckPseudoSourceValue : public PseudoSourceValue { +public: + AddressCheckPseudoSourceValue(const TargetMachine &TM) + : PseudoSourceValue(TargetCustom, TM) {} + + bool isConstant(const MachineFrameInfo *) const override { return false; } + bool isAliased(const MachineFrameInfo *) const override { return true; } + bool mayAlias(const MachineFrameInfo *) const override { return true; } + void printCustom(raw_ostream &OS) const override { OS << "AddressCheck"; } +}; + +// Mark dummy LDR instruction as volatile to prevent removing it as dead code. +MachineMemOperand *createCheckMemOperand(MachineFunction &MF) { + AddressCheckPseudoSourceValue CheckPSV(MF.getTarget()); + MachinePointerInfo PointerInfo(&CheckPSV); + auto MOVolatileLoad = + MachineMemOperand::MOLoad | MachineMemOperand::MOVolatile; + + return MF.getMachineMemOperand(PointerInfo, MOVolatileLoad, 4, Align(4)); +} + +} // namespace + +MachineBasicBlock &llvm::checkAuthenticatedRegister( + MachineBasicBlock::iterator MBBI, AuthCheckMethod Method, + Register AuthenticatedReg, Register TmpReg, bool UseIKey, unsigned BrkImm) { + + MachineBasicBlock &MBB = *MBBI->getParent(); + MachineFunction &MF = *MBB.getParent(); + const AArch64Subtarget &Subtarget = MF.getSubtarget(); + const AArch64InstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MBBI->getDebugLoc(); + + // First, handle the methods not requiring creating extra MBBs. + switch (Method) { + default: + break; + case AuthCheckMethod::None: + return MBB; + case AuthCheckMethod::DummyLoad: + BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRWui), getWRegFromXReg(TmpReg)) + .addReg(AArch64::LR) + .addImm(0) + .addMemOperand(createCheckMemOperand(MF)) + .setMIFlags(MachineInstr::FrameDestroy); + return MBB; + } + + // Control flow has to be changed, so arrange new MBBs. + + assert(MBBI != MBB.begin() && + "Cannot insert the check at the very beginning of MBB"); + // The block to insert check into. + MachineBasicBlock *CheckBlock = &MBB; + // The remaining part of the original MBB that is executed on success. + MachineBasicBlock *SuccessBlock = MBB.splitAt(*std::prev(MBBI)); + + // The block that explicitly generates a break-point exception on failure. + MachineBasicBlock *BreakBlock = + MF.CreateMachineBasicBlock(MBB.getBasicBlock()); + MF.push_back(BreakBlock); + MBB.splitSuccessor(SuccessBlock, BreakBlock); + + assert(CheckBlock->getFallThrough() == SuccessBlock); + BuildMI(BreakBlock, DL, TII->get(AArch64::BRK)) + .addImm(BrkImm) + .setMIFlags(MachineInstr::FrameDestroy); + + switch (Method) { + case AuthCheckMethod::None: + case AuthCheckMethod::DummyLoad: + llvm_unreachable("Should be handled above"); + case AuthCheckMethod::HighBitsNoTBI: + BuildMI(CheckBlock, DL, TII->get(AArch64::EORXrs), TmpReg) + .addReg(AuthenticatedReg) + .addReg(AuthenticatedReg) + .addImm(1) + .setMIFlags(MachineInstr::FrameDestroy); + BuildMI(CheckBlock, DL, TII->get(AArch64::TBNZX)) + .addReg(TmpReg) + .addImm(62) + .addMBB(BreakBlock) + .setMIFlags(MachineInstr::FrameDestroy); + return *SuccessBlock; + case AuthCheckMethod::XPACHint: + assert(AuthenticatedReg == AArch64::LR && + "XPACHint mode is only compatible with checking the LR register"); + assert(UseIKey && "XPACHint mode is only compatible with I-keys"); + BuildMI(CheckBlock, DL, TII->get(TargetOpcode::COPY), TmpReg) + .addReg(AArch64::LR) + .setMIFlags(MachineInstr::FrameDestroy); + BuildMI(CheckBlock, DL, TII->get(AArch64::XPACLRI)) + .setMIFlags(MachineInstr::FrameDestroy); + BuildMI(CheckBlock, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) + .addReg(TmpReg) + .addReg(AArch64::LR) + .addImm(0) + .setMIFlags(MachineInstr::FrameDestroy); + BuildMI(CheckBlock, DL, TII->get(AArch64::Bcc)) + .addImm(AArch64CC::NE) + .addMBB(BreakBlock) + .setMIFlags(MachineInstr::FrameDestroy); + return *SuccessBlock; + } +} diff --git a/llvm/lib/Target/AArch64/Utils/CMakeLists.txt b/llvm/lib/Target/AArch64/Utils/CMakeLists.txt --- a/llvm/lib/Target/AArch64/Utils/CMakeLists.txt +++ b/llvm/lib/Target/AArch64/Utils/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMAArch64Utils AArch64BaseInfo.cpp + AArch64PointerAuth.cpp AArch64SMEAttributes.cpp LINK_COMPONENTS diff --git a/llvm/test/CodeGen/AArch64/sign-return-address-cfi-negate-ra-state.ll b/llvm/test/CodeGen/AArch64/sign-return-address-cfi-negate-ra-state.ll --- a/llvm/test/CodeGen/AArch64/sign-return-address-cfi-negate-ra-state.ll +++ b/llvm/test/CodeGen/AArch64/sign-return-address-cfi-negate-ra-state.ll @@ -75,6 +75,7 @@ ; CHECK-V8A-NEXT: .cfi_def_cfa_offset 0 ; CHECK-V8A-NEXT: hint #29 ; CHECK-V8A-NEXT: .cfi_negate_ra_state +; CHECK-V8A-NEXT: ldr w16, [x30] ; CHECK-V8A-NEXT: .cfi_restore w30 ; CHECK-V8A-NEXT: b _Z3bari ; CHECK-V8A-NEXT: .LBB1_2: // %if.else @@ -104,6 +105,7 @@ ; CHECK-V83A-NEXT: .cfi_def_cfa_offset 0 ; CHECK-V83A-NEXT: autiasp ; CHECK-V83A-NEXT: .cfi_negate_ra_state +; CHECK-V83A-NEXT: ldr w16, [x30] ; CHECK-V83A-NEXT: .cfi_restore w30 ; CHECK-V83A-NEXT: b _Z3bari ; CHECK-V83A-NEXT: .LBB1_2: // %if.else @@ -151,6 +153,7 @@ ; CHECK-V8A-NEXT: bl _Z3bari ; CHECK-V8A-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-V8A-NEXT: hint #29 +; CHECK-V8A-NEXT: ldr w16, [x30] ; CHECK-V8A-NEXT: b _Z3bari ; CHECK-V8A-NEXT: .LBB2_2: // %if.else ; CHECK-V8A-NEXT: bl _Z4quuxi @@ -172,6 +175,7 @@ ; CHECK-V83A-NEXT: bl _Z3bari ; CHECK-V83A-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-V83A-NEXT: autiasp +; CHECK-V83A-NEXT: ldr w16, [x30] ; CHECK-V83A-NEXT: b _Z3bari ; CHECK-V83A-NEXT: .LBB2_2: // %if.else ; CHECK-V83A-NEXT: bl _Z4quuxi diff --git a/llvm/test/CodeGen/AArch64/sign-return-address-tailcall.ll b/llvm/test/CodeGen/AArch64/sign-return-address-tailcall.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/sign-return-address-tailcall.ll @@ -0,0 +1,146 @@ +; RUN: llc -mtriple=aarch64 -asm-verbose=0 < %s | FileCheck -DAUTIASP="hint #29" --check-prefixes=COMMON,LDR %s +; RUN: llc -mtriple=aarch64 -asm-verbose=0 -aarch64-authenticated-lr-check-method=high-bits-notbi < %s | FileCheck -DAUTIASP="hint #29" --check-prefixes=COMMON,BITS-NOTBI,BRK %s +; RUN: llc -mtriple=aarch64 -asm-verbose=0 -aarch64-authenticated-lr-check-method=xpac-hint < %s | FileCheck -DAUTIASP="hint #29" -DXPACLRI="hint #7" --check-prefixes=COMMON,XPAC,BRK %s +; RUN: llc -mtriple=aarch64 -asm-verbose=0 -aarch64-authenticated-lr-check-method=xpac-hint -mattr=v8.3a < %s | FileCheck -DAUTIASP="autiasp" -DXPACLRI="xpaclri" --check-prefixes=COMMON,XPAC,BRK %s + +define i32 @tailcall_direct() "sign-return-address"="non-leaf" { +; COMMON-LABEL: tailcall_direct: +; COMMON: str x30, [sp, #-16]! +; COMMON: ldr x30, [sp], #16 +; +; LDR-NEXT: [[AUTIASP]] +; LDR-NEXT: ldr w16, [x30] +; LDR-NEXT: b callee +; +; BITS-NOTBI-NEXT: [[AUTIASP]] +; BITS-NOTBI-NEXT: eor x16, x30, x30, lsl #1 +; BITS-NOTBI-NEXT: tbnz x16, #62, .[[FAIL:LBB[_0-9]+]] +; BITS-NOTBI-NEXT: b callee +; +; XPAC-NEXT: [[AUTIASP]] +; XPAC-NEXT: mov x16, x30 +; XPAC-NEXT: [[XPACLRI]] +; XPAC-NEXT: cmp x16, x30 +; XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; XPAC-NEXT: b callee +; BRK-NEXT: .[[FAIL]]: +; BRK-NEXT: brk #0xc471 + tail call void asm sideeffect "", "~{lr}"() + %call = tail call i32 @callee() + ret i32 %call +} + +define i32 @tailcall_indirect(ptr %fptr) "sign-return-address"="non-leaf" { +; COMMON-LABEL: tailcall_indirect: +; COMMON: str x30, [sp, #-16]! +; COMMON: ldr x30, [sp], #16 +; +; LDR-NEXT: [[AUTIASP]] +; LDR-NEXT: ldr w16, [x30] +; LDR-NEXT: br x0 +; +; BITS-NOTBI-NEXT: [[AUTIASP]] +; BITS-NOTBI-NEXT: eor x16, x30, x30, lsl #1 +; BITS-NOTBI-NEXT: tbnz x16, #62, .[[FAIL:LBB[_0-9]+]] +; BITS-NOTBI-NEXT: br x0 +; +; XPAC-NEXT: [[AUTIASP]] +; XPAC-NEXT: mov x16, x30 +; XPAC-NEXT: [[XPACLRI]] +; XPAC-NEXT: cmp x16, x30 +; XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; XPAC-NEXT: br x0 +; BRK-NEXT: .[[FAIL]]: +; BRK-NEXT: brk #0xc471 + tail call void asm sideeffect "", "~{lr}"() + %call = tail call i32 %fptr() + ret i32 %call +} + +define i32 @tailcall_direct_noframe() "sign-return-address"="non-leaf" { +; COMMON-LABEL: tailcall_direct_noframe: +; COMMON-NEXT: .cfi_startproc +; COMMON-NEXT: b callee + %call = tail call i32 @callee() + ret i32 %call +} + +define i32 @tailcall_indirect_noframe(ptr %fptr) "sign-return-address"="non-leaf" { +; COMMON-LABEL: tailcall_indirect_noframe: +; COMMON-NEXT: .cfi_startproc +; COMMON-NEXT: br x0 + %call = tail call i32 %fptr() + ret i32 %call +} + +define i32 @tailcall_direct_noframe_sign_all() "sign-return-address"="all" { +; COMMON-LABEL: tailcall_direct_noframe_sign_all: +; COMMON-NOT: str{{.*}}x30 +; COMMON-NOT: ldr{{.*}}x30 +; +; LDR: [[AUTIASP]] +; LDR-NEXT: ldr w16, [x30] +; LDR-NEXT: b callee +; +; BITS-NOTBI: [[AUTIASP]] +; BITS-NOTBI-NEXT: eor x16, x30, x30, lsl #1 +; BITS-NOTBI-NEXT: tbnz x16, #62, .[[FAIL:LBB[_0-9]+]] +; BITS-NOTBI-NEXT: b callee +; +; XPAC: [[AUTIASP]] +; XPAC-NEXT: mov x16, x30 +; XPAC-NEXT: [[XPACLRI]] +; XPAC-NEXT: cmp x16, x30 +; XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; XPAC-NEXT: b callee +; BRK-NEXT: .[[FAIL]]: +; BRK-NEXT: brk #0xc471 + %call = tail call i32 @callee() + ret i32 %call +} + +define i32 @tailcall_indirect_noframe_sign_all(ptr %fptr) "sign-return-address"="all" { +; COMMON-LABEL: tailcall_indirect_noframe_sign_all: +; COMMON-NOT: str{{.*}}x30 +; COMMON-NOT: ldr{{.*}}x30 +; +; LDR: [[AUTIASP]] +; LDR-NEXT: ldr w16, [x30] +; LDR-NEXT: br x0 +; +; BITS-NOTBI: [[AUTIASP]] +; BITS-NOTBI-NEXT: eor x16, x30, x30, lsl #1 +; BITS-NOTBI-NEXT: tbnz x16, #62, .[[FAIL:LBB[_0-9]+]] +; BITS-NOTBI-NEXT: br x0 +; +; XPAC: [[AUTIASP]] +; XPAC-NEXT: mov x16, x30 +; XPAC-NEXT: [[XPACLRI]] +; XPAC-NEXT: cmp x16, x30 +; XPAC-NEXT: b.ne .[[FAIL:LBB[_0-9]+]] +; XPAC-NEXT: br x0 +; BRK-NEXT: .[[FAIL]]: +; BRK-NEXT: brk #0xc471 + %call = tail call i32 %fptr() + ret i32 %call +} + +; Do not emit any LR checks when Shadow Call Stack is enabled +define i32 @tailcall_scs(ptr %fptr) "sign-return-address"="all" shadowcallstack "target-features"="+reserve-x18" { +; COMMON-LABEL: tailcall_scs: +; COMMON: str x30, [sp, #-16]! +; COMMON: ldr x30, [sp], #16 +; +; COMMON-NOT: ldr {{.*}}, [x30] +; COMMON-NOT: xpac +; COMMON-NOT: hint #7 +; COMMON-NOT: brk +; +; Match the end of function: +; COMMON: .size tailcall_scs, + tail call void asm sideeffect "", "~{lr}"() + %call = tail call i32 %fptr() + ret i32 %call +} + +declare i32 @callee() diff --git a/llvm/test/CodeGen/AArch64/sign-return-address.ll b/llvm/test/CodeGen/AArch64/sign-return-address.ll --- a/llvm/test/CodeGen/AArch64/sign-return-address.ll +++ b/llvm/test/CodeGen/AArch64/sign-return-address.ll @@ -170,6 +170,7 @@ ; COMPAT-NEXT: //NO_APP ; COMPAT-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; COMPAT-NEXT: hint #29 +; COMPAT-NEXT: ldr w16, [x30] ; COMPAT-NEXT: b bar ; ; V83A-LABEL: spill_lr_and_tail_call: @@ -184,6 +185,7 @@ ; V83A-NEXT: //NO_APP ; V83A-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; V83A-NEXT: autiasp +; V83A-NEXT: ldr w16, [x30] ; V83A-NEXT: b bar call void asm sideeffect "mov x30, $0", "r,~{lr}"(i64 %x) #1 tail call fastcc i64 @bar(i64 %x)