aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-split/llvm-split.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-split/llvm-split.cpp')
-rw-r--r--llvm/tools/llvm-split/llvm-split.cpp176
1 files changed, 176 insertions, 0 deletions
diff --git a/llvm/tools/llvm-split/llvm-split.cpp b/llvm/tools/llvm-split/llvm-split.cpp
index 9f6678a..97713c4 100644
--- a/llvm/tools/llvm-split/llvm-split.cpp
+++ b/llvm/tools/llvm-split/llvm-split.cpp
@@ -11,14 +11,19 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/PassInstrumentation.h"
+#include "llvm/IR/PassManager.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/FormatVariadic.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
@@ -27,7 +32,9 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/TargetParser/Triple.h"
+#include "llvm/Transforms/IPO/GlobalDCE.h"
#include "llvm/Transforms/Utils/SplitModule.h"
+#include "llvm/Transforms/Utils/SplitModuleByCategory.h"
using namespace llvm;
@@ -70,6 +77,164 @@ static cl::opt<std::string>
MCPU("mcpu", cl::desc("Target CPU, ignored if --mtriple is not used"),
cl::value_desc("cpu"), cl::cat(SplitCategory));
+enum class SplitByCategoryType {
+ SBCT_ByModuleId,
+ SBCT_ByKernel,
+ SBCT_None,
+};
+
+static cl::opt<SplitByCategoryType> SplitByCategory(
+ "split-by-category",
+ cl::desc("Split by category. If present, splitting by category is used "
+ "with the specified categorization type."),
+ cl::Optional, cl::init(SplitByCategoryType::SBCT_None),
+ cl::values(clEnumValN(SplitByCategoryType::SBCT_ByModuleId, "module-id",
+ "one output module per translation unit marked with "
+ "\"module-id\" attribute"),
+ clEnumValN(SplitByCategoryType::SBCT_ByKernel, "kernel",
+ "one output module per kernel")),
+ cl::cat(SplitCategory));
+
+static cl::opt<bool> OutputAssembly{
+ "S", cl::desc("Write output as LLVM assembly"), cl::cat(SplitCategory)};
+
+void writeStringToFile(StringRef Content, StringRef Path) {
+ std::error_code EC;
+ raw_fd_ostream OS(Path, EC);
+ if (EC) {
+ errs() << formatv("error opening file: {0}, error: {1}\n", Path,
+ EC.message());
+ exit(1);
+ }
+
+ OS << Content << "\n";
+}
+
+void writeModuleToFile(const Module &M, StringRef Path, bool OutputAssembly) {
+ int FD = -1;
+ if (std::error_code EC = sys::fs::openFileForWrite(Path, FD)) {
+ errs() << formatv("error opening file: {0}, error: {1}", Path, EC.message())
+ << '\n';
+ exit(1);
+ }
+
+ raw_fd_ostream OS(FD, /*ShouldClose*/ true);
+ if (OutputAssembly)
+ M.print(OS, /*AssemblyAnnotationWriter*/ nullptr);
+ else
+ WriteBitcodeToFile(M, OS);
+}
+
+/// EntryPointCategorizer is used for splitting by category either by module-id
+/// or by kernels. It doesn't provide categories for functions other than
+/// kernels. Categorizer computes a string key for the given Function and
+/// records the association between the string key and an integer category. If a
+/// string key is already belongs to some category than the corresponding
+/// integer category is returned.
+class EntryPointCategorizer {
+public:
+ EntryPointCategorizer(SplitByCategoryType Type) : Type(Type) {}
+
+ EntryPointCategorizer() = delete;
+ EntryPointCategorizer(EntryPointCategorizer &) = delete;
+ EntryPointCategorizer &operator=(const EntryPointCategorizer &) = delete;
+ EntryPointCategorizer(EntryPointCategorizer &&) = default;
+ EntryPointCategorizer &operator=(EntryPointCategorizer &&) = default;
+
+ /// Returns integer specifying the category for the given \p F.
+ /// If the given function isn't a kernel then returns std::nullopt.
+ std::optional<int> operator()(const Function &F) {
+ if (!isEntryPoint(F))
+ return std::nullopt; // skip the function.
+
+ auto StringKey = computeFunctionCategory(Type, F);
+ if (auto it = StrKeyToID.find(StringRef(StringKey)); it != StrKeyToID.end())
+ return it->second;
+
+ int ID = static_cast<int>(StrKeyToID.size());
+ return StrKeyToID.try_emplace(std::move(StringKey), ID).first->second;
+ }
+
+private:
+ static bool isEntryPoint(const Function &F) {
+ if (F.isDeclaration())
+ return false;
+
+ return F.getCallingConv() == CallingConv::SPIR_KERNEL ||
+ F.getCallingConv() == CallingConv::AMDGPU_KERNEL ||
+ F.getCallingConv() == CallingConv::PTX_Kernel;
+ }
+
+ static SmallString<0> computeFunctionCategory(SplitByCategoryType Type,
+ const Function &F) {
+ static constexpr char ATTR_MODULE_ID[] = "module-id";
+ SmallString<0> Key;
+ switch (Type) {
+ case SplitByCategoryType::SBCT_ByKernel:
+ Key = F.getName().str();
+ break;
+ case SplitByCategoryType::SBCT_ByModuleId:
+ Key = F.getFnAttribute(ATTR_MODULE_ID).getValueAsString().str();
+ break;
+ default:
+ llvm_unreachable("unexpected mode.");
+ }
+
+ return Key;
+ }
+
+private:
+ struct KeyInfo {
+ static SmallString<0> getEmptyKey() { return SmallString<0>(""); }
+
+ static SmallString<0> getTombstoneKey() { return SmallString<0>("-"); }
+
+ static bool isEqual(const SmallString<0> &LHS, const SmallString<0> &RHS) {
+ return LHS == RHS;
+ }
+
+ static unsigned getHashValue(const SmallString<0> &S) {
+ return llvm::hash_value(StringRef(S));
+ }
+ };
+
+ SplitByCategoryType Type;
+ DenseMap<SmallString<0>, int, KeyInfo> StrKeyToID;
+};
+
+void cleanupModule(Module &M) {
+ ModuleAnalysisManager MAM;
+ MAM.registerPass([&] { return PassInstrumentationAnalysis(); });
+ ModulePassManager MPM;
+ MPM.addPass(GlobalDCEPass()); // Delete unreachable globals.
+ MPM.run(M, MAM);
+}
+
+Error runSplitModuleByCategory(std::unique_ptr<Module> M) {
+ size_t OutputID = 0;
+ auto PostSplitCallback = [&](std::unique_ptr<Module> MPart) {
+ if (verifyModule(*MPart)) {
+ errs() << "Broken Module!\n";
+ exit(1);
+ }
+
+ // TODO: DCE is a crucial pass since it removes unused declarations.
+ // At the moment, LIT checking can't be perfomed without DCE.
+ cleanupModule(*MPart);
+ size_t ID = OutputID;
+ ++OutputID;
+ StringRef ModuleSuffix = OutputAssembly ? ".ll" : ".bc";
+ std::string ModulePath =
+ (Twine(OutputFilename) + "_" + Twine(ID) + ModuleSuffix).str();
+ writeModuleToFile(*MPart, ModulePath, OutputAssembly);
+ };
+
+ auto Categorizer = EntryPointCategorizer(SplitByCategory);
+ splitModuleTransitiveFromEntryPoints(std::move(M), Categorizer,
+ PostSplitCallback);
+ return Error::success();
+}
+
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
@@ -123,6 +288,17 @@ int main(int argc, char **argv) {
Out->keep();
};
+ if (SplitByCategory != SplitByCategoryType::SBCT_None) {
+ auto E = runSplitModuleByCategory(std::move(M));
+ if (E) {
+ errs() << E << "\n";
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ return 0;
+ }
+
if (TM) {
if (PreserveLocals) {
errs() << "warning: --preserve-locals has no effect when using "