[clang] [flang] [llvm] [clang][flang] Turn -fsplit-machine-functions into a mode option (PR #227786)
Rahman Lavaee via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 09:51:45 PDT 2026
https://github.com/rlavaee created https://github.com/llvm/llvm-project/pull/227786
Replace the BoolFOption with -fsplit-machine-functions={none,bbsections,all},
following the -flto pattern: the bare -fsplit-machine-functions and
-fno-split-machine-functions spellings become aliases of the '=' form with
AliasArgs<["all"]> and AliasArgs<["none"]> respectively.
The boolean flag has is not currently well-defined when used along with
-fbasic-block-sections=list= as it is not clear whether it should
override the decision by that flag or not.
This buys a tri-state (absent / explicitly off / explicitly on) which the
backend needs in order to honor -fno-split-machine-functions together with
-fbasic-block-sections=list=, plus last-wins precedence between the spellings,
while keeping every existing command line working.
Because Option::accept() rewrites an aliased Arg to carry the unaliased option,
matches(OPT_fsplit_machine_functions) and hasArg(OPT_fsplit_machine_functions)
silently stop working. All readers are therefore updated to query
OPT_fsplit_machine_functions_EQ and switch on the value: the LTO -plugin-opt=
forwarding, tools::addSplitMachineFunctionsArgs() and flang's
CompilerInvocation.
CodeGenOpts.SplitMachineFunctions becomes the FunctionSplitting enum, which now
feeds TargetOptions::FunctionSplitting, and flang's TargetOptions carries the
mode as well.
Driver behavior is unchanged except that -cc1/-fc1 now receive the explicit
mode (e.g. "-fsplit-machine-functions=all"), and that disabling splitting is
accepted on targets without splitting support. Forwarding the new modes through
LTO is left for a follow-up; for now only 'all' is forwarded, as
-plugin-opt=-function-splitting=all.
>From 6536e87093acbeacead3657051c8255b2c74e055 Mon Sep 17 00:00:00 2001
From: Rahman Lavaee <rahmanl at google.com>
Date: Sat, 19 Sep 2026 18:56:47 +0000
Subject: [PATCH 1/2] [CodeGen] Replace EnableMachineFunctionSplitter with
FunctionSplittingMode
Add TargetOptions::FunctionSplitting and a
-function-splitting={none,bbsections,all} flag, replacing
TargetOptions::EnableMachineFunctionSplitter and the
-split-machine-functions / -enable-split-machine-functions flags.
MachineFunctionSplitter runs when the mode is 'all'. clang and flang map
-fsplit-machine-functions to 'all', including through the LTO plugin-opt.
This is the first step towards merging MachineFunctionSplitter into
BasicBlockSections. After that, 'none' and 'bbsections' will fully take
effect, so splitting can be turned off for some or all functions whether a
bbsections or PGO profile is used.
---
clang/lib/CodeGen/BackendUtil.cpp | 3 +-
clang/lib/Driver/ToolChains/CommonArgs.cpp | 2 +-
clang/test/Driver/fsplit-machine-functions.c | 4 +-
flang/lib/Frontend/CompilerInstance.cpp | 3 +-
llvm/docs/ReleaseNotes.md | 7 +++
.../llvm/CodeGen/BasicBlockSectionUtils.h | 8 +++
llvm/include/llvm/CodeGen/CommandFlags.h | 4 +-
.../include/llvm/Target/CGPassBuilderOption.h | 1 -
llvm/include/llvm/Target/TargetOptions.h | 21 +++++--
llvm/lib/CodeGen/BasicBlockSections.cpp | 11 ++++
llvm/lib/CodeGen/CommandFlags.cpp | 24 +++++---
llvm/lib/CodeGen/TargetPassConfig.cpp | 17 ++----
.../machine-function-splitter-optnone.ll | 2 +-
.../Generic/machine-function-splitter.ll | 38 +++++++-----
.../X86/basic-block-address-map-with-mfs.ll | 4 +-
.../CodeGen/X86/basic-block-address-map.ll | 2 +-
llvm/test/CodeGen/X86/basic-block-sections.ll | 2 +-
.../CodeGen/X86/function-splitting-mode.ll | 61 +++++++++++++++++++
.../test/CodeGen/X86/load-sample-profile-2.ll | 2 +-
llvm/test/CodeGen/X86/load-sample-profile.ll | 2 +-
.../tools/llvm-profgen/X86/func-split.test | 2 +-
21 files changed, 167 insertions(+), 53 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/function-splitting-mode.ll
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 5a01779613c8c..3523f10e630b4 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -418,7 +418,8 @@ static bool initTargetOptions(const CompilerInstance &CI,
Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
}
- Options.EnableMachineFunctionSplitter = CodeGenOpts.SplitMachineFunctions;
+ if (CodeGenOpts.SplitMachineFunctions)
+ Options.FunctionSplitting = llvm::FunctionSplittingMode::All;
Options.EnableStaticDataPartitioning =
CodeGenOpts.PartitionStaticDataSections;
Options.FunctionSections = CodeGenOpts.FunctionSections;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 445eb4ccfbfa7..952e43236c6a7 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1303,7 +1303,7 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
options::OPT_fno_split_machine_functions)) {
if (A->getOption().matches(options::OPT_fsplit_machine_functions))
CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) +
- "-split-machine-functions"));
+ "-function-splitting=all"));
}
if (auto *A =
diff --git a/clang/test/Driver/fsplit-machine-functions.c b/clang/test/Driver/fsplit-machine-functions.c
index 153aafba8fbd0..80485a32db3b6 100644
--- a/clang/test/Driver/fsplit-machine-functions.c
+++ b/clang/test/Driver/fsplit-machine-functions.c
@@ -14,8 +14,8 @@
// RUN: %clang -### --target=x86_64-linux -flto -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=LTO
// RUN: %clang -### --target=x86_64-linux -flto -fsplit-machine-functions -fno-split-machine-functions %s 2>&1 | FileCheck %s --check-prefix=LTO-NEG
-// LTO: "-plugin-opt=-split-machine-functions"
-// LTO-NEG-NOT: "-plugin-opt=-split-machine-functions"
+// LTO: "-plugin-opt=-function-splitting=all"
+// LTO-NEG-NOT: "-plugin-opt=-function-splitting=all"
// RUN: not %clang -### -c --target=arm-unknown-linux -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=ERR
// ERR: error: unsupported option '-fsplit-machine-functions' for target
diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index 1b09fcfd049af..f63b7cb158082 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -380,7 +380,8 @@ bool CompilerInstance::setUpTargetMachine() {
std::optional<llvm::CodeModel::Model> cm = getCodeModel(CGOpts.CodeModel);
llvm::TargetOptions tOpts = llvm::TargetOptions();
- tOpts.EnableMachineFunctionSplitter = targetOpts.SplitMachineFunctions;
+ if (targetOpts.SplitMachineFunctions)
+ tOpts.FunctionSplitting = llvm::FunctionSplittingMode::All;
tOpts.VecLib = convertDriverVectorLibraryToVectorLibrary(CGOpts.getVecLib());
tOpts.MCOptions.DisableIntegratedAS = CGOpts.DisableIntegratedAS;
tOpts.FunctionSections = CGOpts.FunctionSections;
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 41b2fa83d380f..30bdf599a5ad4 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -181,6 +181,13 @@ Makes programs 10x faster by doing Special New Thing.
GNU-vs-EABI distinction is now derived entirely from the target triple's
environment (e.g. `arm-none-gnueabi` vs `arm-none-eabi`).
+* Replaced `TargetOptions::EnableMachineFunctionSplitter` with
+ `TargetOptions::FunctionSplitting`, and the `-split-machine-functions` and
+ `-enable-split-machine-functions` flags with
+ `-function-splitting={none,bbsections,all}`. Use `-function-splitting=all`
+ for the previous behavior. Clang's `-fsplit-machine-functions` is
+ unchanged.
+
### Changes to building LLVM
* A new `LLVM_ENABLE_LZMA` option (`ON`, `OFF` or `FORCE_ON`; default `ON`)
diff --git a/llvm/include/llvm/CodeGen/BasicBlockSectionUtils.h b/llvm/include/llvm/CodeGen/BasicBlockSectionUtils.h
index 25eaf5c32d743..a6298af1c453c 100644
--- a/llvm/include/llvm/CodeGen/BasicBlockSectionUtils.h
+++ b/llvm/include/llvm/CodeGen/BasicBlockSectionUtils.h
@@ -11,6 +11,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Target/TargetOptions.h"
namespace llvm {
@@ -18,6 +19,13 @@ extern LLVM_ABI cl::opt<std::string> BBSectionsColdTextPrefix;
class MachineFunction;
class MachineBasicBlock;
+class TargetMachine;
+
+/// Returns the effective late function splitting mode, resolving the explicit
+/// options against the basic block sections mode. Never returns
+/// FunctionSplittingMode::Default.
+LLVM_ABI FunctionSplittingMode
+resolveFunctionSplittingMode(const TargetMachine &TM);
using MachineBasicBlockComparator =
function_ref<bool(const MachineBasicBlock &, const MachineBasicBlock &)>;
diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h
index 1a5d44570430f..bea2652915865 100644
--- a/llvm/include/llvm/CodeGen/CommandFlags.h
+++ b/llvm/include/llvm/CodeGen/CommandFlags.h
@@ -91,6 +91,8 @@ LLVM_ABI bool getXCOFFTracebackTable();
LLVM_ABI std::string getBBSections();
+LLVM_ABI llvm::FunctionSplittingMode getFunctionSplitting();
+
LLVM_ABI unsigned getTLSSize();
LLVM_ABI bool getEmulatedTLS();
@@ -117,8 +119,6 @@ LLVM_ABI bool getEnableCallGraphSection();
LLVM_ABI bool getEmitCallSiteInfo();
-LLVM_ABI bool getEnableMachineFunctionSplitter();
-
LLVM_ABI bool getEnableStaticDataPartitioning();
LLVM_ABI bool getEnableDebugEntryValues();
diff --git a/llvm/include/llvm/Target/CGPassBuilderOption.h b/llvm/include/llvm/Target/CGPassBuilderOption.h
index f825cbdca439b..8dfcb0176732b 100644
--- a/llvm/include/llvm/Target/CGPassBuilderOption.h
+++ b/llvm/include/llvm/Target/CGPassBuilderOption.h
@@ -55,7 +55,6 @@ struct CGPassBuilderOption {
bool EnableImplicitNullChecks = false;
bool EnableBlockPlacementStats = false;
bool EnableGlobalMergeFunc = false;
- bool EnableMachineFunctionSplitter = false;
bool EnableSinkAndFold = false;
bool EnableTailMerge = true;
/// Enable LoopTermFold immediately after LSR.
diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h
index 7ebc0e0e9ed1e..758f5402dade4 100644
--- a/llvm/include/llvm/Target/TargetOptions.h
+++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -50,6 +50,20 @@ enum class BasicBlockSection {
None // Do not use Basic Block Sections.
};
+/// Late function splitting mode. Decides which functions are eligible to have
+/// their cold blocks moved into a separate section.
+enum class FunctionSplittingMode {
+ Default, // Not specified: infer from the basic block sections mode.
+ None, // Hard off. Never create a cold section, even for functions
+ // which have a basic block sections profile. Such functions
+ // are still laid out using the profile, but are emitted as a
+ // single contiguous section.
+ BBSectionsOnly, // Split only functions which have a basic block sections
+ // profile.
+ All // Split functions using the basic block sections profile
+ // where it is available, and PGO/AutoFDO elsewhere.
+};
+
/// Identify a debugger for "tuning" the debug info.
///
/// The "debugger tuning" concept allows us to present a more intuitive
@@ -113,7 +127,6 @@ class TargetOptions {
TrapUnreachable(false), NoTrapAfterNoreturn(false), TLSSize(0),
EmulatedTLS(false), EnableTLSDESC(false), EnableIPRA(false),
EmitStackSizeSection(false), EnableMachineOutliner(false),
- EnableMachineFunctionSplitter(false),
EnableStaticDataPartitioning(false), SupportsDefaultOutlining(false),
EnableDefaultMachineVerifier(true), EmitAddrsig(false),
BBAddrMap(false), EmitCallGraphSection(false), EmitCallSiteInfo(false),
@@ -209,9 +222,6 @@ class TargetOptions {
/// Enables the MachineOutliner pass.
unsigned EnableMachineOutliner : 1;
- /// Enables the MachineFunctionSplitter pass.
- unsigned EnableMachineFunctionSplitter : 1;
-
/// Enables the StaticDataSplitter pass.
unsigned EnableStaticDataPartitioning : 1;
@@ -232,6 +242,9 @@ class TargetOptions {
/// Emit basic blocks into separate sections.
BasicBlockSection BBSections = BasicBlockSection::None;
+ /// Which functions are eligible for late function splitting.
+ FunctionSplittingMode FunctionSplitting = FunctionSplittingMode::Default;
+
/// Memory Buffer that contains information on sampled basic blocks and used
/// to selectively generate basic block sections.
std::shared_ptr<MemoryBuffer> BBSectionsFuncListBuf;
diff --git a/llvm/lib/CodeGen/BasicBlockSections.cpp b/llvm/lib/CodeGen/BasicBlockSections.cpp
index 9972d431aae8b..5f81747fadd54 100644
--- a/llvm/lib/CodeGen/BasicBlockSections.cpp
+++ b/llvm/lib/CodeGen/BasicBlockSections.cpp
@@ -96,6 +96,17 @@ cl::opt<std::string> llvm::BBSectionsColdTextPrefix(
cl::desc("The text prefix to use for cold basic block clusters"),
cl::init(".text.split."), cl::Hidden);
+FunctionSplittingMode
+llvm::resolveFunctionSplittingMode(const TargetMachine &TM) {
+ // An explicitly requested mode always wins.
+ if (TM.Options.FunctionSplitting != FunctionSplittingMode::Default)
+ return TM.Options.FunctionSplitting;
+ // A basic block sections profile splits the functions it covers.
+ if (TM.getBBSectionsType() == BasicBlockSection::List)
+ return FunctionSplittingMode::BBSectionsOnly;
+ return FunctionSplittingMode::None;
+}
+
static cl::opt<bool> BBSectionsDetectSourceDrift(
"bbsections-detect-source-drift",
cl::desc("This checks if there is a fdo instr. profile hash "
diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp
index 1bebecda62166..900227d5dbd03 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -92,6 +92,7 @@ CGOPT(bool, IgnoreXCOFFVisibility)
CGOPT(bool, XCOFFTracebackTable)
CGOPT(bool, EnableBBAddrMap)
CGOPT(std::string, BBSections)
+CGOPT(FunctionSplittingMode, FunctionSplitting)
CGOPT(unsigned, TLSSize)
CGOPT_EXP(bool, EmulatedTLS)
CGOPT_EXP(bool, EnableTLSDESC)
@@ -104,7 +105,6 @@ CGOPT(bool, EnableStackSizeSection)
CGOPT(bool, EnableAddrsig)
CGOPT(bool, EnableCallGraphSection)
CGOPT(bool, EmitCallSiteInfo)
-CGOPT(bool, EnableMachineFunctionSplitter)
CGOPT(bool, EnableStaticDataPartitioning)
CGOPT(bool, EnableDebugEntryValues)
CGOPT(bool, ForceDwarfFrameSection)
@@ -432,12 +432,20 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
cl::init(false));
CGBINDOPT(EnableDebugEntryValues);
- static cl::opt<bool> EnableMachineFunctionSplitter(
- "split-machine-functions",
- cl::desc("Split out cold basic blocks from machine functions based on "
- "profile information"),
- cl::init(false));
- CGBINDOPT(EnableMachineFunctionSplitter);
+ static cl::opt<FunctionSplittingMode> FunctionSplitting(
+ "function-splitting",
+ cl::desc("Which functions are eligible for late function splitting"),
+ cl::init(FunctionSplittingMode::Default),
+ cl::values(
+ clEnumValN(FunctionSplittingMode::None, "none",
+ "Do not split any function"),
+ clEnumValN(FunctionSplittingMode::BBSectionsOnly, "bbsections",
+ "Only split functions which have a basic block sections "
+ "profile"),
+ clEnumValN(FunctionSplittingMode::All, "all",
+ "Split functions using the basic block sections profile "
+ "where it is available, and PGO/AutoFDO elsewhere")));
+ CGBINDOPT(FunctionSplitting);
static cl::opt<bool> EnableStaticDataPartitioning(
"partition-static-data-sections",
@@ -550,7 +558,7 @@ codegen::InitTargetOptionsFromCodeGenFlags(const Triple &TheTriple) {
Options.ExceptionModel = getExceptionModel();
Options.VecLib = getVectorLibrary();
Options.EmitStackSizeSection = getEnableStackSizeSection();
- Options.EnableMachineFunctionSplitter = getEnableMachineFunctionSplitter();
+ Options.FunctionSplitting = getFunctionSplitting();
Options.EnableStaticDataPartitioning = getEnableStaticDataPartitioning();
Options.EmitAddrsig = getEnableAddrsig();
Options.EmitCallGraphSection = getEnableCallGraphSection();
diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp
index ac9dc278dafcf..6ccd09f7b75f1 100644
--- a/llvm/lib/CodeGen/TargetPassConfig.cpp
+++ b/llvm/lib/CodeGen/TargetPassConfig.cpp
@@ -20,6 +20,7 @@
#include "llvm/Analysis/ScopedNoAliasAA.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
+#include "llvm/CodeGen/BasicBlockSectionUtils.h"
#include "llvm/CodeGen/BasicBlockSectionsProfileReader.h"
#include "llvm/CodeGen/CSEConfigBase.h"
#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"
@@ -263,12 +264,6 @@ static cl::opt<std::string>
cl::desc("Stop compilation before a specific pass"),
cl::value_desc("pass-name"), cl::init(""), cl::Hidden);
-/// Enable the machine function splitter pass.
-static cl::opt<bool> EnableMachineFunctionSplitter(
- "enable-split-machine-functions", cl::Hidden,
- cl::desc("Split out cold blocks from machine functions based on profile "
- "information."));
-
/// Disable the expand reductions pass for testing.
static cl::opt<bool> DisableExpandReductions(
"disable-expand-reductions", cl::init(false), cl::Hidden,
@@ -544,7 +539,6 @@ CGPassBuilderOption llvm::getCGPassBuilderOption() {
SET_OPTION(DebugifyCheckAndStripAll)
SET_OPTION(DisableRAFSProfileLoader)
SET_OPTION(DisableCFIFixup)
- SET_OPTION(EnableMachineFunctionSplitter)
return Opt;
}
@@ -1270,8 +1264,10 @@ void TargetPassConfig::addMachinePasses() {
addPass(createMIRAddFSDiscriminatorsPass(
sampleprof::FSDiscriminatorPass::PassLast));
- if (TM->Options.EnableMachineFunctionSplitter ||
- EnableMachineFunctionSplitter || SplitStaticData ||
+ const bool SplitFunctions =
+ resolveFunctionSplittingMode(*TM) == FunctionSplittingMode::All;
+
+ if (SplitFunctions || SplitStaticData ||
TM->Options.EnableStaticDataPartitioning) {
const std::string ProfileFile = getFSProfileFile(TM);
if (!ProfileFile.empty()) {
@@ -1294,8 +1290,7 @@ void TargetPassConfig::addMachinePasses() {
// feature takes precedence. This means functions eligible for
// basic-block-sections optimizations (`=all`, or `=list=` with function
// included in the list profile) will get that optimization instead.
- if (TM->Options.EnableMachineFunctionSplitter ||
- EnableMachineFunctionSplitter)
+ if (SplitFunctions)
addPass(createMachineFunctionSplitterPass());
if (SplitStaticData || TM->Options.EnableStaticDataPartitioning) {
diff --git a/llvm/test/CodeGen/Generic/machine-function-splitter-optnone.ll b/llvm/test/CodeGen/Generic/machine-function-splitter-optnone.ll
index 67d2ad72ee2f4..2f53e64adf48d 100644
--- a/llvm/test/CodeGen/Generic/machine-function-splitter-optnone.ll
+++ b/llvm/test/CodeGen/Generic/machine-function-splitter-optnone.ll
@@ -1,6 +1,6 @@
; REQUIRES: x86-registered-target
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -split-machine-functions -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s
;; Check that functions with optnone attribute are not split.
; CHECK-LABEL: foo_optnone:
diff --git a/llvm/test/CodeGen/Generic/machine-function-splitter.ll b/llvm/test/CodeGen/Generic/machine-function-splitter.ll
index d798b2875645b..cf8abe3a536b2 100644
--- a/llvm/test/CodeGen/Generic/machine-function-splitter.ll
+++ b/llvm/test/CodeGen/Generic/machine-function-splitter.ll
@@ -2,11 +2,11 @@
; REQUIRES: x86-registered-target
; COM: Machine function splitting with FDO profiles
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -split-machine-functions | FileCheck %s -check-prefixes=MFS-DEFAULTS,MFS-DEFAULTS-X86,MFS-NOBBSECTIONS
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -split-machine-functions -mfs-psi-cutoff=0 -mfs-count-threshold=2000 | FileCheck %s --dump-input=always -check-prefixes=MFS-OPTS1,MFS-OPTS1-X86
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -split-machine-functions -mfs-psi-cutoff=950000 | FileCheck %s -check-prefixes=MFS-OPTS2,MFS-OPTS2-X86
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -split-machine-functions -mfs-split-ehcode | FileCheck %s -check-prefixes=MFS-EH-SPLIT,MFS-EH-SPLIT-X86
-; RUN: llc < %s -mtriple=x86_64 -split-machine-functions -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s -check-prefixes=MFS-O0,MFS-O0-X86
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all | FileCheck %s -check-prefixes=MFS-DEFAULTS,MFS-DEFAULTS-X86,MFS-NOBBSECTIONS
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all -mfs-psi-cutoff=0 -mfs-count-threshold=2000 | FileCheck %s --dump-input=always -check-prefixes=MFS-OPTS1,MFS-OPTS1-X86
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all -mfs-psi-cutoff=950000 | FileCheck %s -check-prefixes=MFS-OPTS2,MFS-OPTS2-X86
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all -mfs-split-ehcode | FileCheck %s -check-prefixes=MFS-EH-SPLIT,MFS-EH-SPLIT-X86
+; RUN: llc < %s -mtriple=x86_64 -function-splitting=all -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s -check-prefixes=MFS-O0,MFS-O0-X86
; COM: Machine function splitting along with -basic-block-sections profile
; RUN: echo 'v1' > %t
@@ -15,19 +15,20 @@
; RUN: echo 'ffoo22' >> %t
; RUN: echo 'c0 1' >> %t
; RUN: echo 'c2' >> %t
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -basic-block-sections=%t -split-machine-functions | FileCheck %s --check-prefixes=MFS-BBSECTIONS
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -basic-block-sections=%t -function-splitting=all | FileCheck %s --check-prefixes=MFS-BBSECTIONS,MFS-BBSECTIONS-ALL
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -basic-block-sections=%t -function-splitting=bbsections | FileCheck %s --check-prefixes=MFS-BBSECTIONS,MFS-BBSECTIONS-ONLY
-; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -enable-split-machine-functions | FileCheck %s -check-prefixes=MFS-DEFAULTS,MFS-DEFAULTS-AARCH64
-; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -enable-split-machine-functions -mfs-psi-cutoff=0 -mfs-count-threshold=2000 | FileCheck %s --dump-input=always -check-prefixes=MFS-OPTS1,MFS-OPTS1-AARCH64
-; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -enable-split-machine-functions -mfs-psi-cutoff=950000 | FileCheck %s -check-prefixes=MFS-OPTS2,MFS-OPTS2-AARCH64
-; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -enable-split-machine-functions -mfs-split-ehcode | FileCheck %s -check-prefixes=MFS-EH-SPLIT,MFS-EH-SPLIT-AARCH64
-; RUN: llc < %s -mtriple=aarch64 -split-machine-functions -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s -check-prefixes=MFS-O0,MFS-O0-AARCH64
-; RUN: llc < %s -mtriple=aarch64 -enable-split-machine-functions -aarch64-redzone | FileCheck %s -check-prefixes=MFS-REDZONE-AARCH64
+; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -function-splitting=all | FileCheck %s -check-prefixes=MFS-DEFAULTS,MFS-DEFAULTS-AARCH64
+; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -function-splitting=all -mfs-psi-cutoff=0 -mfs-count-threshold=2000 | FileCheck %s --dump-input=always -check-prefixes=MFS-OPTS1,MFS-OPTS1-AARCH64
+; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -function-splitting=all -mfs-psi-cutoff=950000 | FileCheck %s -check-prefixes=MFS-OPTS2,MFS-OPTS2-AARCH64
+; RUN: llc < %s -mtriple=aarch64-unknown-linux-gnu -aarch64-min-jump-table-entries=4 -function-splitting=all -mfs-split-ehcode | FileCheck %s -check-prefixes=MFS-EH-SPLIT,MFS-EH-SPLIT-AARCH64
+; RUN: llc < %s -mtriple=aarch64 -function-splitting=all -O0 -mfs-psi-cutoff=0 -mfs-count-threshold=10000 | FileCheck %s -check-prefixes=MFS-O0,MFS-O0-AARCH64
+; RUN: llc < %s -mtriple=aarch64 -function-splitting=all -aarch64-redzone | FileCheck %s -check-prefixes=MFS-REDZONE-AARCH64
; COM: Machine function splitting with AFDO profiles
; RUN: sed 's/InstrProf/SampleProfile/g' %s > %t.ll
-; RUN: llc < %t.ll -mtriple=x86_64-unknown-linux-gnu -split-machine-functions | FileCheck %s --check-prefix=FSAFDO-MFS
-; RUN: llc < %t.ll -mtriple=x86_64-unknown-linux-gnu -split-machine-functions | FileCheck %s --check-prefix=FSAFDO-MFS2
+; RUN: llc < %t.ll -mtriple=x86_64-unknown-linux-gnu -function-splitting=all | FileCheck %s --check-prefix=FSAFDO-MFS
+; RUN: llc < %t.ll -mtriple=x86_64-unknown-linux-gnu -function-splitting=all | FileCheck %s --check-prefix=FSAFDO-MFS2
define void @foo1(i1 zeroext %0) nounwind !prof !14 !section_prefix !15 {
;; Check that cold block is moved to .text.split.
@@ -38,6 +39,15 @@ define void @foo1(i1 zeroext %0) nounwind !prof !14 !section_prefix !15 {
; MFS-DEFAULTS-X86-NEXT: callq baz
; MFS-DEFAULTS-AARCH64-NOT: bl bar
; MFS-DEFAULTS-AARCH64-NEXT: bl baz
+;; Check that with a basic-block-sections profile, a function outside the
+;; profile is still split using PGO with -function-splitting=all.
+; MFS-BBSECTIONS-ALL-LABEL: foo1:
+; MFS-BBSECTIONS-ALL: .section .text.split.foo1
+; MFS-BBSECTIONS-ALL-NEXT: foo1.cold:
+;; Check that with -function-splitting=bbsections, a function outside the
+;; basic-block-sections profile is not split, even though it has PGO data.
+; MFS-BBSECTIONS-ONLY-LABEL: foo1:
+; MFS-BBSECTIONS-ONLY-NOT: foo1.cold:
br i1 %0, label %2, label %4, !prof !17
2: ; preds = %1
diff --git a/llvm/test/CodeGen/X86/basic-block-address-map-with-mfs.ll b/llvm/test/CodeGen/X86/basic-block-address-map-with-mfs.ll
index f8146bb38f71e..a03c2ff9ef3d4 100644
--- a/llvm/test/CodeGen/X86/basic-block-address-map-with-mfs.ll
+++ b/llvm/test/CodeGen/X86/basic-block-address-map-with-mfs.ll
@@ -1,8 +1,8 @@
; COM: Emitting basic-block-address-map when machine function splitting is enabled.
-; RUN: llc < %s -mtriple=x86_64 -function-sections -split-machine-functions -basic-block-address-map | FileCheck %s --check-prefixes=CHECK,BASIC
+; RUN: llc < %s -mtriple=x86_64 -function-sections -function-splitting=all -basic-block-address-map | FileCheck %s --check-prefixes=CHECK,BASIC
; COM: Emitting basic-block-address-map with PGO analysis with machine function splitting enabled.
-; RUN: llc < %s -mtriple=x86_64 -function-sections -split-machine-functions -basic-block-address-map -pgo-analysis-map=func-entry-count,bb-freq,br-prob | FileCheck %s --check-prefixes=CHECK,PGO
+; RUN: llc < %s -mtriple=x86_64 -function-sections -function-splitting=all -basic-block-address-map -pgo-analysis-map=func-entry-count,bb-freq,br-prob | FileCheck %s --check-prefixes=CHECK,PGO
define void @foo(i1 zeroext %0) nounwind !prof !14 {
br i1 %0, label %2, label %4, !prof !15
diff --git a/llvm/test/CodeGen/X86/basic-block-address-map.ll b/llvm/test/CodeGen/X86/basic-block-address-map.ll
index c716e12a57646..21d9066d2a7db 100644
--- a/llvm/test/CodeGen/X86/basic-block-address-map.ll
+++ b/llvm/test/CodeGen/X86/basic-block-address-map.ll
@@ -1,7 +1,7 @@
; Check the basic block sections labels option
; RUN: llc < %s -mtriple=x86_64 -function-sections -unique-section-names=true -basic-block-address-map | FileCheck %s --check-prefixes=CHECK,ELF,UNIQ
; RUN: llc < %s -mtriple=x86_64 -function-sections -unique-section-names=false -basic-block-address-map | FileCheck %s --check-prefixes=CHECK,ELF,NOUNIQ
-; RUN: llc < %s -mtriple=x86_64 -function-sections -unique-section-names=true -basic-block-address-map -split-machine-functions | FileCheck %s --check-prefixes=CHECK,ELF,UNIQ
+; RUN: llc < %s -mtriple=x86_64 -function-sections -unique-section-names=true -basic-block-address-map -function-splitting=all | FileCheck %s --check-prefixes=CHECK,ELF,UNIQ
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc -function-sections -basic-block-address-map | FileCheck %s --check-prefixes=CHECK,COFF
define void @_Z3bazb(i1 zeroext, i1 zeroext) personality ptr @__gxx_personality_v0 {
diff --git a/llvm/test/CodeGen/X86/basic-block-sections.ll b/llvm/test/CodeGen/X86/basic-block-sections.ll
index 8e50416554a0a..fd1db1e29ef39 100644
--- a/llvm/test/CodeGen/X86/basic-block-sections.ll
+++ b/llvm/test/CodeGen/X86/basic-block-sections.ll
@@ -1,6 +1,6 @@
; RUN: llc < %s -mtriple=x86_64-pc-linux -function-sections -basic-block-sections=all -unique-basic-block-section-names | FileCheck %s -check-prefix=LINUX-SECTIONS
; RUN: llc < %s -mtriple=x86_64-pc-linux -basic-block-sections=all -unique-basic-block-section-names | FileCheck %s -check-prefix=LINUX-SECTIONS
-; RUN: llc < %s -mtriple=x86_64-pc-linux -function-sections -basic-block-sections=all -unique-basic-block-section-names -split-machine-functions | FileCheck %s -check-prefix=LINUX-SECTIONS
+; RUN: llc < %s -mtriple=x86_64-pc-linux -function-sections -basic-block-sections=all -unique-basic-block-section-names -function-splitting=all | FileCheck %s -check-prefix=LINUX-SECTIONS
; RUN: llc < %s -mtriple=i386-unknown-linux-gnu -function-sections -basic-block-sections=all -unique-basic-block-section-names | FileCheck %s -check-prefix=LINUX-SECTIONS
; RUN: llc < %s -mtriple=i386-unknown-linux-gnu -basic-block-sections=all -unique-basic-block-section-names | FileCheck %s -check-prefix=LINUX-SECTIONS
diff --git a/llvm/test/CodeGen/X86/function-splitting-mode.ll b/llvm/test/CodeGen/X86/function-splitting-mode.ll
new file mode 100644
index 0000000000000..7486f510360de
--- /dev/null
+++ b/llvm/test/CodeGen/X86/function-splitting-mode.ll
@@ -0,0 +1,61 @@
+;; Tests for the -function-splitting= late function splitting mode.
+; REQUIRES: x86-registered-target
+
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=all | FileCheck %s --check-prefix=SPLIT
+
+;; A function which only has PGO data is not split unless the mode is 'all'.
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefix=NOSPLIT
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=none | FileCheck %s --check-prefix=NOSPLIT
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=bbsections | FileCheck %s --check-prefix=NOSPLIT
+
+;; Invalid modes are rejected.
+; RUN: not llc < %s -mtriple=x86_64-unknown-linux-gnu -function-splitting=bogus 2>&1 | FileCheck %s --check-prefix=ERR
+; ERR: for the --function-splitting option: Cannot find option named 'bogus'!
+
+define void @foo(i1 zeroext %0) nounwind !prof !14 !section_prefix !15 {
+; SPLIT-LABEL: foo
+; SPLIT: .section .text.split.foo
+; SPLIT-NEXT: foo.cold:
+; SPLIT-NOT: callq bar
+; SPLIT-NEXT: callq baz
+;
+; NOSPLIT-LABEL: foo
+; NOSPLIT-NOT: .section .text.split.foo
+; NOSPLIT-NOT: foo.cold:
+ br i1 %0, label %2, label %4, !prof !17
+
+2: ; preds = %1
+ %3 = call i32 @bar()
+ br label %6
+
+4: ; preds = %1
+ %5 = call i32 @baz()
+ br label %6
+
+6: ; preds = %4, %2
+ %7 = tail call i32 @qux()
+ ret void
+}
+
+declare i32 @bar()
+declare i32 @baz()
+declare i32 @qux()
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"ProfileSummary", !1}
+!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}
+!2 = !{!"ProfileFormat", !"InstrProf"}
+!3 = !{!"TotalCount", i64 10000}
+!4 = !{!"MaxCount", i64 10}
+!5 = !{!"MaxInternalCount", i64 1}
+!6 = !{!"MaxFunctionCount", i64 1000}
+!7 = !{!"NumCounts", i64 3}
+!8 = !{!"NumFunctions", i64 5}
+!9 = !{!"DetailedSummary", !10}
+!10 = !{!11, !12, !13}
+!11 = !{i32 10000, i64 100, i32 1}
+!12 = !{i32 999900, i64 100, i32 1}
+!13 = !{i32 999999, i64 1, i32 2}
+!14 = !{!"function_entry_count", i64 7000}
+!15 = !{!"function_section_prefix", !"hot"}
+!17 = !{!"branch_weights", i32 7000, i32 0}
diff --git a/llvm/test/CodeGen/X86/load-sample-profile-2.ll b/llvm/test/CodeGen/X86/load-sample-profile-2.ll
index f608d22843d56..76cc02e7b35c0 100644
--- a/llvm/test/CodeGen/X86/load-sample-profile-2.ll
+++ b/llvm/test/CodeGen/X86/load-sample-profile-2.ll
@@ -1,5 +1,5 @@
;;; MFS with sample profile fails when no -enable-fs-discriminator=true.
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -fs-profile-file=%S/Inputs/fsloader-mfs.afdo -split-machine-functions -enable-fs-discriminator=false 2>&1 | FileCheck %s --check-prefix=NODISCRIMINATOR
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -fs-profile-file=%S/Inputs/fsloader-mfs.afdo -function-splitting=all -enable-fs-discriminator=false 2>&1 | FileCheck %s --check-prefix=NODISCRIMINATOR
; NODISCRIMINATOR: warning: Using AutoFDO without FSDiscriminator for MFS may regress performance.
define void @foo4(i1 zeroext %0, i1 zeroext %1) nounwind {
diff --git a/llvm/test/CodeGen/X86/load-sample-profile.ll b/llvm/test/CodeGen/X86/load-sample-profile.ll
index 2e3f8809d3882..3ac1f39b17e38 100644
--- a/llvm/test/CodeGen/X86/load-sample-profile.ll
+++ b/llvm/test/CodeGen/X86/load-sample-profile.ll
@@ -1,6 +1,6 @@
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -debug-pass=Structure -enable-fs-discriminator=true -improved-fs-discriminator=true 2>&1 | FileCheck %s --check-prefix=NOPROFILE
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -debug-pass=Structure -fs-profile-file=%S/Inputs/fsloader-mfs.afdo -enable-fs-discriminator=true -improved-fs-discriminator=true 2>&1 | FileCheck %s --check-prefix=PROFILE-NOMFS
-; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -debug-pass=Structure -fs-profile-file=%S/Inputs/fsloader-mfs.afdo -split-machine-functions -enable-fs-discriminator=true -improved-fs-discriminator=true 2>&1 | FileCheck %s --check-prefix=PROFILE-MFS
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -debug-pass=Structure -fs-profile-file=%S/Inputs/fsloader-mfs.afdo -function-splitting=all -enable-fs-discriminator=true -improved-fs-discriminator=true 2>&1 | FileCheck %s --check-prefix=PROFILE-MFS
;; No profile is specified, no load passes.
; NOPROFILE: Add FS discriminators in MIR
diff --git a/llvm/test/tools/llvm-profgen/X86/func-split.test b/llvm/test/tools/llvm-profgen/X86/func-split.test
index 734f39ea898ca..3393da2e52dcb 100644
--- a/llvm/test/tools/llvm-profgen/X86/func-split.test
+++ b/llvm/test/tools/llvm-profgen/X86/func-split.test
@@ -30,7 +30,7 @@
; clang -g -O3 -fdebug-info-for-profiling func-split.c -mllvm -mfs-count-threshold=0
-; -fprofile-sample-use=profile.txt -fno-inline -mllvm --enable-split-machine-functions=1
+; -fprofile-sample-use=profile.txt -fno-inline -fsplit-machine-functions
#include <stdio.h>
>From 4c6b49a7ec25ba1cb06cbf439d84348d1550c68e Mon Sep 17 00:00:00 2001
From: Rahman Lavaee <rahmanl at google.com>
Date: Sat, 19 Sep 2026 18:57:00 +0000
Subject: [PATCH 2/2] [clang][flang] Turn -fsplit-machine-functions into a mode
option
Replace the BoolFOption with -fsplit-machine-functions={none,bbsections,all},
following the -flto pattern: the bare -fsplit-machine-functions and
-fno-split-machine-functions spellings become aliases of the '=' form with
AliasArgs<["all"]> and AliasArgs<["none"]> respectively.
The boolean flag has is not currently well-defined when used along with
-fbasic-block-sections=list= as it is not clear whether it should
override the decision by that flag or not.
This buys a tri-state (absent / explicitly off / explicitly on) which the
backend needs in order to honor -fno-split-machine-functions together with
-fbasic-block-sections=list=, plus last-wins precedence between the spellings,
while keeping every existing command line working.
Because Option::accept() rewrites an aliased Arg to carry the unaliased option,
matches(OPT_fsplit_machine_functions) and hasArg(OPT_fsplit_machine_functions)
silently stop working. All readers are therefore updated to query
OPT_fsplit_machine_functions_EQ and switch on the value: the LTO -plugin-opt=
forwarding, tools::addSplitMachineFunctionsArgs() and flang's
CompilerInvocation.
CodeGenOpts.SplitMachineFunctions becomes the FunctionSplitting enum, which now
feeds TargetOptions::FunctionSplitting, and flang's TargetOptions carries the
mode as well.
Driver behavior is unchanged except that -cc1/-fc1 now receive the explicit
mode (e.g. "-fsplit-machine-functions=all"), and that disabling splitting is
accepted on targets without splitting support. Forwarding the new modes through
LTO is left for a follow-up; for now only 'all' is forwarded, as
-plugin-opt=-function-splitting=all.
---
clang/include/clang/Basic/CodeGenOptions.def | 3 +-
clang/include/clang/Basic/CodeGenOptions.h | 1 +
clang/include/clang/Options/Options.td | 20 +++++++++----
clang/lib/CodeGen/BackendUtil.cpp | 3 +-
clang/lib/Driver/ToolChains/CommonArgs.cpp | 29 ++++++++++---------
clang/test/Driver/fsplit-machine-functions.c | 26 +++++++++++++++--
.../test/Frontend/fsplit-machine-functions.c | 11 +++++++
flang/include/flang/Frontend/TargetOptions.h | 6 ++--
flang/lib/Frontend/CompilerInstance.cpp | 3 +-
flang/lib/Frontend/CompilerInvocation.cpp | 10 +++++--
.../test/Driver/fsplit-machine-functions.f90 | 4 +--
11 files changed, 85 insertions(+), 31 deletions(-)
create mode 100644 clang/test/Frontend/fsplit-machine-functions.c
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index dd9590d406d5e..76e9742837e57 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -206,7 +206,8 @@ CODEGENOPT(HIPCorrectlyRoundedDivSqrt, 1, 1, Benign) ///< -fno-hip-fp32-correctl
CODEGENOPT(DisableBlockSignatureString, 1, 0, Benign) ///< Set when -fdisable-block-signature-string is enabled.
CODEGENOPT(HIPSaveKernelArgName, 1, 0, Benign) ///< Set when -fhip-kernel-arg-name is enabled.
CODEGENOPT(UniqueInternalLinkageNames, 1, 0, Benign) ///< Internal Linkage symbols get unique names.
-CODEGENOPT(SplitMachineFunctions, 1, 0, Benign) ///< Split machine functions using profile information.
+ENUM_CODEGENOPT(FunctionSplitting, FunctionSplittingMode, 2,
+ FunctionSplittingMode::Default, Benign) ///< Late function splitting mode.
CODEGENOPT(PartitionStaticDataSections, 1, 0, Benign) /// < Partition static data sections using profile information.
CODEGENOPT(PPCUseFullRegisterNames, 1, 0, Benign) ///< Print full register names in assembly
CODEGENOPT(X86RelaxRelocations, 1, 1, Benign) ///< -Wa,-mrelax-relocations={yes,no}
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index c3a23415c922a..c83797ee5bca2 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -78,6 +78,7 @@ class CodeGenOptionsBase {
llvm::AsanDetectStackUseAfterReturnMode;
using AsanDtorKind = llvm::AsanDtorKind;
using VectorLibrary = llvm::driver::VectorLibrary;
+ using FunctionSplittingMode = llvm::FunctionSplittingMode;
using ZeroCallUsedRegsKind = llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind;
using WinX64EHUnwindMode = llvm::WinX64EHUnwindMode;
using ControlFlowGuardMechanism = llvm::ControlFlowGuardMechanism;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 1b1a00de20040..2389e2373d4fa 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5103,11 +5103,21 @@ defm separate_named_sections : BoolFOption<"separate-named-sections",
"Use separate unique sections for named sections (ELF Only)">,
NegFlag<SetFalse>>;
-defm split_machine_functions: BoolFOption<"split-machine-functions",
- CodeGenOpts<"SplitMachineFunctions">, DefaultFalse,
- PosFlag<SetTrue, [], [ClangOption, CC1Option, FlangOption, FC1Option], "Enable">,
- NegFlag<SetFalse, [], [ClangOption, FlangOption], "Disable">,
- BothFlags<[], [ClangOption], " late function splitting using profile information (x86 and aarch64 ELF)">>;
+def fsplit_machine_functions_EQ : Joined<["-"], "fsplit-machine-functions=">,
+ Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, Group<f_Group>,
+ HelpText<"Late function splitting mode (x86 and aarch64 ELF)">,
+ Values<"none,bbsections,all">,
+ NormalizedValuesScope<"llvm::FunctionSplittingMode">,
+ NormalizedValues<["None", "BBSectionsOnly", "All"]>,
+ MarshallingInfoEnum<CodeGenOpts<"FunctionSplitting">, "Default">;
+def fsplit_machine_functions : Flag<["-"], "fsplit-machine-functions">,
+ Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, Group<f_Group>,
+ Alias<fsplit_machine_functions_EQ>, AliasArgs<["all"]>,
+ HelpText<"Enable late function splitting using profile information (x86 and aarch64 ELF)">;
+def fno_split_machine_functions : Flag<["-"], "fno-split-machine-functions">,
+ Visibility<[ClangOption, FlangOption]>, Group<f_Group>,
+ Alias<fsplit_machine_functions_EQ>, AliasArgs<["none"]>,
+ HelpText<"Disable late function splitting using profile information (x86 and aarch64 ELF)">;
defm partition_static_data_sections: BoolFOption<"partition-static-data-sections",
CodeGenOpts<"PartitionStaticDataSections">, DefaultFalse,
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 3523f10e630b4..46b932d7443eb 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -418,8 +418,7 @@ static bool initTargetOptions(const CompilerInstance &CI,
Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
}
- if (CodeGenOpts.SplitMachineFunctions)
- Options.FunctionSplitting = llvm::FunctionSplittingMode::All;
+ Options.FunctionSplitting = CodeGenOpts.getFunctionSplitting();
Options.EnableStaticDataPartitioning =
CodeGenOpts.PartitionStaticDataSections;
Options.FunctionSections = CodeGenOpts.FunctionSections;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 952e43236c6a7..5d4f30dbc600c 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1299,9 +1299,11 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
}
// Pass an option to enable split machine functions.
- if (auto *A = Args.getLastArg(options::OPT_fsplit_machine_functions,
- options::OPT_fno_split_machine_functions)) {
- if (A->getOption().matches(options::OPT_fsplit_machine_functions))
+ // TODO: Forward the mode itself (-plugin-opt=-function-splitting=<mode>)
+ // once the LTO plumbing for the new modes is in place. Until then only the
+ // legacy "split everywhere" behavior can be requested through LTO.
+ if (auto *A = Args.getLastArg(options::OPT_fsplit_machine_functions_EQ)) {
+ if (StringRef(A->getValue()) == "all")
CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) +
"-function-splitting=all"));
}
@@ -2819,17 +2821,18 @@ void tools::addSplitMachineFunctionsArgs(const Driver &D,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const llvm::Triple &Triple) {
- if (Arg *A = Args.getLastArg(options::OPT_fsplit_machine_functions,
- options::OPT_fno_split_machine_functions)) {
- if (!A->getOption().matches(options::OPT_fno_split_machine_functions)) {
- // This codegen pass is only available on x86 and AArch64 ELF targets.
- if ((Triple.isX86() || Triple.isAArch64()) && Triple.isOSBinFormatELF())
- A->render(Args, CmdArgs);
- else
- D.Diag(diag::err_drv_unsupported_opt_for_target)
- << A->getAsString(Args) << Triple.getTriple();
- }
+ Arg *A = Args.getLastArg(options::OPT_fsplit_machine_functions_EQ);
+ if (!A)
+ return;
+ // Disabling splitting is always supported, everything else is a codegen pass
+ // which is only available on x86 and AArch64 ELF targets.
+ if (StringRef(A->getValue()) != "none" &&
+ !((Triple.isX86() || Triple.isAArch64()) && Triple.isOSBinFormatELF())) {
+ D.Diag(diag::err_drv_unsupported_opt_for_target)
+ << A->getAsString(Args) << Triple.getTriple();
+ return;
}
+ A->render(Args, CmdArgs);
}
void tools::addOpenMPDeviceRTL(const Driver &D,
diff --git a/clang/test/Driver/fsplit-machine-functions.c b/clang/test/Driver/fsplit-machine-functions.c
index 80485a32db3b6..f4918f51504aa 100644
--- a/clang/test/Driver/fsplit-machine-functions.c
+++ b/clang/test/Driver/fsplit-machine-functions.c
@@ -5,11 +5,29 @@
// RUN: %clang -### --target=aarch64 -fprofile-use=%t/default.profdata -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefixes=CHECK,LTO-NEG
// RUN: %clang -### --target=x86_64 -fprofile-use=%t/default.profdata -fsplit-machine-functions -fno-split-machine-functions %s -c 2>&1 | FileCheck -check-prefix=NEG %s
-// CHECK: "-fsplit-machine-functions"
+// CHECK: "-fsplit-machine-functions=all"
// CHECK-SAME: "-fprofile-instrument-use=llvm"
// CHECK-SAME: "-fprofile-instrument-use-path={{.*}}default.profdata"
-// NEG-NOT: "-fsplit-machine-functions"
+// NEG-NOT: "-fsplit-machine-functions=all"
+// NEG: "-fsplit-machine-functions=none"
+
+/// The mode spellings are forwarded to -cc1 verbatim and the last one wins.
+// RUN: %clang -### -c --target=x86_64 -fsplit-machine-functions=bbsections %s 2>&1 | FileCheck %s --check-prefix=BBSECTIONS
+// RUN: %clang -### -c --target=x86_64 -fsplit-machine-functions -fsplit-machine-functions=bbsections %s 2>&1 | FileCheck %s --check-prefix=BBSECTIONS
+// BBSECTIONS: "-fsplit-machine-functions=bbsections"
+
+// RUN: %clang -### -c --target=x86_64 -fsplit-machine-functions=none %s 2>&1 | FileCheck %s --check-prefix=NONE
+// RUN: %clang -### -c --target=x86_64 -fsplit-machine-functions -fno-split-machine-functions %s 2>&1 | FileCheck %s --check-prefix=NONE
+// NONE: "-fsplit-machine-functions=none"
+
+// RUN: %clang -### -c --target=x86_64 -fno-split-machine-functions -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=ALL
+// RUN: %clang -### -c --target=x86_64 -fsplit-machine-functions=all %s 2>&1 | FileCheck %s --check-prefix=ALL
+// ALL: "-fsplit-machine-functions=all"
+
+/// Nothing is forwarded when the option is absent.
+// RUN: %clang -### -c --target=x86_64 %s 2>&1 | FileCheck %s --check-prefix=ABSENT
+// ABSENT-NOT: "-fsplit-machine-functions
// RUN: %clang -### --target=x86_64-linux -flto -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=LTO
// RUN: %clang -### --target=x86_64-linux -flto -fsplit-machine-functions -fno-split-machine-functions %s 2>&1 | FileCheck %s --check-prefix=LTO-NEG
@@ -18,6 +36,10 @@
// LTO-NEG-NOT: "-plugin-opt=-function-splitting=all"
// RUN: not %clang -### -c --target=arm-unknown-linux -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=ERR
+// RUN: not %clang -### -c --target=arm-unknown-linux -fsplit-machine-functions=bbsections %s 2>&1 | FileCheck %s --check-prefix=ERR-BBSECTIONS
// ERR: error: unsupported option '-fsplit-machine-functions' for target
+// ERR-BBSECTIONS: error: unsupported option '-fsplit-machine-functions=bbsections' for target
+/// Disabling splitting is supported on every target.
// RUN: %clang -### --target=arm-unknown-linux -fsplit-machine-functions -fno-split-machine-functions %s
+// RUN: %clang -### -c --target=arm-unknown-linux -fno-split-machine-functions %s
diff --git a/clang/test/Frontend/fsplit-machine-functions.c b/clang/test/Frontend/fsplit-machine-functions.c
new file mode 100644
index 0000000000000..c6420a4d0c814
--- /dev/null
+++ b/clang/test/Frontend/fsplit-machine-functions.c
@@ -0,0 +1,11 @@
+/// -cc1 accepts every late function splitting mode and rejects unknown ones.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsplit-machine-functions=none -emit-llvm -o /dev/null %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsplit-machine-functions=bbsections -emit-llvm -o /dev/null %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsplit-machine-functions=all -emit-llvm -o /dev/null %s
+/// The legacy spelling is an alias for '=all'.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsplit-machine-functions -emit-llvm -o /dev/null %s
+
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fsplit-machine-functions=bogus -emit-llvm -o /dev/null %s 2>&1 | FileCheck %s
+// CHECK: invalid value 'bogus' in '-fsplit-machine-functions=bogus'
+
+void f(void) {}
diff --git a/flang/include/flang/Frontend/TargetOptions.h b/flang/include/flang/Frontend/TargetOptions.h
index 3b81c76ffdcd9..76d54da5bf84d 100644
--- a/flang/include/flang/Frontend/TargetOptions.h
+++ b/flang/include/flang/Frontend/TargetOptions.h
@@ -18,6 +18,7 @@
#ifndef FORTRAN_FRONTEND_TARGETOPTIONS_H
#define FORTRAN_FRONTEND_TARGETOPTIONS_H
+#include "llvm/Target/TargetOptions.h"
#include <string>
#include <vector>
@@ -51,8 +52,9 @@ class TargetOptions {
/// Print verbose assembly
bool asmVerbose = false;
- /// Enable splitting of machine functions using profile information.
- bool SplitMachineFunctions = false;
+ /// Late function splitting mode.
+ llvm::FunctionSplittingMode functionSplitting =
+ llvm::FunctionSplittingMode::Default;
/// Atomic control options
bool atomicIgnoreDenormalMode = false;
diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index f63b7cb158082..28ece9b4da8ec 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -380,8 +380,7 @@ bool CompilerInstance::setUpTargetMachine() {
std::optional<llvm::CodeModel::Model> cm = getCodeModel(CGOpts.CodeModel);
llvm::TargetOptions tOpts = llvm::TargetOptions();
- if (targetOpts.SplitMachineFunctions)
- tOpts.FunctionSplitting = llvm::FunctionSplittingMode::All;
+ tOpts.FunctionSplitting = targetOpts.functionSplitting;
tOpts.VecLib = convertDriverVectorLibraryToVectorLibrary(CGOpts.getVecLib());
tOpts.MCOptions.DisableIntegratedAS = CGOpts.DisableIntegratedAS;
tOpts.FunctionSections = CGOpts.FunctionSections;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 1f9be77057fb0..09af77bdf7b63 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -624,8 +624,14 @@ static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) {
opts.abi = V == "vec-default" ? "" : V.str();
}
- opts.SplitMachineFunctions =
- args.hasArg(clang::options::OPT_fsplit_machine_functions);
+ if (const llvm::opt::Arg *a =
+ args.getLastArg(clang::options::OPT_fsplit_machine_functions_EQ))
+ opts.functionSplitting =
+ llvm::StringSwitch<llvm::FunctionSplittingMode>(a->getValue())
+ .Case("none", llvm::FunctionSplittingMode::None)
+ .Case("bbsections", llvm::FunctionSplittingMode::BBSectionsOnly)
+ .Case("all", llvm::FunctionSplittingMode::All)
+ .Default(llvm::FunctionSplittingMode::Default);
opts.asmVerbose = args.hasFlag(clang::options::OPT_fverbose_asm,
clang::options::OPT_fno_verbose_asm, false);
diff --git a/flang/test/Driver/fsplit-machine-functions.f90 b/flang/test/Driver/fsplit-machine-functions.f90
index 2a0ffe61811fc..f2de6e1d67e2c 100644
--- a/flang/test/Driver/fsplit-machine-functions.f90
+++ b/flang/test/Driver/fsplit-machine-functions.f90
@@ -8,7 +8,7 @@
! RUN: %if arm-registered-target %{ not %flang -### --target=arm-unknown-linux -fsplit-machine-functions %s 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-OPT %}
! RUN: %if arm-registered-target %{ %flang -### --target=arm-unknown-linux -fno-split-machine-functions %s 2>&1 | FileCheck %s --check-prefix=NO-SPLIT-MACHINE-FUNCTIONS %}
-! SPLIT-MACHINE-FUNCTIONS: "-fsplit-machine-functions"
-! NO-SPLIT-MACHINE-FUNCTIONS-NOT: "-fsplit-machine-functions"
+! SPLIT-MACHINE-FUNCTIONS: "-fsplit-machine-functions=all"
+! NO-SPLIT-MACHINE-FUNCTIONS-NOT: "-fsplit-machine-functions=all"
! UNSUPPORTED-OPT: error: unsupported option '-fsplit-machine-functions' for target
More information about the llvm-commits
mailing list