[flang-commits] [flang] [flang] Compiler directives defined by plugins (PR #228757)
via flang-commits
flang-commits at lists.llvm.org
Sat Oct 3 12:33:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
Author: Valentin Churavy (vchuravy)
<details>
<summary>Changes</summary>
For a plugin that I am working on (using Enzyme with Fortran) I would like to add directives.
There are two variants, and either or both would work.
1. !DIR$ prefix
2. !$prefix
The first causes other compilers to warn about unrecognized directives (which is annoying, but fine) and the second is "just a comment". The second form has precedence in the Tapenade or TAF ecosystem where they use `!$AD` or `!$TAF`.
Follow-up on #<!-- -->212195 for more fully featured flang plugins. I am happy to split this PR into smaller chunks if that is helpful.
:robot:
A plugin loaded with `flang -fc1 -load` can define its own compiler directives, which flang parses, checks and attaches to the MLIR it generates for the plugin's passes to act on:
!DIR$ prefix keyword [ ( arg [, arg]... ) ]
arg -> [ name = ] value
value -> name | /common-block/ | integer | character-literal
A plugin registers each directive from a static initializer through flang/Support/PluginDirectives.h: its prefix and keyword, what it applies to (a procedure, a variable, or either) and its keyword arguments, each a procedure, a variable, an integer or a string, possibly required. It may also register `!$prefix` as a comment sentinel, so that `!$prefix keyword ...` is the same directive and other compilers see a comment (free form, and fixed form with `!`, `c` or `*` in column 1; `!$` followed by a blank remains OpenMP conditional compilation).
- Parsing: the form is accepted only for a registered prefix, so no other directive changes meaning. Once the prefix matches, a malformed argument list is a parse error.
- Semantics: name arguments are resolved and checked against the registered kinds. A procedure must be a subprogram or an external procedure; a variable must be a variable, or a COMMON block as `/name/`. The subject is the first positional argument or, without one, the enclosing subprogram; in a function without a RESULT clause its name is the function. A generic interface as the subject stands for each of its specific procedures, for a directive without procedure arguments; as a procedure argument it is an error.
- Module files: a directive on a module entity is written to the module file, so that units using the module see it too. One whose arguments are not visible in the module (e.g. an internal procedure) is left out with a warning.
- Lowering: each directive becomes an entry of a `fir.directives` array attribute on the func.func or fir.global of its subject, its procedure and variable arguments symbol references (declared if the unit does not yet).
flang/examples/DirectivesPlugin registers directives with the prefix "example", and its `!$example` sentinel; the tests in flang/test/Examples/plugin-directives*.f90 check their lowering, generic interfaces, the semantic and parse errors, the sentinel in free and fixed form, and module files with and without the plugin.
Assisted-by: Claude Code (Opus 5.5)
---
Patch is 61.94 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/228757.diff
25 Files Affected:
- (modified) flang/examples/CMakeLists.txt (+1)
- (added) flang/examples/DirectivesPlugin/CMakeLists.txt (+9)
- (added) flang/examples/DirectivesPlugin/DirectivesPlugin.cpp (+52)
- (modified) flang/include/flang/Lower/ConvertVariable.h (+5)
- (modified) flang/include/flang/Parser/dump-parse-tree.h (+3)
- (modified) flang/include/flang/Parser/parse-tree.h (+15-1)
- (modified) flang/include/flang/Semantics/semantics.h (+20)
- (added) flang/include/flang/Support/PluginDirectives.h (+98)
- (modified) flang/lib/Lower/Bridge.cpp (+121)
- (modified) flang/lib/Lower/ConvertVariable.cpp (+8)
- (modified) flang/lib/Parser/Fortran-parsers.cpp (+70-1)
- (modified) flang/lib/Parser/parsing.cpp (+5)
- (modified) flang/lib/Parser/prescan.cpp (+52-11)
- (modified) flang/lib/Parser/prescan.h (+17)
- (modified) flang/lib/Parser/unparse.cpp (+23)
- (modified) flang/lib/Semantics/mod-file.cpp (+69)
- (modified) flang/lib/Semantics/resolve-names.cpp (+275-1)
- (modified) flang/lib/Support/CMakeLists.txt (+1)
- (added) flang/lib/Support/PluginDirectives.cpp (+63)
- (modified) flang/test/CMakeLists.txt (+1)
- (added) flang/test/Examples/plugin-directives-errors.f90 (+65)
- (added) flang/test/Examples/plugin-directives-module.f90 (+57)
- (added) flang/test/Examples/plugin-directives-parse.f90 (+19)
- (added) flang/test/Examples/plugin-directives-sentinel.f (+28)
- (added) flang/test/Examples/plugin-directives.f90 (+73)
``````````diff
diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt
index 746a6f23ae01559..ac11069f96cdd45 100644
--- a/flang/examples/CMakeLists.txt
+++ b/flang/examples/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(DirectivesPlugin)
add_subdirectory(PrintFlangFunctionNames)
add_subdirectory(FlangOmpReport)
add_subdirectory(FeatureList)
diff --git a/flang/examples/DirectivesPlugin/CMakeLists.txt b/flang/examples/DirectivesPlugin/CMakeLists.txt
new file mode 100644
index 000000000000000..f6c533a53a2f307
--- /dev/null
+++ b/flang/examples/DirectivesPlugin/CMakeLists.txt
@@ -0,0 +1,9 @@
+# TODO: Note that this is currently only available on Linux.
+# On Windows, we would also have to specify e.g. `PLUGIN_TOOL`.
+#
+# Nothing is linked in on purpose: the registry the plugin adds its directives
+# to is resolved against `flang -fc1` when the shared object is loaded.
+add_llvm_example_library(flangDirectivesPlugin
+ MODULE
+ DirectivesPlugin.cpp
+)
diff --git a/flang/examples/DirectivesPlugin/DirectivesPlugin.cpp b/flang/examples/DirectivesPlugin/DirectivesPlugin.cpp
new file mode 100644
index 000000000000000..464d7a397415d05
--- /dev/null
+++ b/flang/examples/DirectivesPlugin/DirectivesPlugin.cpp
@@ -0,0 +1,52 @@
+//===-- DirectivesPlugin.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Example plugin defining compiler directives with the prefix "example"
+// (flang/Support/PluginDirectives.h). Loaded with `flang -fc1 -load`, it makes
+// flang accept, check and lower
+//
+// !DIR$ EXAMPLE CALLBACK([proc,] HANDLER=proc [, PRIORITY=n] [, TAG=str])
+// !DIR$ EXAMPLE WATCH(var [, BY=var])
+// !DIR$ EXAMPLE NOTE([proc-or-var,] TEXT=str)
+//
+// which may also be spelled with the plugin's own comment sentinel, e.g.
+// `!$EXAMPLE NOTE(TEXT="...")`, a comment for other compilers.
+//
+// Each one becomes an entry of the `fir.directives` attribute of the
+// func.func or fir.global of its subject, for a pass of the plugin to act on;
+// this one defines no pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Support/PluginDirectives.h"
+
+using namespace Fortran::common;
+
+namespace {
+
+[[maybe_unused]] const bool registered{[] {
+ // Call HANDLER when the subject procedure (by default, the subprogram the
+ // directive is in) is called.
+ registerPluginDirective(
+ {"example", "callback", PluginDirectiveSubject::Procedure,
+ {{"handler", PluginDirectiveArgKind::Procedure, /*required=*/true},
+ {"priority", PluginDirectiveArgKind::Integer},
+ {"tag", PluginDirectiveArgKind::String}}});
+ // Track the subject variable, or a COMMON block, possibly through another
+ // variable.
+ registerPluginDirective({"example", "watch", PluginDirectiveSubject::Variable,
+ {{"by", PluginDirectiveArgKind::Variable}}});
+ // A remark on a procedure or a variable.
+ registerPluginDirective({"example", "note", PluginDirectiveSubject::Any,
+ {{"text", PluginDirectiveArgKind::String, /*required=*/true}}});
+ // !$example ... is !DIR$ example ...
+ registerPluginDirectiveSentinel("example");
+ return true;
+}()};
+
+} // namespace
diff --git a/flang/include/flang/Lower/ConvertVariable.h b/flang/include/flang/Lower/ConvertVariable.h
index c8f117e63040f4c..92b3493132984be 100644
--- a/flang/include/flang/Lower/ConvertVariable.h
+++ b/flang/include/flang/Lower/ConvertVariable.h
@@ -76,6 +76,11 @@ void initializeCloneAtRuntime(Fortran::lower::AbstractConverter &converter,
/// called.
void defineModuleVariable(AbstractConverter &, const pft::Variable &var);
+/// Declare the fir::GlobalOp of a module variable used from another module
+/// (as instantiateVariable does), outside of any function, and return it.
+fir::GlobalOp declareModuleVariable(AbstractConverter &,
+ const semantics::Symbol &);
+
/// Create fir::GlobalOp for all common blocks, including their initial values
/// if they have one. This should be called before lowering any scopes so that
/// common block globals are available when a common appear in a scope.
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 7ca404663b4869d..af7c5bb4bde5894 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -234,6 +234,9 @@ class ParseTreeDumper {
NODE(CompilerDirective, NameValue)
NODE(CompilerDirective, NoInline)
NODE(CompilerDirective, Unrecognized)
+ NODE(CompilerDirective, Plugin)
+ NODE(CompilerDirective::Plugin, Arg)
+ NODE(CompilerDirective::Plugin, CommonBlock)
NODE(CompilerDirective, VectorAlways)
NODE_ENUM(CompilerDirective::VectorLength, VectorLength::Kind)
NODE(CompilerDirective, VectorLength)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 207a5543ab77501..c1cad361289e64e 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3487,11 +3487,25 @@ struct CompilerDirective {
EMPTY_CLASS(IVDep);
EMPTY_CLASS(Simd);
EMPTY_CLASS(Unrecognized);
+ // !DIR$ prefix keyword [( arg [, arg]... )] for a prefix registered by a
+ // plugin (see flang/Support/PluginDirectives.h).
+ struct Plugin {
+ // A COMMON block named in an argument, /name/.
+ WRAPPER_CLASS(CommonBlock, Name);
+ struct Arg {
+ TUPLE_CLASS_BOILERPLATE(Arg);
+ std::tuple<std::optional<Name>,
+ std::variant<Name, CommonBlock, std::uint64_t, std::string>>
+ t;
+ };
+ TUPLE_CLASS_BOILERPLATE(Plugin);
+ std::tuple<Name, Name, std::list<Arg>> t;
+ };
CharBlock source;
std::variant<std::list<IgnoreTKR>, LoopCount, std::list<AssumeAligned>,
VectorAlways, VectorLength, std::list<NameValue>, Unroll, UnrollAndJam,
Unrecognized, NoVector, NoUnroll, NoUnrollAndJam, ForceInline, Inline,
- NoInline, InlineAlways, Prefetch, IVDep, Simd>
+ NoInline, InlineAlways, Prefetch, IVDep, Simd, Plugin>
u;
};
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index d1365c4e7f46b68..f064f8068a3c9eb 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -33,6 +33,7 @@ class IntrinsicTypeDefaultKinds;
namespace Fortran::parser {
struct AccObject;
+struct CompilerDirective;
struct Name;
struct Program;
class AllCookedSources;
@@ -417,6 +418,24 @@ class SemanticsContext {
omp::SemanticOverrides &GetOmpSemanticOverrides();
+ // Directives defined by plugins (flang/Support/PluginDirectives.h), after
+ // name resolution, with the procedure or variable they apply to. The names
+ // in their arguments have their symbols set.
+ struct PluginDirective {
+ SymbolRef subject;
+ const parser::CompilerDirective *directive;
+ // Read from a module file, i.e. written in another translation unit.
+ bool fromModFile{false};
+ };
+ void AddPluginDirective(const Symbol &subject,
+ const parser::CompilerDirective &directive, bool fromModFile) {
+ pluginDirectives_.push_back(
+ PluginDirective{subject, &directive, fromModFile});
+ }
+ const std::vector<PluginDirective> &GetPluginDirectives() const {
+ return pluginDirectives_;
+ }
+
private:
struct ScopeIndexComparator {
bool operator()(parser::CharBlock, parser::CharBlock) const;
@@ -464,6 +483,7 @@ class SemanticsContext {
std::map<SymbolRef, const IndexVarInfo, SymbolAddressCompare>
activeIndexVars_;
UnorderedSymbolSet errorSymbols_;
+ std::vector<PluginDirective> pluginDirectives_;
std::set<std::string> tempNames_;
const Scope *builtinsScope_{nullptr}; // module __Fortran_builtins
Scope *ppcBuiltinTypesScope_{nullptr}; // module __Fortran_PPC_types
diff --git a/flang/include/flang/Support/PluginDirectives.h b/flang/include/flang/Support/PluginDirectives.h
new file mode 100644
index 000000000000000..cff6b344f8a95e8
--- /dev/null
+++ b/flang/include/flang/Support/PluginDirectives.h
@@ -0,0 +1,98 @@
+//===-- include/flang/Support/PluginDirectives.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Compiler directives defined by plugins:
+//
+// !DIR$ prefix keyword [ ( arg [, arg]... ) ]
+// arg -> [ name = ] value, value -> name | integer | character-literal
+//
+// A plugin loaded with `flang -fc1 -load` registers the directives it defines
+// from a static initializer. The parser accepts the form above only for a
+// registered prefix; semantics resolves name arguments to symbols and checks
+// them against the registered argument kinds; lowering attaches the resolved
+// directive to its subject (a procedure or a variable) as an MLIR attribute,
+// for the plugin's own passes to interpret.
+//
+// A plugin may also register a comment sentinel for its prefix, so that
+//
+// !$prefix keyword [ ( arg [, arg]... ) ]
+//
+// is the same directive, and other compilers see a comment.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SUPPORT_PLUGINDIRECTIVES_H_
+#define FORTRAN_SUPPORT_PLUGINDIRECTIVES_H_
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace Fortran::common {
+
+/// What a directive argument must be.
+enum class PluginDirectiveArgKind {
+ Procedure, ///< A name resolving to a procedure.
+ Variable, ///< A name resolving to a variable.
+ Integer, ///< An integer literal.
+ String, ///< A character literal, or a name taken as its spelling.
+};
+
+struct PluginDirectiveArg {
+ /// Empty for a positional argument.
+ std::string keyword;
+ PluginDirectiveArgKind kind;
+ bool required{false};
+};
+
+/// What a directive applies to.
+enum class PluginDirectiveSubject {
+ /// A procedure: the first positional argument if it is given, otherwise
+ /// the subprogram whose specification part holds the directive.
+ Procedure,
+ /// A variable: the first positional argument.
+ Variable,
+ /// Either, as for Procedure.
+ Any,
+};
+
+struct PluginDirectiveSpec {
+ std::string prefix; ///< Lower case, e.g. "enzyme".
+ std::string keyword; ///< Lower case, e.g. "custom_rule".
+ PluginDirectiveSubject subject{PluginDirectiveSubject::Procedure};
+ /// The arguments after the (optional) positional subject.
+ std::vector<PluginDirectiveArg> args;
+};
+
+/// Register a directive. Call from a static initializer in a plugin.
+void registerPluginDirective(PluginDirectiveSpec spec);
+
+/// Whether a plugin registered a directive with this (lower case) prefix.
+bool isPluginDirectivePrefix(std::string_view prefix);
+
+/// The registered directive, or null.
+const PluginDirectiveSpec *lookupPluginDirective(
+ std::string_view prefix, std::string_view keyword);
+
+/// Make `!$prefix` a directive sentinel for the directives with this (lower
+/// case) prefix: in free form, and in fixed form with `!`, `c` or `*` in
+/// column 1. The prescanner spells such a line `!dir$ prefix ...`, so it is
+/// parsed, resolved, written to module files and lowered as that spelling.
+/// A fixed form sentinel longer than four characters ends where `$prefix`
+/// does, and the column after it takes the place of column 6: blank on an
+/// initial line, and the continuation mark on a continuation line.
+/// `!$` followed by a blank remains OpenMP conditional compilation.
+/// Call from a static initializer in a plugin, like registerPluginDirective.
+void registerPluginDirectiveSentinel(std::string_view prefix);
+
+/// The sentinels registered by registerPluginDirectiveSentinel, as "$prefix".
+const std::vector<std::string> &getPluginDirectiveSentinels();
+
+} // namespace Fortran::common
+
+#endif // FORTRAN_SUPPORT_PLUGINDIRECTIVES_H_
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 9a35b82b8f20ca2..b8f23da2964c884 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -687,9 +687,130 @@ class FirConverter : public Fortran::lower::AbstractConverter {
*this, bridge.getSemanticsContext());
});
+ createBuilderOutsideOfFuncOpAndDo([&]() { lowerPluginDirectives(); });
+
finalizeOpenMPLowering(globalOmpRequiresSymbols);
}
+ /// Attach the directives defined by plugins to the operations of their
+ /// subjects in this module (a func.func for a procedure, a fir.global for a
+ /// variable), as a `fir.directives` array of dictionaries:
+ /// {prefix = "enzyme", keyword = "custom_rule",
+ /// args = {reverse = @_QMmPrev, ...}}
+ /// Procedure and variable arguments are symbol references, declaring the
+ /// procedure or module variable if this module does not yet; the plugin's
+ /// passes give them a meaning.
+ void lowerPluginDirectives() {
+ mlir::ModuleOp module{getModuleOp()};
+ mlir::MLIRContext *ctx{module.getContext()};
+ // What semantics lets a directive refer to: a subprogram or an external
+ // procedure, a variable, or a COMMON block. Nothing else has an operation
+ // (or even a mangled name).
+ auto isReferable{[](const Fortran::semantics::Symbol &sym) {
+ if (Fortran::semantics::IsProcedure(sym)) {
+ return !sym.has<Fortran::semantics::GenericDetails>() &&
+ !Fortran::semantics::IsDummy(sym) &&
+ !Fortran::semantics::IsProcedurePointer(sym) &&
+ !Fortran::semantics::IsStmtFunction(sym) &&
+ !sym.attrs().test(Fortran::semantics::Attr::INTRINSIC);
+ }
+ return sym.has<Fortran::semantics::ObjectEntityDetails>() ||
+ sym.has<Fortran::semantics::CommonBlockDetails>();
+ }};
+ // The operation of a procedure or variable, declared if need be (as a
+ // reference to it would): procedures, and variables of modules, which
+ // may be another module's.
+ auto getOrDeclare{[&](const Fortran::semantics::Symbol &sym)
+ -> mlir::Operation * {
+ if (!isReferable(sym)) {
+ return nullptr;
+ }
+ if (mlir::Operation *op{module.lookupSymbol(mangleName(sym))}) {
+ return op;
+ }
+ if (Fortran::semantics::IsProcedure(sym)) {
+ return Fortran::lower::getOrDeclareFunction(
+ Fortran::evaluate::ProcedureDesignator{sym}, *this)
+ .getOperation();
+ }
+ if (sym.has<Fortran::semantics::ObjectEntityDetails>() &&
+ sym.owner().IsModule()) {
+ return Fortran::lower::declareModuleVariable(*this, sym).getOperation();
+ }
+ return nullptr;
+ }};
+ for (const auto &[subject, directive, fromModFile] :
+ bridge.getSemanticsContext().GetPluginDirectives()) {
+ const Fortran::semantics::Symbol &ultimate{subject->GetUltimate()};
+ // The unit with the directive keeps it even for a procedure or variable
+ // it does not use otherwise (e.g. an external procedure with an
+ // interface body); a unit that sees it through a module file only for
+ // those it uses.
+ mlir::Operation *target{nullptr};
+ if (!fromModFile)
+ target = getOrDeclare(ultimate);
+ else if (isReferable(ultimate))
+ target = module.lookupSymbol(mangleName(ultimate));
+ if (!target) {
+ continue; // neither defined nor referenced here
+ }
+ const auto &[prefix, keyword, args]{
+ std::get<Fortran::parser::CompilerDirective::Plugin>(directive->u).t};
+ llvm::SmallVector<mlir::NamedAttribute> argAttrs;
+ for (const Fortran::parser::CompilerDirective::Plugin::Arg &arg : args) {
+ const auto &argKeyword{std::get<0>(arg.t)};
+ if (!argKeyword) {
+ continue; // the subject
+ }
+ mlir::Attribute value{Fortran::common::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::Name &n) -> mlir::Attribute {
+ if (!n.symbol) {
+ return mlir::StringAttr::get(ctx, n.ToString());
+ }
+ mlir::Operation *op{getOrDeclare(n.symbol->GetUltimate())};
+ if (!op) {
+ return mlir::StringAttr::get(ctx, n.ToString());
+ }
+ return mlir::FlatSymbolRefAttr::get(
+ mlir::SymbolTable::getSymbolName(op));
+ },
+ [&](const Fortran::parser::CompilerDirective::Plugin::
+ CommonBlock &c) -> mlir::Attribute {
+ std::string name{c.v.symbol ? mangleName(*c.v.symbol)
+ : c.v.ToString()};
+ if (!module.lookupSymbol(name)) {
+ return mlir::StringAttr::get(ctx, c.v.ToString());
+ }
+ return mlir::FlatSymbolRefAttr::get(ctx, name);
+ },
+ [&](std::uint64_t n) -> mlir::Attribute {
+ return builder->getI64IntegerAttr(n);
+ },
+ [&](const std::string &str) -> mlir::Attribute {
+ return mlir::StringAttr::get(ctx, str);
+ },
+ },
+ std::get<1>(arg.t))};
+ argAttrs.push_back(
+ builder->getNamedAttr(argKeyword->ToString(), value));
+ }
+ mlir::Attribute entry{builder->getDictionaryAttr({
+ builder->getNamedAttr("prefix",
+ builder->getStringAttr(prefix.ToString())),
+ builder->getNamedAttr("keyword",
+ builder->getStringAttr(keyword.ToString())),
+ builder->getNamedAttr("args", builder->getDictionaryAttr(argAttrs)),
+ })};
+ llvm::SmallVector<mlir::Attribute> entries;
+ if (auto existing{
+ target->getAttrOfType<mlir::ArrayAttr>("fir.directives")})
+ entries.append(existing.begin(), existing.end());
+ entries.push_back(entry);
+ target->setAttr("fir.directives", builder->getArrayAttr(entries));
+ }
+ }
+
/// Declare a function.
void declareFunction(Fortran::lower::pft::FunctionLikeUnit &funit) {
CHECK(builder && "declareFunction called with uninitialized builder");
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 553032bb3909e7f..31f14abdfd4065d 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -3268,6 +3268,14 @@ void Fortran::lower::defineModuleVariable(
}
}
+fir::GlobalOp
+Fortran::lower::declareModuleVariable(AbstractConverter &converter,
+ const Fortran::semantics::Symbol &sym) {
+ Fortran::lower::pft::Variable var{sym, /*global=*/true};
+ return declareGlobal(converter, var, converter.mangleName(sym),
+ getLinkageAttribute(converter, var));
+}
+
void Fortran::lower::instantiateVariable(AbstractConverter &converter,
const pft::Variable &var,
Fortran::lower::SymMap &symMap,
diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 263ce9249a8b27d..931bdb2c8a0348e 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -38,6 +38,7 @@
#include "type-parser-implementation.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/user-state.h"
+#include "flang/Support/PluginDirectives.h"
namespace Fortran::parser {
@@ -1395,8 +1396,76 @@ constexpr auto inlinealwaysDir{
constexpr auto inlineDir{"INLINE" >> construct<CompilerDirective::Inline>()};
constexpr auto ivdep{"IVDEP" >> construct<CompilerDirective::IVDep>()};
constexpr auto simd{"SIMD" >> construct<CompilerDirective::Simd>()};
+// The prefix of a directive defined by a plugin: a name that a plugin
+// registered (flang/Support/Plug...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/228757
More information about the flang-commits
mailing list