From 4bda8825177239052a841129dccd7e3faa16a61a Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 1 Apr 2016 21:41:15 +0000 Subject: [PATCH] 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 --- docs/LangRef.rst | 19 +++++ include/llvm-c/Core.h | 1 + include/llvm/CodeGen/FastISel.h | 3 +- include/llvm/IR/Argument.h | 3 + include/llvm/IR/Attributes.td | 3 + include/llvm/IR/Instructions.h | 12 +++ include/llvm/Target/TargetCallingConv.h | 5 ++ include/llvm/Target/TargetCallingConv.td | 5 ++ include/llvm/Target/TargetLowering.h | 4 +- lib/AsmParser/LLLexer.cpp | 1 + lib/AsmParser/LLParser.cpp | 8 +- lib/AsmParser/LLToken.h | 1 + lib/Bitcode/Reader/BitcodeReader.cpp | 10 ++- lib/Bitcode/Writer/BitcodeWriter.cpp | 5 +- lib/CodeGen/SelectionDAG/FastISel.cpp | 3 + .../SelectionDAG/SelectionDAGBuilder.cpp | 5 ++ lib/CodeGen/SelectionDAG/TargetLowering.cpp | 1 + lib/IR/AsmWriter.cpp | 2 + lib/IR/Attributes.cpp | 3 + lib/IR/Function.cpp | 5 ++ lib/IR/Instructions.cpp | 1 + lib/IR/Verifier.cpp | 83 ++++++++++++++++++- test/Bitcode/attributes.ll | 25 ++++++ test/Verifier/swifterror.ll | 31 +++++++ test/Verifier/swifterror2.ll | 4 + test/Verifier/swifterror3.ll | 4 + 26 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 test/Verifier/swifterror.ll create mode 100644 test/Verifier/swifterror2.ll create mode 100644 test/Verifier/swifterror3.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 1b5c867b8317..a525c0c99ef6 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -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 diff --git a/include/llvm-c/Core.h b/include/llvm-c/Core.h index 1e6ca3de7024..c62007fad8bb 100644 --- a/include/llvm-c/Core.h +++ b/include/llvm-c/Core.h @@ -95,6 +95,7 @@ typedef enum { LLVMConvergentAttribute = 1ULL << 46, LLVMSafeStackAttribute = 1ULL << 47, LLVMSwiftSelfAttribute = 1ULL << 48, + LLVMSwiftErrorAttribute = 1ULL << 49, */ } LLVMAttribute; diff --git a/include/llvm/CodeGen/FastISel.h b/include/llvm/CodeGen/FastISel.h index 7d228f235de3..7eb47bb5e187 100644 --- a/include/llvm/CodeGen/FastISel.h +++ b/include/llvm/CodeGen/FastISel.h @@ -42,13 +42,14 @@ class FastISel { 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. diff --git a/include/llvm/IR/Argument.h b/include/llvm/IR/Argument.h index bc95e65c4f19..d8b280a66f18 100644 --- a/include/llvm/IR/Argument.h +++ b/include/llvm/IR/Argument.h @@ -76,6 +76,9 @@ class Argument : public Value, public ilist_node { /// \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. diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td index 8b490ec4fc84..3442da2aec8c 100644 --- a/include/llvm/IR/Attributes.td +++ b/include/llvm/IR/Attributes.td @@ -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">; diff --git a/include/llvm/IR/Instructions.h b/include/llvm/IR/Instructions.h index 5a32b7bdec54..5b5834d341a4 100644 --- a/include/llvm/IR/Instructions.h +++ b/include/llvm/IR/Instructions.h @@ -152,6 +152,18 @@ class AllocaInst : public UnaryInstruction { (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); diff --git a/include/llvm/Target/TargetCallingConv.h b/include/llvm/Target/TargetCallingConv.h index a07dc71962a7..ac12f195d004 100644 --- a/include/llvm/Target/TargetCallingConv.h +++ b/include/llvm/Target/TargetCallingConv.h @@ -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; } diff --git a/include/llvm/Target/TargetCallingConv.td b/include/llvm/Target/TargetCallingConv.td index ae6126b3c386..3d8639dfe1da 100644 --- a/include/llvm/Target/TargetCallingConv.td +++ b/include/llvm/Target/TargetCallingConv.td @@ -47,6 +47,11 @@ class CCIfByVal : CCIf<"ArgFlags.isByVal()", A> { class CCIfSwiftSelf : CCIf<"ArgFlags.isSwiftSelf()", A> { } +/// CCIfSwiftError - If the current argument has swifterror parameter attribute, +/// apply Action A. +class CCIfSwiftError : CCIf<"ArgFlags.isSwiftError()", A> { +} + /// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs /// parameter attribute, apply Action A. class CCIfConsecutiveRegs : CCIf<"ArgFlags.isInConsecutiveRegs()", A> { diff --git a/include/llvm/Target/TargetLowering.h b/include/llvm/Target/TargetLowering.h index 4aaffbab8ff6..4e247b7addcd 100644 --- a/include/llvm/Target/TargetLowering.h +++ b/include/llvm/Target/TargetLowering.h @@ -2327,11 +2327,13 @@ class TargetLowering : public TargetLoweringBase { 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); }; diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 5ad5ddd52a0a..0f4a6eb81098 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -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); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index e72686d045cb..d326edfd63fd 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -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; } diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index 4df922b1c786..1581b7d96186 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -152,6 +152,7 @@ namespace lltok { kw_sret, kw_sanitize_thread, kw_sanitize_memory, + kw_swifterror, kw_swiftself, kw_uwtable, kw_zeroext, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 1840b60cc012..45435962a7c6 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -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(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; diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index 18fb7ad8d111..84bdd8ca2e0a 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -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; } diff --git a/lib/CodeGen/SelectionDAG/FastISel.cpp b/lib/CodeGen/SelectionDAG/FastISel.cpp index cff78c453c80..b89d6a0d3322 100644 --- a/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -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) { diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index e3acfd2436e3..db145cf11a22 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -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)) { diff --git a/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 418ab63c05dd..366b7f84926b 100644 --- a/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -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); } diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index 62c66924a8e7..6276cb65aefd 100644 --- a/lib/IR/AsmWriter.cpp +++ b/lib/IR/AsmWriter.cpp @@ -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 diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index 7fb5bf1f42fe..b8c03def70e4 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -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; diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp index 5efde82f18f2..7826971abdd8 100644 --- a/lib/IR/Function.cpp +++ b/lib/IR/Function.cpp @@ -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 { diff --git a/lib/IR/Instructions.cpp b/lib/IR/Instructions.cpp index 0eac79fbea83..9c2f765687da 100644 --- a/lib/IR/Instructions.cpp +++ b/lib/IR/Instructions.cpp @@ -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; } diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 807fe092ac70..5770d6c87705 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -426,6 +426,8 @@ class Verifier : public InstVisitor, VerifierSupport { 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(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(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(U) || isa(U) || isa(U) || + isa(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(U)) + Assert(StoreI->getOperand(1) == SwiftErrorVal, + "swifterror value should be the second operand when used " + "by stores", SwiftErrorVal, U); + if (auto CallI = dyn_cast(U)) + verifySwiftErrorCallSite(const_cast(CallI), SwiftErrorVal); + if (auto II = dyn_cast(U)) + verifySwiftErrorCallSite(const_cast(II), SwiftErrorVal); + } +} + void Verifier::visitAllocaInst(AllocaInst &AI) { SmallPtrSet 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); } diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index 74b0386c33d3..dec3608fb2b4 100644 --- a/test/Bitcode/attributes.ll +++ b/test/Bitcode/attributes.ll @@ -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 } diff --git a/test/Verifier/swifterror.ll b/test/Verifier/swifterror.ll new file mode 100644 index 000000000000..d8eacf5480ed --- /dev/null +++ b/test/Verifier/swifterror.ll @@ -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) diff --git a/test/Verifier/swifterror2.ll b/test/Verifier/swifterror2.ll new file mode 100644 index 000000000000..23d3b6321d6a --- /dev/null +++ b/test/Verifier/swifterror2.ll @@ -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) diff --git a/test/Verifier/swifterror3.ll b/test/Verifier/swifterror3.ll new file mode 100644 index 000000000000..bf2483f0a38a --- /dev/null +++ b/test/Verifier/swifterror3.ll @@ -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)