Fix a long standing tail call optimization bug. When a libcall is emitted

legalizer always use the DAG entry node. This is wrong when the libcall is
emitted as a tail call since it effectively folds the return node. If
the return node's input chain is not the entry (i.e. call, load, or store)
use that as the tail call input chain.

PR12419
rdar://9770785
rdar://11195178


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@154370 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Evan Cheng 2012-04-10 01:51:00 +00:00
parent fdb230a154
commit bf010eb911
9 changed files with 89 additions and 49 deletions

View File

@ -27,6 +27,7 @@ namespace llvm {
class GlobalVariable;
class TargetLowering;
class SDNode;
class SDValue;
class SelectionDAG;
/// ComputeLinearIndex - Given an LLVM IR aggregate type and a sequence
@ -89,7 +90,7 @@ bool isInTailCallPosition(ImmutableCallSite CS, Attributes CalleeRetAttr,
const TargetLowering &TLI);
bool isInTailCallPosition(SelectionDAG &DAG, SDNode *Node,
const TargetLowering &TLI);
SDValue &Chain, const TargetLowering &TLI);
} // End llvm namespace

View File

@ -1274,9 +1274,11 @@ public:
}
/// isUsedByReturnOnly - Return true if result of the specified node is used
/// by a return node only. This is used to determine whether it is possible
/// by a return node only. It also compute and return the input chain for the
/// tail call.
/// This is used to determine whether it is possible
/// to codegen a libcall as tail call at legalization time.
virtual bool isUsedByReturnOnly(SDNode *) const {
virtual bool isUsedByReturnOnly(SDNode *, SDValue &Chain) const {
return false;
}

View File

@ -290,7 +290,7 @@ bool llvm::isInTailCallPosition(ImmutableCallSite CS, Attributes CalleeRetAttr,
}
bool llvm::isInTailCallPosition(SelectionDAG &DAG, SDNode *Node,
const TargetLowering &TLI) {
SDValue &Chain, const TargetLowering &TLI) {
const Function *F = DAG.getMachineFunction().getFunction();
// Conservatively require the attributes of the call to match those of
@ -304,5 +304,5 @@ bool llvm::isInTailCallPosition(SelectionDAG &DAG, SDNode *Node,
return false;
// Check if the only use is a function return node.
return TLI.isUsedByReturnOnly(Node);
return TLI.isUsedByReturnOnly(Node, Chain);
}

View File

@ -1767,11 +1767,6 @@ SDValue SelectionDAGLegalize::ExpandBUILD_VECTOR(SDNode *Node) {
// and leave the Hi part unset.
SDValue SelectionDAGLegalize::ExpandLibCall(RTLIB::Libcall LC, SDNode *Node,
bool isSigned) {
// The input chain to this libcall is the entry node of the function.
// Legalizing the call will automatically add the previous call to the
// dependence.
SDValue InChain = DAG.getEntryNode();
TargetLowering::ArgListTy Args;
TargetLowering::ArgListEntry Entry;
for (unsigned i = 0, e = Node->getNumOperands(); i != e; ++i) {
@ -1787,9 +1782,15 @@ SDValue SelectionDAGLegalize::ExpandLibCall(RTLIB::Libcall LC, SDNode *Node,
Type *RetTy = Node->getValueType(0).getTypeForEVT(*DAG.getContext());
// By default, the input chain to this libcall is the entry node of the
// function. If the libcall is going to be emitted as a tail call then
// TLI.isUsedByReturnOnly will change it to the right chain if the return
// node which is being folded has a non-entry input chain.
SDValue InChain = DAG.getEntryNode();
// isTailCall may be true since the callee does not reference caller stack
// frame. Check if it's in the right position.
bool isTailCall = isInTailCallPosition(DAG, Node, TLI);
bool isTailCall = isInTailCallPosition(DAG, Node, InChain, TLI);
std::pair<SDValue, SDValue> CallInfo =
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
0, TLI.getLibcallCallingConv(LC), isTailCall,
@ -1825,7 +1826,7 @@ SDValue SelectionDAGLegalize::ExpandLibCall(RTLIB::Libcall LC, EVT RetVT,
Type *RetTy = RetVT.getTypeForEVT(*DAG.getContext());
std::pair<SDValue,SDValue> CallInfo =
TLI.LowerCallTo(DAG.getEntryNode(), RetTy, isSigned, !isSigned, false,
false, 0, TLI.getLibcallCallingConv(LC), false,
false, 0, TLI.getLibcallCallingConv(LC), /*isTailCall=*/false,
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
Callee, Args, DAG, dl);

View File

@ -1934,59 +1934,68 @@ ARMTargetLowering::LowerReturn(SDValue Chain,
return result;
}
bool ARMTargetLowering::isUsedByReturnOnly(SDNode *N) const {
bool ARMTargetLowering::isUsedByReturnOnly(SDNode *N, SDValue &Chain) const {
if (N->getNumValues() != 1)
return false;
if (!N->hasNUsesOfValue(1, 0))
return false;
unsigned NumCopies = 0;
SDNode* Copies[2] = { 0, 0 };
SDNode *Use = *N->use_begin();
if (Use->getOpcode() == ISD::CopyToReg) {
Copies[NumCopies++] = Use;
} else if (Use->getOpcode() == ARMISD::VMOVRRD) {
SDValue TCChain = Chain;
SDNode *Copy = *N->use_begin();
if (Copy->getOpcode() == ISD::CopyToReg) {
// If the copy has a glue operand, we conservatively assume it isn't safe to
// perform a tail call.
if (Copy->getOperand(Copy->getNumOperands()-1).getValueType() == MVT::Glue)
return false;
TCChain = Copy->getOperand(0);
} else if (Copy->getOpcode() == ARMISD::VMOVRRD) {
SDNode *VMov = Copy;
// f64 returned in a pair of GPRs.
for (SDNode::use_iterator UI = Use->use_begin(), UE = Use->use_end();
SmallPtrSet<SDNode*, 2> Copies;
for (SDNode::use_iterator UI = VMov->use_begin(), UE = VMov->use_end();
UI != UE; ++UI) {
if (UI->getOpcode() != ISD::CopyToReg)
return false;
Copies[UI.getUse().getResNo()] = *UI;
++NumCopies;
Copies.insert(*UI);
}
} else if (Use->getOpcode() == ISD::BITCAST) {
if (Copies.size() > 2)
return false;
for (SDNode::use_iterator UI = VMov->use_begin(), UE = VMov->use_end();
UI != UE; ++UI) {
SDValue UseChain = UI->getOperand(0);
if (Copies.count(UseChain.getNode()))
// Second CopyToReg
Copy = *UI;
else
// First CopyToReg
TCChain = UseChain;
}
} else if (Copy->getOpcode() == ISD::BITCAST) {
// f32 returned in a single GPR.
if (!Use->hasNUsesOfValue(1, 0))
if (!Copy->hasOneUse())
return false;
Use = *Use->use_begin();
if (Use->getOpcode() != ISD::CopyToReg || !Use->hasNUsesOfValue(1, 0))
Copy = *Copy->use_begin();
if (Copy->getOpcode() != ISD::CopyToReg || !Copy->hasNUsesOfValue(1, 0))
return false;
Copies[NumCopies++] = Use;
Chain = Copy->getOperand(0);
} else {
return false;
}
if (NumCopies != 1 && NumCopies != 2)
return false;
bool HasRet = false;
for (unsigned i = 0; i < NumCopies; ++i) {
SDNode *Copy = Copies[i];
for (SDNode::use_iterator UI = Copy->use_begin(), UE = Copy->use_end();
UI != UE; ++UI) {
if (UI->getOpcode() == ISD::CopyToReg) {
SDNode *Use = *UI;
if (Use == Copies[0] || ((NumCopies == 2) && (Use == Copies[1])))
continue;
return false;
}
if (UI->getOpcode() != ARMISD::RET_FLAG)
return false;
HasRet = true;
}
for (SDNode::use_iterator UI = Copy->use_begin(), UE = Copy->use_end();
UI != UE; ++UI) {
if (UI->getOpcode() != ARMISD::RET_FLAG)
return false;
HasRet = true;
}
return HasRet;
if (!HasRet)
return false;
Chain = TCChain;
return true;
}
bool ARMTargetLowering::mayBeEmittedAsTailCall(CallInst *CI) const {

View File

@ -493,7 +493,7 @@ namespace llvm {
const SmallVectorImpl<SDValue> &OutVals,
DebugLoc dl, SelectionDAG &DAG) const;
virtual bool isUsedByReturnOnly(SDNode *N) const;
virtual bool isUsedByReturnOnly(SDNode *N, SDValue &Chain) const;
virtual bool mayBeEmittedAsTailCall(CallInst *CI) const;

View File

@ -1578,18 +1578,20 @@ X86TargetLowering::LowerReturn(SDValue Chain,
MVT::Other, &RetOps[0], RetOps.size());
}
bool X86TargetLowering::isUsedByReturnOnly(SDNode *N) const {
bool X86TargetLowering::isUsedByReturnOnly(SDNode *N, SDValue &Chain) const {
if (N->getNumValues() != 1)
return false;
if (!N->hasNUsesOfValue(1, 0))
return false;
SDValue TCChain = Chain;
SDNode *Copy = *N->use_begin();
if (Copy->getOpcode() == ISD::CopyToReg) {
// If the copy has a glue operand, we conservatively assume it isn't safe to
// perform a tail call.
if (Copy->getOperand(Copy->getNumOperands()-1).getValueType() == MVT::Glue)
return false;
TCChain = Copy->getOperand(0);
} else if (Copy->getOpcode() != ISD::FP_EXTEND)
return false;
@ -1601,7 +1603,11 @@ bool X86TargetLowering::isUsedByReturnOnly(SDNode *N) const {
HasRet = true;
}
return HasRet;
if (!HasRet)
return false;
Chain = TCChain;
return true;
}
EVT

View File

@ -805,7 +805,7 @@ namespace llvm {
const SmallVectorImpl<SDValue> &OutVals,
DebugLoc dl, SelectionDAG &DAG) const;
virtual bool isUsedByReturnOnly(SDNode *N) const;
virtual bool isUsedByReturnOnly(SDNode *N, SDValue &Chain) const;
virtual bool mayBeEmittedAsTailCall(CallInst *CI) const;

View File

@ -138,3 +138,24 @@ declare i32 @a(i32)
declare i32 @b(i32)
declare i32 @c(i32)
; PR12419
; rdar://11195178
; Use the correct input chain for the tailcall node or else the call to
; _ZN9MutexLockD1Ev would be lost.
%class.MutexLock = type { i8 }
@x = external global i32, align 4
define i32 @_Z5test1v() nounwind {
%lock = alloca %class.MutexLock, align 1
%1 = call %class.MutexLock* @_ZN9MutexLockC1Ev(%class.MutexLock* %lock)
%2 = load i32* @x, align 4
%3 = sdiv i32 1000, %2
%4 = call %class.MutexLock* @_ZN9MutexLockD1Ev(%class.MutexLock* %lock)
ret i32 %3
}
declare %class.MutexLock* @_ZN9MutexLockC1Ev(%class.MutexLock*) unnamed_addr nounwind align 2
declare %class.MutexLock* @_ZN9MutexLockD1Ev(%class.MutexLock*) unnamed_addr nounwind align 2