diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp index 330cb4e0e206f..72019008bbd1c 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.cpp +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.cpp @@ -247,8 +247,8 @@ Mips16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool Mips16TargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const { + const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, + bool IsMustTail) const { // No tail call optimization for mips16. return false; } diff --git a/llvm/lib/Target/Mips/Mips16ISelLowering.h b/llvm/lib/Target/Mips/Mips16ISelLowering.h index f120cbbd24f91..5281a62791590 100644 --- a/llvm/lib/Target/Mips/Mips16ISelLowering.h +++ b/llvm/lib/Target/Mips/Mips16ISelLowering.h @@ -31,9 +31,10 @@ namespace llvm { MachineBasicBlock *MBB) const override; private: - bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const override; + bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const override; void setMips16HardFloatLibCalls(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index 881ba8e2f9eff..262c2212b1a4f 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3404,21 +3404,24 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) CSInfo = MachineFunction::CallSiteInfo(*CB); - // Check if it's really possible to do a tail call. Restrict it to functions - // that are part of this compilation unit. - bool InternalLinkage = false; + // Check if it's really possible to do a tail call. + // For non-musttail calls, restrict to functions that won't require $gp + // restoration. In PIC mode, calling external functions via tail call can + // cause issues with $gp register handling (see D24763). + bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( - CCInfo, StackSize, *MF.getInfo()); - if (GlobalAddressSDNode *G = dyn_cast(Callee)) { - InternalLinkage = G->getGlobal()->hasInternalLinkage(); - IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || - G->getGlobal()->hasPrivateLinkage() || - G->getGlobal()->hasHiddenVisibility() || - G->getGlobal()->hasProtectedVisibility()); - } + CCInfo, StackSize, *MF.getInfo(), IsMustTail); + // For non-musttail calls, restrict to local or non-interposable functions + if (IsTailCall && !IsMustTail) { + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + IsTailCall &= (G->getGlobal()->hasLocalLinkage() || + G->getGlobal()->hasHiddenVisibility() || + G->getGlobal()->hasProtectedVisibility()); + } + } } - if (!IsTailCall && CLI.CB && CLI.CB->isMustTailCall()) + if (!IsTailCall && IsMustTail) report_fatal_error("failed to perform tail call elimination on a call " "site marked musttail"); @@ -3595,6 +3598,7 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } } + bool InternalLinkage = false; if (GlobalAddressSDNode *G = dyn_cast(Callee)) { if (Subtarget.isTargetCOFF() && G->getGlobal()->hasDLLImportStorageClass()) { diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index c65c76ccffc75..3aa0a0d3ad7c4 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -595,10 +595,10 @@ class TargetRegisterClass; /// isEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. - virtual bool - isEligibleForTailCallOptimization(const CCState &CCInfo, - unsigned NextStackOffset, - const MipsFunctionInfo &FI) const = 0; + virtual bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp index 71a70d9c2dd46..cb6bd9c385519 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp @@ -1179,9 +1179,9 @@ MipsSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, } bool MipsSETargetLowering::isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const { - if (!UseMipsTailCalls) + const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI, + bool IsMustTail) const { + if (!UseMipsTailCalls && !IsMustTail) return false; // Exception has to be cleared with eret. diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.h b/llvm/lib/Target/Mips/MipsSEISelLowering.h index 675131aefb6dd..392e8dc3dbb11 100644 --- a/llvm/lib/Target/Mips/MipsSEISelLowering.h +++ b/llvm/lib/Target/Mips/MipsSEISelLowering.h @@ -63,9 +63,10 @@ class TargetRegisterClass; const TargetRegisterClass *getRepRegClassFor(MVT VT) const override; private: - bool isEligibleForTailCallOptimization( - const CCState &CCInfo, unsigned NextStackOffset, - const MipsFunctionInfo &FI) const override; + bool isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MipsFunctionInfo &FI, + bool IsMustTail) const override; void getOpndList(SmallVectorImpl &Ops, diff --git a/llvm/test/CodeGen/Mips/musttail.ll b/llvm/test/CodeGen/Mips/musttail.ll new file mode 100644 index 0000000000000..c486ea5c59eea --- /dev/null +++ b/llvm/test/CodeGen/Mips/musttail.ll @@ -0,0 +1,114 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=mips-unknown-linux-gnu < %s | FileCheck %s --check-prefix=MIPS32 +; RUN: llc -mtriple=mips64-unknown-linux-gnu < %s | FileCheck %s --check-prefix=MIPS64 + +; Test musttail support for MIPS + +define dso_local i32 @callee_args(i32 %a, i32 %b, i32 %c) { + ret i32 %a; +} + +define i32 @test_musttail_args(i32 %x, i32 %y, i32 %z) { +; MIPS32-LABEL: test_musttail_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: j callee_args +; MIPS32-NEXT: nop +; +; MIPS64-LABEL: test_musttail_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j callee_args +; MIPS64-NEXT: nop + %ret = musttail call i32 @callee_args(i32 %x, i32 %y, i32 %z) + ret i32 %ret +} + +; Test musttail with many arguments that spill to stack (involves memory) +; MIPS O32 ABI: first 4 args in $a0-$a3, rest on stack +declare i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) + +define i32 @test_musttail_many_args(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) { +; MIPS32-LABEL: test_musttail_many_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 24($sp) +; MIPS32-NEXT: lw $2, 20($sp) +; MIPS32-NEXT: lw $3, 16($sp) +; MIPS32-NEXT: sw $3, 16($sp) +; MIPS32-NEXT: sw $2, 20($sp) +; MIPS32-NEXT: sw $1, 24($sp) +; MIPS32-NEXT: lw $1, 28($sp) +; MIPS32-NEXT: j many_args_callee +; MIPS32-NEXT: sw $1, 28($sp) +; +; MIPS64-LABEL: test_musttail_many_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j many_args_callee +; MIPS64-NEXT: nop + %ret = musttail call i32 @many_args_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) + ret i32 %ret +} + +; Test musttail with large struct passed by value (involves memory) +%struct.large = type { i32, i32, i32, i32, i32, i32, i32, i32 } + +declare i32 @callee_with_struct(%struct.large %s, i32 %x) + +define i32 @test_musttail_struct(%struct.large %s, i32 %x) { +; MIPS32-LABEL: test_musttail_struct: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 28($sp) +; MIPS32-NEXT: lw $2, 24($sp) +; MIPS32-NEXT: lw $3, 20($sp) +; MIPS32-NEXT: lw $8, 16($sp) +; MIPS32-NEXT: sw $8, 16($sp) +; MIPS32-NEXT: sw $3, 20($sp) +; MIPS32-NEXT: sw $2, 24($sp) +; MIPS32-NEXT: sw $1, 28($sp) +; MIPS32-NEXT: lw $1, 32($sp) +; MIPS32-NEXT: j callee_with_struct +; MIPS32-NEXT: sw $1, 32($sp) +; +; MIPS64-LABEL: test_musttail_struct: +; MIPS64: # %bb.0: +; MIPS64-NEXT: ld $1, 0($sp) +; MIPS64-NEXT: j callee_with_struct +; MIPS64-NEXT: sd $1, 0($sp) + %ret = musttail call i32 @callee_with_struct(%struct.large %s, i32 %x) + ret i32 %ret +} + +; Test musttail with mixed int and float arguments that use stack +declare float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) + +define float @test_musttail_mixed_args(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) { +; MIPS32-LABEL: test_musttail_mixed_args: +; MIPS32: # %bb.0: +; MIPS32-NEXT: lw $1, 16($sp) +; MIPS32-NEXT: sw $1, 16($sp) +; MIPS32-NEXT: lwc1 $f0, 20($sp) +; MIPS32-NEXT: j mixed_args_callee +; MIPS32-NEXT: swc1 $f0, 20($sp) +; +; MIPS64-LABEL: test_musttail_mixed_args: +; MIPS64: # %bb.0: +; MIPS64-NEXT: j mixed_args_callee +; MIPS64-NEXT: nop + %ret = musttail call float @mixed_args_callee(i32 %a, float %b, i32 %c, float %d, i32 %e, float %f) + ret float %ret +} + +; Test musttail with indirect call +define i32 @test_musttail_fptr(ptr %fptr, i32 %x) { +; MIPS32-LABEL: test_musttail_fptr: +; MIPS32: # %bb.0: +; MIPS32-NEXT: move $25, $4 +; MIPS32-NEXT: jr $25 +; MIPS32-NEXT: nop +; +; MIPS64-LABEL: test_musttail_fptr: +; MIPS64: # %bb.0: +; MIPS64-NEXT: move $25, $4 +; MIPS64-NEXT: jr $25 +; MIPS64-NEXT: nop + %ret = musttail call i32 %fptr(ptr %fptr, i32 %x) + ret i32 %ret +}