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

>From f713db4156e5afec60ff02c8a3df5511ade93c63 Mon Sep 17 00:00:00 2001
From: Shivarama Rao <[email protected]>
Date: Sat, 17 Oct 2026 13:38:07 +0530
Subject: [PATCH] [flang] Add -f[no]keep-inline-functions option

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

Restrict definition retention to the current translation unit, ensuring
that definitions from linked bitcode (BC) files are not preserved.

added testcases to test this functionality.

Assisted by Cursor to generate the testcases.

Fixes #223359

Co-authored-by: Cursor <[email protected]>
---
 clang/include/clang/Options/Options.td        |  2 +-
 clang/lib/Driver/ToolChains/Flang.cpp         | 12 +-----
 flang/docs/Directives.md                      | 11 +++++
 flang/docs/ReleaseNotes.md                    |  7 ++++
 .../include/flang/Frontend/CodeGenOptions.def |  1 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  4 ++
 flang/lib/Frontend/FrontendActions.cpp        | 28 +++++++++++++
 flang/test/Driver/flang-f-opts.f90            | 25 ++++++++++--
 .../Integration/Inputs/keep-inline-lib.f90    |  5 +++
 .../{inputs => Inputs}/pgo-sample.prof        |  0
 .../keep-inline-functions-link.f90            | 29 ++++++++++++++
 .../Integration/keep-inline-functions.f90     | 40 +++++++++++++++++++
 flang/test/Integration/profile-sample-use.f90 |  2 +-
 13 files changed, 150 insertions(+), 16 deletions(-)
 create mode 100644 flang/test/Integration/Inputs/keep-inline-lib.f90
 rename flang/test/Integration/{inputs => Inputs}/pgo-sample.prof (100%)
 create mode 100644 flang/test/Integration/keep-inline-functions-link.f90
 create mode 100644 flang/test/Integration/keep-inline-functions.f90

diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 41afddd0ffe82e..1834392a15d908 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2613,7 +2613,7 @@ 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, FlangOption], "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.">>;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index a13c69a5b2a50a..59112b67d74a35 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -357,6 +357,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);
 
@@ -1339,16 +1341,6 @@ void Flang::ConstructJob(Compilation &C, const JobAction 
&JA,
     A->claim();
   }
 
-  // -fkeep-inline-functions/-fno-keep-inline-functions are real Clang options
-  // but are not supported by Flang; warn and ignore them.
-  for (options::ID Opt : {options::OPT_fkeep_inline_functions,
-                          options::OPT_fno_keep_inline_functions}) {
-    if (const Arg *A = Args.getLastArg(Opt)) {
-      D.Diag(diag::warn_ignored_gcc_optimization) << A->getAsString(Args);
-      A->claim();
-    }
-  }
-
   const InputInfo &Input = Inputs[0];
   types::ID InputType = Input.getType();
 
diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md
index f2f4078c479a2a..a566d2018f2de4 100644
--- a/flang/docs/Directives.md
+++ b/flang/docs/Directives.md
@@ -154,6 +154,17 @@ contains
     ...
   end function
   ```
+  By default, once such a procedure has been inlined into every caller, its
+  definition is removed. With `-fkeep-inline-functions`, the definition is
+  kept as well. On ELF targets it is added to `llvm.compiler.used`, so the
+  compiler keeps it but the linker may still discard it; other object formats
+  use `llvm.used`. Unlike Clang, where the flag applies to all inline
+  functions, in Flang it applies only to procedures that name themselves in
+  `!dir$ inlinealways <name>`. The call-site forms (`!dir$ inline`,
+  `!dir$ forceinline`, and `!dir$ inlinealways` without a name) do not make
+  the callee eligible. Clang also emits inline functions that are never
+  called, but Flang does not: an `INLINEALWAYS` procedure with no callers is
+  removed before LLVM IR is generated, even with `-fkeep-inline-functions`.
 * `!dir$ noinline` works in the same way as the `inline` directive, but 
prevents
   any attempt of inlining by the compiler on a function call statement.
 
diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index 609ca499da3965..a1315a354b33fd 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -158,6 +158,13 @@ page](https://llvm.org/releases/).
   Both modes fill every storage byte including struct padding.
   PowerPC vector locals (`vector(real(4))` etc.) are not initialized.
 
+- Added `-fkeep-inline-functions` and `-fno-keep-inline-functions`. With
+  `-fkeep-inline-functions`, a procedure marked with
+  `!dir$ inlinealways <name>` keeps its definition after it has been inlined
+  into every caller. Call-site inlining directives do not count, and a
+  procedure that is never called is still removed. See
+  [Directives](Directives.md) for details.
+
 ## Windows Support
 
 ## Fortran Language Changes in Flang
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def 
b/flang/include/flang/Frontend/CodeGenOptions.def
index 86994f7710576c..86006b0f8387b1 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.
 CODEGENOPT(DisableTailCalls, 1, 0) ///< Do not emit tail calls
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp 
b/flang/lib/Frontend/CompilerInvocation.cpp
index d4c6b44e114772..c00a36c7bdbece 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -384,6 +384,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);
+
   if (args.hasArg(clang::options::OPT_fno_optimize_sibling_calls))
     opts.DisableTailCalls = 1;
 
diff --git a/flang/lib/Frontend/FrontendActions.cpp 
b/flang/lib/Frontend/FrontendActions.cpp
index 62dd3fb0548636..8232983de7338d 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -1447,6 +1447,34 @@ void CodeGenAction::executeAction() {
   llvmModule->setTargetTriple(theTriple);
   llvmModule->setDataLayout(theTriple.computeDataLayout(targetOpts.abi));
 
+  // -fkeep-inline-functions: retain definitions of alwaysinline functions
+  // 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. Only functions defined
+  // in this translation unit are considered.
+  //
+  // This covers less than Clang's flag. The function-level alwaysinline
+  // attribute comes only from a named !dir$ inlinealways <name>, so call-site
+  // inlining directives do not count. A procedure that is never called has
+  // already been removed by the MLIR pipeline before this point, so it is not
+  // kept either. See flang/docs/Directives.md.
+  if (codeGenOpts.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 (theTriple.isOSBinFormatELF())
+        llvm::appendToCompilerUsed(*llvmModule, kept);
+      else
+        llvm::appendToUsed(*llvmModule, kept);
+    }
+  }
+
   // Link in builtin bitcode libraries
   if (!codeGenOpts.BuiltinBCLibs.empty())
     linkBuiltinBCLibs();
diff --git a/flang/test/Driver/flang-f-opts.f90 
b/flang/test/Driver/flang-f-opts.f90
index a082545206ea33..153082bc2c9889 100644
--- a/flang/test/Driver/flang-f-opts.f90
+++ b/flang/test/Driver/flang-f-opts.f90
@@ -68,8 +68,6 @@
 ! RUN:     -fexpensive-optimizations                                        \
 ! RUN:     -fno-expensive-optimizations                                     \
 ! RUN:     -fno-defer-pop                                                   \
-! RUN:     -fkeep-inline-functions                                          \
-! RUN:     -fno-keep-inline-functions                                       \
 ! RUN:     -freorder-blocks                                                 \
 ! RUN:     -ffloat-store                                                    \
 ! RUN:     -fgcse                                                           \
@@ -122,8 +120,6 @@
 ! CHECK-WARNING-DAG: optimization flag '-fexpensive-optimizations' is not 
supported
 ! CHECK-WARNING-DAG: optimization flag '-fno-expensive-optimizations' is not 
supported
 ! CHECK-WARNING-DAG: optimization flag '-fno-defer-pop' is not supported
-! CHECK-WARNING-DAG: optimization flag '-fkeep-inline-functions' is not 
supported
-! CHECK-WARNING-DAG: optimization flag '-fno-keep-inline-functions' is not 
supported
 ! CHECK-WARNING-DAG: optimization flag '-freorder-blocks' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-ffloat-store' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-fgcse' is not supported
@@ -169,3 +165,24 @@
 ! CHECK-WARNING-DAG: optimization flag '-ftree-ter' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-ftree-vrp' is not supported
 ! CHECK-WARNING-DAG: optimization flag '-fno-devirtualize' is not supported
+!
+! 
------------------------------------------------------------------------------
+! Unlike the gfortran-specific optimization flags above, 
-fkeep-inline-functions
+! and -fno-keep-inline-functions are implemented by Flang, so using them must
+! not produce the "optimization flag ... is not supported" warning, or any
+! other warning. Neither flag is the default: the driver forwards nothing to
+! fc1 unless -fkeep-inline-functions wins.
+! RUN: %flang -### -fkeep-inline-functions %s 2>&1 | FileCheck %s 
--check-prefix=DRIVER-KEEP
+! RUN: %flang -### %s 2>&1 | FileCheck %s --check-prefix=DRIVER-NOKEEP
+! 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-NOT: warning:
+! DRIVER-KEEP: "-fc1"
+! DRIVER-KEEP-SAME: "-fkeep-inline-functions"
+
+! DRIVER-NOKEEP-NOT: warning:
+! DRIVER-NOKEEP: "-fc1"
+! DRIVER-NOKEEP-NOT: "-fkeep-inline-functions"
+! DRIVER-NOKEEP-NOT: "-fno-keep-inline-functions"
diff --git a/flang/test/Integration/Inputs/keep-inline-lib.f90 
b/flang/test/Integration/Inputs/keep-inline-lib.f90
new file mode 100644
index 00000000000000..6bdd17faf26f56
--- /dev/null
+++ b/flang/test/Integration/Inputs/keep-inline-lib.f90
@@ -0,0 +1,5 @@
+integer function lib_add_two(n)
+  !dir$ inlinealways lib_add_two
+  integer :: n
+  lib_add_two = n + 2
+end function
diff --git a/flang/test/Integration/inputs/pgo-sample.prof 
b/flang/test/Integration/Inputs/pgo-sample.prof
similarity index 100%
rename from flang/test/Integration/inputs/pgo-sample.prof
rename to flang/test/Integration/Inputs/pgo-sample.prof
diff --git a/flang/test/Integration/keep-inline-functions-link.f90 
b/flang/test/Integration/keep-inline-functions-link.f90
new file mode 100644
index 00000000000000..ebe47cfd79cc27
--- /dev/null
+++ b/flang/test/Integration/keep-inline-functions-link.f90
@@ -0,0 +1,29 @@
+! -fkeep-inline-functions must only record alwaysinline definitions that
+! belong to this translation unit. A builtin bitcode library brought in via
+! -mlink-builtin-bitcode can itself contain an alwaysinline function (for
+! its own, unrelated reasons); that function was never named by a
+! !DIR$ INLINEALWAYS in this file and must not be added to llvm.used /
+! llvm.compiler.used because of this flag. This requires the check to run
+! before the builtin bitcode library is linked into the module.
+
+! RUN: %flang -emit-llvm -c -o %t.bc %S/Inputs/keep-inline-lib.f90
+! RUN: %flang_fc1 -emit-llvm -O2 -fkeep-inline-functions 
-mlink-builtin-bitcode %t.bc -o - %s | FileCheck %s
+
+program main
+  external lib_add_two
+  integer :: lib_add_two
+  integer :: n
+  n = lib_add_two(5)
+  n = own_add_three(n)
+  print *, n
+contains
+  integer function own_add_three(n)
+    !dir$ inlinealways own_add_three
+    integer :: n
+    own_add_three = n + 3
+  end function
+end program
+
+! The list has exactly one entry: this translation unit's own
+! own_add_three, and not the linked library's lib_add_two.
+! CHECK: @llvm.{{(compiler.)?}}used = appending global [1 x ptr] [ptr 
@_QFPown_add_three]
diff --git a/flang/test/Integration/keep-inline-functions.f90 
b/flang/test/Integration/keep-inline-functions.f90
new file mode 100644
index 00000000000000..b349683aa12ede
--- /dev/null
+++ b/flang/test/Integration/keep-inline-functions.f90
@@ -0,0 +1,40 @@
+! The driver-only checks (-### forwarding, no warning) live in
+! flang/test/Driver/flang-f-opts.f90; this file checks the generated IR.
+
+! Without the flag, an alwaysinline contained procedure is inlined and deleted.
+! RUN: %flang_fc1 -emit-llvm -O2 -o - %s | FileCheck %s --check-prefix=DEFAULT 
\
+! RUN:     --implicit-check-not="@_QFtest_alwaysPadd_two("
+! -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 \
+! RUN:     --implicit-check-not="@_QFtest_plainPadd_one("
+
+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_(
+
+! 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_(
diff --git a/flang/test/Integration/profile-sample-use.f90 
b/flang/test/Integration/profile-sample-use.f90
index 6292ef817077c8..8c377eca2ba9d3 100644
--- a/flang/test/Integration/profile-sample-use.f90
+++ b/flang/test/Integration/profile-sample-use.f90
@@ -1,5 +1,5 @@
 ! Test to check the working of option "-fprofile-sample-use".
-! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone 
-fprofile-sample-use=%S/inputs/pgo-sample.prof -o - %s | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone 
-fprofile-sample-use=%S/Inputs/pgo-sample.prof -o - %s | FileCheck %s
 
 ! CHECK: attributes #[[A:.*]] = { {{.*}}"use-sample-profile"{{.*}} }
 ! CHECK: !{i32 {{.*}}, !"ProfileSummary"{{.*}}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to