[llvm] [RFC][LTO] Allow target-specific module splittting (PR #83128)
Pierre van Houtryve via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 19 01:44:09 PDT 2024
https://github.com/Pierre-vh updated https://github.com/llvm/llvm-project/pull/83128
>From 6bb50651090d6dc474198491b8aa4b2f6dad8dda Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Tue, 27 Feb 2024 13:26:18 +0100
Subject: [PATCH 1/2] [RFC][LTO] Allow target-specific module splittting
Allow targets to implement custom module splitting logic for
--lto-partitions.
---
llvm/include/llvm/Target/TargetMachine.h | 12 +++
llvm/lib/LTO/LTOBackend.cpp | 13 ++-
llvm/tools/llvm-split/CMakeLists.txt | 7 ++
llvm/tools/llvm-split/llvm-split.cpp | 101 +++++++++++++++++------
4 files changed, 104 insertions(+), 29 deletions(-)
diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h
index ceb371bdc73480..48ea3cfe02775b 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -418,6 +418,18 @@ class TargetMachine {
virtual unsigned getAddressSpaceForPseudoSourceKind(unsigned Kind) const {
return 0;
}
+
+ /// Entry point for module splitting. Targets can implement custom module
+ /// splitting logic, mainly used by LTO for --lto-partitions.
+ ///
+ /// \returns `true` if the module was split, `false` otherwise. When `false`
+ /// is returned, it is assumed that \p ModuleCallback has never been called
+ /// and \p M has not been modified.
+ virtual bool splitModule(
+ Module &M, unsigned NumParts,
+ function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback) const {
+ return false;
+ }
};
/// This class describes a target machine that is implemented with the LLVM
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 71e8849dc3cc91..d4b89ede2d7134 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -436,8 +436,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
unsigned ThreadCount = 0;
const Target *T = &TM->getTarget();
- SplitModule(
- Mod, ParallelCodeGenParallelismLevel,
+ const auto HandleModulePartition =
[&](std::unique_ptr<Module> MPart) {
// We want to clone the module in a new context to multi-thread the
// codegen. We do it by serializing partition modules to bitcode
@@ -469,8 +468,14 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
// Pass BC using std::move to ensure that it get moved rather than
// copied into the thread's context.
std::move(BC), ThreadCount++);
- },
- false);
+ };
+
+ // Try target-specific module splitting first, then fallback to the default.
+ if (!TM->splitModule(Mod, ParallelCodeGenParallelismLevel,
+ HandleModulePartition)) {
+ SplitModule(Mod, ParallelCodeGenParallelismLevel, HandleModulePartition,
+ false);
+ }
// Because the inner lambda (which runs in a worker thread) captures our local
// variables, we need to wait for the worker threads to terminate before we
diff --git a/llvm/tools/llvm-split/CMakeLists.txt b/llvm/tools/llvm-split/CMakeLists.txt
index 52eedeb9f53f32..0579298462d113 100644
--- a/llvm/tools/llvm-split/CMakeLists.txt
+++ b/llvm/tools/llvm-split/CMakeLists.txt
@@ -1,9 +1,16 @@
set(LLVM_LINK_COMPONENTS
+ AllTargetsAsmParsers
+ AllTargetsCodeGens
+ AllTargetsDescs
+ AllTargetsInfos
TransformUtils
BitWriter
+ CodeGen
Core
IRReader
+ MC
Support
+ Target
)
add_llvm_tool(llvm-split
diff --git a/llvm/tools/llvm-split/llvm-split.cpp b/llvm/tools/llvm-split/llvm-split.cpp
index c6e20e0373c717..252e04e77941f2 100644
--- a/llvm/tools/llvm-split/llvm-split.cpp
+++ b/llvm/tools/llvm-split/llvm-split.cpp
@@ -1,4 +1,4 @@
-//===-- llvm-split: command line tool for testing module splitter ---------===//
+//===-- llvm-split: command line tool for testing module splitting --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This program can be used to test the llvm::SplitModule function.
+// This program can be used to test the llvm::SplitModule and
+// TargetMachine::splitModule functions.
//
//===----------------------------------------------------------------------===//
@@ -15,12 +16,17 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
+#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/SplitModule.h"
using namespace llvm;
@@ -47,12 +53,45 @@ static cl::opt<bool>
cl::desc("Split without externalizing locals"),
cl::cat(SplitCategory));
+static cl::opt<std::string>
+ MTarget("mtarget",
+ cl::desc("Target triple. When present, a TargetMachine is created "
+ "and TargetMachine::splitModule is used instead of the "
+ "common SplitModule logic."),
+ cl::value_desc("triple"), cl::cat(SplitCategory));
+
+static cl::opt<std::string>
+ MCPU("mcpu", cl::desc("Target CPU, ignored if -mtarget is not used"),
+ cl::value_desc("cpu"), cl::cat(SplitCategory));
+
int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // NOTE: If mtarget is not present we could avoid initializing targets to save
+ // time, but this is a testing tool and it's likely not worth the added
+ // complexity.
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+
LLVMContext Context;
SMDiagnostic Err;
cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
+ TargetMachine *TM = nullptr;
+ if (!MTarget.empty()) {
+ std::string Error;
+ const Target *T = TargetRegistry::lookupTarget(MTarget, Error);
+ if (!T) {
+ errs() << "unknown target '" << MTarget << "': " << Error << "\n";
+ return 1;
+ }
+
+ TargetOptions Options;
+ TM = T->createTargetMachine(MTarget, MCPU, /*FS*/ "", Options, std::nullopt,
+ std::nullopt);
+ }
+
std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
if (!M) {
@@ -61,28 +100,40 @@ int main(int argc, char **argv) {
}
unsigned I = 0;
- SplitModule(
- *M, NumOutputs,
- [&](std::unique_ptr<Module> MPart) {
- std::error_code EC;
- std::unique_ptr<ToolOutputFile> Out(new ToolOutputFile(
- OutputFilename + utostr(I++), EC, sys::fs::OF_None));
- if (EC) {
- errs() << EC.message() << '\n';
- exit(1);
- }
-
- if (verifyModule(*MPart, &errs())) {
- errs() << "Broken module!\n";
- exit(1);
- }
-
- WriteBitcodeToFile(*MPart, Out->os());
-
- // Declare success.
- Out->keep();
- },
- PreserveLocals);
+ const auto HandleModulePart = [&](std::unique_ptr<Module> MPart) {
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(OutputFilename + utostr(I++), EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ exit(1);
+ }
+
+ if (verifyModule(*MPart, &errs())) {
+ errs() << "Broken module!\n";
+ exit(1);
+ }
+
+ WriteBitcodeToFile(*MPart, Out->os());
+
+ // Declare success.
+ Out->keep();
+ };
+
+ if (TM) {
+ if (PreserveLocals) {
+ errs() << "warning: -preserve-locals has no effect when using "
+ "TargetMachine::splitModule\n";
+ }
+
+ if (TM->splitModule(*M, NumOutputs, HandleModulePart))
+ return 0;
+
+ errs() << "warning:"
+ "TargetMachine::splitModule failed, falling back to default "
+ "splitModule implementation\n";
+ }
+ SplitModule(*M, NumOutputs, HandleModulePart, PreserveLocals);
return 0;
}
>From 629a585b00fb01c2b3da66ec7bfa405833f1d605 Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Fri, 19 Apr 2024 10:43:55 +0200
Subject: [PATCH 2/2] comments
---
.../tools/llvm-split/target-specific-split.ll | 10 ++++++++
llvm/tools/llvm-split/llvm-split.cpp | 23 ++++++++-----------
2 files changed, 20 insertions(+), 13 deletions(-)
create mode 100644 llvm/test/tools/llvm-split/target-specific-split.ll
diff --git a/llvm/test/tools/llvm-split/target-specific-split.ll b/llvm/test/tools/llvm-split/target-specific-split.ll
new file mode 100644
index 00000000000000..ea4d38084d759a
--- /dev/null
+++ b/llvm/test/tools/llvm-split/target-specific-split.ll
@@ -0,0 +1,10 @@
+; RUN: llvm-split -o %t %s -mtriple x86_64 -preserve-locals 2>&1 | FileCheck %s
+
+; Basic test for a target that doesn't support target-specific module splitting.
+
+; CHECK: warning: -preserve-locals has no effect when using TargetMachine::splitModule
+; CHECK: warning: TargetMachine::splitModule failed, falling back to default splitModule implementation
+
+define void @bar() {
+ ret void
+}
diff --git a/llvm/tools/llvm-split/llvm-split.cpp b/llvm/tools/llvm-split/llvm-split.cpp
index 252e04e77941f2..41f392bcc53117 100644
--- a/llvm/tools/llvm-split/llvm-split.cpp
+++ b/llvm/tools/llvm-split/llvm-split.cpp
@@ -54,41 +54,38 @@ static cl::opt<bool>
cl::cat(SplitCategory));
static cl::opt<std::string>
- MTarget("mtarget",
+ MTriple("mtriple",
cl::desc("Target triple. When present, a TargetMachine is created "
"and TargetMachine::splitModule is used instead of the "
"common SplitModule logic."),
cl::value_desc("triple"), cl::cat(SplitCategory));
static cl::opt<std::string>
- MCPU("mcpu", cl::desc("Target CPU, ignored if -mtarget is not used"),
+ MCPU("mcpu", cl::desc("Target CPU, ignored if -mtriple is not used"),
cl::value_desc("cpu"), cl::cat(SplitCategory));
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
- // NOTE: If mtarget is not present we could avoid initializing targets to save
- // time, but this is a testing tool and it's likely not worth the added
- // complexity.
- InitializeAllTargets();
- InitializeAllTargetMCs();
-
LLVMContext Context;
SMDiagnostic Err;
cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
TargetMachine *TM = nullptr;
- if (!MTarget.empty()) {
+ if (!MTriple.empty()) {
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+
std::string Error;
- const Target *T = TargetRegistry::lookupTarget(MTarget, Error);
+ const Target *T = TargetRegistry::lookupTarget(MTriple, Error);
if (!T) {
- errs() << "unknown target '" << MTarget << "': " << Error << "\n";
+ errs() << "unknown target '" << MTriple << "': " << Error << "\n";
return 1;
}
TargetOptions Options;
- TM = T->createTargetMachine(MTarget, MCPU, /*FS*/ "", Options, std::nullopt,
+ TM = T->createTargetMachine(MTriple, MCPU, /*FS*/ "", Options, std::nullopt,
std::nullopt);
}
@@ -129,7 +126,7 @@ int main(int argc, char **argv) {
if (TM->splitModule(*M, NumOutputs, HandleModulePart))
return 0;
- errs() << "warning:"
+ errs() << "warning: "
"TargetMachine::splitModule failed, falling back to default "
"splitModule implementation\n";
}
More information about the llvm-commits
mailing list