https://github.com/shivaramaarao updated https://github.com/llvm/llvm-project/pull/230007
>From 3ec5988b664de664fdedbe5c27c706a09dd33778 Mon Sep 17 00:00:00 2001 From: Shivarama Rao <[email protected]> Date: Thu, 8 Oct 2026 12:32:21 +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. added a testcase to test this function. Assisted by Cursor to generate the testcase. Fixes the issue #223359 --- 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 ++++++++ .../Integration/keep-inline-functions.f90 | 52 +++++++++++++++++++ 6 files changed, 83 insertions(+), 2 deletions(-) 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 56194af32f68f3..72a0a0ee05c95c 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 0e6858d3bb6452..4f64b3ba050c1b 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 d49a7f3647eec5..4cc8cbd008d8a3 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 b76b38ff6497f7..9e5d805f4ca683 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 35c2b9ec881e8d..9f947f9f93a895 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/Integration/keep-inline-functions.f90 b/flang/test/Integration/keep-inline-functions.f90 new file mode 100644 index 00000000000000..270edcc2471d7b --- /dev/null +++ b/flang/test/Integration/keep-inline-functions.f90 @@ -0,0 +1,52 @@ +! RUN: %flang -### -fkeep-inline-functions %s 2>&1 | FileCheck %s --check-prefix=DRIVER-KEEP +! Neither flag is the default: the driver forwards nothing to fc1. +! 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: "-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( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
