diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -5126,6 +5126,12 @@ return true; } +// AIX and 64-bit ELF ABIs w/o PCRel require a TOC save/restore around calls. +static inline bool isTOCSaveRestoreRequired(const PPCSubtarget &Subtarget) { + return Subtarget.isAIXABI() || + (Subtarget.is64BitELFABI() && !Subtarget.isUsingPCRelativeCalls()); +} + static unsigned getCallOpcode(PPCTargetLowering::CallFlags CFlags, const Function &Caller, const SDValue &Callee, @@ -5142,14 +5148,10 @@ // pointer is modeled by using a pseudo instruction for the call opcode that // represents the 2 instruction sequence of an indirect branch and link, // immediately followed by a load of the TOC pointer from the the stack save - // slot into gpr2. - // For 64-bit ELF ABI PCREL, a indirect call does not need a TOC restore. - if (Subtarget.isAIXABI() || - (Subtarget.is64BitELFABI() && !Subtarget.isUsingPCRelativeCalls())) - return PPCISD::BCTRL_LOAD_TOC; - - // An indirect call that does not need a TOC restore. - return PPCISD::BCTRL; + // slot into gpr2. For 64-bit ELFv2 ABI with PCRel, do not restore the TOC + // as it is not saved or used. + return isTOCSaveRestoreRequired(Subtarget) ? PPCISD::BCTRL_LOAD_TOC + : PPCISD::BCTRL; } if (Subtarget.isUsingPCRelativeCalls()) { @@ -5422,9 +5424,9 @@ // pointer from the linkage area. The operand for the TOC restore is an add // of the TOC save offset to the stack pointer. This must be the second // operand: after the chain input but before any other variadic arguments. - // For 64-bit ELFv2 ABI PCRel, disable the code as TOC is not used. - if ((Subtarget.is64BitELFABI() && !Subtarget.isUsingPCRelativeCalls()) || - Subtarget.isAIXABI()) { + // For 64-bit ELFv2 ABI with PCRel, do not restore the TOC as it is not + // saved or used. + if (isTOCSaveRestoreRequired(Subtarget)) { const MCRegister StackPtrReg = Subtarget.getStackPointerRegister(); SDValue StackPtr = DAG.getRegister(StackPtrReg, RegVT); @@ -6495,10 +6497,10 @@ // See prepareDescriptorIndirectCall and buildCallOperands for more // information about calls through function pointers in the 64-bit SVR4 ABI. if (CFlags.IsIndirect) { - // When PCRel is enabled for 64-bit ELFv2 ABI, do not save the TOC of the - // caller in the TOC save area - if (!Subtarget.isUsingPCRelativeCalls()) { - assert(!CFlags.IsTailCall && "Indirect tails calls not supported"); + // For 64-bit ELFv2 ABI with PCRel, do not save the TOC of the + // caller in the TOC save area. + if (isTOCSaveRestoreRequired(Subtarget)) { + assert(!CFlags.IsTailCall && "Indirect tails calls not supported"); // Load r2 into a virtual register and store it to the TOC save area. setUsesTOCBasePtr(DAG); SDValue Val = DAG.getCopyFromReg(Chain, dl, PPC::X2, MVT::i64); @@ -6506,9 +6508,9 @@ unsigned TOCSaveOffset = Subtarget.getFrameLowering()->getTOCSaveOffset(); SDValue PtrOff = DAG.getIntPtrConstant(TOCSaveOffset, dl); SDValue AddPtr = DAG.getNode(ISD::ADD, dl, PtrVT, StackPtr, PtrOff); - Chain = DAG.getStore( - Val.getValue(1), dl, Val, AddPtr, - MachinePointerInfo::getStack(DAG.getMachineFunction(), TOCSaveOffset)); + Chain = DAG.getStore(Val.getValue(1), dl, Val, AddPtr, + MachinePointerInfo::getStack( + DAG.getMachineFunction(), TOCSaveOffset)); } // In the ELFv2 ABI, R12 must contain the address of an indirect callee. // This does not mean the MTCTR instruction must use R12; it's easier diff --git a/llvm/test/CodeGen/PowerPC/pcrel-indirect-call.ll b/llvm/test/CodeGen/PowerPC/pcrel-indirect-call.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/pcrel-indirect-call.ll @@ -0,0 +1,37 @@ +; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \ +; RUN: -mcpu=future -ppc-asm-full-reg-names < %s | FileCheck %s + +; The test checks the behavior of PC Relative indirect calls. When using +; PC Relative, TOC save and restore are no longer required. Function pointer +; is passed as a parameter in this test. + +; Function Attrs: noinline +define dso_local void @IndirectCallExternFuncPtr(void ()* nocapture %ptrfunc) { +; CHECK-LABEL: IndirectCallExternFuncPtr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mflr r0 +; CHECK-NEXT: std r0, 16(r1) +; CHECK-NEXT: stdu r1, -32(r1) + +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset lr, 16 +; CHECK-NEXT: mtctr r3 +; CHECK-NEXT: mr r12, r3 +; CHECK-NEXT: bctrl + +; CHECK-NEXT: addi r1, r1, 32 +; CHECK-NEXT: ld r0, 16(r1) +; CHECK-NEXT: mtlr r0 +; CHECK-NEXT: blr +entry: + tail call void %ptrfunc() + ret void +} + +define dso_local void @FuncPtrPassAsParam() { +entry: + tail call void @IndirectCallExternFuncPtr(void ()* nonnull @Function) + ret void +} + +declare void @Function()