diff options
Diffstat (limited to 'flang/lib/Parser/openmp-parsers.cpp')
| -rw-r--r-- | flang/lib/Parser/openmp-parsers.cpp | 244 | 
1 files changed, 225 insertions, 19 deletions
| diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index d1e081c..4159d2e 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -275,6 +275,13 @@ struct SpecificModifierParser {  // --- Iterator helpers ----------------------------------------------- +static EntityDecl MakeEntityDecl(ObjectName &&name) { +  return EntityDecl( +      /*ObjectName=*/std::move(name), std::optional<ArraySpec>{}, +      std::optional<CoarraySpec>{}, std::optional<CharLength>{}, +      std::optional<Initialization>{}); +} +  // [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not  // specified then the type of that iterator is default integer.  // [5.0:49:14] The iterator-type must be an integer type. @@ -282,11 +289,7 @@ static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {    std::list<EntityDecl> entities;    for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) { -    EntityDecl entityDecl( -        /*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{}, -        std::optional<CoarraySpec>{}, std::optional<CharLength>{}, -        std::optional<Initialization>{}); -    entities.push_back(std::move(entityDecl)); +    entities.push_back(MakeEntityDecl(std::move(*iter)));    }    return entities;  } @@ -306,6 +309,217 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {        makeEntityList(std::move(names)));  } +// --- Stylized expression handling ----------------------------------- + +// OpenMP has a concept of am "OpenMP stylized expression". Syntactially +// it looks like a typical Fortran expression (or statement), except: +// - the only variables allowed in it are OpenMP special variables, the +//   exact set of these variables depends on the specific case of the +//   stylized expression +// - the special OpenMP variables present may assume one or more types, +//   and the expression should be semantically valid for each type. +// +// The stylized expression can be thought of as a template, which will be +// instantiated for each type provided somewhere in the context in which +// the stylized expression appears. +// +// AST nodes: +// - OmpStylizedExpression: contains the source string for the expression, +//   plus the list of instances (OmpStylizedInstance). +// - OmpStylizedInstance: corresponds to the instantiation of the stylized +//   expression for a specific type. The way that the type is specified is +//   by creating declarations (OmpStylizedDeclaration) for the special +//   variables. Together with the AST tree corresponding to the stylized +//   expression the instantiation has enough information for semantic +//   analysis. Each instance has its own scope, and the special variables +//   have their own Symbol's (local to the scope). +// - OmpStylizedDeclaration: encapsulates the information that the visitors +//   in resolve-names can use to "emulate" a declaration for a special +//   variable and allow name resolution in the instantiation AST to work. +// +// Implementation specifics: +// The semantic analysis stores "evaluate::Expr" in each AST node rooted +// in parser::Expr (in the typedExpr member). The evaluate::Expr is specific +// to a given type, and so to allow different types for a given expression, +// for each type a separate copy of the parser::Expr subtree is created. +// Normally, AST nodes are non-copyable (copy-ctor is deleted), so to create +// several copies of a subtree, the same source string is parsed several +// times. The ParseState member in OmpStylizedExpression is the parser state +// immediately before the stylized expression. +// +// Initially, when OmpStylizedExpression is first created, the expression is +// parsed as if it was an actual code, but this parsing is only done to +// establish where the stylized expression ends (in the source). The source +// and the initial parser state are stored in the object, and the instance +// list is empty. +// Once the parsing of the containing OmpDirectiveSpecification completes, +// a post-processing "parser" (OmpStylizedInstanceCreator) executes. This +// post-processor examines the directive specification to see if it expects +// any stylized expressions to be contained in it, and then instantiates +// them for each such directive. + +template <typename A> struct NeverParser { +  using resultType = A; +  std::optional<resultType> Parse(ParseState &state) const { +    // Always fail, but without any messages. +    return std::nullopt; +  } +}; + +template <typename A> constexpr auto never() { return NeverParser<A>{}; } + +// Parser for optional<T> which always succeeds and returns std::nullptr. +// It's only needed to produce "std::optional<CallStmt::Chevrons>" in +// CallStmt. +template <typename A, typename B = void> struct NullParser; +template <typename B> struct NullParser<std::optional<B>> { +  using resultType = std::optional<B>; +  std::optional<resultType> Parse(ParseState &) const { +    return resultType{std::nullopt}; +  } +}; + +template <typename A> constexpr auto null() { return NullParser<A>{}; } + +// OmpStylizedDeclaration and OmpStylizedInstance are helper classes, and +// don't correspond to anything in the source. Their parsers should still +// exist, but they should never be executed. +TYPE_PARSER(construct<OmpStylizedDeclaration>(never<OmpStylizedDeclaration>())) +TYPE_PARSER(construct<OmpStylizedInstance>(never<OmpStylizedInstance>())) + +TYPE_PARSER( // +    construct<OmpStylizedInstance::Instance>(Parser<AssignmentStmt>{}) || +    construct<OmpStylizedInstance::Instance>( +        sourced(construct<CallStmt>(Parser<ProcedureDesignator>{}, +            null<std::optional<CallStmt::Chevrons>>(), +            parenthesized(optionalList(actualArgSpec))))) || +    construct<OmpStylizedInstance::Instance>(indirect(expr))) + +struct OmpStylizedExpressionParser { +  using resultType = OmpStylizedExpression; + +  std::optional<resultType> Parse(ParseState &state) const { +    auto *saved{new ParseState(state)}; +    auto getSource{verbatim(Parser<OmpStylizedInstance::Instance>{} >> ok)}; +    if (auto &&ok{getSource.Parse(state)}) { +      OmpStylizedExpression result{std::list<OmpStylizedInstance>{}}; +      result.source = ok->source; +      result.state = saved; +      // result.v remains empty +      return std::move(result); +    } +    delete saved; +    return std::nullopt; +  } +}; + +static void Instantiate(OmpStylizedExpression &ose, +    llvm::ArrayRef<const OmpTypeName *> types, llvm::ArrayRef<CharBlock> vars) { +  // 1. For each var in the vars list, declare it with the corresponding +  //    type from types. +  // 2. Run the parser to get the AST for the stylized expression. +  // 3. Create OmpStylizedInstance and append it to the list in ose. +  assert(types.size() == vars.size() && "List size mismatch"); +  // A ParseState object is irreversibly modified during parsing (in +  // particular, it cannot be rewound to an earlier position in the source). +  // Because of that we need to create a local copy for each instantiation. +  // If rewinding was possible, we could just use the current one, and we +  // wouldn't need to save it in the AST node. +  ParseState state{DEREF(ose.state)}; + +  std::list<OmpStylizedDeclaration> decls; +  for (auto [type, var] : llvm::zip_equal(types, vars)) { +    decls.emplace_back(OmpStylizedDeclaration{ +        common::Reference(*type), MakeEntityDecl(Name{var})}); +  } + +  if (auto &&instance{Parser<OmpStylizedInstance::Instance>{}.Parse(state)}) { +    ose.v.emplace_back( +        OmpStylizedInstance{std::move(decls), std::move(*instance)}); +  } +} + +static void InstantiateForTypes(OmpStylizedExpression &ose, +    const OmpTypeNameList &typeNames, llvm::ArrayRef<CharBlock> vars) { +  // For each type in the type list, declare all variables in vars with +  // that type, and complete the instantiation. +  for (const OmpTypeName &t : typeNames.v) { +    std::vector<const OmpTypeName *> types(vars.size(), &t); +    Instantiate(ose, types, vars); +  } +} + +static void InstantiateDeclareReduction(OmpDirectiveSpecification &spec) { +  // There can be arguments/clauses that don't make sense, that analysis +  // is left until semantic checks. Tolerate any unexpected stuff. +  auto *rspec{GetFirstArgument<OmpReductionSpecifier>(spec)}; +  if (!rspec) { +    return; +  } + +  const OmpTypeNameList *typeNames{nullptr}; + +  if (auto *cexpr{ +          const_cast<OmpCombinerExpression *>(GetCombinerExpr(*rspec))}) { +    typeNames = &std::get<OmpTypeNameList>(rspec->t); + +    InstantiateForTypes(*cexpr, *typeNames, OmpCombinerExpression::Variables()); +    delete cexpr->state; +    cexpr->state = nullptr; +  } else { +    // If there are no types, there is nothing else to do. +    return; +  } + +  for (const OmpClause &clause : spec.Clauses().v) { +    llvm::omp::Clause id{clause.Id()}; +    if (id == llvm::omp::Clause::OMPC_initializer) { +      if (auto *iexpr{const_cast<OmpInitializerExpression *>( +              GetInitializerExpr(clause))}) { +        InstantiateForTypes( +            *iexpr, *typeNames, OmpInitializerExpression::Variables()); +        delete iexpr->state; +        iexpr->state = nullptr; +      } +    } +  } +} + +static void InstantiateStylizedDirective(OmpDirectiveSpecification &spec) { +  const OmpDirectiveName &dirName{spec.DirName()}; +  if (dirName.v == llvm::omp::Directive::OMPD_declare_reduction) { +    InstantiateDeclareReduction(spec); +  } +} + +template <typename P, +    typename = std::enable_if_t< +        std::is_same_v<typename P::resultType, OmpDirectiveSpecification>>> +struct OmpStylizedInstanceCreator { +  using resultType = OmpDirectiveSpecification; +  constexpr OmpStylizedInstanceCreator(P p) : parser_(p) {} + +  std::optional<resultType> Parse(ParseState &state) const { +    if (auto &&spec{parser_.Parse(state)}) { +      InstantiateStylizedDirective(*spec); +      return std::move(spec); +    } +    return std::nullopt; +  } + +private: +  const P parser_; +}; + +template <typename P> +OmpStylizedInstanceCreator(P) -> OmpStylizedInstanceCreator<P>; + +// --- Parsers for types ---------------------------------------------- + +TYPE_PARSER( // +    sourced(construct<OmpTypeName>(Parser<DeclarationTypeSpec>{})) || +    sourced(construct<OmpTypeName>(Parser<TypeSpec>{}))) +  // --- Parsers for arguments ------------------------------------------  // At the moment these are only directive arguments. This is needed for @@ -366,10 +580,6 @@ struct OmpArgumentListParser {    }  }; -TYPE_PARSER( // -    construct<OmpTypeName>(Parser<DeclarationTypeSpec>{}) || -    construct<OmpTypeName>(Parser<TypeSpec>{})) -  // 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)  TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||      construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{})) @@ -1065,7 +1275,8 @@ TYPE_PARSER(construct<OmpOtherwiseClause>(  TYPE_PARSER(construct<OmpWhenClause>(      maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"), -    maybe(indirect(Parser<OmpDirectiveSpecification>{})))) +    maybe(indirect( +        OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{})))))  // OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)  TYPE_PARSER(construct<OmpGrainsizeClause>( @@ -1777,12 +1988,7 @@ TYPE_PARSER(              Parser<OpenMPInteropConstruct>{})) /      endOfLine) -TYPE_PARSER(construct<OmpInitializerProc>(Parser<ProcedureDesignator>{}, -    parenthesized(many(maybe(","_tok) >> Parser<ActualArgSpec>{})))) - -TYPE_PARSER(construct<OmpInitializerClause>( -    construct<OmpInitializerClause>(assignmentStmt) || -    construct<OmpInitializerClause>(Parser<OmpInitializerProc>{}))) +TYPE_PARSER(construct<OmpInitializerClause>(Parser<OmpInitializerExpression>{}))  // OpenMP 5.2: 7.5.4 Declare Variant directive  TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>( @@ -1794,7 +2000,7 @@ TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(  TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(      predicated(Parser<OmpDirectiveName>{},          IsDirective(llvm::omp::Directive::OMPD_declare_reduction)) >= -    Parser<OmpDirectiveSpecification>{}))) +    OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{}))))  // 2.10.6 Declare Target Construct  TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>( @@ -1832,8 +2038,8 @@ TYPE_PARSER(sourced(construct<OpenMPDeclareMapperConstruct>(          IsDirective(llvm::omp::Directive::OMPD_declare_mapper)) >=      Parser<OmpDirectiveSpecification>{}))) -TYPE_PARSER(construct<OmpCombinerExpression>(Parser<AssignmentStmt>{}) || -    construct<OmpCombinerExpression>(Parser<FunctionReference>{})) +TYPE_PARSER(construct<OmpCombinerExpression>(OmpStylizedExpressionParser{})) +TYPE_PARSER(construct<OmpInitializerExpression>(OmpStylizedExpressionParser{}))  TYPE_PARSER(sourced(construct<OpenMPCriticalConstruct>(      OmpBlockConstructParser{llvm::omp::Directive::OMPD_critical}))) | 
