From 3bfe74e70673bb74cfed0a86ce66c8c37095352b Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Jan 2025 05:39:43 -0600 Subject: [PATCH 1/4] [flang][OpenMP] Make parsing of trait properties more context-sensitive A trait poperty can be one of serveral alternatives, and each property in a list was parsed as if it could be any of these alternatives independently from other properties. This made the parsing vulnerable to certain ambiguities in the trait grammar (provided in the OpenMP spec). At the same time the OpenMP spec gives the expected types of properties for almost every trait: all properties listed for a given trait are usually of the same type, e.g. names, clauses, etc. Incorporate these restrictions into the parser, and additionally use property extensions as the fallback if the parsing of the expected property type failed. This is intended to allow the parser to succeed, and instead let the semantic-checking code emit a more user-friendly message. --- flang/include/flang/Parser/dump-parse-tree.h | 2 +- flang/include/flang/Parser/parse-tree.h | 40 ++---- flang/lib/Parser/openmp-parsers.cpp | 125 +++++++++++++++---- flang/lib/Parser/unparse.cpp | 5 +- 4 files changed, 116 insertions(+), 56 deletions(-) diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 11725991e9c9a..49eeed0e7b439 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -479,7 +479,7 @@ class ParseTreeDumper { NODE(parser, OmpTraitPropertyName) NODE(parser, OmpTraitScore) NODE(parser, OmpTraitPropertyExtension) - NODE(OmpTraitPropertyExtension, ExtensionValue) + NODE(OmpTraitPropertyExtension, Complex) NODE(parser, OmpTraitProperty) NODE(parser, OmpTraitSelectorName) NODE_ENUM(OmpTraitSelectorName, Value) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 00d85aa05fb3a..f8175ea1de679 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3505,37 +3505,22 @@ struct OmpTraitScore { }; // trait-property-extension -> -// trait-property-name (trait-property-value, ...) -// trait-property-value -> // trait-property-name | -// scalar-integer-expression | -// trait-property-extension -// -// The grammar in OpenMP 5.2+ spec is ambiguous, the above is a different -// version (but equivalent) that doesn't have ambiguities. -// The ambiguity is in -// trait-property: -// trait-property-name <- (a) -// trait-property-clause -// trait-property-expression <- (b) -// trait-property-extension <- this conflicts with (a) and (b) -// trait-property-extension: -// trait-property-name <- conflict with (a) -// identifier(trait-property-extension[, trait-property-extension[, ...]]) -// constant integer expression <- conflict with (b) +// scalar-expr | +// trait-property-name (trait-property-extension, ...) // struct OmpTraitPropertyExtension { CharBlock source; - TUPLE_CLASS_BOILERPLATE(OmpTraitPropertyExtension); - struct ExtensionValue { + UNION_CLASS_BOILERPLATE(OmpTraitPropertyExtension); + struct Complex { // name (prop-ext, prop-ext, ...) CharBlock source; - UNION_CLASS_BOILERPLATE(ExtensionValue); - std::variant> - u; + TUPLE_CLASS_BOILERPLATE(Complex); + std::tuple>> + t; }; - using ExtensionList = std::list; - std::tuple t; + + std::variant u; }; // trait-property -> @@ -3568,9 +3553,10 @@ struct OmpTraitProperty { // UID | T // unique-string-id /impl-defined // VENDOR | I // name-list (vendor-id /add-def-doc) // EXTENSION | I // name-list (ext_name /impl-defined) -// ATOMIC_DEFAULT_MEM_ORDER I | // value of admo +// ATOMIC_DEFAULT_MEM_ORDER I | // clause-list (value of admo) // REQUIRES | I // clause-list (from requires) // CONDITION U // logical-expr +// I // treated as extension // // Trait-set-selectors: // [D]evice, [T]arget_device, [C]onstruct, [I]mplementation, [U]ser. @@ -3579,7 +3565,7 @@ struct OmpTraitSelectorName { UNION_CLASS_BOILERPLATE(OmpTraitSelectorName); ENUM_CLASS(Value, Arch, Atomic_Default_Mem_Order, Condition, Device_Num, Extension, Isa, Kind, Requires, Simd, Uid, Vendor) - std::variant u; + std::variant u; }; // trait-selector -> diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 5ff91da082c85..a7b3986845d98 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -158,31 +158,23 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list &&names) { static std::string nameToString(Name &&name) { return name.ToString(); } TYPE_PARSER(sourced(construct( // - (space >> charLiteralConstantWithoutKind) || - applyFunction(nameToString, Parser{})))) + construct(space >> charLiteralConstantWithoutKind) || + construct( + applyFunction(nameToString, Parser{}))))) TYPE_PARSER(sourced(construct( // - "SCORE" >> parenthesized(scalarIntExpr)))) + "SCORE"_tok >> parenthesized(scalarIntExpr)))) -TYPE_PARSER(sourced(construct( - // Parse nested extension first. - construct( - indirect(Parser{})) || - construct( - Parser{}) || - construct(scalarExpr)))) - -TYPE_PARSER(sourced(construct( // +TYPE_PARSER(sourced(construct( Parser{}, parenthesized(nonemptySeparated( - Parser{}, ","_tok))))) + indirect(Parser{}), ","_tok))))) -TYPE_PARSER(sourced(construct( - // Try clause first, then extension before OmpTraitPropertyName. - construct(indirect(Parser{})) || - construct(Parser{}) || - construct(Parser{}) || - construct(scalarExpr)))) +TYPE_PARSER(sourced(construct( + construct( + Parser{}) || + construct(Parser{}) || + construct(scalarExpr)))) TYPE_PARSER(construct( "ARCH" >> pure(OmpTraitSelectorName::Value::Arch) || @@ -201,15 +193,96 @@ TYPE_PARSER(construct( TYPE_PARSER(sourced(construct( // Parse predefined names first (because of SIMD). construct(Parser{}) || - construct(OmpDirectiveNameParser{})))) + construct(OmpDirectiveNameParser{}) || + // identifier-or-string for extensions + construct( + applyFunction(nameToString, Parser{})) || + construct(space >> charLiteralConstantWithoutKind)))) + +// Parser for OmpTraitSelector::Properties +template +static constexpr auto propertyListParser(PropParser... pp) { + // Parse the property list "(score(expr): item1...)" in three steps: + // 1. Parse the "(" + // 2. Parse the optional "score(expr):" + // 3. Parse the "item1, ...)", together with the ")". + // The reason for including the ")" in the 3rd step is to force parsing + // the entire list in each of the alternative property parsers. Otherwise, + // the name parser could stop after "foo" in "(foo, bar(1))", without + // allowing the next parser to give the list a try. + + using P = OmpTraitProperty; + return maybe("(" >> + construct( + maybe(Parser{} / ":"_tok), + (attempt(nonemptySeparated(construct

(pp), ","_tok) / ")"_tok) || + ...))); +} + +// Parser for OmpTraitSelector +struct TraitSelectorParser { + using resultType = OmpTraitSelector; + + constexpr TraitSelectorParser(Parser p) : np(p) {} + + std::optional Parse(ParseState &state) const { + auto name{attempt(np).Parse(state)}; + if (!name.has_value()) { + return std::nullopt; + } + + // Default fallback parser for lists that cannot be parser using the + // primary property parser. + auto extParser{Parser{}}; + + if (auto *v{std::get_if(&name->u)}) { + switch (*v) { + // name-list properties + case OmpTraitSelectorName::Value::Arch: // [6.0:319:18] + case OmpTraitSelectorName::Value::Extension: // [6.0:319:30] + case OmpTraitSelectorName::Value::Isa: // [6.0:319:15] + case OmpTraitSelectorName::Value::Kind: // [6.0:319:10] + case OmpTraitSelectorName::Value::Uid: // [6.0:319:23](*) + case OmpTraitSelectorName::Value::Vendor: { // [6.0:319:27] + auto pp{propertyListParser(Parser{}, extParser)}; + return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state))); + } + // clause-list + case OmpTraitSelectorName::Value::Atomic_Default_Mem_Order: + // [6.0:321:26-29](*) + case OmpTraitSelectorName::Value::Requires: // [6.0:319:33] + case OmpTraitSelectorName::Value::Simd: { // [6.0:318:31] + auto pp{propertyListParser(indirect(Parser{}), extParser)}; + return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state))); + } + // expr-list + case OmpTraitSelectorName::Value::Condition: // [6.0:321:33](*) + case OmpTraitSelectorName::Value::Device_Num: { // [6.0:321:23-24](*) + auto pp{propertyListParser(scalarExpr, extParser)}; + return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state))); + } + // (*) The spec doesn't assign any list-type to these traits, but for + // convenience they can be treated as if they were. + } // switch + } else { + // The other alternatives are `llvm::omp::Directive`, and `std::string`. + // The former doesn't take any properties[1], the latter is a name of an + // extension[2]. + // [1] [6.0:319:1-2] + // [2] [6.0:319:36-37] + auto pp{propertyListParser(extParser)}; + return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state))); + } -TYPE_PARSER(construct( - maybe(Parser{} / ":"_tok), - nonemptySeparated(Parser{}, ","_tok))) + llvm_unreachable("Unhandled trait name?"); + } + +private: + const Parser np; +}; -TYPE_PARSER(sourced(construct( // - Parser{}, // - maybe(parenthesized(Parser{}))))) +TYPE_PARSER(construct( + TraitSelectorParser(Parser{}))) TYPE_PARSER(construct( "CONSTRUCT" >> pure(OmpTraitSetSelectorName::Value::Construct) || diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 7bf404bba2c3e..af5259e1daec4 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2075,10 +2075,11 @@ class UnparseVisitor { Walk(x.v); Put(")"); } - void Unparse(const OmpTraitPropertyExtension &x) { + void Unparse(const OmpTraitPropertyExtension::Complex &x) { + using PropList = std::list>; Walk(std::get(x.t)); Put("("); - Walk(std::get(x.t), ","); + Walk(std::get(x.t), ","); Put(")"); } void Unparse(const OmpTraitSelector &x) { From c44051da5290258af794ddccf58fa6ab022edd32 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Jan 2025 07:36:13 -0600 Subject: [PATCH 2/4] format/comment --- flang/include/flang/Parser/parse-tree.h | 2 +- flang/lib/Parser/openmp-parsers.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index f8175ea1de679..88ce141d17cf9 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3512,7 +3512,7 @@ struct OmpTraitScore { struct OmpTraitPropertyExtension { CharBlock source; UNION_CLASS_BOILERPLATE(OmpTraitPropertyExtension); - struct Complex { // name (prop-ext, prop-ext, ...) + struct Complex { // name (prop-ext, prop-ext, ...) CharBlock source; TUPLE_CLASS_BOILERPLATE(Complex); std::tuple{}}; if (auto *v{std::get_if(&name->u)}) { + // (*) The comments below show the sections of the OpenMP spec that + // describe given trait. The cases marked with a (*) are those where + // the spec doesn't assign any list-type to these traits, but for + // convenience they can be treated as if they were. switch (*v) { // name-list properties case OmpTraitSelectorName::Value::Arch: // [6.0:319:18] case OmpTraitSelectorName::Value::Extension: // [6.0:319:30] - case OmpTraitSelectorName::Value::Isa: // [6.0:319:15] + case OmpTraitSelectorName::Value::Isa: // [6.0:319:15] case OmpTraitSelectorName::Value::Kind: // [6.0:319:10] case OmpTraitSelectorName::Value::Uid: // [6.0:319:23](*) case OmpTraitSelectorName::Value::Vendor: { // [6.0:319:27] @@ -261,8 +265,6 @@ struct TraitSelectorParser { auto pp{propertyListParser(scalarExpr, extParser)}; return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state))); } - // (*) The spec doesn't assign any list-type to these traits, but for - // convenience they can be treated as if they were. } // switch } else { // The other alternatives are `llvm::omp::Directive`, and `std::string`. From 13740f921868ebb0bbc1b407db1f399211456149 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Jan 2025 08:58:24 -0600 Subject: [PATCH 3/4] add "sourced" --- flang/lib/Parser/openmp-parsers.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 7e6259155b226..029226d523ba9 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -210,13 +210,15 @@ static constexpr auto propertyListParser(PropParser... pp) { // the entire list in each of the alternative property parsers. Otherwise, // the name parser could stop after "foo" in "(foo, bar(1))", without // allowing the next parser to give the list a try. + auto listOf{[](auto parser) { // + return nonemptySeparated(parser, ","_tok); + }}; using P = OmpTraitProperty; - return maybe("(" >> + return maybe("(" >> // construct( maybe(Parser{} / ":"_tok), - (attempt(nonemptySeparated(construct

(pp), ","_tok) / ")"_tok) || - ...))); + (attempt(listOf(sourced(construct

(pp))) / ")"_tok) || ...))); } // Parser for OmpTraitSelector @@ -283,8 +285,8 @@ struct TraitSelectorParser { const Parser np; }; -TYPE_PARSER(construct( - TraitSelectorParser(Parser{}))) +TYPE_PARSER(sourced(construct( + sourced(TraitSelectorParser(Parser{}))))) TYPE_PARSER(construct( "CONSTRUCT" >> pure(OmpTraitSetSelectorName::Value::Construct) || From 527315fc389df871c622f43124e5305571ee6ac3 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Wed, 15 Jan 2025 12:19:18 -0600 Subject: [PATCH 4/4] fix parser --- flang/lib/Parser/openmp-parsers.cpp | 47 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 029226d523ba9..30e4d4d491d58 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -163,12 +163,12 @@ TYPE_PARSER(sourced(construct( // applyFunction(nameToString, Parser{}))))) TYPE_PARSER(sourced(construct( // - "SCORE"_tok >> parenthesized(scalarIntExpr)))) + "SCORE"_id >> parenthesized(scalarIntExpr)))) TYPE_PARSER(sourced(construct( Parser{}, parenthesized(nonemptySeparated( - indirect(Parser{}), ","_tok))))) + indirect(Parser{}), ","))))) TYPE_PARSER(sourced(construct( construct( @@ -177,18 +177,18 @@ TYPE_PARSER(sourced(construct( construct(scalarExpr)))) TYPE_PARSER(construct( - "ARCH" >> pure(OmpTraitSelectorName::Value::Arch) || - "ATOMIC_DEFAULT_MEM_ORDER" >> + "ARCH"_id >> pure(OmpTraitSelectorName::Value::Arch) || + "ATOMIC_DEFAULT_MEM_ORDER"_id >> pure(OmpTraitSelectorName::Value::Atomic_Default_Mem_Order) || - "CONDITION" >> pure(OmpTraitSelectorName::Value::Condition) || - "DEVICE_NUM" >> pure(OmpTraitSelectorName::Value::Device_Num) || - "EXTENSION" >> pure(OmpTraitSelectorName::Value::Extension) || - "ISA" >> pure(OmpTraitSelectorName::Value::Isa) || - "KIND" >> pure(OmpTraitSelectorName::Value::Kind) || - "REQUIRES" >> pure(OmpTraitSelectorName::Value::Requires) || - "SIMD" >> pure(OmpTraitSelectorName::Value::Simd) || - "UID" >> pure(OmpTraitSelectorName::Value::Uid) || - "VENDOR" >> pure(OmpTraitSelectorName::Value::Vendor))) + "CONDITION"_id >> pure(OmpTraitSelectorName::Value::Condition) || + "DEVICE_NUM"_id >> pure(OmpTraitSelectorName::Value::Device_Num) || + "EXTENSION"_id >> pure(OmpTraitSelectorName::Value::Extension) || + "ISA"_id >> pure(OmpTraitSelectorName::Value::Isa) || + "KIND"_id >> pure(OmpTraitSelectorName::Value::Kind) || + "REQUIRES"_id >> pure(OmpTraitSelectorName::Value::Requires) || + "SIMD"_id >> pure(OmpTraitSelectorName::Value::Simd) || + "UID"_id >> pure(OmpTraitSelectorName::Value::Uid) || + "VENDOR"_id >> pure(OmpTraitSelectorName::Value::Vendor))) TYPE_PARSER(sourced(construct( // Parse predefined names first (because of SIMD). @@ -211,14 +211,14 @@ static constexpr auto propertyListParser(PropParser... pp) { // the name parser could stop after "foo" in "(foo, bar(1))", without // allowing the next parser to give the list a try. auto listOf{[](auto parser) { // - return nonemptySeparated(parser, ","_tok); + return nonemptySeparated(parser, ","); }}; using P = OmpTraitProperty; return maybe("(" >> // construct( - maybe(Parser{} / ":"_tok), - (attempt(listOf(sourced(construct

(pp))) / ")"_tok) || ...))); + maybe(Parser{} / ":"), + (attempt(listOf(sourced(construct

(pp))) / ")") || ...))); } // Parser for OmpTraitSelector @@ -289,21 +289,22 @@ TYPE_PARSER(sourced(construct( sourced(TraitSelectorParser(Parser{}))))) TYPE_PARSER(construct( - "CONSTRUCT" >> pure(OmpTraitSetSelectorName::Value::Construct) || - "DEVICE" >> pure(OmpTraitSetSelectorName::Value::Device) || - "IMPLEMENTATION" >> pure(OmpTraitSetSelectorName::Value::Implementation) || - "TARGET_DEVICE" >> pure(OmpTraitSetSelectorName::Value::Target_Device) || - "USER" >> pure(OmpTraitSetSelectorName::Value::User))) + "CONSTRUCT"_id >> pure(OmpTraitSetSelectorName::Value::Construct) || + "DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Device) || + "IMPLEMENTATION"_id >> + pure(OmpTraitSetSelectorName::Value::Implementation) || + "TARGET_DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Target_Device) || + "USER"_id >> pure(OmpTraitSetSelectorName::Value::User))) TYPE_PARSER(sourced(construct( Parser{}))) TYPE_PARSER(sourced(construct( // Parser{}, - "=" >> braced(nonemptySeparated(Parser{}, ","_tok))))) + "=" >> braced(nonemptySeparated(Parser{}, ","))))) TYPE_PARSER(sourced(construct( - nonemptySeparated(Parser{}, ","_tok)))) + nonemptySeparated(Parser{}, ",")))) // Parser == Parser