mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-25 04:39:51 +00:00
Swift Calling Convention: add swifterror attribute.
A ``swifterror`` attribute can be applied to a function parameter or an AllocaInst. This commit does not include any target-specific change. The target-specific optimization will come as a follow-up patch. Differential Revision: http://reviews.llvm.org/D18092 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@265189 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
44e6fffbb4
commit
4bda882517
@ -1063,6 +1063,25 @@ Currently, only the following parameter attributes are defined:
|
||||
a valid attribute for return values and can only be applied to one
|
||||
parameter.
|
||||
|
||||
``swifterror``
|
||||
This attribute is motivated to model and optimize Swift error handling. It
|
||||
can be applied to a parameter with pointer to pointer type or a
|
||||
pointer-sized alloca. At the call site, the actual argument that corresponds
|
||||
to a ``swifterror`` parameter has to come from a ``swifterror`` alloca. A
|
||||
``swifterror`` value (either the parameter or the alloca) can only be loaded
|
||||
and stored from, or used as a ``swifterror`` argument. This is not a valid
|
||||
attribute for return values and can only be applied to one parameter.
|
||||
|
||||
These constraints allow the calling convention to optimize access to
|
||||
``swifterror`` variables by associating them with a specific register at
|
||||
call boundaries rather than placing them in memory. Since this does change
|
||||
the calling convention, a function which uses the ``swifterror`` attribute
|
||||
on a parameter is not ABI-compatible with one which does not.
|
||||
|
||||
These constraints also allow LLVM to assume that a ``swifterror`` argument
|
||||
does not alias any other memory visible within a function and that a
|
||||
``swifterror`` alloca passed as an argument does not escape.
|
||||
|
||||
.. _gc:
|
||||
|
||||
Garbage Collector Strategy Names
|
||||
|
@ -95,6 +95,7 @@ typedef enum {
|
||||
LLVMConvergentAttribute = 1ULL << 46,
|
||||
LLVMSafeStackAttribute = 1ULL << 47,
|
||||
LLVMSwiftSelfAttribute = 1ULL << 48,
|
||||
LLVMSwiftErrorAttribute = 1ULL << 49,
|
||||
*/
|
||||
} LLVMAttribute;
|
||||
|
||||
|
@ -42,13 +42,14 @@ public:
|
||||
bool IsInAlloca : 1;
|
||||
bool IsReturned : 1;
|
||||
bool IsSwiftSelf : 1;
|
||||
bool IsSwiftError : 1;
|
||||
uint16_t Alignment;
|
||||
|
||||
ArgListEntry()
|
||||
: Val(nullptr), Ty(nullptr), IsSExt(false), IsZExt(false),
|
||||
IsInReg(false), IsSRet(false), IsNest(false), IsByVal(false),
|
||||
IsInAlloca(false), IsReturned(false), IsSwiftSelf(false),
|
||||
Alignment(0) {}
|
||||
IsSwiftError(false), Alignment(0) {}
|
||||
|
||||
/// \brief Set CallLoweringInfo attribute flags based on a call instruction
|
||||
/// and called function attributes.
|
||||
|
@ -76,6 +76,9 @@ public:
|
||||
/// \brief Return true if this argument has the swiftself attribute.
|
||||
bool hasSwiftSelfAttr() const;
|
||||
|
||||
/// \brief Return true if this argument has the swifterror attribute.
|
||||
bool hasSwiftErrorAttr() const;
|
||||
|
||||
/// \brief Return true if this argument has the byval attribute or inalloca
|
||||
/// attribute on it in its containing function. These attributes both
|
||||
/// represent arguments being passed by value.
|
||||
|
@ -154,6 +154,9 @@ def SanitizeThread : EnumAttr<"sanitize_thread">;
|
||||
/// MemorySanitizer is on.
|
||||
def SanitizeMemory : EnumAttr<"sanitize_memory">;
|
||||
|
||||
/// Argument is swift error.
|
||||
def SwiftError : EnumAttr<"swifterror">;
|
||||
|
||||
/// Argument is swift self/context.
|
||||
def SwiftSelf : EnumAttr<"swiftself">;
|
||||
|
||||
|
@ -152,6 +152,18 @@ public:
|
||||
(V ? 32 : 0));
|
||||
}
|
||||
|
||||
/// \brief Return true if this alloca is used as a swifterror argument to a
|
||||
/// call.
|
||||
bool isSwiftError() const {
|
||||
return getSubclassDataFromInstruction() & 64;
|
||||
}
|
||||
|
||||
/// \brief Specify whether this alloca is used to represent a swifterror.
|
||||
void setSwiftError(bool V) {
|
||||
setInstructionSubclassData((getSubclassDataFromInstruction() & ~64) |
|
||||
(V ? 64 : 0));
|
||||
}
|
||||
|
||||
// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||
static inline bool classof(const Instruction *I) {
|
||||
return (I->getOpcode() == Instruction::Alloca);
|
||||
|
@ -50,6 +50,8 @@ namespace ISD {
|
||||
static const uint64_t SplitEndOffs = 13;
|
||||
static const uint64_t SwiftSelf = 1ULL<<14; ///< Swift self parameter
|
||||
static const uint64_t SwiftSelfOffs = 14;
|
||||
static const uint64_t SwiftError = 1ULL<<15; ///< Swift error parameter
|
||||
static const uint64_t SwiftErrorOffs = 15;
|
||||
static const uint64_t OrigAlign = 0x1FULL<<27;
|
||||
static const uint64_t OrigAlignOffs = 27;
|
||||
static const uint64_t ByValSize = 0x3fffffffULL<<32; ///< Struct size
|
||||
@ -87,6 +89,9 @@ namespace ISD {
|
||||
bool isSwiftSelf() const { return Flags & SwiftSelf; }
|
||||
void setSwiftSelf() { Flags |= One << SwiftSelfOffs; }
|
||||
|
||||
bool isSwiftError() const { return Flags & SwiftError; }
|
||||
void setSwiftError() { Flags |= One << SwiftErrorOffs; }
|
||||
|
||||
bool isNest() const { return Flags & Nest; }
|
||||
void setNest() { Flags |= One << NestOffs; }
|
||||
|
||||
|
@ -47,6 +47,11 @@ class CCIfByVal<CCAction A> : CCIf<"ArgFlags.isByVal()", A> {
|
||||
class CCIfSwiftSelf<CCAction A> : CCIf<"ArgFlags.isSwiftSelf()", A> {
|
||||
}
|
||||
|
||||
/// CCIfSwiftError - If the current argument has swifterror parameter attribute,
|
||||
/// apply Action A.
|
||||
class CCIfSwiftError<CCAction A> : CCIf<"ArgFlags.isSwiftError()", A> {
|
||||
}
|
||||
|
||||
/// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs
|
||||
/// parameter attribute, apply Action A.
|
||||
class CCIfConsecutiveRegs<CCAction A> : CCIf<"ArgFlags.isInConsecutiveRegs()", A> {
|
||||
|
@ -2327,11 +2327,13 @@ public:
|
||||
bool isInAlloca : 1;
|
||||
bool isReturned : 1;
|
||||
bool isSwiftSelf : 1;
|
||||
bool isSwiftError : 1;
|
||||
uint16_t Alignment;
|
||||
|
||||
ArgListEntry() : isSExt(false), isZExt(false), isInReg(false),
|
||||
isSRet(false), isNest(false), isByVal(false), isInAlloca(false),
|
||||
isReturned(false), isSwiftSelf(false), Alignment(0) { }
|
||||
isReturned(false), isSwiftSelf(false), isSwiftError(false),
|
||||
Alignment(0) { }
|
||||
|
||||
void setAttributes(ImmutableCallSite *CS, unsigned AttrIdx);
|
||||
};
|
||||
|
@ -648,6 +648,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(sanitize_address);
|
||||
KEYWORD(sanitize_thread);
|
||||
KEYWORD(sanitize_memory);
|
||||
KEYWORD(swifterror);
|
||||
KEYWORD(swiftself);
|
||||
KEYWORD(uwtable);
|
||||
KEYWORD(zeroext);
|
||||
|
@ -1088,6 +1088,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
|
||||
case lltok::kw_nonnull:
|
||||
case lltok::kw_returned:
|
||||
case lltok::kw_sret:
|
||||
case lltok::kw_swifterror:
|
||||
case lltok::kw_swiftself:
|
||||
HaveError |=
|
||||
Error(Lex.getLoc(),
|
||||
@ -1362,6 +1363,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
|
||||
case lltok::kw_returned: B.addAttribute(Attribute::Returned); break;
|
||||
case lltok::kw_signext: B.addAttribute(Attribute::SExt); break;
|
||||
case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break;
|
||||
case lltok::kw_swifterror: B.addAttribute(Attribute::SwiftError); break;
|
||||
case lltok::kw_swiftself: B.addAttribute(Attribute::SwiftSelf); break;
|
||||
case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break;
|
||||
|
||||
@ -1450,6 +1452,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
|
||||
case lltok::kw_nocapture:
|
||||
case lltok::kw_returned:
|
||||
case lltok::kw_sret:
|
||||
case lltok::kw_swifterror:
|
||||
case lltok::kw_swiftself:
|
||||
HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute");
|
||||
break;
|
||||
@ -5802,7 +5805,8 @@ bool LLParser::ParseCall(Instruction *&Inst, PerFunctionState &PFS,
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// ParseAlloc
|
||||
/// ::= 'alloca' 'inalloca'? Type (',' TypeAndValue)? (',' 'align' i32)?
|
||||
/// ::= 'alloca' 'inalloca'? 'swifterror'? Type (',' TypeAndValue)?
|
||||
/// (',' 'align' i32)?
|
||||
int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
Value *Size = nullptr;
|
||||
LocTy SizeLoc, TyLoc;
|
||||
@ -5810,6 +5814,7 @@ int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
Type *Ty = nullptr;
|
||||
|
||||
bool IsInAlloca = EatIfPresent(lltok::kw_inalloca);
|
||||
bool IsSwiftError = EatIfPresent(lltok::kw_swifterror);
|
||||
|
||||
if (ParseType(Ty, TyLoc)) return true;
|
||||
|
||||
@ -5834,6 +5839,7 @@ int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
|
||||
AllocaInst *AI = new AllocaInst(Ty, Size, Alignment);
|
||||
AI->setUsedWithInAlloca(IsInAlloca);
|
||||
AI->setSwiftError(IsSwiftError);
|
||||
Inst = AI;
|
||||
return AteExtraComma ? InstExtraComma : InstNormal;
|
||||
}
|
||||
|
@ -152,6 +152,7 @@ namespace lltok {
|
||||
kw_sret,
|
||||
kw_sanitize_thread,
|
||||
kw_sanitize_memory,
|
||||
kw_swifterror,
|
||||
kw_swiftself,
|
||||
kw_uwtable,
|
||||
kw_zeroext,
|
||||
|
@ -1317,6 +1317,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||
return Attribute::SanitizeThread;
|
||||
case bitc::ATTR_KIND_SANITIZE_MEMORY:
|
||||
return Attribute::SanitizeMemory;
|
||||
case bitc::ATTR_KIND_SWIFT_ERROR:
|
||||
return Attribute::SwiftError;
|
||||
case bitc::ATTR_KIND_SWIFT_SELF:
|
||||
return Attribute::SwiftSelf;
|
||||
case bitc::ATTR_KIND_UW_TABLE:
|
||||
@ -4843,10 +4845,11 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
|
||||
uint64_t AlignRecord = Record[3];
|
||||
const uint64_t InAllocaMask = uint64_t(1) << 5;
|
||||
const uint64_t ExplicitTypeMask = uint64_t(1) << 6;
|
||||
// Reserve bit 7 for SwiftError flag.
|
||||
// const uint64_t SwiftErrorMask = uint64_t(1) << 7;
|
||||
const uint64_t FlagMask = InAllocaMask | ExplicitTypeMask;
|
||||
const uint64_t SwiftErrorMask = uint64_t(1) << 7;
|
||||
const uint64_t FlagMask = InAllocaMask | ExplicitTypeMask |
|
||||
SwiftErrorMask;
|
||||
bool InAlloca = AlignRecord & InAllocaMask;
|
||||
bool SwiftError = AlignRecord & SwiftErrorMask;
|
||||
Type *Ty = getTypeByID(Record[0]);
|
||||
if ((AlignRecord & ExplicitTypeMask) == 0) {
|
||||
auto *PTy = dyn_cast_or_null<PointerType>(Ty);
|
||||
@ -4865,6 +4868,7 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
|
||||
return error("Invalid record");
|
||||
AllocaInst *AI = new AllocaInst(Ty, Size, Align);
|
||||
AI->setUsedWithInAlloca(InAlloca);
|
||||
AI->setSwiftError(SwiftError);
|
||||
I = AI;
|
||||
InstructionList.push_back(I);
|
||||
break;
|
||||
|
@ -261,6 +261,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||
return bitc::ATTR_KIND_SANITIZE_THREAD;
|
||||
case Attribute::SanitizeMemory:
|
||||
return bitc::ATTR_KIND_SANITIZE_MEMORY;
|
||||
case Attribute::SwiftError:
|
||||
return bitc::ATTR_KIND_SWIFT_ERROR;
|
||||
case Attribute::SwiftSelf:
|
||||
return bitc::ATTR_KIND_SWIFT_SELF;
|
||||
case Attribute::UWTable:
|
||||
@ -2142,8 +2144,7 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
|
||||
assert(AlignRecord < 1 << 5 && "alignment greater than 1 << 64");
|
||||
AlignRecord |= AI.isUsedWithInAlloca() << 5;
|
||||
AlignRecord |= 1 << 6;
|
||||
// Reserve bit 7 for SwiftError flag.
|
||||
// AlignRecord |= AI.isSwiftError() << 7;
|
||||
AlignRecord |= AI.isSwiftError() << 7;
|
||||
Vals.push_back(AlignRecord);
|
||||
break;
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ void FastISel::ArgListEntry::setAttributes(ImmutableCallSite *CS,
|
||||
IsInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca);
|
||||
IsReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned);
|
||||
IsSwiftSelf = CS->paramHasAttr(AttrIdx, Attribute::SwiftSelf);
|
||||
IsSwiftError = CS->paramHasAttr(AttrIdx, Attribute::SwiftError);
|
||||
Alignment = CS->getParamAlignment(AttrIdx);
|
||||
}
|
||||
|
||||
@ -960,6 +961,8 @@ bool FastISel::lowerCallTo(CallLoweringInfo &CLI) {
|
||||
Flags.setSRet();
|
||||
if (Arg.IsSwiftSelf)
|
||||
Flags.setSwiftSelf();
|
||||
if (Arg.IsSwiftError)
|
||||
Flags.setSwiftError();
|
||||
if (Arg.IsByVal)
|
||||
Flags.setByVal();
|
||||
if (Arg.IsInAlloca) {
|
||||
|
@ -7282,6 +7282,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
|
||||
Entry.isByVal = false;
|
||||
Entry.isReturned = false;
|
||||
Entry.isSwiftSelf = false;
|
||||
Entry.isSwiftError = false;
|
||||
Entry.Alignment = Align;
|
||||
CLI.getArgs().insert(CLI.getArgs().begin(), Entry);
|
||||
CLI.RetTy = Type::getVoidTy(CLI.RetTy->getContext());
|
||||
@ -7341,6 +7342,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
|
||||
Flags.setSRet();
|
||||
if (Args[i].isSwiftSelf)
|
||||
Flags.setSwiftSelf();
|
||||
if (Args[i].isSwiftError)
|
||||
Flags.setSwiftError();
|
||||
if (Args[i].isByVal)
|
||||
Flags.setByVal();
|
||||
if (Args[i].isInAlloca) {
|
||||
@ -7623,6 +7626,8 @@ void SelectionDAGISel::LowerArguments(const Function &F) {
|
||||
Flags.setSRet();
|
||||
if (F.getAttributes().hasAttribute(Idx, Attribute::SwiftSelf))
|
||||
Flags.setSwiftSelf();
|
||||
if (F.getAttributes().hasAttribute(Idx, Attribute::SwiftError))
|
||||
Flags.setSwiftError();
|
||||
if (F.getAttributes().hasAttribute(Idx, Attribute::ByVal))
|
||||
Flags.setByVal();
|
||||
if (F.getAttributes().hasAttribute(Idx, Attribute::InAlloca)) {
|
||||
|
@ -78,6 +78,7 @@ void TargetLowering::ArgListEntry::setAttributes(ImmutableCallSite *CS,
|
||||
isInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca);
|
||||
isReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned);
|
||||
isSwiftSelf = CS->paramHasAttr(AttrIdx, Attribute::SwiftSelf);
|
||||
isSwiftError = CS->paramHasAttr(AttrIdx, Attribute::SwiftError);
|
||||
Alignment = CS->getParamAlignment(AttrIdx);
|
||||
}
|
||||
|
||||
|
@ -3028,6 +3028,8 @@ void AssemblyWriter::printInstruction(const Instruction &I) {
|
||||
Out << ' ';
|
||||
if (AI->isUsedWithInAlloca())
|
||||
Out << "inalloca ";
|
||||
if (AI->isSwiftError())
|
||||
Out << "swifterror ";
|
||||
TypePrinter.print(AI->getAllocatedType(), Out);
|
||||
|
||||
// Explicitly write the array size if the code is broken, if it's an array
|
||||
|
@ -195,6 +195,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
return "byval";
|
||||
if (hasAttribute(Attribute::Convergent))
|
||||
return "convergent";
|
||||
if (hasAttribute(Attribute::SwiftError))
|
||||
return "swifterror";
|
||||
if (hasAttribute(Attribute::SwiftSelf))
|
||||
return "swiftself";
|
||||
if (hasAttribute(Attribute::InaccessibleMemOnly))
|
||||
@ -451,6 +453,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
|
||||
case Attribute::InaccessibleMemOnly: return 1ULL << 49;
|
||||
case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50;
|
||||
case Attribute::SwiftSelf: return 1ULL << 51;
|
||||
case Attribute::SwiftError: return 1ULL << 52;
|
||||
case Attribute::Dereferenceable:
|
||||
llvm_unreachable("dereferenceable attribute not supported in raw format");
|
||||
break;
|
||||
|
@ -97,6 +97,11 @@ bool Argument::hasSwiftSelfAttr() const {
|
||||
hasAttribute(getArgNo()+1, Attribute::SwiftSelf);
|
||||
}
|
||||
|
||||
bool Argument::hasSwiftErrorAttr() const {
|
||||
return getParent()->getAttributes().
|
||||
hasAttribute(getArgNo()+1, Attribute::SwiftError);
|
||||
}
|
||||
|
||||
/// \brief Return true if this argument has the inalloca attribute on it in
|
||||
/// its containing function.
|
||||
bool Argument::hasInAllocaAttr() const {
|
||||
|
@ -3811,6 +3811,7 @@ AllocaInst *AllocaInst::cloneImpl() const {
|
||||
AllocaInst *Result = new AllocaInst(getAllocatedType(),
|
||||
(Value *)getOperand(0), getAlignment());
|
||||
Result->setUsedWithInAlloca(isUsedWithInAlloca());
|
||||
Result->setSwiftError(isSwiftError());
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -426,6 +426,8 @@ private:
|
||||
void visitCleanupReturnInst(CleanupReturnInst &CRI);
|
||||
|
||||
void verifyCallSite(CallSite CS);
|
||||
void verifySwiftErrorCallSite(CallSite CS, const Value *SwiftErrorVal);
|
||||
void verifySwiftErrorValue(const Value *SwiftErrorVal);
|
||||
void verifyMustTailCall(CallInst &CI);
|
||||
bool performTypeCheck(Intrinsic::ID ID, Function *F, Type *Ty, int VT,
|
||||
unsigned ArgNo, std::string &Suffix);
|
||||
@ -1348,9 +1350,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
|
||||
!Attrs.hasAttribute(Idx, Attribute::NoCapture) &&
|
||||
!Attrs.hasAttribute(Idx, Attribute::Returned) &&
|
||||
!Attrs.hasAttribute(Idx, Attribute::InAlloca) &&
|
||||
!Attrs.hasAttribute(Idx, Attribute::SwiftSelf),
|
||||
!Attrs.hasAttribute(Idx, Attribute::SwiftSelf) &&
|
||||
!Attrs.hasAttribute(Idx, Attribute::SwiftError),
|
||||
"Attributes 'byval', 'inalloca', 'nest', 'sret', 'nocapture', "
|
||||
"'returned', and 'swiftself' do not apply to return "
|
||||
"'returned', 'swiftself', and 'swifterror' do not apply to return "
|
||||
"values!",
|
||||
V);
|
||||
|
||||
@ -1411,10 +1414,19 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty,
|
||||
"Attributes 'byval' and 'inalloca' do not support unsized types!",
|
||||
V);
|
||||
}
|
||||
if (!isa<PointerType>(PTy->getElementType()))
|
||||
Assert(!Attrs.hasAttribute(Idx, Attribute::SwiftError),
|
||||
"Attribute 'swifterror' only applies to parameters "
|
||||
"with pointer to pointer type!",
|
||||
V);
|
||||
} else {
|
||||
Assert(!Attrs.hasAttribute(Idx, Attribute::ByVal),
|
||||
"Attribute 'byval' only applies to parameters with pointer type!",
|
||||
V);
|
||||
Assert(!Attrs.hasAttribute(Idx, Attribute::SwiftError),
|
||||
"Attribute 'swifterror' only applies to parameters "
|
||||
"with pointer type!",
|
||||
V);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1429,6 +1441,7 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
|
||||
bool SawReturned = false;
|
||||
bool SawSRet = false;
|
||||
bool SawSwiftSelf = false;
|
||||
bool SawSwiftError = false;
|
||||
|
||||
for (unsigned i = 0, e = Attrs.getNumSlots(); i != e; ++i) {
|
||||
unsigned Idx = Attrs.getSlotIndex(i);
|
||||
@ -1473,6 +1486,12 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
|
||||
SawSwiftSelf = true;
|
||||
}
|
||||
|
||||
if (Attrs.hasAttribute(Idx, Attribute::SwiftError)) {
|
||||
Assert(!SawSwiftError, "Cannot have multiple 'swifterror' parameters!",
|
||||
V);
|
||||
SawSwiftError = true;
|
||||
}
|
||||
|
||||
if (Attrs.hasAttribute(Idx, Attribute::InAlloca)) {
|
||||
Assert(Idx == FT->getNumParams(), "inalloca isn't on the last parameter!",
|
||||
V);
|
||||
@ -1886,6 +1905,11 @@ void Verifier::visitFunction(const Function &F) {
|
||||
Assert(!Arg.getType()->isTokenTy(),
|
||||
"Function takes token but isn't an intrinsic", &Arg, &F);
|
||||
}
|
||||
|
||||
// Check that swifterror argument is only used by loads and stores.
|
||||
if (Attrs.hasAttribute(i+1, Attribute::SwiftError)) {
|
||||
verifySwiftErrorValue(&Arg);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
@ -2462,6 +2486,18 @@ void Verifier::verifyCallSite(CallSite CS) {
|
||||
"inalloca argument for call has mismatched alloca", AI, I);
|
||||
}
|
||||
|
||||
// For each argument of the callsite, if it has the swifterror argument,
|
||||
// make sure the underlying alloca has swifterror as well.
|
||||
for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i)
|
||||
if (CS.paramHasAttr(i+1, Attribute::SwiftError)) {
|
||||
Value *SwiftErrorArg = CS.getArgument(i);
|
||||
auto AI = dyn_cast<AllocaInst>(SwiftErrorArg->stripInBoundsOffsets());
|
||||
Assert(AI, "swifterror argument should come from alloca", AI, I);
|
||||
if (AI)
|
||||
Assert(AI->isSwiftError(),
|
||||
"swifterror argument for call has mismatched alloca", AI, I);
|
||||
}
|
||||
|
||||
if (FTy->isVarArg()) {
|
||||
// FIXME? is 'nest' even legal here?
|
||||
bool SawNest = false;
|
||||
@ -2565,7 +2601,8 @@ static bool isTypeCongruent(Type *L, Type *R) {
|
||||
static AttrBuilder getParameterABIAttributes(int I, AttributeSet Attrs) {
|
||||
static const Attribute::AttrKind ABIAttrs[] = {
|
||||
Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca,
|
||||
Attribute::InReg, Attribute::Returned, Attribute::SwiftSelf};
|
||||
Attribute::InReg, Attribute::Returned, Attribute::SwiftSelf,
|
||||
Attribute::SwiftError};
|
||||
AttrBuilder Copy;
|
||||
for (auto AK : ABIAttrs) {
|
||||
if (Attrs.hasAttribute(I + 1, AK))
|
||||
@ -2924,6 +2961,42 @@ void Verifier::visitStoreInst(StoreInst &SI) {
|
||||
visitInstruction(SI);
|
||||
}
|
||||
|
||||
/// Check that SwiftErrorVal is used as a swifterror argument in CS.
|
||||
void Verifier::verifySwiftErrorCallSite(CallSite CS,
|
||||
const Value *SwiftErrorVal) {
|
||||
unsigned Idx = 0;
|
||||
for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
|
||||
I != E; ++I, ++Idx) {
|
||||
if (*I == SwiftErrorVal) {
|
||||
Assert(CS.paramHasAttr(Idx+1, Attribute::SwiftError),
|
||||
"swifterror value when used in a callsite should be marked "
|
||||
"with swifterror attribute",
|
||||
SwiftErrorVal, CS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Verifier::verifySwiftErrorValue(const Value *SwiftErrorVal) {
|
||||
// Check that swifterror value is only used by loads, stores, or as
|
||||
// a swifterror argument.
|
||||
for (const User *U : SwiftErrorVal->users()) {
|
||||
Assert(isa<LoadInst>(U) || isa<StoreInst>(U) || isa<CallInst>(U) ||
|
||||
isa<InvokeInst>(U),
|
||||
"swifterror value can only be loaded and stored from, or "
|
||||
"as a swifterror argument!",
|
||||
SwiftErrorVal, U);
|
||||
// If it is used by a store, check it is the second operand.
|
||||
if (auto StoreI = dyn_cast<StoreInst>(U))
|
||||
Assert(StoreI->getOperand(1) == SwiftErrorVal,
|
||||
"swifterror value should be the second operand when used "
|
||||
"by stores", SwiftErrorVal, U);
|
||||
if (auto CallI = dyn_cast<CallInst>(U))
|
||||
verifySwiftErrorCallSite(const_cast<CallInst*>(CallI), SwiftErrorVal);
|
||||
if (auto II = dyn_cast<InvokeInst>(U))
|
||||
verifySwiftErrorCallSite(const_cast<InvokeInst*>(II), SwiftErrorVal);
|
||||
}
|
||||
}
|
||||
|
||||
void Verifier::visitAllocaInst(AllocaInst &AI) {
|
||||
SmallPtrSet<Type*, 4> Visited;
|
||||
PointerType *PTy = AI.getType();
|
||||
@ -2937,6 +3010,10 @@ void Verifier::visitAllocaInst(AllocaInst &AI) {
|
||||
Assert(AI.getAlignment() <= Value::MaximumAlignment,
|
||||
"huge alignment values are unsupported", &AI);
|
||||
|
||||
if (AI.isSwiftError()) {
|
||||
verifySwiftErrorValue(&AI);
|
||||
}
|
||||
|
||||
visitInstruction(AI);
|
||||
}
|
||||
|
||||
|
@ -293,6 +293,31 @@ define void @f50(i8* swiftself)
|
||||
ret void;
|
||||
}
|
||||
|
||||
; CHECK: define i32 @f51(i8** swifterror)
|
||||
define i32 @f51(i8** swifterror)
|
||||
{
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
; CHECK: define i32 @f52(i32, i8** swifterror)
|
||||
define i32 @f52(i32, i8** swifterror)
|
||||
{
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
%swift_error = type {i64, i8}
|
||||
declare float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
|
||||
; CHECK: define float @f53
|
||||
; CHECK: alloca swifterror
|
||||
define float @f53(i8* %error_ref) {
|
||||
entry:
|
||||
%error_ptr_ref = alloca swifterror %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
%call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { noreturn }
|
||||
; CHECK: attributes #1 = { nounwind }
|
||||
; CHECK: attributes #2 = { readnone }
|
||||
|
31
test/Verifier/swifterror.ll
Normal file
31
test/Verifier/swifterror.ll
Normal file
@ -0,0 +1,31 @@
|
||||
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
%swift_error = type {i64, i8}
|
||||
|
||||
; CHECK: swifterror value can only be loaded and stored from, or as a swifterror argument!
|
||||
; CHECK: %swift_error** %error_ptr_ref
|
||||
; CHECK: %t = getelementptr inbounds %swift_error*, %swift_error** %error_ptr_ref, i64 1
|
||||
define float @foo(%swift_error** swifterror %error_ptr_ref) {
|
||||
%t = getelementptr inbounds %swift_error*, %swift_error** %error_ptr_ref, i64 1
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; CHECK: swifterror argument for call has mismatched alloca
|
||||
; CHECK: %error_ptr_ref = alloca %swift_error*
|
||||
; CHECK: %call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
define float @caller(i8* %error_ref) {
|
||||
entry:
|
||||
%error_ptr_ref = alloca %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
%call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; CHECK: Cannot have multiple 'swifterror' parameters!
|
||||
declare void @a(i32** swifterror %a, i32** swifterror %b)
|
||||
|
||||
; CHECK: Attribute 'swifterror' only applies to parameters with pointer type!
|
||||
declare void @b(i32 swifterror %a)
|
||||
|
||||
; CHECK: Attribute 'swifterror' only applies to parameters with pointer to pointer type!
|
||||
declare void @c(i32* swifterror %a)
|
4
test/Verifier/swifterror2.ll
Normal file
4
test/Verifier/swifterror2.ll
Normal file
@ -0,0 +1,4 @@
|
||||
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: invalid use of parameter-only attribute
|
||||
declare swifterror void @c(i32** swifterror %a)
|
4
test/Verifier/swifterror3.ll
Normal file
4
test/Verifier/swifterror3.ll
Normal file
@ -0,0 +1,4 @@
|
||||
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: expected type
|
||||
declare void @c(swifterror i32* %a)
|
Loading…
Reference in New Issue
Block a user