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; } +// For 64-bit ELFv2 ABI with PCRel, TOC save/restore is not required. +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,20 +5148,12 @@ // 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. - if (Subtarget.isAIXABI() || Subtarget.is64BitELFABI()) - 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; } - // FIXME: At this moment indirect calls are treated ahead of the - // PC Relative condition because binaries can still contain a possible - // mix of functions that use a TOC and functions that do not use a TOC. - // Once the PC Relative feature is complete this condition should be moved - // up ahead of the indirect calls and should return a PPCISD::BCTRL for - // that case. if (Subtarget.isUsingPCRelativeCalls()) { assert(Subtarget.is64BitELFABI() && "PC Relative is only on ELF ABI."); return PPCISD::CALL_NOTOC; @@ -5426,7 +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. - if (Subtarget.is64BitELFABI() || 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); @@ -6497,17 +6497,21 @@ // See prepareDescriptorIndirectCall and buildCallOperands for more // information about calls through function pointers in the 64-bit SVR4 ABI. if (CFlags.IsIndirect) { - 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); - // TOC save area offset. - 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)); + // 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); + // TOC save area offset. + 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)); + } // 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 // to model this as an extra parameter, so do that. diff --git a/llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll b/llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll --- a/llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll +++ b/llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll @@ -219,14 +219,12 @@ ; CHECK-NEXT: mflr r0 ; CHECK-NEXT: std r0, 16(r1) ; CHECK-NEXT: stdu r1, -32(r1) -; CHECK-NEXT: std r2, 24(r1) ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: .cfi_offset lr, 16 ; CHECK-NEXT: pld r3, ptrfunc@got@pcrel(0), 1 ; CHECK-NEXT: ld r12, 0(r3) ; CHECK-NEXT: mtctr r12 ; CHECK-NEXT: bctrl -; CHECK-NEXT: ld 2, 24(r1) ; CHECK-NEXT: addi r1, r1, 32 ; CHECK-NEXT: ld r0, 16(r1) ; CHECK-NEXT: mtlr r0 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,38 @@ +; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \ +; RUN: -mcpu=future -ppc-asm-full-reg-names < %s | FileCheck %s \ +; RUN: --check-prefix=CHECK + +; The test checks the behavior of PC Relative indirect calls. When using +; PC Relative, TOC save and restore is 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()