[flang-commits] [clang] [flang] [llvm] [Clang] Add -fno-inline-functions-called-once (PR #223315)
Shivam Gupta via flang-commits
flang-commits at lists.llvm.org
Mon Sep 14 07:05:36 PDT 2026
https://github.com/xgupta updated https://github.com/llvm/llvm-project/pull/223315
>From 947084c95b9e1f0b0e7713760525a48309c1c59d Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Wed, 2 Sep 2026 08:35:24 +0530
Subject: [PATCH 1/3] [Clang] Add -fno-inline-functions-called-once
GCC provides -fno-inline-functions-called-once to inhibit inlining
of TU-local functions that are called exactly once. Clang currently
accepts the corresponding option only as an ignored GCC optimization flag.
This patch makes the option a real Clang driver option and forwards
it to LLVM as -mllvm -no-inline-functions-called-once. The LLVM
inliner disables the special bonus normally applied to internal
functions with a single call site and assigns the call a
prohibitively high cost, preventing it from being inlined.
As alternative one can use __attribute__((noinline)) but that requires
application changes and not feasible for large codebases.
Rework of #160343.
---
clang/include/clang/Options/Options.td | 5 +++-
clang/lib/Driver/ToolChains/Clang.cpp | 8 ++++++
.../no-inline-functions-called-once.cpp | 27 +++++++++++++++++++
clang/test/Driver/clang_f_opts.c | 3 ---
.../Driver/no-inline-functions-called-once.c | 13 +++++++++
llvm/lib/Analysis/InlineCost.cpp | 17 ++++++++++--
6 files changed, 67 insertions(+), 6 deletions(-)
create mode 100644 clang/test/CodeGen/no-inline-functions-called-once.cpp
create mode 100644 clang/test/Driver/no-inline-functions-called-once.c
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 3b88dce9c822b..34f63855e9dc4 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -7694,7 +7694,10 @@ defm modulo_sched : BooleanFFlag<"modulo-sched">, Group<clang_ignored_gcc_optimi
defm modulo_sched_allow_regmoves : BooleanFFlag<"modulo-sched-allow-regmoves">,
Group<clang_ignored_gcc_optimization_f_Group>;
defm inline_functions_called_once : BooleanFFlag<"inline-functions-called-once">,
- Group<clang_ignored_gcc_optimization_f_Group>;
+ Group<f_Group>,
+ Visibility<[ClangOption]>,
+ HelpText<"Control inlining of TU-local functions called exactly once "
+ "(Use -fno-inline-functions-called-once to inhibit it)">;
def finline_limit_EQ : Joined<["-"], "finline-limit=">, Group<clang_ignored_gcc_optimization_f_Group>;
defm finline_limit : BooleanFFlag<"inline-limit">, Group<clang_ignored_gcc_optimization_f_Group>;
defm inline_small_functions : BooleanFFlag<"inline-small-functions">,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 072664e6040f3..fb70f75c83b2d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5414,6 +5414,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
<< TripleStr;
}
+ // Respect the last -f[no-]inline-functions-called-once option.
+ if (!Args.hasFlag(options::OPT_finline_functions_called_once,
+ options::OPT_fno_inline_functions_called_once,
+ /*Default=*/true)) {
+ CmdArgs.push_back("-mllvm");
+ CmdArgs.push_back("-no-inline-functions-called-once");
+ }
+
// Push all default warning arguments that are specific to
// the given target. These come before user provided warning options
// are provided.
diff --git a/clang/test/CodeGen/no-inline-functions-called-once.cpp b/clang/test/CodeGen/no-inline-functions-called-once.cpp
new file mode 100644
index 0000000000000..049ec28643f06
--- /dev/null
+++ b/clang/test/CodeGen/no-inline-functions-called-once.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang -O2 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=INLINE
+// RUN: %clang -O2 -S -emit-llvm %s -fno-inline-functions-called-once -o - | FileCheck %s --check-prefix=NOINLINE
+
+// INLINE-LABEL: define{{.*}}@main
+// INLINE-NOT: call{{.*}}@_ZL12bad_functionv
+// INLINE: ret void
+
+// NOINLINE-LABEL: define{{.*}}@_ZL4testv
+// NOINLINE: call{{.*}}@_ZL12bad_functionv
+// NOINLINE: ret void
+
+// NOINLINE: define internal{{.*}}@_ZL12bad_functionv
+
+volatile int G;
+
+static void bad_function(void) {
+ G++;
+}
+
+static void test(void) {
+ bad_function();
+}
+
+int main(void) {
+ test();
+ return 0;
+}
diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c
index 5871f1580d6b7..0b29fc5920552 100644
--- a/clang/test/Driver/clang_f_opts.c
+++ b/clang/test/Driver/clang_f_opts.c
@@ -283,7 +283,6 @@
// RUN: -fgcse-las \
// RUN: -fgcse-sm \
// RUN: -fipa-cp \
-// RUN: -finline-functions-called-once \
// RUN: -fmodulo-sched \
// RUN: -fmodulo-sched-allow-regmoves \
// RUN: -fpeel-loops \
@@ -355,7 +354,6 @@
// RUN: -fgcse-las \
// RUN: -fgcse-sm \
// RUN: -fipa-cp \
-// RUN: -finline-functions-called-once \
// RUN: -fmodulo-sched \
// RUN: -fmodulo-sched-allow-regmoves \
// RUN: -fpeel-loops \
@@ -414,7 +412,6 @@
// CHECK-WARNING-DAG: optimization flag '-fgcse-las' is not supported
// CHECK-WARNING-DAG: optimization flag '-fgcse-sm' is not supported
// CHECK-WARNING-DAG: optimization flag '-fipa-cp' is not supported
-// CHECK-WARNING-DAG: optimization flag '-finline-functions-called-once' is not supported
// CHECK-WARNING-DAG: optimization flag '-fmodulo-sched' is not supported
// CHECK-WARNING-DAG: optimization flag '-fmodulo-sched-allow-regmoves' is not supported
// CHECK-WARNING-DAG: optimization flag '-fpeel-loops' is not supported
diff --git a/clang/test/Driver/no-inline-functions-called-once.c b/clang/test/Driver/no-inline-functions-called-once.c
new file mode 100644
index 0000000000000..402fdaa11254c
--- /dev/null
+++ b/clang/test/Driver/no-inline-functions-called-once.c
@@ -0,0 +1,13 @@
+// RUN: %clang -### -c %s 2>&1 | FileCheck %s --check-prefix=DEFAULT
+// DEFAULT-NOT: "-no-inline-functions-called-once"
+
+// RUN: %clang -### -c -fno-inline-functions-called-once %s 2>&1 | FileCheck %s --check-prefix=DISABLED
+// DISABLED: "-mllvm" "-no-inline-functions-called-once"
+
+// RUN: %clang -### -c -fno-inline-functions-called-once -finline-functions-called-once %s 2>&1 \
+// RUN: | FileCheck %s --check-prefix=REENABLED
+// REENABLED-NOT: "-no-inline-functions-called-once"
+
+// RUN: %clang -### -c -finline-functions-called-once -fno-inline-functions-called-once %s 2>&1 \
+// RUN: | FileCheck %s --check-prefix=DISABLED
+// DISABLED: "-mllvm" "-no-inline-functions-called-once"
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index d4f4d937aa06a..8645bee789d4a 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -183,6 +183,11 @@ static cl::opt<bool> InlineAllViableCalls(
"inline-all-viable-calls", cl::Hidden, cl::init(false),
cl::desc("Inline all viable calls, even if they exceed the inlining "
"threshold"));
+
+static cl::opt<bool> NoInlineFunctionsCalledOnce(
+ "no-inline-functions-called-once", cl::Hidden, cl::init(false),
+ cl::desc("Disable inlining of functions with internal linkage that are "
+ "called only once"));
namespace llvm {
std::optional<int> getStringFnAttrAsInt(const Attribute &Attr) {
if (Attr.isValid()) {
@@ -2197,9 +2202,17 @@ void InlineCostCallAnalyzer::updateThreshold(CallBase &Call, Function &Callee) {
// If there is only one call of the function, and it has internal linkage,
// the cost of inlining it drops dramatically. It may seem odd to update
// Cost in updateThreshold, but the bonus depends on the logic in this method.
+ // When -fno-inline-functions-called-once is enabled, disable this bonus
+ // and instead apply a large cost penalty to prevent inlining of such
+ // functions.
if (isSoleCallToLocalFunction(Call, F)) {
- addCost(-LastCallToStaticBonus);
- StaticBonusApplied = LastCallToStaticBonus;
+ if (NoInlineFunctionsCalledOnce) {
+ addCost(INT_MAX);
+ StaticBonusApplied = 0;
+ } else {
+ addCost(-LastCallToStaticBonus);
+ StaticBonusApplied = LastCallToStaticBonus;
+ }
}
}
>From 305a1590e7345ae03ab4722318b76d554a802b59 Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Mon, 14 Sep 2026 16:33:46 +0530
Subject: [PATCH 2/3] fix test cases
---
clang/test/CodeGen/no-inline-functions-called-once.cpp | 2 +-
clang/test/Driver/no-inline-functions-called-once.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/no-inline-functions-called-once.cpp b/clang/test/CodeGen/no-inline-functions-called-once.cpp
index 049ec28643f06..3867f0a646358 100644
--- a/clang/test/CodeGen/no-inline-functions-called-once.cpp
+++ b/clang/test/CodeGen/no-inline-functions-called-once.cpp
@@ -3,7 +3,7 @@
// INLINE-LABEL: define{{.*}}@main
// INLINE-NOT: call{{.*}}@_ZL12bad_functionv
-// INLINE: ret void
+// INLINE: ret i32 0
// NOINLINE-LABEL: define{{.*}}@_ZL4testv
// NOINLINE: call{{.*}}@_ZL12bad_functionv
diff --git a/clang/test/Driver/no-inline-functions-called-once.c b/clang/test/Driver/no-inline-functions-called-once.c
index 402fdaa11254c..8c7b286479810 100644
--- a/clang/test/Driver/no-inline-functions-called-once.c
+++ b/clang/test/Driver/no-inline-functions-called-once.c
@@ -2,12 +2,12 @@
// DEFAULT-NOT: "-no-inline-functions-called-once"
// RUN: %clang -### -c -fno-inline-functions-called-once %s 2>&1 | FileCheck %s --check-prefix=DISABLED
-// DISABLED: "-mllvm" "-no-inline-functions-called-once"
+// DISABLED: {{.*}} "-mllvm" "-no-inline-functions-called-once"
// RUN: %clang -### -c -fno-inline-functions-called-once -finline-functions-called-once %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=REENABLED
// REENABLED-NOT: "-no-inline-functions-called-once"
// RUN: %clang -### -c -finline-functions-called-once -fno-inline-functions-called-once %s 2>&1 \
-// RUN: | FileCheck %s --check-prefix=DISABLED
-// DISABLED: "-mllvm" "-no-inline-functions-called-once"
+// RUN: | FileCheck %s --check-prefix=DISABLED2
+// DISABLED2: {{.*}} "-mllvm" "-no-inline-functions-called-once"
>From 43a34c2a7767cb1e2ac79b764d6083291dc80aae Mon Sep 17 00:00:00 2001
From: Shivam Gupta <shivam98.tkg at gmail.com>
Date: Mon, 14 Sep 2026 19:35:11 +0530
Subject: [PATCH 3/3] fix flang test case
---
flang/test/Driver/flang-f-opts.f90 | 2 --
1 file changed, 2 deletions(-)
diff --git a/flang/test/Driver/flang-f-opts.f90 b/flang/test/Driver/flang-f-opts.f90
index a082545206ea3..ee82fd0e44f23 100644
--- a/flang/test/Driver/flang-f-opts.f90
+++ b/flang/test/Driver/flang-f-opts.f90
@@ -94,7 +94,6 @@
! RUN: -fgcse-las \
! RUN: -fgcse-sm \
! RUN: -fipa-cp \
-! RUN: -finline-functions-called-once \
! RUN: -fmodulo-sched \
! RUN: -fmodulo-sched-allow-regmoves \
! RUN: -fpeel-loops \
@@ -148,7 +147,6 @@
! CHECK-WARNING-DAG: optimization flag '-fgcse-las' is not supported
! CHECK-WARNING-DAG: optimization flag '-fgcse-sm' is not supported
! CHECK-WARNING-DAG: optimization flag '-fipa-cp' is not supported
-! CHECK-WARNING-DAG: optimization flag '-finline-functions-called-once' is not supported
! CHECK-WARNING-DAG: optimization flag '-fmodulo-sched' is not supported
! CHECK-WARNING-DAG: optimization flag '-fmodulo-sched-allow-regmoves' is not supported
! CHECK-WARNING-DAG: optimization flag '-fpeel-loops' is not supported
More information about the flang-commits
mailing list