From c724a83e2054d65a26141bc7518450519dd9b4c7 Mon Sep 17 00:00:00 2001 From: Tyler Nowicki Date: Sun, 12 Oct 2014 20:46:07 +0000 Subject: [PATCH] Allow constant expressions in pragma loop hints. Previously loop hints such as #pragma loop vectorize_width(#) required a constant. This patch allows a constant expression to be used as well. Such as a non-type template parameter or an expression (2 * c + 1). Reviewed by Richard Smith llvm-svn: 219589 --- clang/include/clang/AST/Attr.h | 1 + clang/include/clang/Basic/Attr.td | 4 +- .../clang/Basic/DiagnosticParseKinds.td | 11 +- .../clang/Basic/DiagnosticSemaKinds.td | 6 +- clang/include/clang/Sema/Sema.h | 3 + clang/lib/CodeGen/CGStmt.cpp | 11 +- clang/lib/Parse/ParsePragma.cpp | 143 ++++++++++++------ clang/lib/Sema/SemaExpr.cpp | 28 ++++ clang/lib/Sema/SemaStmtAttr.cpp | 12 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +++ clang/lib/Sema/TreeTransform.h | 59 +++++++- clang/test/CodeGen/pragma-loop.cpp | 84 ++++++++-- clang/test/Misc/ast-print-pragmas.cpp | 15 ++ clang/test/PCH/pragma-loop.cpp | 12 ++ clang/test/Parser/pragma-loop.cpp | 129 +++++++++++++--- clang/test/Parser/pragma-unroll.cpp | 14 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 30 +++- 17 files changed, 466 insertions(+), 116 deletions(-) diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index fc4881619bce..787843e64f56 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -16,6 +16,7 @@ #include "clang/AST/AttrIterator.h" #include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Basic/AttrKinds.h" #include "clang/Basic/LLVM.h" diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index fe47ef5bf98b..cc9629334c33 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1845,7 +1845,7 @@ def LoopHint : Attr { EnumArgument<"State", "LoopHintState", ["default", "enable", "disable"], ["Default", "Enable", "Disable"]>, - DefaultIntArgument<"Value", 1>]; + ExprArgument<"Value">]; let AdditionalMembers = [{ static const char *getOptionName(int Option) { @@ -1885,7 +1885,7 @@ def LoopHint : Attr { OS << "("; if (option == VectorizeWidth || option == InterleaveCount || option == UnrollCount) - OS << value; + value->printPretty(OS, nullptr, Policy); else if (state == Default) return ""; else if (state == Enable) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 96737ae97306..7e6964a80ac6 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -949,16 +949,15 @@ def err_omp_immediate_directive : Error< def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; -// Pragma support. -def err_pragma_invalid_keyword : Error< - "%select{invalid|missing}0 argument; expected '%select{enable|full}1' or 'disable'">; - // Pragma loop support. +def err_pragma_loop_missing_argument : Error< + "missing argument; expected %select{an integer value|" + "'%select{enable|full}1' or 'disable'}0">; def err_pragma_loop_invalid_option : Error< "%select{invalid|missing}0 option%select{ %1|}0; expected vectorize, " "vectorize_width, interleave, interleave_count, unroll, or unroll_count">; -def err_pragma_loop_numeric_value : Error< - "invalid argument; expected a positive integer value">; +def err_pragma_invalid_keyword : Error< + "invalid argument; expected '%select{enable|full}0' or 'disable'">; // Pragma unroll support. def warn_pragma_unroll_cuda_value_in_parens : Warning< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7f5be652e006..70ead3ad0953 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -551,8 +551,10 @@ def err_pragma_pop_visibility_mismatch : Error< "#pragma visibility pop with no matching #pragma visibility push">; def note_surrounding_namespace_starts_here : Note< "surrounding namespace with visibility attribute starts here">; -def err_pragma_loop_invalid_value : Error< - "invalid argument; expected a positive integer value">; +def err_pragma_loop_invalid_argument_type : Error< + "invalid argument of type %0; expected an integer type">; +def err_pragma_loop_invalid_argument_value : Error< + "%select{invalid value '%0'; must be positive|value '%0' is too large}1">; def err_pragma_loop_compatibility : Error< "%select{incompatible|duplicate}0 directives '%1' and '%2'">; def err_pragma_loop_precedes_nonloop : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3f52f2dccb55..fa53a3812e89 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3486,6 +3486,9 @@ public: PredefinedExpr::IdentType IT); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); + + bool CheckLoopHintExpr(Expr *E, SourceLocation Loc); + ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr); ExprResult ActOnCharacterConstant(const Token &Tok, Scope *UDLScope = nullptr); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index b970e4f1ab4c..d4fd3b5178d9 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -601,8 +601,6 @@ void CodeGenFunction::EmitCondBrHints(llvm::LLVMContext &Context, LoopHintAttr::OptionType Option = LH->getOption(); LoopHintAttr::LoopHintState State = LH->getState(); - int ValueInt = LH->getValue(); - const char *MetadataName; switch (Option) { case LoopHintAttr::Vectorize: @@ -622,6 +620,15 @@ void CodeGenFunction::EmitCondBrHints(llvm::LLVMContext &Context, MetadataName = "llvm.loop.unroll.count"; break; } + + Expr *ValueExpr = LH->getValue(); + int ValueInt = 1; + if (ValueExpr) { + llvm::APSInt ValueAPS = + ValueExpr->EvaluateKnownConstInt(CGM.getContext()); + ValueInt = static_cast(ValueAPS.getSExtValue()); + } + llvm::Value *Value; llvm::MDString *Name; switch (Option) { diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 64066c1ec29d..55919fd50beb 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -724,16 +724,28 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName, struct PragmaLoopHintInfo { Token PragmaName; Token Option; - Token Value; - bool HasValue; - PragmaLoopHintInfo() : HasValue(false) {} + Token *Toks; + size_t TokSize; + PragmaLoopHintInfo() : Toks(nullptr), TokSize(0) {} }; +static std::string PragmaLoopHintString(Token PragmaName, Token Option) { + std::string PragmaString; + if (PragmaName.getIdentifierInfo()->getName() == "loop") { + PragmaString = "clang loop "; + PragmaString += Option.getIdentifierInfo()->getName(); + } else { + assert(PragmaName.getIdentifierInfo()->getName() == "unroll" && + "Unexpected pragma name"); + PragmaString = "unroll"; + } + return PragmaString; +} + bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { assert(Tok.is(tok::annot_pragma_loop_hint)); PragmaLoopHintInfo *Info = static_cast(Tok.getAnnotationValue()); - ConsumeToken(); // The annotation token. IdentifierInfo *PragmaNameInfo = Info->PragmaName.getIdentifierInfo(); Hint.PragmaNameLoc = IdentifierLoc::create( @@ -747,50 +759,88 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { Hint.OptionLoc = IdentifierLoc::create( Actions.Context, Info->Option.getLocation(), OptionInfo); + Token *Toks = Info->Toks; + size_t TokSize = Info->TokSize; + // Return a valid hint if pragma unroll or nounroll were specified // without an argument. bool PragmaUnroll = PragmaNameInfo->getName() == "unroll"; bool PragmaNoUnroll = PragmaNameInfo->getName() == "nounroll"; - if (!Info->HasValue && (PragmaUnroll || PragmaNoUnroll)) { + if (TokSize == 0 && (PragmaUnroll || PragmaNoUnroll)) { + ConsumeToken(); // The annotation token. Hint.Range = Info->PragmaName.getLocation(); return true; } - // If no option is specified the argument is assumed to be numeric. + // The constant expression is always followed by an eof token, which increases + // the TokSize by 1. + assert(TokSize > 0 && + "PragmaLoopHintInfo::Toks must contain at least one token."); + + // If no option is specified the argument is assumed to be a constant expr. bool StateOption = false; - if (OptionInfo) + if (OptionInfo) { // Pragma unroll does not specify an option. StateOption = llvm::StringSwitch(OptionInfo->getName()) .Case("vectorize", true) .Case("interleave", true) .Case("unroll", true) .Default(false); + } + + // Verify loop hint has an argument. + if (Toks[0].is(tok::eof)) { + ConsumeToken(); // The annotation token. + Diag(Toks[0].getLocation(), diag::err_pragma_loop_missing_argument) + << /*StateArgument=*/StateOption << /*FullKeyword=*/PragmaUnroll; + return false; + } // Validate the argument. if (StateOption) { + ConsumeToken(); // The annotation token. bool OptionUnroll = OptionInfo->isStr("unroll"); - SourceLocation StateLoc = Info->Value.getLocation(); - IdentifierInfo *StateInfo = Info->Value.getIdentifierInfo(); + SourceLocation StateLoc = Toks[0].getLocation(); + IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo(); if (!StateInfo || ((OptionUnroll ? !StateInfo->isStr("full") : !StateInfo->isStr("enable")) && !StateInfo->isStr("disable"))) { - Diag(StateLoc, diag::err_pragma_invalid_keyword) - << /*MissingArgument=*/false << /*FullKeyword=*/OptionUnroll; + Diag(Toks[0].getLocation(), diag::err_pragma_invalid_keyword) + << /*FullKeyword=*/OptionUnroll; return false; } + if (TokSize > 2) + Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaLoopHintString(Info->PragmaName, Info->Option); Hint.StateLoc = IdentifierLoc::create(Actions.Context, StateLoc, StateInfo); } else { - // FIXME: We should allow non-type template parameters for the loop hint - // value. See bug report #19610 - if (Info->Value.is(tok::numeric_constant)) - Hint.ValueExpr = Actions.ActOnNumericConstant(Info->Value).get(); - else { - Diag(Info->Value.getLocation(), diag::err_pragma_loop_numeric_value); - return false; + // Enter constant expression including eof terminator into token stream. + PP.EnterTokenStream(Toks, TokSize, /*DisableMacroExpansion=*/false, + /*OwnsTokens=*/false); + ConsumeToken(); // The annotation token. + + ExprResult R = ParseConstantExpression(); + + // Tokens following an error in an ill-formed constant expression will + // remain in the token stream and must be removed. + if (Tok.isNot(tok::eof)) { + Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaLoopHintString(Info->PragmaName, Info->Option); + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); } + + ConsumeToken(); // Consume the constant expression eof terminator. + + if (R.isInvalid() || + Actions.CheckLoopHintExpr(R.get(), Toks[0].getLocation())) + return false; + + // Argument is a constant expression with an integer type. + Hint.ValueExpr = R.get(); } - Hint.Range = - SourceRange(Info->PragmaName.getLocation(), Info->Value.getLocation()); + Hint.Range = SourceRange(Info->PragmaName.getLocation(), + Info->Toks[TokSize - 1].getLocation()); return true; } @@ -1796,31 +1846,21 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP, static bool ParseLoopHintValue(Preprocessor &PP, Token &Tok, Token PragmaName, Token Option, bool ValueInParens, PragmaLoopHintInfo &Info) { - if (ValueInParens) { - if (Tok.is(tok::r_paren)) { - // Nothing between the parentheses. - std::string PragmaString; - if (PragmaName.getIdentifierInfo()->getName() == "loop") { - PragmaString = "clang loop "; - PragmaString += Option.getIdentifierInfo()->getName(); - } else { - assert(PragmaName.getIdentifierInfo()->getName() == "unroll" && - "Unexpected pragma name"); - PragmaString = "unroll"; - } - // Don't try to emit what the pragma is expecting with the diagnostic - // because the logic is non-trivial and we give expected values in sema - // diagnostics if an invalid argument is given. Here, just note that the - // pragma is missing an argument. - PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument) - << PragmaString << /*Expected=*/false; - return true; + SmallVector ValueList; + int OpenParens = ValueInParens ? 1 : 0; + // Read constant expression. + while (Tok.isNot(tok::eod)) { + if (Tok.is(tok::l_paren)) + OpenParens++; + else if (Tok.is(tok::r_paren)) { + OpenParens--; + if (OpenParens == 0 && ValueInParens) + break; } - } - // FIXME: Value should be stored and parsed as a constant expression. - Token Value = Tok; - PP.Lex(Tok); + ValueList.push_back(Tok); + PP.Lex(Tok); + } if (ValueInParens) { // Read ')' @@ -1831,10 +1871,20 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token &Tok, Token PragmaName, PP.Lex(Tok); } + Token EOFTok; + EOFTok.startToken(); + EOFTok.setKind(tok::eof); + EOFTok.setLocation(Tok.getLocation()); + ValueList.push_back(EOFTok); // Terminates expression for parsing. + + Token *TokenArray = + new (PP.getPreprocessorAllocator()) Token[ValueList.size()]; + std::copy(ValueList.begin(), ValueList.end(), TokenArray); + Info.Toks = TokenArray; + Info.TokSize = ValueList.size(); + Info.PragmaName = PragmaName; Info.Option = Option; - Info.Value = Value; - Info.HasValue = true; return false; } @@ -1975,7 +2025,6 @@ void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, if (Tok.is(tok::eod)) { // nounroll or unroll pragma without an argument. Info->PragmaName = PragmaName; - Info->HasValue = false; Info->Option.startToken(); } else if (PragmaName.getIdentifierInfo()->getName() == "nounroll") { PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) @@ -1997,7 +2046,7 @@ void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, // In CUDA, the argument to '#pragma unroll' should not be contained in // parentheses. if (PP.getLangOpts().CUDA && ValueInParens) - PP.Diag(Info->Value.getLocation(), + PP.Diag(Info->Toks[0].getLocation(), diag::warn_pragma_unroll_cuda_value_in_parens); if (Tok.isNot(tok::eod)) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index c0c074d316a5..d3190d2af0e3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3021,6 +3021,34 @@ static Expr *BuildFloatingLiteral(Sema &S, NumericLiteralParser &Literal, return FloatingLiteral::Create(S.Context, Val, isExact, Ty, Loc); } +bool Sema::CheckLoopHintExpr(Expr *E, SourceLocation Loc) { + assert(E && "Invalid expression"); + + if (E->isValueDependent()) + return false; + + QualType QT = E->getType(); + if (!QT->isIntegerType() || QT->isBooleanType() || QT->isCharType()) { + Diag(E->getExprLoc(), diag::err_pragma_loop_invalid_argument_type) << QT; + return true; + } + + llvm::APSInt ValueAPS; + ExprResult R = VerifyIntegerConstantExpression(E, &ValueAPS); + + if (R.isInvalid()) + return true; + + bool ValueIsPositive = ValueAPS.isStrictlyPositive(); + if (!ValueIsPositive || ValueAPS.getActiveBits() > 31) { + Diag(E->getExprLoc(), diag::err_pragma_loop_invalid_argument_value) + << ValueAPS.toString(10) << ValueIsPositive; + return true; + } + + return false; +} + ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { // Fast path for a single digit (which is quite common). A single digit // cannot have a trigraph, escaped newline, radix prefix, or suffix. diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 4cb0fde11d06..19e2c8ed652f 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -88,21 +88,15 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, Spelling = LoopHintAttr::Pragma_clang_loop; } - int ValueInt = 1; LoopHintAttr::LoopHintState State = LoopHintAttr::Default; if (PragmaNoUnroll) { State = LoopHintAttr::Disable; } else if (Option == LoopHintAttr::VectorizeWidth || Option == LoopHintAttr::InterleaveCount || Option == LoopHintAttr::UnrollCount) { - // FIXME: We should support template parameters for the loop hint value. - // See bug report #19610. - llvm::APSInt ValueAPS; - if (!ValueExpr || !ValueExpr->isIntegerConstantExpr(ValueAPS, S.Context) || - (ValueInt = ValueAPS.getSExtValue()) < 1) { - S.Diag(A.getLoc(), diag::err_pragma_loop_invalid_value); + assert(ValueExpr && "Attribute must have a valid value expression."); + if (S.CheckLoopHintExpr(ValueExpr, St->getLocStart())) return nullptr; - } } else if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) { @@ -117,7 +111,7 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, } return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, State, - ValueInt, A.getRange()); + ValueExpr, A.getRange()); } static void diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f44780407bad..9c1ffb0c6547 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -767,6 +767,8 @@ namespace { QualType ObjectType = QualType(), NamedDecl *FirstQualifierInScope = nullptr); + const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH); + ExprResult TransformPredefinedExpr(PredefinedExpr *E); ExprResult TransformDeclRefExpr(DeclRefExpr *E); ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E); @@ -1127,6 +1129,24 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return transformNonTypeTemplateParmRef(NTTP, E->getLocation(), Arg); } +const LoopHintAttr * +TemplateInstantiator::TransformLoopHintAttr(const LoopHintAttr *LH) { + Expr *TransformedExpr = getDerived().TransformExpr(LH->getValue()).get(); + + if (TransformedExpr == LH->getValue()) + return LH; + + // Generate error if there is a problem with the value. + if (getSema().CheckLoopHintExpr(TransformedExpr, LH->getLocation())) + return LH; + + // Create new LoopHintValueAttr with integral expression in place of the + // non-type template parameter. + return LoopHintAttr::CreateImplicit( + getSema().Context, LH->getSemanticSpelling(), LH->getOption(), + LH->getState(), TransformedExpr, LH->getRange()); +} + ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( NonTypeTemplateParmDecl *parm, SourceLocation loc, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index df4ab10e748b..799678e799c9 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -327,6 +327,27 @@ public: /// \returns the transformed OpenMP clause. OMPClause *TransformOMPClause(OMPClause *S); + /// \brief Transform the given attribute. + /// + /// By default, this routine transforms a statement by delegating to the + /// appropriate TransformXXXAttr function to transform a specific kind + /// of attribute. Subclasses may override this function to transform + /// attributed statements using some other mechanism. + /// + /// \returns the transformed attribute + const Attr *TransformAttr(const Attr *S); + +/// \brief Transform the specified attribute. +/// +/// Subclasses should override the transformation of attributes with a pragma +/// spelling to transform expressions stored within the attribute. +/// +/// \returns the transformed attribute. +#define ATTR(X) +#define PRAGMA_SPELLING_ATTR(X) \ + const X##Attr *Transform##X##Attr(const X##Attr *R) { return R; } +#include "clang/Basic/AttrList.inc" + /// \brief Transform the given expression. /// /// By default, this routine transforms an expression by delegating to the @@ -5543,19 +5564,43 @@ TreeTransform::TransformLabelStmt(LabelStmt *S) { SubStmt.get()); } -template -StmtResult -TreeTransform::TransformAttributedStmt(AttributedStmt *S) { +template +const Attr *TreeTransform::TransformAttr(const Attr *R) { + if (!R) + return R; + + switch (R->getKind()) { +// Transform attributes with a pragma spelling by calling TransformXXXAttr. +#define ATTR(X) +#define PRAGMA_SPELLING_ATTR(X) \ + case attr::X: \ + return getDerived().Transform##X##Attr(cast(R)); +#include "clang/Basic/AttrList.inc" + default: + return R; + } +} + +template +StmtResult TreeTransform::TransformAttributedStmt(AttributedStmt *S) { + bool AttrsChanged = false; + SmallVector Attrs; + + // Visit attributes and keep track if any are transformed. + for (const auto *I : S->getAttrs()) { + const Attr *R = getDerived().TransformAttr(I); + AttrsChanged |= (I != R); + Attrs.push_back(R); + } + StmtResult SubStmt = getDerived().TransformStmt(S->getSubStmt()); if (SubStmt.isInvalid()) return StmtError(); - // TODO: transform attributes - if (SubStmt.get() == S->getSubStmt() /* && attrs are the same */) + if (SubStmt.get() == S->getSubStmt() && !AttrsChanged) return S; - return getDerived().RebuildAttributedStmt(S->getAttrLoc(), - S->getAttrs(), + return getDerived().RebuildAttributedStmt(S->getAttrLoc(), Attrs, SubStmt.get()); } diff --git a/clang/test/CodeGen/pragma-loop.cpp b/clang/test/CodeGen/pragma-loop.cpp index b75c7ee225cc..9a9665e3974d 100644 --- a/clang/test/CodeGen/pragma-loop.cpp +++ b/clang/test/CodeGen/pragma-loop.cpp @@ -28,11 +28,13 @@ void do_test(int *List, int Length) { } while (i < Length); } +enum struct Tuner : short { Interleave = 4, Unroll = 8 }; + // Verify for loop is recognized after sequence of pragma clang loop directives. void for_test(int *List, int Length) { #pragma clang loop interleave(enable) -#pragma clang loop interleave_count(4) -#pragma clang loop unroll_count(8) +#pragma clang loop interleave_count(static_cast(Tuner::Interleave)) +#pragma clang loop unroll_count(static_cast(Tuner::Unroll)) for (int i = 0; i < Length; i++) { // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_3:.*]] List[i] = i * 2; @@ -74,28 +76,74 @@ void for_define_test(int *List, int Length, int Value) { } } +// Verify constant expressions are handled correctly. +void for_contant_expression_test(int *List, int Length) { +#pragma clang loop vectorize_width(1 + 4) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_7:.*]] + List[i] = i; + } + +#pragma clang loop vectorize_width(3 + VECWIDTH) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_8:.*]] + List[i] += i; + } +} + // Verify metadata is generated when template is used. template void for_template_test(A *List, int Length, A Value) { - #pragma clang loop vectorize_width(8) interleave_count(8) unroll_count(8) for (int i = 0; i < Length; i++) { - // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_7:.*]] + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_9:.*]] List[i] = i * Value; } } // Verify define is resolved correctly when template is used. -template +template void for_template_define_test(A *List, int Length, A Value) { -#pragma clang loop vectorize_width(VECWIDTH) interleave_count(INTCOUNT) -#pragma clang loop unroll_count(UNROLLCOUNT) + const T VWidth = VECWIDTH; + const T ICount = INTCOUNT; + const T UCount = UNROLLCOUNT; +#pragma clang loop vectorize_width(VWidth) interleave_count(ICount) +#pragma clang loop unroll_count(UCount) for (int i = 0; i < Length; i++) { - // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_8:.*]] + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_10:.*]] List[i] = i * Value; } } +// Verify templates and constant expressions are handled correctly. +template +void for_template_constant_expression_test(A *List, int Length) { +#pragma clang loop vectorize_width(V) interleave_count(I) unroll_count(U) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_11:.*]] + List[i] = i; + } + +#pragma clang loop vectorize_width(V * 2 + VECWIDTH) interleave_count(I * 2 + INTCOUNT) unroll_count(U * 2 + UNROLLCOUNT) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_12:.*]] + List[i] += i; + } + + const int Scale = 4; +#pragma clang loop vectorize_width(Scale * V) interleave_count(Scale * I) unroll_count(Scale * U) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_13:.*]] + List[i] += i; + } + +#pragma clang loop vectorize_width((Scale * V) + 2) + for (int i = 0; i < Length; i++) { + // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_14:.*]] + List[i] += i; + } +} + #undef VECWIDTH #undef INTCOUNT #undef UNROLLCOUNT @@ -105,7 +153,8 @@ void template_test(double *List, int Length) { double Value = 10; for_template_test(List, Length, Value); - for_template_define_test(List, Length, Value); + for_template_define_test(List, Length, Value); + for_template_constant_expression_test(List, Length); } // CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLL_FULL:.*]], metadata ![[WIDTH_4:.*]], metadata ![[INTERLEAVE_4:.*]], metadata ![[INTENABLE_1:.*]]} @@ -124,6 +173,19 @@ void template_test(double *List, int Length) { // CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLL_DISABLE:.*]], metadata ![[WIDTH_1:.*]]} // CHECK: ![[WIDTH_1]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 1} // CHECK: ![[LOOP_6]] = metadata !{metadata ![[LOOP_6]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]} -// CHECK: ![[LOOP_7]] = metadata !{metadata ![[LOOP_7]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_8:.*]], metadata ![[WIDTH_8:.*]]} +// CHECK: ![[LOOP_7]] = metadata !{metadata ![[LOOP_7]], metadata ![[WIDTH_5:.*]]} +// CHECK: ![[WIDTH_5]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 5} +// CHECK: ![[LOOP_8]] = metadata !{metadata ![[LOOP_8]], metadata ![[WIDTH_5:.*]]} +// CHECK: ![[LOOP_9]] = metadata !{metadata ![[LOOP_9]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_8:.*]], metadata ![[WIDTH_8:.*]]} // CHECK: ![[INTERLEAVE_8]] = metadata !{metadata !"llvm.loop.interleave.count", i32 8} -// CHECK: ![[LOOP_8]] = metadata !{metadata ![[LOOP_8]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]} +// CHECK: ![[LOOP_10]] = metadata !{metadata ![[LOOP_10]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]} +// CHECK: ![[LOOP_11]] = metadata !{metadata ![[LOOP_11]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_4:.*]], metadata ![[WIDTH_2:.*]]} +// CHECK: ![[LOOP_12]] = metadata !{metadata ![[LOOP_12]], metadata ![[UNROLL_24:.*]], metadata ![[INTERLEAVE_10:.*]], metadata ![[WIDTH_6:.*]]} +// CHECK: ![[UNROLL_24]] = metadata !{metadata !"llvm.loop.unroll.count", i32 24} +// CHECK: ![[INTERLEAVE_10]] = metadata !{metadata !"llvm.loop.interleave.count", i32 10} +// CHECK: ![[WIDTH_6]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 6} +// CHECK: ![[LOOP_13]] = metadata !{metadata ![[LOOP_13]], metadata ![[UNROLL_32:.*]], metadata ![[INTERLEAVE_16:.*]], metadata ![[WIDTH_8:.*]]} +// CHECK: ![[UNROLL_32]] = metadata !{metadata !"llvm.loop.unroll.count", i32 32} +// CHECK: ![[INTERLEAVE_16]] = metadata !{metadata !"llvm.loop.interleave.count", i32 16} +// CHECK: ![[LOOP_14]] = metadata !{metadata ![[LOOP_14]], metadata ![[WIDTH_10:.*]]} +// CHECK: ![[WIDTH_10]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 10} diff --git a/clang/test/Misc/ast-print-pragmas.cpp b/clang/test/Misc/ast-print-pragmas.cpp index 25421fcd0c3e..23f533fc3766 100644 --- a/clang/test/Misc/ast-print-pragmas.cpp +++ b/clang/test/Misc/ast-print-pragmas.cpp @@ -38,3 +38,18 @@ void test(int *List, int Length) { i++; } } + +template +void test_nontype_template_param(int *List, int Length) { +#pragma clang loop vectorize_width(V) interleave_count(I) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +// CHECK: #pragma clang loop interleave_count(I) +// CHECK: #pragma clang loop vectorize_width(V) + +void test_templates(int *List, int Length) { + test_nontype_template_param<2, 4>(List, Length); +} diff --git a/clang/test/PCH/pragma-loop.cpp b/clang/test/PCH/pragma-loop.cpp index 3eb1f60d96b7..2640020f0007 100644 --- a/clang/test/PCH/pragma-loop.cpp +++ b/clang/test/PCH/pragma-loop.cpp @@ -16,6 +16,8 @@ // CHECK: #pragma unroll // CHECK: #pragma unroll (32) // CHECK: #pragma nounroll +// CHECK: #pragma clang loop interleave_count(I) +// CHECK: #pragma clang loop vectorize_width(V) #ifndef HEADER #define HEADER @@ -81,6 +83,15 @@ public: i++; } } + + template + inline void run7(int *List, int Length) { +#pragma clang loop vectorize_width(V) +#pragma clang loop interleave_count(I) + for (int i = 0; i < Length; i++) { + List[i] = i; + } + } }; #else @@ -95,6 +106,7 @@ void test() { pt.run4(List, 100); pt.run5(List, 100); pt.run6(List, 100); + pt.run7<2, 4>(List, 100); } #endif diff --git a/clang/test/Parser/pragma-loop.cpp b/clang/test/Parser/pragma-loop.cpp index 742a5d71e018..547d87395ef5 100644 --- a/clang/test/Parser/pragma-loop.cpp +++ b/clang/test/Parser/pragma-loop.cpp @@ -3,6 +3,79 @@ // Note that this puts the expected lines before the directives to work around // limitations in the -verify mode. +template +void test_nontype_template_param(int *List, int Length) { +#pragma clang loop vectorize_width(V) interleave_count(I) + for (int i = 0; i < Length; i++) { + List[i] = i; + } + +#pragma clang loop vectorize_width(V + 4) interleave_count(I + 4) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +template +void test_nontype_template_vectorize(int *List, int Length) { + /* expected-error {{invalid value '-1'; must be positive}} */ #pragma clang loop vectorize_width(V) + for (int i = 0; i < Length; i++) { + List[i] = i; + } + + /* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop vectorize_width(V / 2) + for (int i = 0; i < Length; i++) { + List[i] += i; + } +} + +template +void test_nontype_template_interleave(int *List, int Length) { + /* expected-error {{invalid value '-1'; must be positive}} */ #pragma clang loop interleave_count(I) + for (int i = 0; i < Length; i++) { + List[i] = i; + } + + /* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop interleave_count(2 % I) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +template +void test_nontype_template_char(int *List, int Length) { + /* expected-error {{invalid argument of type 'char'; expected an integer type}} */ #pragma clang loop vectorize_width(V) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +template +void test_nontype_template_bool(int *List, int Length) { + /* expected-error {{invalid argument of type 'bool'; expected an integer type}} */ #pragma clang loop vectorize_width(V) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +template +void test_nontype_template_badarg(int *List, int Length) { + /* expected-error {{use of undeclared identifier 'Vec'}} */ #pragma clang loop vectorize_width(Vec) interleave_count(I) + /* expected-error {{use of undeclared identifier 'Int'}} */ #pragma clang loop vectorize_width(V) interleave_count(Int) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + +template +void test_type_template_vectorize(int *List, int Length) { + const T Value = -1; + /* expected-error {{invalid value '-1'; must be positive}} */ #pragma clang loop vectorize_width(Value) + for (int i = 0; i < Length; i++) { + List[i] = i; + } +} + void test(int *List, int Length) { int i = 0; @@ -43,6 +116,8 @@ void test(int *List, int Length) { VList[j] = List[j]; } + test_nontype_template_param<4, 8>(List, Length); + /* expected-error {{expected '('}} */ #pragma clang loop vectorize /* expected-error {{expected '('}} */ #pragma clang loop interleave /* expected-error {{expected '('}} */ #pragma clang loop unroll @@ -55,40 +130,56 @@ void test(int *List, int Length) { /* expected-error {{expected ')'}} */ #pragma clang loop interleave_count(4 /* expected-error {{expected ')'}} */ #pragma clang loop unroll_count(4 -/* expected-error {{missing argument to '#pragma clang loop vectorize'}} */ #pragma clang loop vectorize() -/* expected-error {{missing argument to '#pragma clang loop interleave_count'}} */ #pragma clang loop interleave_count() -/* expected-error {{missing argument to '#pragma clang loop unroll'}} */ #pragma clang loop unroll() +/* expected-error {{missing argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize() +/* expected-error {{missing argument; expected an integer value}} */ #pragma clang loop interleave_count() +/* expected-error {{missing argument; expected 'enable' or 'disable'}} */ #pragma clang loop unroll() -/* expected-error {{missing option}} */ #pragma clang loop +/* expected-error {{missing option; expected vectorize, vectorize_width, interleave, interleave_count, unroll, or unroll_count}} */ #pragma clang loop /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword(enable) /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop vectorize(enable) badkeyword(4) /* expected-warning {{extra tokens at end of '#pragma clang loop'}} */ #pragma clang loop vectorize(enable) , - while (i-4 < Length) { List[i] = i; } -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(0) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(0) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(0) +/* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop vectorize_width(0) +/* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop interleave_count(0) +/* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop unroll_count(0) + +/* expected-error {{expression is not an integral constant expression}} expected-note {{division by zero}} */ #pragma clang loop vectorize_width(10 / 0) +/* expected-error {{invalid value '0'; must be positive}} */ #pragma clang loop interleave_count(10 / 5 - 2) while (i-5 < Length) { List[i] = i; } -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(3000000000) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(3000000000) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(3000000000) +test_nontype_template_vectorize<4>(List, Length); +/* expected-note {{in instantiation of function template specialization}} */ test_nontype_template_vectorize<-1>(List, Length); +test_nontype_template_interleave<8>(List, Length); +/* expected-note {{in instantiation of function template specialization}} */ test_nontype_template_interleave<-1>(List, Length); + +/* expected-note {{in instantiation of function template specialization}} */ test_nontype_template_char<'A'>(List, Length); // Loop hint arg cannot be a char. +/* expected-note {{in instantiation of function template specialization}} */ test_nontype_template_bool(List, Length); // Or a bool. +/* expected-note {{in instantiation of function template specialization}} */ test_type_template_vectorize(List, Length); // Or a template type. + +/* expected-error {{value '3000000000' is too large}} */ #pragma clang loop vectorize_width(3000000000) +/* expected-error {{value '3000000000' is too large}} */ #pragma clang loop interleave_count(3000000000) +/* expected-error {{value '3000000000' is too large}} */ #pragma clang loop unroll_count(3000000000) while (i-6 < Length) { List[i] = i; } -/* expected-error {{expected ')'}} */ #pragma clang loop vectorize_width(1 +) 1 +/* expected-warning {{extra tokens at end of '#pragma clang loop'}} */ #pragma clang loop vectorize_width(1 +) 1 /* expected-warning {{extra tokens at end of '#pragma clang loop'}} */ #pragma clang loop vectorize_width(1) +1 +const int VV = 4; +/* expected-error {{expected expression}} */ #pragma clang loop vectorize_width(VV +/ 2) +/* expected-error {{use of undeclared identifier 'undefined'}} */ #pragma clang loop vectorize_width(VV+undefined) +/* expected-error {{expected ')'}} */ #pragma clang loop vectorize_width(1+(^*/2 * () +/* expected-warning {{extra tokens at end of '#pragma clang loop' - ignored}} */ #pragma clang loop vectorize_width(1+(-0[0])))))) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(badvalue) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(badvalue) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(badvalue) +/* expected-error {{use of undeclared identifier 'badvalue'}} */ #pragma clang loop vectorize_width(badvalue) +/* expected-error {{use of undeclared identifier 'badvalue'}} */ #pragma clang loop interleave_count(badvalue) +/* expected-error {{use of undeclared identifier 'badvalue'}} */ #pragma clang loop unroll_count(badvalue) while (i-6 < Length) { List[i] = i; } @@ -102,12 +193,12 @@ void test(int *List, int Length) { // PR20069 - Loop pragma arguments that are not identifiers or numeric // constants crash FE. -/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize(() +/* expected-error {{expected ')'}} */ #pragma clang loop vectorize(() /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop interleave(*) /* expected-error {{invalid argument; expected 'full' or 'disable'}} */ #pragma clang loop unroll(=) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(^) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(/) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(==) +/* expected-error {{type name requires a specifier or qualifier}} expected-error {{expected expression}} */ #pragma clang loop vectorize_width(^) +/* expected-error {{expected expression}} expected-error {{expected expression}} */ #pragma clang loop interleave_count(/) +/* expected-error {{expected expression}} expected-error {{expected expression}} */ #pragma clang loop unroll_count(==) while (i-8 < Length) { List[i] = i; } diff --git a/clang/test/Parser/pragma-unroll.cpp b/clang/test/Parser/pragma-unroll.cpp index 5d28dae2d84d..260945756f2f 100644 --- a/clang/test/Parser/pragma-unroll.cpp +++ b/clang/test/Parser/pragma-unroll.cpp @@ -27,7 +27,7 @@ void test(int *List, int Length) { } /* expected-error {{expected ')'}} */ #pragma unroll(4 -/* expected-error {{missing argument to '#pragma unroll'}} */ #pragma unroll() +/* expected-error {{missing argument; expected an integer value}} */ #pragma unroll() /* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 2 while (i-6 < Length) { List[i] = i; @@ -38,12 +38,12 @@ void test(int *List, int Length) { List[i] = i; } -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(() -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll - -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(0) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 0 -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(3000000000) -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 3000000000 +/* expected-error {{expected ')'}} */ #pragma unroll(() +/* expected-error {{expected expression}} */ #pragma unroll - +/* expected-error {{invalid value '0'; must be positive}} */ #pragma unroll(0) +/* expected-error {{invalid value '0'; must be positive}} */ #pragma unroll 0 +/* expected-error {{value '3000000000' is too large}} */ #pragma unroll(3000000000) +/* expected-error {{value '3000000000' is too large}} */ #pragma unroll 3000000000 while (i-8 < Length) { List[i] = i; } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 53a5758db768..a180e1f20d7c 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1669,8 +1669,16 @@ static void EmitAttrList(raw_ostream &OS, StringRef Class, } } -namespace clang { +// Determines if an attribute has a Pragma spelling. +static bool AttrHasPragmaSpelling(const Record *R) { + std::vector Spellings = GetFlattenedSpellings(*R); + return std::find_if(Spellings.begin(), Spellings.end(), + [](const FlattenedSpelling &S) { + return S.variety() == "Pragma"; + }) != Spellings.end(); +} +namespace clang { // Emits the enumeration list for attributes. void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("List of all attributes that Clang recognizes", OS); @@ -1696,14 +1704,25 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { " INHERITABLE_PARAM_ATTR(NAME)\n"; OS << "#endif\n\n"; + OS << "#ifndef PRAGMA_SPELLING_ATTR\n"; + OS << "#define PRAGMA_SPELLING_ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef LAST_PRAGMA_SPELLING_ATTR\n"; + OS << "#define LAST_PRAGMA_SPELLING_ATTR(NAME) PRAGMA_SPELLING_ATTR(NAME)\n"; + OS << "#endif\n\n"; + Record *InhClass = Records.getClass("InheritableAttr"); Record *InhParamClass = Records.getClass("InheritableParamAttr"); - std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), - NonInhAttrs, InhAttrs, InhParamAttrs; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), + NonInhAttrs, InhAttrs, InhParamAttrs, PragmaAttrs; for (auto *Attr : Attrs) { if (!Attr->getValueAsBit("ASTNode")) continue; - + + if (AttrHasPragmaSpelling(Attr)) + PragmaAttrs.push_back(Attr); + if (Attr->isSubClassOf(InhParamClass)) InhParamAttrs.push_back(Attr); else if (Attr->isSubClassOf(InhClass)) @@ -1712,6 +1731,7 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { NonInhAttrs.push_back(Attr); } + EmitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); EmitAttrList(OS, "ATTR", NonInhAttrs); @@ -1720,6 +1740,8 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { OS << "#undef INHERITABLE_ATTR\n"; OS << "#undef LAST_INHERITABLE_ATTR\n"; OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; + OS << "#undef LAST_PRAGMA_ATTR\n"; + OS << "#undef PRAGMA_SPELLING_ATTR\n"; OS << "#undef ATTR\n"; }