diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -5139,6 +5139,20 @@ if (BranchTargetEnforcement) Fn->addFnAttr("branch-target-enforcement"); } + llvm::Value *decodeReturnAddress(CodeGen::CodeGenFunction &CGF, + llvm::Value *Address) const override { + CodeGenOptions::SignReturnAddressScope Scope = + CGF.CGM.getCodeGenOpts().getSignReturnAddress(); + if (Scope == CodeGenOptions::SignReturnAddressScope::None) { + return Address; + } + llvm::Function *F = + CGF.CGM.getIntrinsic(llvm::Intrinsic::extractreturnaddress, + {Address->getType(), Address->getType()}); + llvm::CallInst *Call = CGF.Builder.CreateCall(F, Address); + Call->setDoesNotAccessMemory(); + return Call; + } }; class WindowsAArch64TargetCodeGenInfo : public AArch64TargetCodeGenInfo { diff --git a/clang/test/CodeGen/arm64-extractreturnaddress.c b/clang/test/CodeGen/arm64-extractreturnaddress.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/arm64-extractreturnaddress.c @@ -0,0 +1,22 @@ +// REQUIRES: aarch64-registered-target +// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s +// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-PAC +// RUN: %clang -target arm64-none-linux-gnu -march=armv8.3-a -mbranch-protection=pac-ret \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-PAC83 +// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=standard \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BPROT8 +// RUN: %clang -target arm64-none-linux-gnu -march=armv8.5-a -mbranch-protection=standard \ +// RUN: -S -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-BPROT83 + +// CHECK:; Function Attrs: noinline nounwind optnone +// CHECK: define dso_local i8* @bar() #0 { +// CHECK-NEXT: ret i8* inttoptr (i64 42 to i8*) +// CHECK-PAC: %1 = call i8* @llvm.extractreturnaddress.p0i8.p0i8(i8* inttoptr (i64 42 to i8*)) +// CHECK-PAC83: %1 = call i8* @llvm.extractreturnaddress.p0i8.p0i8(i8* inttoptr (i64 42 to i8*)) +// CHECK-BPROT8: %1 = call i8* @llvm.extractreturnaddress.p0i8.p0i8(i8* inttoptr (i64 42 to i8*)) +// CHECK-BPROT83: %1 = call i8* @llvm.extractreturnaddress.p0i8.p0i8(i8* inttoptr (i64 42 to i8*)) +void *bar() { + return __builtin_extract_return_addr((void *)42); +} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -11179,6 +11179,36 @@ other aggressive transformations, so the value returned may not be that of the obvious source-language caller. +'``llvm.extractreturnaddress``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i8* @llvm.extractreturnaddress(i8* ) + +Overview: +""""""""" + +The '``llvm.extractreturnaddress``' intrinsic post processes the return +address. On most architechutre it could return with the address as it is. + +Arguments: +"""""""""" + +The argument to this intrinsic is an address from '``llvm.returnaddress``'. + +Semantics: +"""""""""" + +The '``llvm.extractreturnaddress``' intrinsic returns a pointer that is +post processed. There is no validation on the address therefore the required +transformation will be performed on any argument. + +This intrinsic is only implemented for aarch64. + '``llvm.addressofreturnaddress``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -71,7 +71,7 @@ /// of the frame or return address to return. An index of zero corresponds /// to the current function's frame or return address, an index of one to /// the parent's frame or return address, and so on. - FRAMEADDR, RETURNADDR, ADDROFRETURNADDR, SPONENTRY, + FRAMEADDR, RETURNADDR, ADDROFRETURNADDR, EXTRACTRETURNADDR, SPONENTRY, /// LOCAL_RECOVER - Represents the llvm.localrecover intrinsic. /// Materializes the offset from the local object pointer of another diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -425,6 +425,7 @@ def int_returnaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem, ImmArg<0>]>; def int_addressofreturnaddress : Intrinsic<[llvm_anyptr_ty], [], [IntrNoMem]>; def int_frameaddress : Intrinsic<[llvm_anyptr_ty], [llvm_i32_ty], [IntrNoMem, ImmArg<0>]>; +def int_extractreturnaddress : Intrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty], [IntrNoMem]>; def int_sponentry : Intrinsic<[llvm_anyptr_ty], [], [IntrNoMem]>; def int_read_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty], [IntrReadMem], "llvm.read_register">; diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1088,6 +1088,7 @@ case ISD::ADJUST_TRAMPOLINE: case ISD::FRAMEADDR: case ISD::RETURNADDR: + case ISD::EXTRACTRETURNADDR: case ISD::ADDROFRETURNADDR: case ISD::SPONENTRY: // These operations lie about being legal: when they claim to be legal, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5770,6 +5770,11 @@ TLI.getPointerTy(DAG.getDataLayout()), getValue(I.getArgOperand(0)))); return; + case Intrinsic::extractreturnaddress: + setValue(&I, DAG.getNode(ISD::EXTRACTRETURNADDR, sdl, + TLI.getPointerTy(DAG.getDataLayout()), + getValue(I.getArgOperand(0)))); + return; case Intrinsic::addressofreturnaddress: setValue(&I, DAG.getNode(ISD::ADDROFRETURNADDR, sdl, TLI.getPointerTy(DAG.getDataLayout()))); diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -738,6 +738,7 @@ SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSPONENTRY(SDValue Op, SelectionDAG &DAG) const; SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerEXTRACTRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFLT_ROUNDS_(SDValue Op, SelectionDAG &DAG) const; SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3249,6 +3249,8 @@ return LowerSPONENTRY(Op, DAG); case ISD::RETURNADDR: return LowerRETURNADDR(Op, DAG); + case ISD::EXTRACTRETURNADDR: + return LowerEXTRACTRETURNADDR(Op, DAG); case ISD::ADDROFRETURNADDR: return LowerADDROFRETURNADDR(Op, DAG); case ISD::INSERT_VECTOR_ELT: @@ -5953,6 +5955,27 @@ return DAG.getCopyFromReg(DAG.getEntryNode(), DL, Reg, VT); } +SDValue AArch64TargetLowering::LowerEXTRACTRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT VT = Op.getValueType(); + + // The XPACLRI instruction assembles to a hint-space instruction before + // Armv8.3-A therefore this instruction can be safely used for any pre + // Armv8.3-A architectures. On Armv8.3-A and onwards XPACI is available so use + // that instead. + if (Subtarget->hasV8_3aOps()) { + SDNode *St = DAG.getMachineNode(AArch64::XPACI, DL, VT, Op.getOperand(0)); + return SDValue(St, 0); + } else { + // XPACLRI operates on LR therefore we must move the operand accordingly. + SDValue Reg = + DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::LR, Op.getOperand(0)); + SDNode *St = DAG.getMachineNode(AArch64::XPACLRI, DL, VT, Reg); + return SDValue(St, 0); + } +} + /// LowerShiftRightParts - Lower SRA_PARTS, which returns two /// i64 values and take a 2 x i64 value to shift plus a shift amount. SDValue AArch64TargetLowering::LowerShiftRightParts(SDValue Op, diff --git a/llvm/test/CodeGen/AArch64/aarch64-extractreturnaddress.ll b/llvm/test/CodeGen/AArch64/aarch64-extractreturnaddress.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/aarch64-extractreturnaddress.ll @@ -0,0 +1,29 @@ +; RUN: llc < %s -mtriple=arm64-eabi -asm-verbose=false -mattr=v8.2a | FileCheck %s +; RUN: llc < %s -mtriple=arm64-eabi -asm-verbose=false -mattr=v8.3a | FileCheck %s --check-prefix=CHECKV83 + +; Armv8.3-A Pointer Authetication requires a special intsruction to strip the +; pointer authentication code from the pointer. +; The XPACLRI instruction assembles to a hint-space instruction before Armv8.3-A +; therefore this instruction can be safely used for any pre Armv8.3-A architectures. +; On Armv8.3-A and onwards XPACI is available so use that instead. + +define i8* @era0(i8* %x) nounwind readnone #0 { +entry: +; CHECK-LABEL: era0: +; CHECK: hint #25 +; CHECK-NEXT: str x30, [sp, #-16]! +; CHECK-NEXT: mov x30, x0 +; CHECK-NEXT: hint #7 +; CHECK-NEXT: mov x0, x30 +; CHECK-NEXT: ldr x30, [sp], #16 +; CHECK-NEXT: hint #29 +; CHECK-NEXT: ret +; CHECKV83: paciasp +; CHECKV83-NEXT: xpaci x0 +; CHECKV83-NEXT: retaa + %0 = tail call i8* @llvm.extractreturnaddress(i8* %x) + ret i8* %0 +} +attributes #0 = { "sign-return-address"="all" } + +declare i8* @llvm.extractreturnaddress(i8*) nounwind readnone