1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

Add CodeGen support for functions that always return arguments via a new parameter attribute 'returned', which is taken advantage of in target-independent tail call opportunity detection and in ARM call lowering (when placed on an integral first parameter).

llvm-svn: 179925
This commit is contained in:
Stephen Lin 2013-04-20 05:14:40 +00:00
parent 65c1101eba
commit 9d99ba2071
20 changed files with 371 additions and 43 deletions

View File

@ -719,7 +719,17 @@ Currently, only the following parameter attributes are defined:
``nest`` ``nest``
This indicates that the pointer parameter can be excised using the This indicates that the pointer parameter can be excised using the
:ref:`trampoline intrinsics <int_trampoline>`. This is not a valid :ref:`trampoline intrinsics <int_trampoline>`. This is not a valid
attribute for return values. attribute for return values and can only be applied to one parameter.
``returned``
This indicates that the value of the function always returns the value
of the parameter as its return value. This is an optimization hint to
the code generator when generating the caller, allowing tail call
optimization and omission of register saves and restores in some cases;
it is not checked or enforced when generating the callee. The parameter
and the function return type must be valid operands for the
:ref:`bitcast instruction <i_bitcast>`. This is not a valid attribute for
return values and can only be applied to one parameter.
.. _gc: .. _gc:

View File

@ -78,6 +78,10 @@ public:
/// containing function. /// containing function.
bool hasStructRetAttr() const; bool hasStructRetAttr() const;
/// \brief Return true if this argument has the returned attribute on it in
/// its containing function.
bool hasReturnedAttr() const;
/// \brief Add a Attribute to an argument. /// \brief Add a Attribute to an argument.
void addAttr(AttributeSet AS); void addAttr(AttributeSet AS);

View File

@ -87,6 +87,7 @@ public:
OptimizeForSize, ///< opt_size OptimizeForSize, ///< opt_size
ReadNone, ///< Function does not access memory ReadNone, ///< Function does not access memory
ReadOnly, ///< Function only reads from memory ReadOnly, ///< Function only reads from memory
Returned, ///< Return value is always equal to this argument
ReturnsTwice, ///< Function can return twice ReturnsTwice, ///< Function can return twice
SExt, ///< Sign extended before/after call SExt, ///< Sign extended before/after call
StackAlignment, ///< Alignment of stack for function (3 bits) StackAlignment, ///< Alignment of stack for function (3 bits)

View File

@ -36,10 +36,12 @@ namespace ISD {
static const uint64_t ByValOffs = 4; static const uint64_t ByValOffs = 4;
static const uint64_t Nest = 1ULL<<5; ///< Nested fn static chain static const uint64_t Nest = 1ULL<<5; ///< Nested fn static chain
static const uint64_t NestOffs = 5; static const uint64_t NestOffs = 5;
static const uint64_t ByValAlign = 0xFULL << 6; ///< Struct alignment static const uint64_t Returned = 1ULL<<6;
static const uint64_t ByValAlignOffs = 6; static const uint64_t ReturnedOffs = 6;
static const uint64_t Split = 1ULL << 10; static const uint64_t ByValAlign = 0xFULL<<7; ///< Struct alignment
static const uint64_t SplitOffs = 10; static const uint64_t ByValAlignOffs = 7;
static const uint64_t Split = 1ULL<<11;
static const uint64_t SplitOffs = 11;
static const uint64_t OrigAlign = 0x1FULL<<27; static const uint64_t OrigAlign = 0x1FULL<<27;
static const uint64_t OrigAlignOffs = 27; static const uint64_t OrigAlignOffs = 27;
static const uint64_t ByValSize = 0xffffffffULL << 32; ///< Struct size static const uint64_t ByValSize = 0xffffffffULL << 32; ///< Struct size
@ -51,23 +53,26 @@ namespace ISD {
public: public:
ArgFlagsTy() : Flags(0) { } ArgFlagsTy() : Flags(0) { }
bool isZExt() const { return Flags & ZExt; } bool isZExt() const { return Flags & ZExt; }
void setZExt() { Flags |= One << ZExtOffs; } void setZExt() { Flags |= One << ZExtOffs; }
bool isSExt() const { return Flags & SExt; } bool isSExt() const { return Flags & SExt; }
void setSExt() { Flags |= One << SExtOffs; } void setSExt() { Flags |= One << SExtOffs; }
bool isInReg() const { return Flags & InReg; } bool isInReg() const { return Flags & InReg; }
void setInReg() { Flags |= One << InRegOffs; } void setInReg() { Flags |= One << InRegOffs; }
bool isSRet() const { return Flags & SRet; } bool isSRet() const { return Flags & SRet; }
void setSRet() { Flags |= One << SRetOffs; } void setSRet() { Flags |= One << SRetOffs; }
bool isByVal() const { return Flags & ByVal; } bool isByVal() const { return Flags & ByVal; }
void setByVal() { Flags |= One << ByValOffs; } void setByVal() { Flags |= One << ByValOffs; }
bool isNest() const { return Flags & Nest; } bool isNest() const { return Flags & Nest; }
void setNest() { Flags |= One << NestOffs; } void setNest() { Flags |= One << NestOffs; }
bool isReturned() const { return Flags & Returned; }
void setReturned() { Flags |= One << ReturnedOffs; }
unsigned getByValAlign() const { unsigned getByValAlign() const {
return (unsigned) return (unsigned)

View File

@ -1910,16 +1910,18 @@ public:
struct ArgListEntry { struct ArgListEntry {
SDValue Node; SDValue Node;
Type* Ty; Type* Ty;
bool isSExt : 1; bool isSExt : 1;
bool isZExt : 1; bool isZExt : 1;
bool isInReg : 1; bool isInReg : 1;
bool isSRet : 1; bool isSRet : 1;
bool isNest : 1; bool isNest : 1;
bool isByVal : 1; bool isByVal : 1;
bool isReturned : 1;
uint16_t Alignment; uint16_t Alignment;
ArgListEntry() : isSExt(false), isZExt(false), isInReg(false), ArgListEntry() : isSExt(false), isZExt(false), isInReg(false),
isSRet(false), isNest(false), isByVal(false), Alignment(0) { } isSRet(false), isNest(false), isByVal(false), isReturned(false),
Alignment(0) { }
}; };
typedef std::vector<ArgListEntry> ArgListTy; typedef std::vector<ArgListEntry> ArgListTy;

View File

@ -582,6 +582,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(optsize); KEYWORD(optsize);
KEYWORD(readnone); KEYWORD(readnone);
KEYWORD(readonly); KEYWORD(readonly);
KEYWORD(returned);
KEYWORD(returns_twice); KEYWORD(returns_twice);
KEYWORD(signext); KEYWORD(signext);
KEYWORD(sret); KEYWORD(sret);

View File

@ -944,6 +944,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_nest: case lltok::kw_nest:
case lltok::kw_noalias: case lltok::kw_noalias:
case lltok::kw_nocapture: case lltok::kw_nocapture:
case lltok::kw_returned:
case lltok::kw_sret: case lltok::kw_sret:
HaveError |= HaveError |=
Error(Lex.getLoc(), Error(Lex.getLoc(),
@ -1156,6 +1157,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_nest: B.addAttribute(Attribute::Nest); break; case lltok::kw_nest: B.addAttribute(Attribute::Nest); break;
case lltok::kw_noalias: B.addAttribute(Attribute::NoAlias); break; case lltok::kw_noalias: B.addAttribute(Attribute::NoAlias); break;
case lltok::kw_nocapture: B.addAttribute(Attribute::NoCapture); break; case lltok::kw_nocapture: B.addAttribute(Attribute::NoCapture); break;
case lltok::kw_returned: B.addAttribute(Attribute::Returned); break;
case lltok::kw_signext: B.addAttribute(Attribute::SExt); break; case lltok::kw_signext: B.addAttribute(Attribute::SExt); break;
case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break; case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break;
case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break; case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break;
@ -1199,6 +1201,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_byval: case lltok::kw_byval:
case lltok::kw_nest: case lltok::kw_nest:
case lltok::kw_nocapture: case lltok::kw_nocapture:
case lltok::kw_returned:
case lltok::kw_sret: case lltok::kw_sret:
HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute"); HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute");
break; break;

View File

@ -114,6 +114,7 @@ namespace lltok {
kw_optsize, kw_optsize,
kw_readnone, kw_readnone,
kw_readonly, kw_readonly,
kw_returned,
kw_returns_twice, kw_returns_twice,
kw_signext, kw_signext,
kw_ssp, kw_ssp,

View File

@ -261,6 +261,32 @@ static bool sameNoopInput(const Value *V1, const Value *V2,
TLI.getPointerTy().getSizeInBits() == TLI.getPointerTy().getSizeInBits() ==
cast<IntegerType>(I->getType())->getBitWidth()) cast<IntegerType>(I->getType())->getBitWidth())
NoopInput = Op; NoopInput = Op;
} else if (isa<CallInst>(I)) {
// Look through call
for (User::const_op_iterator i = I->op_begin(),
// Skip Callee
e = I->op_end() - 1;
i != e; ++i) {
unsigned attrInd = i - I->op_begin() + 1;
if (cast<CallInst>(I)->paramHasAttr(attrInd, Attribute::Returned) &&
isNoopBitcast((*i)->getType(), I->getType(), TLI)) {
NoopInput = *i;
break;
}
}
} else if (isa<InvokeInst>(I)) {
// Look through invoke
for (User::const_op_iterator i = I->op_begin(),
// Skip BB, BB, Callee
e = I->op_end() - 3;
i != e; ++i) {
unsigned attrInd = i - I->op_begin() + 1;
if (cast<InvokeInst>(I)->paramHasAttr(attrInd, Attribute::Returned) &&
isNoopBitcast((*i)->getType(), I->getType(), TLI)) {
NoopInput = *i;
break;
}
}
} }
} }

View File

@ -5232,6 +5232,7 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee,
Entry.isSRet = true; Entry.isSRet = true;
Entry.isNest = false; Entry.isNest = false;
Entry.isByVal = false; Entry.isByVal = false;
Entry.isReturned = false;
Entry.Alignment = Align; Entry.Alignment = Align;
Args.push_back(Entry); Args.push_back(Entry);
RetTy = Type::getVoidTy(FTy->getContext()); RetTy = Type::getVoidTy(FTy->getContext());
@ -5249,13 +5250,14 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee,
Entry.Node = ArgNode; Entry.Ty = V->getType(); Entry.Node = ArgNode; Entry.Ty = V->getType();
unsigned attrInd = i - CS.arg_begin() + 1; unsigned attrInd = i - CS.arg_begin() + 1;
Entry.isSExt = CS.paramHasAttr(attrInd, Attribute::SExt); Entry.isSExt = CS.paramHasAttr(attrInd, Attribute::SExt);
Entry.isZExt = CS.paramHasAttr(attrInd, Attribute::ZExt); Entry.isZExt = CS.paramHasAttr(attrInd, Attribute::ZExt);
Entry.isInReg = CS.paramHasAttr(attrInd, Attribute::InReg); Entry.isInReg = CS.paramHasAttr(attrInd, Attribute::InReg);
Entry.isSRet = CS.paramHasAttr(attrInd, Attribute::StructRet); Entry.isSRet = CS.paramHasAttr(attrInd, Attribute::StructRet);
Entry.isNest = CS.paramHasAttr(attrInd, Attribute::Nest); Entry.isNest = CS.paramHasAttr(attrInd, Attribute::Nest);
Entry.isByVal = CS.paramHasAttr(attrInd, Attribute::ByVal); Entry.isByVal = CS.paramHasAttr(attrInd, Attribute::ByVal);
Entry.Alignment = CS.getParamAlignment(attrInd); Entry.isReturned = CS.paramHasAttr(attrInd, Attribute::Returned);
Entry.Alignment = CS.getParamAlignment(attrInd);
Args.push_back(Entry); Args.push_back(Entry);
} }
@ -6430,6 +6432,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
} }
if (Args[i].isNest) if (Args[i].isNest)
Flags.setNest(); Flags.setNest();
if (Args[i].isReturned)
Flags.setReturned();
Flags.setOrigAlign(OriginalAlignment); Flags.setOrigAlign(OriginalAlignment);
MVT PartVT = getRegisterType(CLI.RetTy->getContext(), VT); MVT PartVT = getRegisterType(CLI.RetTy->getContext(), VT);

View File

@ -195,6 +195,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "readnone"; return "readnone";
if (hasAttribute(Attribute::ReadOnly)) if (hasAttribute(Attribute::ReadOnly))
return "readonly"; return "readonly";
if (hasAttribute(Attribute::Returned))
return "returned";
if (hasAttribute(Attribute::ReturnsTwice)) if (hasAttribute(Attribute::ReturnsTwice))
return "returns_twice"; return "returns_twice";
if (hasAttribute(Attribute::SExt)) if (hasAttribute(Attribute::SExt))
@ -393,6 +395,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::SanitizeThread: return 1ULL << 36; case Attribute::SanitizeThread: return 1ULL << 36;
case Attribute::SanitizeMemory: return 1ULL << 37; case Attribute::SanitizeMemory: return 1ULL << 37;
case Attribute::NoBuiltin: return 1ULL << 38; case Attribute::NoBuiltin: return 1ULL << 38;
case Attribute::Returned: return 1ULL << 39;
} }
llvm_unreachable("Unsupported attribute type"); llvm_unreachable("Unsupported attribute type");
} }

View File

@ -124,6 +124,13 @@ bool Argument::hasStructRetAttr() const {
hasAttribute(1, Attribute::StructRet); hasAttribute(1, Attribute::StructRet);
} }
/// hasReturnedAttr - Return true if this argument has the returned attribute on
/// it in its containing function.
bool Argument::hasReturnedAttr() const {
return getParent()->getAttributes().
hasAttribute(getArgNo()+1, Attribute::Returned);
}
/// addAttr - Add attributes to an argument. /// addAttr - Add attributes to an argument.
void Argument::addAttr(AttributeSet AS) { void Argument::addAttr(AttributeSet AS) {
assert(AS.getNumSlots() <= 1 && assert(AS.getNumSlots() <= 1 &&

View File

@ -694,8 +694,9 @@ void Verifier::VerifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal) && Assert1(!Attrs.hasAttribute(Idx, Attribute::ByVal) &&
!Attrs.hasAttribute(Idx, Attribute::Nest) && !Attrs.hasAttribute(Idx, Attribute::Nest) &&
!Attrs.hasAttribute(Idx, Attribute::StructRet) && !Attrs.hasAttribute(Idx, Attribute::StructRet) &&
!Attrs.hasAttribute(Idx, Attribute::NoCapture), !Attrs.hasAttribute(Idx, Attribute::NoCapture) &&
"Attribute 'byval', 'nest', 'sret', and 'nocapture' " !Attrs.hasAttribute(Idx, Attribute::Returned),
"Attribute 'byval', 'nest', 'sret', 'nocapture', and 'returned' "
"do not apply to return values!", V); "do not apply to return values!", V);
// Check for mutually incompatible attributes. // Check for mutually incompatible attributes.
@ -750,6 +751,7 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
return; return;
bool SawNest = false; bool SawNest = false;
bool SawReturned = false;
for (unsigned i = 0, e = Attrs.getNumSlots(); i != e; ++i) { for (unsigned i = 0, e = Attrs.getNumSlots(); i != e; ++i) {
unsigned Idx = Attrs.getSlotIndex(i); unsigned Idx = Attrs.getSlotIndex(i);
@ -764,11 +766,22 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
VerifyParameterAttrs(Attrs, Idx, Ty, Idx == 0, V); VerifyParameterAttrs(Attrs, Idx, Ty, Idx == 0, V);
if (Attrs.hasAttribute(i, Attribute::Nest)) { if (Idx == 0)
continue;
if (Attrs.hasAttribute(Idx, Attribute::Nest)) {
Assert1(!SawNest, "More than one parameter has attribute nest!", V); Assert1(!SawNest, "More than one parameter has attribute nest!", V);
SawNest = true; SawNest = true;
} }
if (Attrs.hasAttribute(Idx, Attribute::Returned)) {
Assert1(!SawReturned, "More than one parameter has attribute returned!",
V);
Assert1(Ty->canLosslesslyBitCastTo(FT->getReturnType()), "Incompatible "
"argument and return types for 'returned' attribute", V);
SawReturned = true;
}
if (Attrs.hasAttribute(Idx, Attribute::StructRet)) if (Attrs.hasAttribute(Idx, Attribute::StructRet))
Assert1(Idx == 1, "Attribute sret is not on first parameter!", V); Assert1(Idx == 1, "Attribute sret is not on first parameter!", V);
} }
@ -1348,15 +1361,41 @@ void Verifier::VerifyCallSite(CallSite CS) {
// Verify call attributes. // Verify call attributes.
VerifyFunctionAttrs(FTy, Attrs, I); VerifyFunctionAttrs(FTy, Attrs, I);
if (FTy->isVarArg()) if (FTy->isVarArg()) {
// FIXME? is 'nest' even legal here?
bool SawNest = false;
bool SawReturned = false;
for (unsigned Idx = 1; Idx < 1 + FTy->getNumParams(); ++Idx) {
if (Attrs.hasAttribute(Idx, Attribute::Nest))
SawNest = true;
if (Attrs.hasAttribute(Idx, Attribute::Returned))
SawReturned = true;
}
// Check attributes on the varargs part. // Check attributes on the varargs part.
for (unsigned Idx = 1 + FTy->getNumParams(); Idx <= CS.arg_size(); ++Idx) { for (unsigned Idx = 1 + FTy->getNumParams(); Idx <= CS.arg_size(); ++Idx) {
VerifyParameterAttrs(Attrs, Idx, CS.getArgument(Idx-1)->getType(), Type *Ty = CS.getArgument(Idx-1)->getType();
false, I); VerifyParameterAttrs(Attrs, Idx, Ty, false, I);
if (Attrs.hasAttribute(Idx, Attribute::Nest)) {
Assert1(!SawNest, "More than one parameter has attribute nest!", I);
SawNest = true;
}
if (Attrs.hasAttribute(Idx, Attribute::Returned)) {
Assert1(!SawReturned, "More than one parameter has attribute returned!",
I);
Assert1(Ty->canLosslesslyBitCastTo(FTy->getReturnType()),
"Incompatible argument and return types for 'returned' "
"attribute", I);
SawReturned = true;
}
Assert1(!Attrs.hasAttribute(Idx, Attribute::StructRet), Assert1(!Attrs.hasAttribute(Idx, Attribute::StructRet),
"Attribute 'sret' cannot be used for vararg call arguments!", I); "Attribute 'sret' cannot be used for vararg call arguments!", I);
} }
}
// Verify that there's no metadata unless it's a direct call to an intrinsic. // Verify that there's no metadata unless it's a direct call to an intrinsic.
if (CS.getCalledFunction() == 0 || if (CS.getCalledFunction() == 0 ||

View File

@ -74,6 +74,12 @@ ARMBaseRegisterInfo::getCallPreservedMask(CallingConv::ID) const {
? CSR_iOS_RegMask : CSR_AAPCS_RegMask; ? CSR_iOS_RegMask : CSR_AAPCS_RegMask;
} }
const uint32_t*
ARMBaseRegisterInfo::getThisReturnPreservedMask(CallingConv::ID) const {
return (STI.isTargetIOS() && !STI.isAAPCS_ABI())
? CSR_iOS_ThisReturn_RegMask : CSR_AAPCS_ThisReturn_RegMask;
}
const uint32_t* const uint32_t*
ARMBaseRegisterInfo::getNoPreservedMask() const { ARMBaseRegisterInfo::getNoPreservedMask() const {
return CSR_NoRegs_RegMask; return CSR_NoRegs_RegMask;

View File

@ -96,6 +96,7 @@ public:
/// Code Generation virtual methods... /// Code Generation virtual methods...
const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const; const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const;
const uint32_t *getCallPreservedMask(CallingConv::ID) const; const uint32_t *getCallPreservedMask(CallingConv::ID) const;
const uint32_t *getThisReturnPreservedMask(CallingConv::ID) const;
const uint32_t *getNoPreservedMask() const; const uint32_t *getNoPreservedMask() const;
BitVector getReservedRegs(const MachineFunction &MF) const; BitVector getReservedRegs(const MachineFunction &MF) const;

View File

@ -195,10 +195,21 @@ def CSR_NoRegs : CalleeSavedRegs<(add)>;
def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4, def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4,
(sequence "D%u", 15, 8))>; (sequence "D%u", 15, 8))>;
// Constructors and destructors return 'this' in the ARM C++ ABI; since 'this'
// and the pointer return value are both passed in R0 in these cases, this can
// be partially modelled by treating R0 as a callee-saved register
// Only the resulting RegMask is used; the SaveList is ignored
def CSR_AAPCS_ThisReturn : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6,
R5, R4, (sequence "D%u", 15, 8),
R0)>;
// iOS ABI deviates from ARM standard ABI. R9 is not a callee-saved register. // iOS ABI deviates from ARM standard ABI. R9 is not a callee-saved register.
// Also save R7-R4 first to match the stack frame fixed spill areas. // Also save R7-R4 first to match the stack frame fixed spill areas.
def CSR_iOS : CalleeSavedRegs<(add LR, R7, R6, R5, R4, (sub CSR_AAPCS, R9))>; def CSR_iOS : CalleeSavedRegs<(add LR, R7, R6, R5, R4, (sub CSR_AAPCS, R9))>;
def CSR_iOS_ThisReturn : CalleeSavedRegs<(add LR, R7, R6, R5, R4,
(sub CSR_AAPCS_ThisReturn, R9))>;
// GHC set of callee saved regs is empty as all those regs are // GHC set of callee saved regs is empty as all those regs are
// used for passing STG regs around // used for passing STG regs around
// add is a workaround for not being able to compile empty list: // add is a workaround for not being able to compile empty list:

View File

@ -1238,7 +1238,8 @@ ARMTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag,
CallingConv::ID CallConv, bool isVarArg, CallingConv::ID CallConv, bool isVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SmallVectorImpl<ISD::InputArg> &Ins,
DebugLoc dl, SelectionDAG &DAG, DebugLoc dl, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const { SmallVectorImpl<SDValue> &InVals,
bool isThisReturn, SDValue ThisVal) const {
// Assign locations to each value returned by this call. // Assign locations to each value returned by this call.
SmallVector<CCValAssign, 16> RVLocs; SmallVector<CCValAssign, 16> RVLocs;
@ -1252,6 +1253,14 @@ ARMTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag,
for (unsigned i = 0; i != RVLocs.size(); ++i) { for (unsigned i = 0; i != RVLocs.size(); ++i) {
CCValAssign VA = RVLocs[i]; CCValAssign VA = RVLocs[i];
// Pass 'this' value directly from the argument to return value, to avoid
// reg unit interference
if (i == 0 && isThisReturn) {
assert(!VA.needsCustom() && VA.getLocVT() == MVT::i32);
InVals.push_back(ThisVal);
continue;
}
SDValue Val; SDValue Val;
if (VA.needsCustom()) { if (VA.needsCustom()) {
// Handle f64 or half of a v2f64. // Handle f64 or half of a v2f64.
@ -1364,7 +1373,8 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
MachineFunction &MF = DAG.getMachineFunction(); MachineFunction &MF = DAG.getMachineFunction();
bool IsStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet(); bool IsStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet();
bool IsSibCall = false; bool IsThisReturn = false;
bool IsSibCall = false;
// Disable tail calls if they're not supported. // Disable tail calls if they're not supported.
if (!EnableARMTailCalls && !Subtarget->supportsTailCall()) if (!EnableARMTailCalls && !Subtarget->supportsTailCall())
isTailCall = false; isTailCall = false;
@ -1460,6 +1470,11 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
StackPtr, MemOpChains, Flags); StackPtr, MemOpChains, Flags);
} }
} else if (VA.isRegLoc()) { } else if (VA.isRegLoc()) {
if (realArgIdx == 0 && Flags.isReturned() && VA.getLocVT() == MVT::i32) {
assert(!Ins.empty() && Ins[0].VT == Outs[0].VT &&
"unexpected use of 'returned'");
IsThisReturn = true;
}
RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg));
} else if (isByVal) { } else if (isByVal) {
assert(VA.isMemLoc()); assert(VA.isMemLoc());
@ -1680,8 +1695,15 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
RegsToPass[i].second.getValueType())); RegsToPass[i].second.getValueType()));
// Add a register mask operand representing the call-preserved registers. // Add a register mask operand representing the call-preserved registers.
const uint32_t *Mask;
const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo(); const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo();
const uint32_t *Mask = TRI->getCallPreservedMask(CallConv); const ARMBaseRegisterInfo *ARI = static_cast<const ARMBaseRegisterInfo*>(TRI);
if (IsThisReturn)
// For 'this' returns, use the R0-preserving mask
Mask = ARI->getThisReturnPreservedMask(CallConv);
else
Mask = ARI->getCallPreservedMask(CallConv);
assert(Mask && "Missing call preserved mask for calling convention"); assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask)); Ops.push_back(DAG.getRegisterMask(Mask));
@ -1703,8 +1725,9 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// Handle result values, copying them out of physregs into vregs that we // Handle result values, copying them out of physregs into vregs that we
// return. // return.
return LowerCallResult(Chain, InFlag, CallConv, isVarArg, Ins, return LowerCallResult(Chain, InFlag, CallConv, isVarArg, Ins, dl, DAG,
dl, DAG, InVals); InVals, IsThisReturn,
IsThisReturn ? OutVals[0] : SDValue());
} }
/// HandleByVal - Every parameter *after* a byval parameter is passed /// HandleByVal - Every parameter *after* a byval parameter is passed

View File

@ -464,7 +464,8 @@ namespace llvm {
CallingConv::ID CallConv, bool isVarArg, CallingConv::ID CallConv, bool isVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins, const SmallVectorImpl<ISD::InputArg> &Ins,
DebugLoc dl, SelectionDAG &DAG, DebugLoc dl, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const; SmallVectorImpl<SDValue> &InVals,
bool isThisReturn, SDValue ThisVal) const;
virtual SDValue virtual SDValue
LowerFormalArguments(SDValue Chain, LowerFormalArguments(SDValue Chain,

View File

@ -0,0 +1,91 @@
; RUN: llc < %s -mtriple=armv6-linux-gnueabi -arm-tail-calls | FileCheck %s -check-prefix=CHECKELF
; RUN: llc < %s -mtriple=thumbv7-apple-ios -arm-tail-calls | FileCheck %s -check-prefix=CHECKT2D
%struct.A = type { i8 }
%struct.B = type { i32 }
%struct.C = type { %struct.B }
%struct.D = type { %struct.B }
declare %struct.A* @A_ctor_base(%struct.A* returned)
declare %struct.B* @B_ctor_base(%struct.B* returned, i32)
declare %struct.B* @B_ctor_complete(%struct.B* returned, i32)
declare %struct.A* @A_ctor_base_nothisret(%struct.A*)
declare %struct.B* @B_ctor_base_nothisret(%struct.B*, i32)
declare %struct.B* @B_ctor_complete_nothisret(%struct.B*, i32)
define %struct.C* @C_ctor_base(%struct.C* returned %this, i32 %x) {
entry:
; CHECKELF: C_ctor_base:
; CHECKELF-NOT: mov {{r[0-9]+}}, r0
; CHECKELF: bl A_ctor_base
; CHECKELF-NOT: mov r0, {{r[0-9]+}}
; CHECKELF: b B_ctor_base
; CHECKT2D: C_ctor_base:
; CHECKT2D-NOT: mov {{r[0-9]+}}, r0
; CHECKT2D: blx _A_ctor_base
; CHECKT2D-NOT: mov r0, {{r[0-9]+}}
; CHECKT2D: b.w _B_ctor_base
%0 = bitcast %struct.C* %this to %struct.A*
%call = tail call %struct.A* @A_ctor_base(%struct.A* %0)
%1 = getelementptr inbounds %struct.C* %this, i32 0, i32 0
%call2 = tail call %struct.B* @B_ctor_base(%struct.B* %1, i32 %x)
ret %struct.C* %this
}
define %struct.C* @C_ctor_base_nothisret(%struct.C* %this, i32 %x) {
entry:
; CHECKELF: C_ctor_base_nothisret:
; CHECKELF: mov [[SAVETHIS:r[0-9]+]], r0
; CHECKELF: bl A_ctor_base_nothisret
; CHECKELF: mov r0, [[SAVETHIS]]
; CHECKELF-NOT: b B_ctor_base_nothisret
; CHECKT2D: C_ctor_base_nothisret:
; CHECKT2D: mov [[SAVETHIS:r[0-9]+]], r0
; CHECKT2D: blx _A_ctor_base_nothisret
; CHECKT2D: mov r0, [[SAVETHIS]]
; CHECKT2D-NOT: b.w _B_ctor_base_nothisret
%0 = bitcast %struct.C* %this to %struct.A*
%call = tail call %struct.A* @A_ctor_base_nothisret(%struct.A* %0)
%1 = getelementptr inbounds %struct.C* %this, i32 0, i32 0
%call2 = tail call %struct.B* @B_ctor_base_nothisret(%struct.B* %1, i32 %x)
ret %struct.C* %this
}
define %struct.C* @C_ctor_complete(%struct.C* %this, i32 %x) {
entry:
; CHECKELF: C_ctor_complete:
; CHECKELF: b C_ctor_base
; CHECKT2D: C_ctor_complete:
; CHECKT2D: b.w _C_ctor_base
%call = tail call %struct.C* @C_ctor_base(%struct.C* %this, i32 %x)
ret %struct.C* %this
}
define %struct.C* @C_ctor_complete_nothisret(%struct.C* %this, i32 %x) {
entry:
; CHECKELF: C_ctor_complete_nothisret:
; CHECKELF-NOT: b C_ctor_base_nothisret
; CHECKT2D: C_ctor_complete_nothisret:
; CHECKT2D-NOT: b.w _C_ctor_base_nothisret
%call = tail call %struct.C* @C_ctor_base_nothisret(%struct.C* %this, i32 %x)
ret %struct.C* %this
}
define %struct.D* @D_ctor_base(%struct.D* %this, i32 %x) {
entry:
; CHECKELF: D_ctor_base:
; CHECKELF-NOT: mov {{r[0-9]+}}, r0
; CHECKELF: bl B_ctor_complete
; CHECKELF-NOT: mov r0, {{r[0-9]+}}
; CHECKELF: b B_ctor_complete
; CHECKT2D: D_ctor_base:
; CHECKT2D-NOT: mov {{r[0-9]+}}, r0
; CHECKT2D: blx _B_ctor_complete
; CHECKT2D-NOT: mov r0, {{r[0-9]+}}
; CHECKT2D: b.w _B_ctor_complete
%b = getelementptr inbounds %struct.D* %this, i32 0, i32 0
%call = tail call %struct.B* @B_ctor_complete(%struct.B* %b, i32 %x)
%call2 = tail call %struct.B* @B_ctor_complete(%struct.B* %b, i32 %x)
ret %struct.D* %this
}

View File

@ -0,0 +1,89 @@
; RUN: llc < %s -mtriple=x86_64-pc-win32 | FileCheck %s
%struct.A = type { i8 }
%struct.B = type { i32 }
%struct.C = type { %struct.B }
%struct.D = type { %struct.B }
%struct.E = type { %struct.B }
declare %struct.A* @A_ctor(%struct.A* returned)
declare %struct.B* @B_ctor(%struct.B* returned, i32)
declare %struct.A* @A_ctor_nothisret(%struct.A*)
declare %struct.B* @B_ctor_nothisret(%struct.B*, i32)
define %struct.C* @C_ctor(%struct.C* %this, i32 %y) {
entry:
; CHECK: C_ctor:
; CHECK: jmp B_ctor # TAILCALL
%0 = getelementptr inbounds %struct.C* %this, i64 0, i32 0
%call = tail call %struct.B* @B_ctor(%struct.B* %0, i32 %y)
ret %struct.C* %this
}
define %struct.C* @C_ctor_nothisret(%struct.C* %this, i32 %y) {
entry:
; CHECK: C_ctor_nothisret:
; CHECK-NOT: jmp B_ctor_nothisret
%0 = getelementptr inbounds %struct.C* %this, i64 0, i32 0
%call = tail call %struct.B* @B_ctor_nothisret(%struct.B* %0, i32 %y)
ret %struct.C* %this
}
define %struct.D* @D_ctor(%struct.D* %this, i32 %y) {
entry:
; CHECK: D_ctor:
; CHECK: movq %rcx, [[SAVETHIS:%r[0-9a-z]+]]
; CHECK: callq A_ctor
; CHECK: movq [[SAVETHIS]], %rcx
; CHECK: jmp B_ctor # TAILCALL
%0 = bitcast %struct.D* %this to %struct.A*
%call = tail call %struct.A* @A_ctor(%struct.A* %0)
%1 = getelementptr inbounds %struct.D* %this, i64 0, i32 0
%call2 = tail call %struct.B* @B_ctor(%struct.B* %1, i32 %y)
; (this next line would never be generated by Clang, actually)
%2 = bitcast %struct.A* %call to %struct.D*
ret %struct.D* %2
}
define %struct.D* @D_ctor_nothisret(%struct.D* %this, i32 %y) {
entry:
; CHECK: D_ctor_nothisret:
; CHECK: movq %rcx, [[SAVETHIS:%r[0-9a-z]+]]
; CHECK: callq A_ctor_nothisret
; CHECK: movq [[SAVETHIS]], %rcx
; CHECK-NOT: jmp B_ctor_nothisret
%0 = bitcast %struct.D* %this to %struct.A*
%call = tail call %struct.A* @A_ctor_nothisret(%struct.A* %0)
%1 = getelementptr inbounds %struct.D* %this, i64 0, i32 0
%call2 = tail call %struct.B* @B_ctor_nothisret(%struct.B* %1, i32 %y)
; (this next line would never be generated by Clang, actually)
%2 = bitcast %struct.A* %call to %struct.D*
ret %struct.D* %2
}
define %struct.E* @E_ctor(%struct.E* %this, i32 %x) {
entry:
; CHECK: E_ctor:
; CHECK: movq %rcx, [[SAVETHIS:%r[0-9a-z]+]]
; CHECK: callq B_ctor
; CHECK: movq [[SAVETHIS]], %rcx
; CHECK: jmp B_ctor # TAILCALL
%b = getelementptr inbounds %struct.E* %this, i64 0, i32 0
%call = tail call %struct.B* @B_ctor(%struct.B* %b, i32 %x)
%call4 = tail call %struct.B* @B_ctor(%struct.B* %b, i32 %x)
ret %struct.E* %this
}
define %struct.E* @E_ctor_nothisret(%struct.E* %this, i32 %x) {
entry:
; CHECK: E_ctor_nothisret:
; CHECK: movq %rcx, [[SAVETHIS:%r[0-9a-z]+]]
; CHECK: callq B_ctor_nothisret
; CHECK: movq [[SAVETHIS]], %rcx
; CHECK-NOT: jmp B_ctor_nothisret
%b = getelementptr inbounds %struct.E* %this, i64 0, i32 0
%call = tail call %struct.B* @B_ctor_nothisret(%struct.B* %b, i32 %x)
%call4 = tail call %struct.B* @B_ctor_nothisret(%struct.B* %b, i32 %x)
ret %struct.E* %this
}