Skip to content

Commit 6bb5065

Browse files
committed
[RFC][LTO] Allow target-specific module splittting
Allow targets to implement custom module splitting logic for --lto-partitions.
1 parent 73b255c commit 6bb5065

File tree

4 files changed

+104
-29
lines changed

4 files changed

+104
-29
lines changed

llvm/include/llvm/Target/TargetMachine.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,18 @@ class TargetMachine {
418418
virtual unsigned getAddressSpaceForPseudoSourceKind(unsigned Kind) const {
419419
return 0;
420420
}
421+
422+
/// Entry point for module splitting. Targets can implement custom module
423+
/// splitting logic, mainly used by LTO for --lto-partitions.
424+
///
425+
/// \returns `true` if the module was split, `false` otherwise. When `false`
426+
/// is returned, it is assumed that \p ModuleCallback has never been called
427+
/// and \p M has not been modified.
428+
virtual bool splitModule(
429+
Module &M, unsigned NumParts,
430+
function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback) const {
431+
return false;
432+
}
421433
};
422434

423435
/// This class describes a target machine that is implemented with the LLVM

llvm/lib/LTO/LTOBackend.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
436436
unsigned ThreadCount = 0;
437437
const Target *T = &TM->getTarget();
438438

439-
SplitModule(
440-
Mod, ParallelCodeGenParallelismLevel,
439+
const auto HandleModulePartition =
441440
[&](std::unique_ptr<Module> MPart) {
442441
// We want to clone the module in a new context to multi-thread the
443442
// codegen. We do it by serializing partition modules to bitcode
@@ -469,8 +468,14 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
469468
// Pass BC using std::move to ensure that it get moved rather than
470469
// copied into the thread's context.
471470
std::move(BC), ThreadCount++);
472-
},
473-
false);
471+
};
472+
473+
// Try target-specific module splitting first, then fallback to the default.
474+
if (!TM->splitModule(Mod, ParallelCodeGenParallelismLevel,
475+
HandleModulePartition)) {
476+
SplitModule(Mod, ParallelCodeGenParallelismLevel, HandleModulePartition,
477+
false);
478+
}
474479

475480
// Because the inner lambda (which runs in a worker thread) captures our local
476481
// variables, we need to wait for the worker threads to terminate before we

llvm/tools/llvm-split/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
set(LLVM_LINK_COMPONENTS
2+
AllTargetsAsmParsers
3+
AllTargetsCodeGens
4+
AllTargetsDescs
5+
AllTargetsInfos
26
TransformUtils
37
BitWriter
8+
CodeGen
49
Core
510
IRReader
11+
MC
612
Support
13+
Target
714
)
815

916
add_llvm_tool(llvm-split

llvm/tools/llvm-split/llvm-split.cpp

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
//===-- llvm-split: command line tool for testing module splitter ---------===//
1+
//===-- llvm-split: command line tool for testing module splitting --------===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// This program can be used to test the llvm::SplitModule function.
9+
// This program can be used to test the llvm::SplitModule and
10+
// TargetMachine::splitModule functions.
1011
//
1112
//===----------------------------------------------------------------------===//
1213

@@ -15,12 +16,17 @@
1516
#include "llvm/IR/LLVMContext.h"
1617
#include "llvm/IR/Verifier.h"
1718
#include "llvm/IRReader/IRReader.h"
19+
#include "llvm/MC/TargetRegistry.h"
1820
#include "llvm/Support/CommandLine.h"
1921
#include "llvm/Support/FileSystem.h"
22+
#include "llvm/Support/InitLLVM.h"
2023
#include "llvm/Support/SourceMgr.h"
24+
#include "llvm/Support/TargetSelect.h"
2125
#include "llvm/Support/ToolOutputFile.h"
22-
#include "llvm/Support/raw_ostream.h"
2326
#include "llvm/Support/WithColor.h"
27+
#include "llvm/Support/raw_ostream.h"
28+
#include "llvm/Target/TargetMachine.h"
29+
#include "llvm/TargetParser/Triple.h"
2430
#include "llvm/Transforms/Utils/SplitModule.h"
2531

2632
using namespace llvm;
@@ -47,12 +53,45 @@ static cl::opt<bool>
4753
cl::desc("Split without externalizing locals"),
4854
cl::cat(SplitCategory));
4955

56+
static cl::opt<std::string>
57+
MTarget("mtarget",
58+
cl::desc("Target triple. When present, a TargetMachine is created "
59+
"and TargetMachine::splitModule is used instead of the "
60+
"common SplitModule logic."),
61+
cl::value_desc("triple"), cl::cat(SplitCategory));
62+
63+
static cl::opt<std::string>
64+
MCPU("mcpu", cl::desc("Target CPU, ignored if -mtarget is not used"),
65+
cl::value_desc("cpu"), cl::cat(SplitCategory));
66+
5067
int main(int argc, char **argv) {
68+
InitLLVM X(argc, argv);
69+
70+
// NOTE: If mtarget is not present we could avoid initializing targets to save
71+
// time, but this is a testing tool and it's likely not worth the added
72+
// complexity.
73+
InitializeAllTargets();
74+
InitializeAllTargetMCs();
75+
5176
LLVMContext Context;
5277
SMDiagnostic Err;
5378
cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
5479
cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
5580

81+
TargetMachine *TM = nullptr;
82+
if (!MTarget.empty()) {
83+
std::string Error;
84+
const Target *T = TargetRegistry::lookupTarget(MTarget, Error);
85+
if (!T) {
86+
errs() << "unknown target '" << MTarget << "': " << Error << "\n";
87+
return 1;
88+
}
89+
90+
TargetOptions Options;
91+
TM = T->createTargetMachine(MTarget, MCPU, /*FS*/ "", Options, std::nullopt,
92+
std::nullopt);
93+
}
94+
5695
std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
5796

5897
if (!M) {
@@ -61,28 +100,40 @@ int main(int argc, char **argv) {
61100
}
62101

63102
unsigned I = 0;
64-
SplitModule(
65-
*M, NumOutputs,
66-
[&](std::unique_ptr<Module> MPart) {
67-
std::error_code EC;
68-
std::unique_ptr<ToolOutputFile> Out(new ToolOutputFile(
69-
OutputFilename + utostr(I++), EC, sys::fs::OF_None));
70-
if (EC) {
71-
errs() << EC.message() << '\n';
72-
exit(1);
73-
}
74-
75-
if (verifyModule(*MPart, &errs())) {
76-
errs() << "Broken module!\n";
77-
exit(1);
78-
}
79-
80-
WriteBitcodeToFile(*MPart, Out->os());
81-
82-
// Declare success.
83-
Out->keep();
84-
},
85-
PreserveLocals);
103+
const auto HandleModulePart = [&](std::unique_ptr<Module> MPart) {
104+
std::error_code EC;
105+
std::unique_ptr<ToolOutputFile> Out(
106+
new ToolOutputFile(OutputFilename + utostr(I++), EC, sys::fs::OF_None));
107+
if (EC) {
108+
errs() << EC.message() << '\n';
109+
exit(1);
110+
}
111+
112+
if (verifyModule(*MPart, &errs())) {
113+
errs() << "Broken module!\n";
114+
exit(1);
115+
}
116+
117+
WriteBitcodeToFile(*MPart, Out->os());
118+
119+
// Declare success.
120+
Out->keep();
121+
};
122+
123+
if (TM) {
124+
if (PreserveLocals) {
125+
errs() << "warning: -preserve-locals has no effect when using "
126+
"TargetMachine::splitModule\n";
127+
}
128+
129+
if (TM->splitModule(*M, NumOutputs, HandleModulePart))
130+
return 0;
131+
132+
errs() << "warning:"
133+
"TargetMachine::splitModule failed, falling back to default "
134+
"splitModule implementation\n";
135+
}
86136

137+
SplitModule(*M, NumOutputs, HandleModulePart, PreserveLocals);
87138
return 0;
88139
}

0 commit comments

Comments
 (0)