diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index d81f61c59c85..dbdc09dde718 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -650,6 +650,7 @@ enum AttributeKindCodes { ATTR_KIND_NULL_POINTER_IS_VALID = 67, ATTR_KIND_NOUNDEF = 68, ATTR_KIND_BYREF = 69, + ATTR_KIND_MUSTPROGRESS = 70, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 96f7b73fba32..ead701e82d44 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -235,6 +235,9 @@ def WriteOnly : EnumAttr<"writeonly">; /// Zero extended before/after call. def ZExt : EnumAttr<"zeroext">; +/// Function is required to make Forward Progress. +def MustProgress : TypeAttr<"mustprogress">; + /// Target-independent string attributes. def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">; def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">; diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index dab0ce38f336..350d94ea09ed 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -622,6 +622,13 @@ public: addFnAttr(Attribute::NoRecurse); } + /// Determine if the function is required to make forward progress. + bool mustProgress() const { + return hasFnAttribute(Attribute::MustProgress) || + hasFnAttribute(Attribute::WillReturn); + } + void setMustProgress() { addFnAttr(Attribute::MustProgress); } + /// True if the ABI mandates (or the user requested) that this /// function be in a unwind table. bool hasUWTable() const { diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 591c6c3be2f1..61a6e130252e 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -698,6 +698,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(zeroext); KEYWORD(immarg); KEYWORD(byref); + KEYWORD(mustprogress); KEYWORD(type); KEYWORD(opaque); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 71ea7fbeba5c..c31518728fc6 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1309,6 +1309,9 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, case lltok::kw_inlinehint: B.addAttribute(Attribute::InlineHint); break; case lltok::kw_jumptable: B.addAttribute(Attribute::JumpTable); break; case lltok::kw_minsize: B.addAttribute(Attribute::MinSize); break; + case lltok::kw_mustprogress: + B.addAttribute(Attribute::MustProgress); + break; case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break; @@ -1724,6 +1727,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_inlinehint: case lltok::kw_jumptable: case lltok::kw_minsize: + case lltok::kw_mustprogress: case lltok::kw_naked: case lltok::kw_nobuiltin: case lltok::kw_noduplicate: @@ -1828,6 +1832,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_inlinehint: case lltok::kw_jumptable: case lltok::kw_minsize: + case lltok::kw_mustprogress: case lltok::kw_naked: case lltok::kw_nobuiltin: case lltok::kw_noduplicate: diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 8bdeb57999c6..cfd6f18d98eb 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -241,6 +241,7 @@ enum Kind { kw_zeroext, kw_immarg, kw_byref, + kw_mustprogress, kw_type, kw_opaque, diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index bc479fa152f2..f8069b93103f 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1535,6 +1535,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::NoUndef; case bitc::ATTR_KIND_BYREF: return Attribute::ByRef; + case bitc::ATTR_KIND_MUSTPROGRESS: + return Attribute::MustProgress; } } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 125125c88467..0b2b1d6cf5ce 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -743,6 +743,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_NOUNDEF; case Attribute::ByRef: return bitc::ATTR_KIND_BYREF; + case Attribute::MustProgress: + return bitc::ATTR_KIND_MUSTPROGRESS; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 9e4ff203d6aa..63384689074d 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -451,6 +451,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "immarg"; if (hasAttribute(Attribute::NoUndef)) return "noundef"; + if (hasAttribute(Attribute::MustProgress)) + return "mustprogress"; const bool IsByVal = hasAttribute(Attribute::ByVal); if (IsByVal || hasAttribute(Attribute::StructRet)) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 4bb26ea747aa..a9bf9cf90f91 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1613,6 +1613,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { case Attribute::Speculatable: case Attribute::StrictFP: case Attribute::NullPointerIsValid: + case Attribute::MustProgress: return true; default: break; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 691d0ddf9fcf..98e4d62aeeab 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -931,6 +931,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::StrictFP: case Attribute::UWTable: case Attribute::NoCfCheck: + case Attribute::MustProgress: break; } diff --git a/llvm/test/Assembler/mustprogress-parse-error-0.ll b/llvm/test/Assembler/mustprogress-parse-error-0.ll new file mode 100644 index 000000000000..56d4e6eb6fa4 --- /dev/null +++ b/llvm/test/Assembler/mustprogress-parse-error-0.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: [[@LINE+1]]:35: error: invalid use of function-only attribute +define void @test_mustprogress(i8 mustprogress %a) { + ret void +} + diff --git a/llvm/test/Assembler/mustprogress-parse-error-1.ll b/llvm/test/Assembler/mustprogress-parse-error-1.ll new file mode 100644 index 000000000000..ced1650e5a9c --- /dev/null +++ b/llvm/test/Assembler/mustprogress-parse-error-1.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: [[@LINE+1]]:8: error: invalid use of function-only attribute +define mustprogress void @test_mustprogress(i8 %a) { + ret void +} + diff --git a/llvm/test/Assembler/mustprogress-parse-error-2.ll b/llvm/test/Assembler/mustprogress-parse-error-2.ll new file mode 100644 index 000000000000..2d75b465a426 --- /dev/null +++ b/llvm/test/Assembler/mustprogress-parse-error-2.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as < %s 2>&1 | FileCheck %s + +; CHECK: [[@LINE+1]]:52: error: expected '{' in function body +define i32* @test_mustprogress(i8 %a) mustprogress 8 { + ret void +} + diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index a9e00650d559..f87708e83471 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -398,6 +398,12 @@ define void @f67(i32* byref(i32) %a) ret void } +; CHECK: define void @f68() #41 +define void @f68() mustprogress +{ + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -439,4 +445,5 @@ define void @f67(i32* byref(i32) %a) ; CHECK: attributes #38 = { nosync } ; CHECK: attributes #39 = { sanitize_memtag } ; CHECK: attributes #40 = { null_pointer_is_valid } +; CHECK: attributes #41 = { mustprogress } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }