diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -650,6 +650,7 @@ 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 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -235,6 +235,9 @@ /// 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 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -622,6 +622,13 @@ 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 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -698,6 +698,7 @@ 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 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1309,6 +1309,9 @@ 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 @@ 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 @@ 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 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -241,6 +241,7 @@ 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 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1535,6 +1535,8 @@ 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 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -743,6 +743,8 @@ 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 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -451,6 +451,8 @@ 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 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1613,6 +1613,7 @@ 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 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -931,6 +931,7 @@ 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 --- /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 --- /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 --- /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 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -398,6 +398,12 @@ 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 @@ ; CHECK: attributes #38 = { nosync } ; CHECK: attributes #39 = { sanitize_memtag } ; CHECK: attributes #40 = { null_pointer_is_valid } +; CHECK: attributes #41 = { mustprogress } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }