[flang-commits] [flang] [Flang] Add `INLINEALWAYS` Compiler Directive (PR #192674)

via flang-commits flang-commits at lists.llvm.org
Fri Apr 17 07:49:57 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Jack Styles (Stylie777)

<details>
<summary>Changes</summary>

Adds support for the INLINEALWAYS Compiler Directive to Flang. This was previously supported in Classic-Flang, and works in the same way as FORCEINLINE.

It can either be defined at the call site, or within the function the user wishes to inline.

The missing support was highlighted while building an opensource benchmark, as build warnings were indicating that this compiler directive was being ignored.

---
Full diff: https://github.com/llvm/llvm-project/pull/192674.diff


9 Files Affected:

- (modified) flang/include/flang/Parser/dump-parse-tree.h (+1) 
- (modified) flang/include/flang/Parser/parse-tree.h (+6-1) 
- (modified) flang/lib/Lower/Bridge.cpp (+27) 
- (modified) flang/lib/Parser/Fortran-parsers.cpp (+3) 
- (modified) flang/lib/Parser/unparse.cpp (+7) 
- (modified) flang/lib/Semantics/canonicalize-directives.cpp (+2-1) 
- (modified) flang/lib/Semantics/resolve-names.cpp (+2-1) 
- (added) flang/test/Lower/inlinealways-directive.f90 (+26) 
- (added) flang/test/Parser/inlinealways-directive.f90 (+58) 


``````````diff
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index eefab487413da..e13fbefcc8c2e 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -239,6 +239,7 @@ class ParseTreeDumper {
   NODE(CompilerDirective, NoUnroll)
   NODE(CompilerDirective, NoUnrollAndJam)
   NODE(CompilerDirective, Prefetch)
+  NODE(CompilerDirective, InlineAlways)
   NODE(parser, ComplexLiteralConstant)
   NODE(parser, ComplexPart)
   NODE(parser, ComponentArraySpec)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 960ebbcc99efb..bc4f7fd443972 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3344,6 +3344,7 @@ struct StmtFunctionStmt {
 // !DIR$ FORCEINLINE
 // !DIR$ INLINE
 // !DIR$ NOINLINE
+// !DIR$ INLINEALWAYS
 // !DIR$ IVDEP
 // !DIR$ <anything else>
 struct CompilerDirective {
@@ -3380,6 +3381,10 @@ struct CompilerDirective {
     WRAPPER_CLASS_BOILERPLATE(
         Prefetch, std::list<common::Indirection<Designator>>);
   };
+  struct InlineAlways {
+    WRAPPER_CLASS_BOILERPLATE(
+      InlineAlways, std::optional<Name>);
+  };
   EMPTY_CLASS(NoVector);
   EMPTY_CLASS(NoUnroll);
   EMPTY_CLASS(NoUnrollAndJam);
@@ -3392,7 +3397,7 @@ struct CompilerDirective {
   std::variant<std::list<IgnoreTKR>, LoopCount, std::list<AssumeAligned>,
       VectorAlways, VectorLength, std::list<NameValue>, Unroll, UnrollAndJam,
       Unrecognized, NoVector, NoUnroll, NoUnrollAndJam, ForceInline, Inline,
-      NoInline, Prefetch, IVDep>
+      NoInline, InlineAlways, Prefetch, IVDep>
       u;
 };
 
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 28c82a6ca99ce..733e02cd58558 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -2083,6 +2083,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
               [&](const Fortran::parser::CompilerDirective::ForceInline &) {
                 stmt.typedCall->setAlwaysInline(true);
               },
+              [&](const Fortran::parser::CompilerDirective::InlineAlways &) {
+                stmt.typedCall->setAlwaysInline(true);
+              },
               [&](const Fortran::parser::CompilerDirective::Inline &) {
                 stmt.typedCall->setInlineHint(true);
               },
@@ -2434,6 +2437,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
                 [&](const Fortran::parser::CompilerDirective::ForceInline &) {
                   callOp.setInlineAttr(fir::FortranInlineEnum::always_inline);
                 },
+                [&](const Fortran::parser::CompilerDirective::InlineAlways &) {
+                  callOp.setInlineAttr(fir::FortranInlineEnum::always_inline);
+                },
                 [&](const auto &) {}},
             dir->u);
       }
@@ -3508,6 +3514,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
       e->dirs.push_back(&dir);
   }
 
+  void markCurrentFuncAsAlwaysInline(const Fortran::parser::CompilerDirective::InlineAlways &dir) {
+    mlir::func::FuncOp func = builder->getFunction();
+    if (currentFunctionUnit && !currentFunctionUnit->isMainProgram()) {
+      const std::string symName =
+          currentFunctionUnit->getSubprogramSymbol().name().ToString();
+      if (!llvm::StringRef(dir.v->ToString()).equals_insensitive(symName)) {
+        mlir::emitWarning(toLocation())
+            << "Directive Ignored: INLINEALWAYS directive function name '" << dir.v->ToString()
+            << "' does not match the function '" << symName << "' where this is declared.";
+        return;
+      }
+    }
+    func->setAttr("llvm.always_inline", builder->getUnitAttr());
+  }
+
   void
   attachInliningDirectiveToStmt(const Fortran::parser::CompilerDirective &dir,
                                 Fortran::lower::pft::Evaluation *e) {
@@ -3586,6 +3607,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
             [&](const Fortran::parser::CompilerDirective::IVDep &) {
               attachDirectiveToLoop(dir, &eval);
             },
+            [&](const Fortran::parser::CompilerDirective::InlineAlways &inlineAlways) {
+              if (inlineAlways.v.has_value())
+                markCurrentFuncAsAlwaysInline(inlineAlways);
+              else
+                attachInliningDirectiveToStmt(dir, &eval);
+            },
             [&](const auto &) {}},
         dir.u);
   }
diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index b67475074217c..bba8adae42950 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -1339,6 +1339,8 @@ constexpr auto forceinlineDir{
     "FORCEINLINE" >> construct<CompilerDirective::ForceInline>()};
 constexpr auto noinlineDir{
     "NOINLINE" >> construct<CompilerDirective::NoInline>()};
+constexpr auto inlinealwaysDir{
+    "INLINEALWAYS" >> construct<CompilerDirective::InlineAlways>(maybe(name))};
 constexpr auto inlineDir{"INLINE" >> construct<CompilerDirective::Inline>()};
 constexpr auto ivdep{"IVDEP" >> construct<CompilerDirective::IVDep>()};
 TYPE_PARSER(beginDirective >> some(letter) >> "$ "_tok >>
@@ -1355,6 +1357,7 @@ TYPE_PARSER(beginDirective >> some(letter) >> "$ "_tok >>
                 construct<CompilerDirective>(nounroll) ||
                 construct<CompilerDirective>(noinlineDir) ||
                 construct<CompilerDirective>(forceinlineDir) ||
+                construct<CompilerDirective>(inlinealwaysDir) ||
                 construct<CompilerDirective>(inlineDir) ||
                 construct<CompilerDirective>(ivdep) ||
                 construct<CompilerDirective>(
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 5ddd0cfc3a1ef..e9ee0576eb4ad 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1886,6 +1886,13 @@ class UnparseVisitor {
               Word("!DIR$ NOINLINE");
             },
             [&](const CompilerDirective::IVDep &) { Word("!DIR$ IVDEP"); },
+            [&](const CompilerDirective::InlineAlways &InlineAlways) {
+              Word("!DIR$ INLINEALWAYS");
+              if (InlineAlways.v.has_value()){
+                Word(" ");
+                Word (InlineAlways.v->ToString());
+              }
+            },
             [&](const CompilerDirective::Unrecognized &) {
               Word("!DIR$ ");
               Word(x.source.ToString());
diff --git a/flang/lib/Semantics/canonicalize-directives.cpp b/flang/lib/Semantics/canonicalize-directives.cpp
index f32a3d34c6572..8a1c4d235a666 100644
--- a/flang/lib/Semantics/canonicalize-directives.cpp
+++ b/flang/lib/Semantics/canonicalize-directives.cpp
@@ -66,7 +66,8 @@ static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
       std::holds_alternative<parser::CompilerDirective::ForceInline>(dir.u) ||
       std::holds_alternative<parser::CompilerDirective::Inline>(dir.u) ||
       std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u) ||
-      std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u);
+      std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u) ||
+      std::holds_alternative<parser::CompilerDirective::InlineAlways>(dir.u);
 }
 
 void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index e1c1167af1604..42556954d7126 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -10279,7 +10279,8 @@ void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
       std::holds_alternative<parser::CompilerDirective::Inline>(x.u) ||
       std::holds_alternative<parser::CompilerDirective::Prefetch>(x.u) ||
       std::holds_alternative<parser::CompilerDirective::NoInline>(x.u) ||
-      std::holds_alternative<parser::CompilerDirective::IVDep>(x.u)) {
+      std::holds_alternative<parser::CompilerDirective::IVDep>(x.u) ||
+      std::holds_alternative<parser::CompilerDirective::InlineAlways>(x.u)) {
     return;
   }
   if (const auto *tkr{
diff --git a/flang/test/Lower/inlinealways-directive.f90 b/flang/test/Lower/inlinealways-directive.f90
new file mode 100644
index 0000000000000..6ebefbdf4b43c
--- /dev/null
+++ b/flang/test/Lower/inlinealways-directive.f90
@@ -0,0 +1,26 @@
+! Check the appropriate flags are added to inline functions when inlinealways is used
+
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir %s 2>&1 | FileCheck %s --check-prefix=CHECK-WARN
+
+subroutine test_function()
+  !DIR$ INLINEALWAYS test_function
+end subroutine
+
+subroutine test_function2()
+end subroutine
+
+subroutine test_function3()
+  !DIR$ INLINEALWAYS wrong_func
+end subroutine
+
+subroutine test()
+  !DIR$ INLINEALWAYS
+  call test_function2()
+end subroutine
+
+! CHECK: func.func @_QPtest_function() attributes {llvm.always_inline} {
+! CHECK: fir.call @_QPtest_function2() fastmath<contract> {inline_attr = #fir.inline_attrs<always_inline>} : () -> ()
+
+! CHECK-WARN:      warning: loc({{.*}}inlinealways-directive.f90{{.*}}): Directive Ignored:
+! CHECK-WARN-SAME: INLINEALWAYS directive function name 'wrong_func' does not match the function 'test_function3' where this is declared.
diff --git a/flang/test/Parser/inlinealways-directive.f90 b/flang/test/Parser/inlinealways-directive.f90
new file mode 100644
index 0000000000000..28ec3e7f01aff
--- /dev/null
+++ b/flang/test/Parser/inlinealways-directive.f90
@@ -0,0 +1,58 @@
+! Test that the INLINEALWAYS directive can be parsed, both at callsite and within the function
+
+! RUN: %flang_fc1 -fdebug-dump-parse-tree %s 2>&1 | FileCheck %s --check-prefix=PARSE-TREE
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=UNPARSE
+
+subroutine test_function()
+  !DIR$ INLINEALWAYS test_function
+end subroutine
+
+subroutine test_function2()
+end subroutine
+
+subroutine test()
+  call test_function()
+  !DIR$ INLINEALWAYS
+  call test_function2()
+end subroutine
+
+! UNPARSE: SUBROUTINE test_function
+! UNPARSE:  !DIR$ INLINEALWAYS TEST_FUNCTION
+! UNPARSE: END SUBROUTINE
+! UNPARSE: SUBROUTINE test_function2
+! UNPARSE: END SUBROUTINE
+! UNPARSE: SUBROUTINE test
+! UNPARSE:   CALL test_function()
+! UNPARSE:  !DIR$ INLINEALWAYS
+! UNPARSE:   CALL test_function2()
+! UNPARSE: END SUBROUTINE
+
+! PARSE-TREE: Program -> ProgramUnit -> SubroutineSubprogram
+! PARSE-TREE: | SubroutineStmt
+! PARSE-TREE: | | Name = 'test_function'
+! PARSE-TREE: | SpecificationPart
+! PARSE-TREE: | | ImplicitPart -> 
+! PARSE-TREE: | ExecutionPart -> Block
+! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> CompilerDirective -> InlineAlways -> Name = 'test_function'
+! PARSE-TREE: | EndSubroutineStmt -> 
+! PARSE-TREE: ProgramUnit -> SubroutineSubprogram
+! PARSE-TREE: | SubroutineStmt
+! PARSE-TREE: | | Name = 'test_function2'
+! PARSE-TREE: | SpecificationPart
+! PARSE-TREE: | | ImplicitPart -> 
+! PARSE-TREE: | ExecutionPart -> Block
+! PARSE-TREE: | EndSubroutineStmt -> 
+! PARSE-TREE: ProgramUnit -> SubroutineSubprogram
+! PARSE-TREE: | SubroutineStmt
+! PARSE-TREE: | | Name = 'test'
+! PARSE-TREE: | SpecificationPart
+! PARSE-TREE: | | ImplicitPart -> 
+! PARSE-TREE: | ExecutionPart -> Block
+! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> CallStmt = 'CALL test_function()'
+! PARSE-TREE: | | | Call
+! PARSE-TREE: | | | | ProcedureDesignator -> Name = 'test_function'
+! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> CompilerDirective -> InlineAlways -> 
+! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> CallStmt = 'CALL test_function2()'
+! PARSE-TREE: | | | Call
+! PARSE-TREE: | | | | ProcedureDesignator -> Name = 'test_function2'
+! PARSE-TREE: | EndSubroutineStmt -> 

``````````

</details>


https://github.com/llvm/llvm-project/pull/192674


More information about the flang-commits mailing list