mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
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 llvm-svn: 154370
This commit is contained in:
parent
9febd1fbf7
commit
5825e9dbf5
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user