llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-analysis Author: Shivam Gupta (xgupta) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/223315.diff 6 Files Affected: - (modified) clang/include/clang/Options/Options.td (+4-1) - (modified) clang/lib/Driver/ToolChains/Clang.cpp (+8) - (added) clang/test/CodeGen/no-inline-functions-called-once.cpp (+27) - (modified) clang/test/Driver/clang_f_opts.c (-3) - (added) clang/test/Driver/no-inline-functions-called-once.c (+13) - (modified) llvm/lib/Analysis/InlineCost.cpp (+15-2) ``````````diff 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; + } } } `````````` </details> https://github.com/llvm/llvm-project/pull/223315 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
