[flang-commits] [clang] [flang] [flang][Driver] Add support for -f[no]keep-inline-functions option (PR #230007)

via flang-commits flang-commits at lists.llvm.org
Thu Oct 8 00:20:00 PDT 2026


https://github.com/shivaramaarao updated https://github.com/llvm/llvm-project/pull/230007

>From 92cccd02e02defa356a2b5284f84bafb49b1afbc Mon Sep 17 00:00:00 2001
From: Shivarama Rao <shivarama.rao at amd.com>
Date: Thu, 8 Oct 2026 12:32:21 +0530
Subject: [PATCH] [flang] Add -f[no]keep-inline-functions

Treat definitions marked alwaysinline (!dir$ inlinealways) as the functions inlined, and
keep those definitions after they are inlined into every caller.

added a testcase to test this function.

Assisted by Cursor to generate the testcase.

addresses the issue #223359 partially
---
 clang/include/clang/Options/Options.td        |  4 +-
 clang/lib/Driver/ToolChains/Flang.cpp         |  2 +
 .../include/flang/Frontend/CodeGenOptions.def |  1 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  4 ++
 flang/lib/Frontend/FrontendActions.cpp        | 22 ++++++++
 flang/test/Driver/inline-functions.f90        | 50 +++++++++++++++++++
 6 files changed, 81 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Driver/inline-functions.f90

diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 56194af32f68f..72a0a0ee05c95 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2598,8 +2598,8 @@ defm keep_persistent_storage_variables : BoolFOption<"keep-persistent-storage-va
           " keeping all variables that have a persistent storage duration, including global, static and thread-local variables, to guarantee that they can be directly addressed">>;
 defm keep_inline_functions : BoolFOption<"keep-inline-functions",
   CodeGenOpts<"KeepInlineFunctions">, DefaultFalse,
-  PosFlag<SetTrue, [], [ClangOption, CC1Option], "Keep">,
-  NegFlag<SetFalse, [], [ClangOption], "Don't keep">,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option, FlangOption, FC1Option], "Keep">,
+  NegFlag<SetFalse, [], [ClangOption, FlangOption], "Don't keep">,
   BothFlags<[], [],
           " inline functions whose definition is owned by this translation unit, even if they are inlined into all callers.">>;
 defm fixed_point : BoolFOption<"fixed-point",
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 0e6858d3bb645..4f64b3ba050c1 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -355,6 +355,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
 
   Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_loop_fusion,
                     options::OPT_fno_experimental_loop_fusion);
+  Args.addOptInFlag(CmdArgs, options::OPT_fkeep_inline_functions,
+                    options::OPT_fno_keep_inline_functions);
   Args.AddLastArg(CmdArgs, options::OPT_ffp_sum_reassociation,
                   options::OPT_fno_fp_sum_reassociation);
 
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def
index d49a7f3647eec..4cc8cbd008d8a 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.def
+++ b/flang/include/flang/Frontend/CodeGenOptions.def
@@ -30,6 +30,7 @@ ENUM_CODEGENOPT(ProfileInstr, llvm::driver::ProfileInstrKind, 2, llvm::driver::P
 /// Choose profile kind for PGO use compilation.
 ENUM_CODEGENOPT(ProfileUse, llvm::driver::ProfileInstrKind, 2, llvm::driver::ProfileInstrKind::ProfileNone)
 
+CODEGENOPT(KeepInlineFunctions, 1, 0) ///< -fkeep-inline-functions.
 CODEGENOPT(InstrumentFunctions, 1, 0) ///< Set when -finstrument_functions is
                                       ///< enabled on the compile step.
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index b76b38ff6497f..9e5d805f4ca68 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -380,6 +380,10 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
   if (args.hasArg(clang::options::OPT_finstrument_functions))
     opts.InstrumentFunctions = 1;
 
+  // -fkeep-inline-functions. The driver forwards only the positive flag.
+  opts.KeepInlineFunctions =
+      args.hasArg(clang::options::OPT_fkeep_inline_functions);
+
   // -fno-integrated-as: emit GNU Assembler compatible assembly.
   if (!args.hasFlag(clang::options::OPT_fintegrated_as,
                     clang::options::OPT_fno_integrated_as, true))
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 35c2b9ec881e8..9f947f9f93a89 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -1103,6 +1103,28 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
     return;
   }
 
+  // -fkeep-inline-functions: retain definitions of inline functions (those
+  // marked alwaysinline) after they have been inlined into every caller.
+  // On ELF this uses llvm.compiler.used so the compiler keeps the definition
+  // but the linker may still drop it. Other object formats use llvm.used.
+  // This is matching Clang functionality.
+  if (opts.KeepInlineFunctions) {
+    llvm::SmallVector<llvm::GlobalValue *, 8> kept;
+    for (llvm::Function &func : *llvmModule) {
+      if (func.isDeclaration() || func.hasAvailableExternallyLinkage())
+        continue;
+      if (!func.hasFnAttribute(llvm::Attribute::AlwaysInline))
+        continue;
+      kept.push_back(&func);
+    }
+    if (!kept.empty()) {
+      if (triple.isOSBinFormatELF())
+        llvm::appendToCompilerUsed(*llvmModule, kept);
+      else
+        llvm::appendToUsed(*llvmModule, kept);
+    }
+  }
+
   // Run the passes.
   mpm.run(*llvmModule, mam);
 
diff --git a/flang/test/Driver/inline-functions.f90 b/flang/test/Driver/inline-functions.f90
new file mode 100644
index 0000000000000..1d76c555943bc
--- /dev/null
+++ b/flang/test/Driver/inline-functions.f90
@@ -0,0 +1,50 @@
+! RUN: %flang -### -fkeep-inline-functions %s 2>&1 | FileCheck %s --check-prefix=DRIVER-KEEP
+! RUN: %flang -### -fno-keep-inline-functions %s 2>&1 | FileCheck %s --check-prefix=DRIVER-NOKEEP
+! RUN: %flang -### -fkeep-inline-functions -fno-keep-inline-functions %s 2>&1 | FileCheck %s --check-prefix=DRIVER-NOKEEP
+! RUN: %flang -### -fno-keep-inline-functions -fkeep-inline-functions %s 2>&1 | FileCheck %s --check-prefix=DRIVER-KEEP
+
+! DRIVER-KEEP: "-fc1"
+! DRIVER-KEEP-SAME: "-fkeep-inline-functions"
+! DRIVER-NOKEEP: "-fc1"
+! DRIVER-NOKEEP-NOT: "-fkeep-inline-functions"
+! DRIVER-NOKEEP-NOT: "-fno-keep-inline-functions"
+
+! Without the flag, an alwaysinline contained procedure is inlined and deleted.
+! RUN: %flang_fc1 -emit-llvm -O2 -o - %s | FileCheck %s --check-prefix=DEFAULT
+! -fkeep-inline-functions retains that definition, matching Clang's retention
+! of inline functions owned by this translation unit.
+! RUN: %flang_fc1 -emit-llvm -O2 -fkeep-inline-functions -o - %s | FileCheck %s --check-prefix=KEEP
+
+subroutine test_always(n)
+  integer :: n
+  n = add_two(n)
+contains
+  integer function add_two(n)
+    !dir$ inlinealways add_two
+    integer :: n
+    add_two = n + 2
+  end function
+end subroutine
+
+subroutine test_plain(n)
+  integer :: n
+  n = add_one(n)
+contains
+  integer function add_one(n)
+    integer :: n
+    add_one = n + 1
+  end function
+end subroutine
+
+! DEFAULT-LABEL: define void @test_always_(
+! DEFAULT-NOT: @_QFtest_alwaysPadd_two(
+! DEFAULT-NOT: define {{.*}}@_QFtest_alwaysPadd_two(
+
+! KEEP: @llvm.{{(compiler.)?}}used = {{.*}}@_QFtest_alwaysPadd_two
+! KEEP-LABEL: define void @test_always_(
+! KEEP-NOT: call {{.*}}@_QFtest_alwaysPadd_two(
+! KEEP: define internal {{.*}}@_QFtest_alwaysPadd_two(
+! A contained procedure that is not marked inline is still deleted.
+! KEEP-LABEL: define void @test_plain_(
+! KEEP-NOT: @_QFtest_plainPadd_one(
+! KEEP-NOT: define {{.*}}@_QFtest_plainPadd_one(



More information about the flang-commits mailing list