mirror of
https://github.com/RPCSX/llvm.git
synced 2025-01-05 19:29:01 +00:00
[X86] Clean up the tail call eligibility logic
Summary: The logic here isn't straightforward because our support for TargetOptions::GuaranteedTailCallOpt. Also fix a bug where we were allowing tail calls to cdecl functions from fastcall and vectorcall functions. We were special casing thiscall and stdcall callers rather than checking for any convention that requires clearing stack arguments before returning. Reviewers: hans Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D14024 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@251137 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
2f7d3cda4a
commit
34f572a0d2
@ -2458,19 +2458,29 @@ CreateCopyOfByValArgument(SDValue Src, SDValue Dst, SDValue Chain,
|
|||||||
MachinePointerInfo(), MachinePointerInfo());
|
MachinePointerInfo(), MachinePointerInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the calling convention is one that
|
/// Return true if the calling convention is one that we can guarantee TCO for.
|
||||||
/// supports tail call optimization.
|
static bool canGuaranteeTCO(CallingConv::ID CC) {
|
||||||
static bool IsTailCallConvention(CallingConv::ID CC) {
|
|
||||||
return (CC == CallingConv::Fast || CC == CallingConv::GHC ||
|
return (CC == CallingConv::Fast || CC == CallingConv::GHC ||
|
||||||
CC == CallingConv::HiPE || CC == CallingConv::HHVM);
|
CC == CallingConv::HiPE || CC == CallingConv::HHVM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Return true if the calling convention is a C calling convention.
|
/// \brief Return true if the calling convention is a C calling convention.
|
||||||
static bool IsCCallConvention(CallingConv::ID CC) {
|
static bool isCCallConvention(CallingConv::ID CC) {
|
||||||
return (CC == CallingConv::C || CC == CallingConv::X86_64_Win64 ||
|
return (CC == CallingConv::C || CC == CallingConv::X86_64_Win64 ||
|
||||||
CC == CallingConv::X86_64_SysV);
|
CC == CallingConv::X86_64_SysV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if we might ever do TCO for calls with this calling convention.
|
||||||
|
static bool mayTailCallThisCC(CallingConv::ID CC) {
|
||||||
|
return isCCallConvention(CC) || canGuaranteeTCO(CC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true if the function is being made into a tailcall target by
|
||||||
|
/// changing its ABI.
|
||||||
|
static bool shouldGuaranteeTCO(CallingConv::ID CC, bool GuaranteedTailCallOpt) {
|
||||||
|
return GuaranteedTailCallOpt && canGuaranteeTCO(CC);
|
||||||
|
}
|
||||||
|
|
||||||
bool X86TargetLowering::mayBeEmittedAsTailCall(CallInst *CI) const {
|
bool X86TargetLowering::mayBeEmittedAsTailCall(CallInst *CI) const {
|
||||||
auto Attr =
|
auto Attr =
|
||||||
CI->getParent()->getParent()->getFnAttribute("disable-tail-calls");
|
CI->getParent()->getParent()->getFnAttribute("disable-tail-calls");
|
||||||
@ -2479,19 +2489,12 @@ bool X86TargetLowering::mayBeEmittedAsTailCall(CallInst *CI) const {
|
|||||||
|
|
||||||
CallSite CS(CI);
|
CallSite CS(CI);
|
||||||
CallingConv::ID CalleeCC = CS.getCallingConv();
|
CallingConv::ID CalleeCC = CS.getCallingConv();
|
||||||
if (!IsTailCallConvention(CalleeCC) && !IsCCallConvention(CalleeCC))
|
if (!mayTailCallThisCC(CalleeCC))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the function is being made into
|
|
||||||
/// a tailcall target by changing its ABI.
|
|
||||||
static bool FuncIsMadeTailCallSafe(CallingConv::ID CC,
|
|
||||||
bool GuaranteedTailCallOpt) {
|
|
||||||
return GuaranteedTailCallOpt && IsTailCallConvention(CC);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDValue
|
SDValue
|
||||||
X86TargetLowering::LowerMemArgument(SDValue Chain,
|
X86TargetLowering::LowerMemArgument(SDValue Chain,
|
||||||
CallingConv::ID CallConv,
|
CallingConv::ID CallConv,
|
||||||
@ -2502,7 +2505,7 @@ X86TargetLowering::LowerMemArgument(SDValue Chain,
|
|||||||
unsigned i) const {
|
unsigned i) const {
|
||||||
// Create the nodes corresponding to a load from this parameter slot.
|
// Create the nodes corresponding to a load from this parameter slot.
|
||||||
ISD::ArgFlagsTy Flags = Ins[i].Flags;
|
ISD::ArgFlagsTy Flags = Ins[i].Flags;
|
||||||
bool AlwaysUseMutable = FuncIsMadeTailCallSafe(
|
bool AlwaysUseMutable = shouldGuaranteeTCO(
|
||||||
CallConv, DAG.getTarget().Options.GuaranteedTailCallOpt);
|
CallConv, DAG.getTarget().Options.GuaranteedTailCallOpt);
|
||||||
bool isImmutable = !AlwaysUseMutable && !Flags.isByVal();
|
bool isImmutable = !AlwaysUseMutable && !Flags.isByVal();
|
||||||
EVT ValVT;
|
EVT ValVT;
|
||||||
@ -2605,7 +2608,7 @@ SDValue X86TargetLowering::LowerFormalArguments(
|
|||||||
bool Is64Bit = Subtarget->is64Bit();
|
bool Is64Bit = Subtarget->is64Bit();
|
||||||
bool IsWin64 = Subtarget->isCallingConvWin64(CallConv);
|
bool IsWin64 = Subtarget->isCallingConvWin64(CallConv);
|
||||||
|
|
||||||
assert(!(isVarArg && IsTailCallConvention(CallConv)) &&
|
assert(!(isVarArg && canGuaranteeTCO(CallConv)) &&
|
||||||
"Var args not supported with calling convention fastcc, ghc or hipe");
|
"Var args not supported with calling convention fastcc, ghc or hipe");
|
||||||
|
|
||||||
// Assign locations to all of the incoming arguments.
|
// Assign locations to all of the incoming arguments.
|
||||||
@ -2716,8 +2719,8 @@ SDValue X86TargetLowering::LowerFormalArguments(
|
|||||||
|
|
||||||
unsigned StackSize = CCInfo.getNextStackOffset();
|
unsigned StackSize = CCInfo.getNextStackOffset();
|
||||||
// Align stack specially for tail calls.
|
// Align stack specially for tail calls.
|
||||||
if (FuncIsMadeTailCallSafe(CallConv,
|
if (shouldGuaranteeTCO(CallConv,
|
||||||
MF.getTarget().Options.GuaranteedTailCallOpt))
|
MF.getTarget().Options.GuaranteedTailCallOpt))
|
||||||
StackSize = GetAlignedArgumentStackSize(StackSize, DAG);
|
StackSize = GetAlignedArgumentStackSize(StackSize, DAG);
|
||||||
|
|
||||||
// If the function takes variable number of arguments, make a frame index for
|
// If the function takes variable number of arguments, make a frame index for
|
||||||
@ -2870,7 +2873,7 @@ SDValue X86TargetLowering::LowerFormalArguments(
|
|||||||
} else {
|
} else {
|
||||||
FuncInfo->setBytesToPopOnReturn(0); // Callee pops nothing.
|
FuncInfo->setBytesToPopOnReturn(0); // Callee pops nothing.
|
||||||
// If this is an sret function, the return should pop the hidden pointer.
|
// If this is an sret function, the return should pop the hidden pointer.
|
||||||
if (!Is64Bit && !IsTailCallConvention(CallConv) &&
|
if (!Is64Bit && !canGuaranteeTCO(CallConv) &&
|
||||||
!Subtarget->getTargetTriple().isOSMSVCRT() &&
|
!Subtarget->getTargetTriple().isOSMSVCRT() &&
|
||||||
argsAreStructReturn(Ins) == StackStructReturn)
|
argsAreStructReturn(Ins) == StackStructReturn)
|
||||||
FuncInfo->setBytesToPopOnReturn(4);
|
FuncInfo->setBytesToPopOnReturn(4);
|
||||||
@ -3037,7 +3040,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
++NumTailCalls;
|
++NumTailCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!(isVarArg && IsTailCallConvention(CallConv)) &&
|
assert(!(isVarArg && canGuaranteeTCO(CallConv)) &&
|
||||||
"Var args not supported with calling convention fastcc, ghc or hipe");
|
"Var args not supported with calling convention fastcc, ghc or hipe");
|
||||||
|
|
||||||
// Analyze operands of the call, assigning locations to each operand.
|
// Analyze operands of the call, assigning locations to each operand.
|
||||||
@ -3057,7 +3060,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
// own caller's stack.
|
// own caller's stack.
|
||||||
NumBytes = 0;
|
NumBytes = 0;
|
||||||
else if (MF.getTarget().Options.GuaranteedTailCallOpt &&
|
else if (MF.getTarget().Options.GuaranteedTailCallOpt &&
|
||||||
IsTailCallConvention(CallConv))
|
canGuaranteeTCO(CallConv))
|
||||||
NumBytes = GetAlignedArgumentStackSize(NumBytes, DAG);
|
NumBytes = GetAlignedArgumentStackSize(NumBytes, DAG);
|
||||||
|
|
||||||
int FPDiff = 0;
|
int FPDiff = 0;
|
||||||
@ -3461,7 +3464,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
if (X86::isCalleePop(CallConv, Is64Bit, isVarArg,
|
if (X86::isCalleePop(CallConv, Is64Bit, isVarArg,
|
||||||
DAG.getTarget().Options.GuaranteedTailCallOpt))
|
DAG.getTarget().Options.GuaranteedTailCallOpt))
|
||||||
NumBytesForCalleeToPop = NumBytes; // Callee pops everything
|
NumBytesForCalleeToPop = NumBytes; // Callee pops everything
|
||||||
else if (!Is64Bit && !IsTailCallConvention(CallConv) &&
|
else if (!Is64Bit && !canGuaranteeTCO(CallConv) &&
|
||||||
!Subtarget->getTargetTriple().isOSMSVCRT() &&
|
!Subtarget->getTargetTriple().isOSMSVCRT() &&
|
||||||
SR == StackStructReturn)
|
SR == StackStructReturn)
|
||||||
// If this is a call to a struct-return function, the callee
|
// If this is a call to a struct-return function, the callee
|
||||||
@ -3603,11 +3606,11 @@ bool X86TargetLowering::IsEligibleForTailCallOptimization(
|
|||||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||||
const SmallVectorImpl<SDValue> &OutVals,
|
const SmallVectorImpl<SDValue> &OutVals,
|
||||||
const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG) const {
|
const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG) const {
|
||||||
if (!IsTailCallConvention(CalleeCC) && !IsCCallConvention(CalleeCC))
|
if (!mayTailCallThisCC(CalleeCC))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If -tailcallopt is specified, make fastcc functions tail-callable.
|
// If -tailcallopt is specified, make fastcc functions tail-callable.
|
||||||
const MachineFunction &MF = DAG.getMachineFunction();
|
MachineFunction &MF = DAG.getMachineFunction();
|
||||||
const Function *CallerF = MF.getFunction();
|
const Function *CallerF = MF.getFunction();
|
||||||
|
|
||||||
// If the function return type is x86_fp80 and the callee return type is not,
|
// If the function return type is x86_fp80 and the callee return type is not,
|
||||||
@ -3628,7 +3631,7 @@ bool X86TargetLowering::IsEligibleForTailCallOptimization(
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (DAG.getTarget().Options.GuaranteedTailCallOpt) {
|
if (DAG.getTarget().Options.GuaranteedTailCallOpt) {
|
||||||
if (IsTailCallConvention(CalleeCC) && CCMatch)
|
if (canGuaranteeTCO(CalleeCC) && CCMatch)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3647,13 +3650,15 @@ bool X86TargetLowering::IsEligibleForTailCallOptimization(
|
|||||||
if (isCalleeStructRet || isCallerStructRet)
|
if (isCalleeStructRet || isCallerStructRet)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// An stdcall/thiscall caller is expected to clean up its arguments; the
|
// Don't do TCO when the current function is expected to clear its stack and
|
||||||
// callee isn't going to do that.
|
// the callee's convention does not match.
|
||||||
// FIXME: this is more restrictive than needed. We could produce a tailcall
|
// FIXME: this is more restrictive than needed. We could produce a tailcall
|
||||||
// when the stack adjustment matches. For example, with a thiscall that takes
|
// when the stack adjustment matches. For example, with a thiscall that takes
|
||||||
// only one argument.
|
// only one argument.
|
||||||
if (!CCMatch && (CallerCC == CallingConv::X86_StdCall ||
|
bool CallerPopsArgs =
|
||||||
CallerCC == CallingConv::X86_ThisCall))
|
X86::isCalleePop(CallerCC, Subtarget->is64Bit(), CallerF->isVarArg(),
|
||||||
|
/*GuaranteeTCO=*/false);
|
||||||
|
if (CallerPopsArgs && !CCMatch)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Do not sibcall optimize vararg calls unless all arguments are passed via
|
// Do not sibcall optimize vararg calls unless all arguments are passed via
|
||||||
@ -3742,7 +3747,6 @@ bool X86TargetLowering::IsEligibleForTailCallOptimization(
|
|||||||
|
|
||||||
CCInfo.AnalyzeCallOperands(Outs, CC_X86);
|
CCInfo.AnalyzeCallOperands(Outs, CC_X86);
|
||||||
if (CCInfo.getNextStackOffset()) {
|
if (CCInfo.getNextStackOffset()) {
|
||||||
MachineFunction &MF = DAG.getMachineFunction();
|
|
||||||
if (MF.getInfo<X86MachineFunctionInfo>()->getBytesToPopOnReturn())
|
if (MF.getInfo<X86MachineFunctionInfo>()->getBytesToPopOnReturn())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -3931,10 +3935,11 @@ bool X86::isOffsetSuitableForCodeModel(int64_t Offset, CodeModel::Model M,
|
|||||||
/// Determines whether the callee is required to pop its own arguments.
|
/// Determines whether the callee is required to pop its own arguments.
|
||||||
/// Callee pop is necessary to support tail calls.
|
/// Callee pop is necessary to support tail calls.
|
||||||
bool X86::isCalleePop(CallingConv::ID CallingConv,
|
bool X86::isCalleePop(CallingConv::ID CallingConv,
|
||||||
bool is64Bit, bool IsVarArg, bool TailCallOpt) {
|
bool is64Bit, bool IsVarArg, bool GuaranteeTCO) {
|
||||||
|
// If GuaranteeTCO is true, we force some calls to be callee pop so that we
|
||||||
if (IsTailCallConvention(CallingConv))
|
// can guarantee TCO.
|
||||||
return IsVarArg ? false : TailCallOpt;
|
if (!IsVarArg && shouldGuaranteeTCO(CallingConv, GuaranteeTCO))
|
||||||
|
return true;
|
||||||
|
|
||||||
switch (CallingConv) {
|
switch (CallingConv) {
|
||||||
default:
|
default:
|
||||||
@ -3942,6 +3947,7 @@ bool X86::isCalleePop(CallingConv::ID CallingConv,
|
|||||||
case CallingConv::X86_StdCall:
|
case CallingConv::X86_StdCall:
|
||||||
case CallingConv::X86_FastCall:
|
case CallingConv::X86_FastCall:
|
||||||
case CallingConv::X86_ThisCall:
|
case CallingConv::X86_ThisCall:
|
||||||
|
case CallingConv::X86_VectorCall:
|
||||||
return !is64Bit;
|
return !is64Bit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
test/CodeGen/X86/tailcall-msvc-conventions.ll
Normal file
40
test/CodeGen/X86/tailcall-msvc-conventions.ll
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
; RUN: llc -mtriple=i686-windows-msvc -O1 < %s | FileCheck %s
|
||||||
|
; RUN: llc -mtriple=i686-windows-msvc -O0 < %s | FileCheck %s
|
||||||
|
|
||||||
|
; The MSVC family of x86 calling conventions makes tail calls really tricky.
|
||||||
|
; Tests of all the various combinations should live here.
|
||||||
|
|
||||||
|
declare i32 @cdecl_i32()
|
||||||
|
declare void @cdecl_void()
|
||||||
|
|
||||||
|
; Don't allow tail calling these cdecl functions, because we need to clear the
|
||||||
|
; incoming stack arguments for these argument-clearing conventions.
|
||||||
|
|
||||||
|
define x86_thiscallcc void @thiscall_cdecl_notail(i32 %a, i32 %b, i32 %c) {
|
||||||
|
tail call void @cdecl_void()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; CHECK-LABEL: thiscall_cdecl_notail: # @thiscall_cdecl_notail
|
||||||
|
; CHECK: calll _cdecl_void
|
||||||
|
; CHECK: retl $8
|
||||||
|
define x86_stdcallcc void @stdcall_cdecl_notail(i32 %a, i32 %b, i32 %c) {
|
||||||
|
tail call void @cdecl_void()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; CHECK-LABEL: _stdcall_cdecl_notail@12: # @stdcall_cdecl_notail
|
||||||
|
; CHECK: calll _cdecl_void
|
||||||
|
; CHECK: retl $12
|
||||||
|
define x86_vectorcallcc void @vectorcall_cdecl_notail(i32 inreg %a, i32 inreg %b, i32 %c) {
|
||||||
|
tail call void @cdecl_void()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; CHECK-LABEL: vectorcall_cdecl_notail@@12: # @vectorcall_cdecl_notail
|
||||||
|
; CHECK: calll _cdecl_void
|
||||||
|
; CHECK: retl $4
|
||||||
|
define x86_fastcallcc void @fastcall_cdecl_notail(i32 inreg %a, i32 inreg %b, i32 %c) {
|
||||||
|
tail call void @cdecl_void()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; CHECK-LABEL: @fastcall_cdecl_notail@12: # @fastcall_cdecl_notail
|
||||||
|
; CHECK: calll _cdecl_void
|
||||||
|
; CHECK: retl $4
|
Loading…
Reference in New Issue
Block a user