Index: include/llvm/CodeGen/ParallelCG.h =================================================================== --- include/llvm/CodeGen/ParallelCG.h +++ include/llvm/CodeGen/ParallelCG.h @@ -17,6 +17,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/CodeGen.h" #include "llvm/Target/TargetMachine.h" +#include namespace llvm { @@ -30,13 +31,16 @@ /// file that would have been code generated from M. /// /// \returns M if OSs.size() == 1, otherwise returns std::unique_ptr(). -std::unique_ptr -splitCodeGen(std::unique_ptr M, ArrayRef OSs, - StringRef CPU, StringRef Features, const TargetOptions &Options, - Reloc::Model RM = Reloc::Default, - CodeModel::Model CM = CodeModel::Default, - CodeGenOpt::Level OL = CodeGenOpt::Default, - TargetMachine::CodeGenFileType FT = TargetMachine::CGFT_ObjectFile); +std::unique_ptr splitCodeGen( + std::unique_ptr M, ArrayRef OSs, StringRef CPU, + StringRef Features, const TargetOptions &Options, + std::function< + void(std::unique_ptr M, unsigned N, + std::function MPart)> ModuleCallback)> + SplitFunction, + Reloc::Model RM = Reloc::Default, CodeModel::Model CM = CodeModel::Default, + CodeGenOpt::Level OL = CodeGenOpt::Default, + TargetMachine::CodeGenFileType FT = TargetMachine::CGFT_ObjectFile); } // namespace llvm Index: include/llvm/Transforms/Utils/SplitModule.h =================================================================== --- include/llvm/Transforms/Utils/SplitModule.h +++ include/llvm/Transforms/Utils/SplitModule.h @@ -38,6 +38,11 @@ std::unique_ptr M, unsigned N, std::function MPart)> ModuleCallback); +/// Similar to the above, but splitting without globalizing locals. +void SplitModuleSelective( + std::unique_ptr M, unsigned N, + std::function MPart)> ModuleCallback); + } // End llvm namespace #endif Index: lib/CodeGen/ParallelCG.cpp =================================================================== --- lib/CodeGen/ParallelCG.cpp +++ lib/CodeGen/ParallelCG.cpp @@ -39,12 +39,15 @@ CodeGenPasses.run(*M); } -std::unique_ptr -llvm::splitCodeGen(std::unique_ptr M, - ArrayRef OSs, StringRef CPU, - StringRef Features, const TargetOptions &Options, - Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL, - TargetMachine::CodeGenFileType FileType) { +std::unique_ptr llvm::splitCodeGen( + std::unique_ptr M, ArrayRef OSs, + StringRef CPU, StringRef Features, const TargetOptions &Options, + std::function< + void(std::unique_ptr M, unsigned N, + std::function MPart)> ModuleCallback)> + SplitFunction, + Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL, + TargetMachine::CodeGenFileType FileType) { StringRef TripleStr = M->getTargetTriple(); std::string ErrMsg; const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg); @@ -58,7 +61,7 @@ } std::vector Threads; - SplitModule(std::move(M), OSs.size(), [&](std::unique_ptr MPart) { + SplitFunction(std::move(M), OSs.size(), [&](std::unique_ptr 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 (while still on the // main thread, in order to avoid data races) and spinning up new threads Index: lib/LTO/LTOCodeGenerator.cpp =================================================================== --- lib/LTO/LTOCodeGenerator.cpp +++ lib/LTO/LTOCodeGenerator.cpp @@ -53,6 +53,7 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/ObjCARC.h" +#include "llvm/Transforms/Utils/SplitModule.h" #include using namespace llvm; @@ -511,7 +512,8 @@ // MergedModule. MergedModule = splitCodeGen(std::move(MergedModule), Out, MCpu, FeatureStr, Options, - RelocModel, CodeModel::Default, CGOptLevel, FileType); + SplitModule, RelocModel, CodeModel::Default, CGOptLevel, + FileType); return true; } Index: lib/Transforms/Utils/SplitModule.cpp =================================================================== --- lib/Transforms/Utils/SplitModule.cpp +++ lib/Transforms/Utils/SplitModule.cpp @@ -13,19 +13,218 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "split-module" + #include "llvm/Transforms/Utils/SplitModule.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalObject.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/MD5.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Cloning.h" +#include +#include +#include using namespace llvm; +namespace { +typedef SetVector GVSetType; +typedef MapVector> + ClusterMapType; +typedef std::unordered_multimap + ComdatMembersType; +typedef DenseMap ClusterIDMapType; +} + +static void addGlobalValueUser(ClusterMapType &GVtoClusterMap, + const GlobalValue *GV, + const GlobalValue *UserGV) { + assert(GV->hasLocalLinkage() && "Only process local GVs here"); + + // This entry does not have a map yet... + if (!GVtoClusterMap.count(GV)) { + if (GVtoClusterMap.count(UserGV)) { + assert(!GVtoClusterMap[UserGV]->empty() && + "There should not be an empty set here"); + // But it's use already does, and it is not empty and is possibly shared + // by multiple GVs. + GVtoClusterMap[GV] = GVtoClusterMap[UserGV]; + // Now add GV itself to the user set. + GVtoClusterMap[UserGV]->insert(GV); + } else { + // Create brand new mapping. + GVtoClusterMap[GV] = std::make_shared(); + GVtoClusterMap[GV]->insert(UserGV); + // Add the key to its own set. + GVtoClusterMap[GV]->insert(GV); + GVtoClusterMap[UserGV] = GVtoClusterMap[GV]; + } + } else { + // This entry already exist, and it needs to be updated. + GVtoClusterMap[GV]->insert(UserGV); + if (GVtoClusterMap.count(UserGV)) { + // These two sets need to be merged. + if (GVtoClusterMap[UserGV] != GVtoClusterMap[GV]) { + GVtoClusterMap[GV]->insert(GVtoClusterMap[UserGV]->begin(), + GVtoClusterMap[UserGV]->end()); + std::shared_ptr OldSet = GVtoClusterMap[UserGV]; + + for (const GlobalValue *Entry : *OldSet) + if (GVtoClusterMap.count(Entry)) + GVtoClusterMap[Entry] = GVtoClusterMap[GV]; + } + } else { + GVtoClusterMap[UserGV] = GVtoClusterMap[GV]; + } + } +} + +static void addNonConstUser(ClusterMapType &GVtoClusterMap, + const GlobalValue *GV, const User *U) { + assert((!isa(U) || isa(U)) && "Bad user"); + + if (const Instruction *I = dyn_cast(U)) { + const GlobalValue *F = I->getParent()->getParent(); + addGlobalValueUser(GVtoClusterMap, GV, F); + } else if (isa(U) || isa(U) || + isa(U)) { + addGlobalValueUser(GVtoClusterMap, GV, cast(U)); + } else { + llvm_unreachable("Underimplemented use case"); + } +} + +static void processComdat(ClusterMapType &GVtoClusterMap, + ComdatMembersType &ComdatMembers, + const GlobalValue *GV, const Comdat *C) { + // If this variable shares comdat marker with some other function, it is the + // same as having a "use" for our purposes. + // Note that it includes both external and local objects. + ComdatMembers.insert(std::make_pair(C, GV)); + + for (auto &&CM : make_range(ComdatMembers.equal_range(C))) { + if (CM.second == GV) + continue; + DEBUG(dbgs() << "In the same comdat: (" << GV->getName() << ") and (" + << CM.second->getName() << ")\n"); + if (GV->hasLocalLinkage()) { + addGlobalValueUser(GVtoClusterMap, GV, CM.second); + } else if (CM.second->hasLocalLinkage()) { + addGlobalValueUser(GVtoClusterMap, CM.second, GV); + } + } +} + +// Find partitions for module in the way that no locals need to be +// globalized. +// Try to balance pack those partitions into N files since this roughly equals +// thread balancing for the backend codegen step. +static void findPartitions(Module *M, ClusterIDMapType &ClusterIDMap, + unsigned N) { + // At this point module should have the proper mix of globals and locals. + // As we attempt to partition this module, we must not change any + // locals to globals. + + DEBUG(dbgs() << "Partition module with (" << M->size() << ")functions\n"); + + ClusterMapType GVtoClusterMap; + ComdatMembersType ComdatMembers; + + auto recordGVSet = [&GVtoClusterMap, &ComdatMembers](GlobalValue &GV) { + if (GV.isDeclaration()) + return; + + if (!GV.hasName()) + GV.setName("__llvmsplit_unnamed"); + + // Comdat groups must not be partitioned. For comdat groups that contain + // locals, record all their members here so we can keep them together. + // Comdat groups that only contain external globals are already handled by + // the MD5-based partitioning. + if (const Comdat *C = GV.getComdat()) + processComdat(GVtoClusterMap, ComdatMembers, &GV, C); + + // Further only iterate over local GVs. + if (!GV.hasLocalLinkage()) + return; + + for (auto *U : GV.users()) { + // For each constant that is not a GV (a pure const): + if (isa(U) && !isa(U)) { + SmallVector Worklist; + Worklist.push_back(U); + while (!Worklist.empty()) { + const User *UU = Worklist.pop_back_val(); + if (isa(UU) && !isa(UU)) { + Worklist.append(UU->user_begin(), UU->user_end()); + continue; + } + addNonConstUser(GVtoClusterMap, &GV, UU); + } + } else { + // User is an instruction, alias or GlobalValue. + addNonConstUser(GVtoClusterMap, &GV, U); + } + } + }; + + std::for_each(M->begin(), M->end(), recordGVSet); + std::for_each(M->global_begin(), M->global_end(), recordGVSet); + std::for_each(M->alias_begin(), M->alias_end(), recordGVSet); + DEBUG(dbgs() << "Recorded " << GVtoClusterMap.size() << " local GVs.\n"); + + // Assigned all GVs to merged clusters while balancing number of objects in + // each. + auto CompareClusters = [](const std::pair &a, + const std::pair &b) { + if (a.second || b.second) + return a.second > b.second; + else + return a.first > b.first; + }; + + std::priority_queue, + std::vector>, + decltype(CompareClusters)> + BalancinQueue(CompareClusters); + // Pre-populate priority queue with N slot blanks. + for (unsigned i = 0; i < N; ++i) + BalancinQueue.push(std::make_pair(i, 0)); + + SmallPtrSet Visited; + for (auto &I : GVtoClusterMap) { + if (!Visited.insert(I.first).second) + continue; + // The top() always returns cluster ID with fewest number of entries. + unsigned CurrentClusterID = BalancinQueue.top().first; + unsigned CurrentClusterSize = BalancinQueue.top().second; + BalancinQueue.pop(); + + DEBUG(dbgs() << "Root[" << CurrentClusterID << "] size(" << I.second->size() + << ") cluster_size(" << CurrentClusterSize << ") ----> " + << I.first->getName() << "\n"); + + assert(I.second && I.second->count(I.first) && "Missing root in the set"); + for (auto &II : *I.second) { + DEBUG(dbgs() << "----> " << II->getName() + << (II->hasLocalLinkage() ? " l " : " e ") << "\n"); + Visited.insert(II); + ClusterIDMap[II] = CurrentClusterID; + } + // Add this set size to the number of entries in this cluster. + BalancinQueue.push(std::make_pair(CurrentClusterID, + CurrentClusterSize + I.second->size())); + } +} + static void externalize(GlobalValue *GV) { if (GV->hasLocalLinkage()) { GV->setLinkage(GlobalValue::ExternalLinkage); @@ -83,3 +282,26 @@ ModuleCallback(std::move(MPart)); } } + +// This performs splitting without a need for externalization, which might not +// always be possible. +void llvm::SplitModuleSelective( + std::unique_ptr M, unsigned N, + std::function MPart)> ModuleCallback) { + ClusterIDMapType ClusterIDMap; + findPartitions(M.get(), ClusterIDMap, N); + + for (unsigned I = 0; I < N; ++I) { + ValueToValueMapTy VMap; + std::unique_ptr MPart( + CloneModule(M.get(), VMap, [&](const GlobalValue *GV) { + if (ClusterIDMap.count(GV)) + return (ClusterIDMap[GV] == I); + else + return isInPartition(GV, I, N); + })); + if (I != 0) + MPart->setModuleInlineAsm(""); + ModuleCallback(std::move(MPart)); + } +} Index: test/tools/llvm-split/preserve-locals.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/preserve-locals.ll @@ -0,0 +1,65 @@ +; RUN: llvm-split -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK0 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK1 %s + +; The local_var and local_func must not be separated. +; CHECK0: @local_var +; CHECK0: define internal fastcc void @local_func +; The main and a must not be separated. +; The main and local_func must not be together. +; CHECK1: @a +; CHECK1: define i32 @main +; CHECK1: declare fastcc void @local_func + +@a = internal global i32 0, align 4 +@global_storage = common global i32 0, align 4 +@local_var = internal global i32 0, align 4 + +; Function Attrs: nounwind +define i32 @main(i32 %x) { +entry: + %call = call fastcc i32 @foo(i32 %x, i32* nonnull @a) + %call1 = call fastcc i32 @baz(i32 %x) + %add = add nsw i32 %call, %call1 + ret i32 %add +} + +; Function Attrs: nounwind +define fastcc i32 @bar(i32 %b) { +entry: + %call = call fastcc i32 @baz(i32 %b) + ret i32 %call +} + +; Function Attrs: nounwind +define fastcc i32 @baz(i32 %x) { +entry: + store i32 %x, i32* @global_storage, align 4 + %shl = shl i32 %x, %x + ret i32 %shl +} + +; Function Attrs: noinline nounwind +define fastcc i32 @foo(i32 %a, i32* nocapture %b) { +entry: + call fastcc void @local_func() + %call = call fastcc i32 @bar(i32 %a) + %0 = load i32, i32* @global_storage, align 4 + %call1 = call fastcc i32 @baz(i32 %0) + %add = add nsw i32 %call, %call1 + store i32 %add, i32* %b, align 4 + %call.i = call fastcc i32 @baz(i32 %add) #2 + %add.i = add nsw i32 %call.i, 2 + %1 = load volatile i32, i32* @local_var, align 4 + %add3 = add nsw i32 %add.i, %1 + ret i32 %add3 +} + +; Function Attrs: noinline nounwind +define internal fastcc void @local_func() section ".text" { +entry: + %0 = load i32, i32* @global_storage, align 4 + %call = call fastcc i32 @foo(i32 %0, i32* null) + store volatile i32 %call, i32* @local_var, align 4 + ret void +} Index: test/tools/llvm-split/scc-alias.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-alias.ll @@ -0,0 +1,48 @@ +; All of the functions in this module must end up +; in the same partition without change of scope. +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare i32 @funInternal +; CHECK0: declare i32 @funExternal +; CHECK0: declare i32 @funInternal2 +; CHECK0: declare i32 @funExternal2 + +; All functions are in the same file. +; Local functions are still local. +; CHECK1: define internal i32 @funInternal +; CHECK1: define i32 @funExternal +; CHECK1: define internal i32 @funInternal2 +; CHECK1: define i32 @funExternal2 + + +@funInternalAlias = internal alias i32 (), i32 ()* @funInternal + +define internal i32 @funInternal() { +entry: + ret i32 0 +} + +; Direct call to local alias + +define i32 @funExternal() { +entry: + %x = call i32 @funInternalAlias() + ret i32 %x +} + +; Call to local function that calls local alias + +define internal i32 @funInternal2() { +entry: + %x = call i32 @funInternalAlias() + ret i32 %x +} + +define i32 @funExternal2() { +entry: + %x = call i32 @funInternal2() + ret i32 %x +} + Index: test/tools/llvm-split/scc-callchain.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-callchain.ll @@ -0,0 +1,48 @@ +; All of the functions in this module must end up +; in the same partition. + +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare i32 @funInternal0 +; CHECK0: declare i32 @funInternal1 +; CHECK0: declare i32 @funInternal2 +; CHECK0: declare i32 @funExternal + +; All functions are in the same file. +; Local functions are still local. +; CHECK1: define internal i32 @funInternal0 +; CHECK1: define internal i32 @funInternal1 +; CHECK1: define internal i32 @funInternal2 +; CHECK1: define i32 @funExternal +; CHECK1: define i32 @funExternal2 + +define internal i32 @funInternal0() { +entry: + ret i32 0 +} + +define internal i32 @funInternal1() { +entry: + %x = call i32 @funInternal0() + ret i32 %x +} + +define internal i32 @funInternal2() { +entry: + %x = call i32 @funInternal1() + ret i32 %x +} + +define i32 @funExternal() { +entry: + %x = call i32 @funInternal2() + ret i32 %x +} + +define i32 @funExternal2() { +entry: + %x = call i32 @funInternal0() + ret i32 %x +} Index: test/tools/llvm-split/scc-comdat.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-comdat.ll @@ -0,0 +1,32 @@ +; All functions in the same comdat group must +; be in the same module + +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare i32 @fun1 +; CHECK0: declare i32 @fun2 +; CHECK0: declare i32 @fun3 + +; CHECK1: define internal i32 @fun1 +; CHECK1: define internal i32 @fun2 +; CHECK1: define i32 @fun3 + +$fun = comdat any + +define internal i32 @fun1() section ".text.funs" comdat($fun) { +entry: + ret i32 0 +} + +define internal i32 @fun2() section ".text.funs" comdat($fun) { +entry: + ret i32 0 +} + +define i32 @fun3() section ".text.funs" comdat($fun) { +entry: + ret i32 0 +} + Index: test/tools/llvm-split/scc-constants.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-constants.ll @@ -0,0 +1,48 @@ +; All of the functions in this module must end up +; in the same partition. + +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare i32 @foo +; CHECK0: declare i32 @baz +; CHECK0: declare i32 @bar +; CHECK0: declare i32 @bar2 + +; CHECK1: @bla +; CHECK1: @bla2 +; CHECK1: define internal i32 @foo +; CHECK1: define internal i32 @baz +; CHECK1: define i32 @bar +; CHECK1: define i32 @bar2 + +%struct.anon = type { i64, i64 } + +@bla = internal global %struct.anon { i64 1, i64 2 }, align 8 +@bla2 = internal global %struct.anon { i64 1, i64 2 }, align 8 + +define internal i32 @foo() { +entry: + store i64 5, i64* getelementptr inbounds (%struct.anon, %struct.anon* @bla, i32 0, i32 0), align 8 + store i32 -1, i32* bitcast (i64* getelementptr inbounds (%struct.anon, %struct.anon* @bla2, i32 0, i32 1) to i32*), align 8 + ret i32 0 +} + +define internal i32 @baz() { +entry: + store i64 5, i64* getelementptr inbounds (%struct.anon, %struct.anon* @bla, i32 0, i32 0), align 8 + store i32 -1, i32* bitcast (i64* getelementptr inbounds (%struct.anon, %struct.anon* @bla2, i32 0, i32 1) to i32*), align 8 + ret i32 0 +} + +define i32 @bar() { + %call = call i32 @foo() + ret i32 0 +} + +define i32 @bar2() { + %call = call i32 @baz() + ret i32 0 +} + Index: test/tools/llvm-split/scc-cycle.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-cycle.ll @@ -0,0 +1,44 @@ +; All of the functions in this module must end up +; in the same partition. + +; Mutually recursive calls +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare i32 @funInternal0 +; CHECK0: declare i32 @funInternal1 +; CHECK0: declare i32 @funExternal0 +; CHECK0: declare i32 @funExternal1 + +; CHECK1: define internal i32 @funInternal0 +; CHECK1: define internal i32 @funInternal1 +; CHECK1: define i32 @funExternal0 +; CHECK1: define i32 @funExternal1 + +define internal i32 @funInternal0() { +entry: + %x = call i32 @funInternal1() + ret i32 %x +} + +define internal i32 @funInternal1() { +entry: + %x = call i32 @funInternal0() + ret i32 %x +} + +; Extrnal functions + +define i32 @funExternal0() { +entry: + %x = call i32 @funInternal0() + ret i32 %x +} + +define i32 @funExternal1() { +entry: + %x = call i32 @funInternal1() + ret i32 %x +} + Index: test/tools/llvm-split/scc-global2global.ll =================================================================== --- /dev/null +++ test/tools/llvm-split/scc-global2global.ll @@ -0,0 +1,28 @@ +; All of the functions and globals in this module must end up +; in the same partition. + +; RUN: llvm-split -j=2 -preserve-locals -o %t %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK1 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK0 %s + +; CHECK0: declare %struct.anon* @local0 +; CHECK0: declare i8** @local1 + +; CHECK1: @bla +; CHECK1: @ptr +; CHECK1: define internal %struct.anon* @local0 +; CHECK1: define internal i8** @local1 + +%struct.anon = type { i64, i64 } + +@bla = internal global %struct.anon { i64 1, i64 2 }, align 8 +@ptr = internal global i8* bitcast (%struct.anon* @bla to i8*), align 4 + +define internal %struct.anon* @local0() { + ret %struct.anon* @bla +} + +define internal i8** @local1() { + ret i8** @ptr +} + Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -43,6 +43,7 @@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/GlobalStatus.h" #include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/SplitModule.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include #include @@ -837,7 +838,8 @@ // Run backend threads. splitCodeGen(std::move(M), OSPtrs, options::mcpu, Features.getString(), - Options, RelocationModel, CodeModel::Default, CGOptLevel); + Options, SplitModule, RelocationModel, CodeModel::Default, + CGOptLevel); } for (auto &Filename : Filenames) { Index: tools/llvm-split/llvm-split.cpp =================================================================== --- tools/llvm-split/llvm-split.cpp +++ tools/llvm-split/llvm-split.cpp @@ -35,6 +35,10 @@ static cl::opt NumOutputs("j", cl::Prefix, cl::init(2), cl::desc("Number of output files")); +static cl::opt + PreserveLocals("preserve-locals", cl::Prefix, cl::init(false), + cl::desc("Split without externalizing locals")); + int main(int argc, char **argv) { LLVMContext &Context = getGlobalContext(); SMDiagnostic Err; @@ -48,20 +52,38 @@ } unsigned I = 0; - SplitModule(std::move(M), NumOutputs, [&](std::unique_ptr MPart) { - std::error_code EC; - std::unique_ptr Out(new tool_output_file( - OutputFilename + utostr(I++), EC, sys::fs::F_None)); - if (EC) { - errs() << EC.message() << '\n'; - exit(1); - } - - WriteBitcodeToFile(MPart.get(), Out->os()); - - // Declare success. - Out->keep(); - }); + if (PreserveLocals) { + SplitModuleSelective( + std::move(M), NumOutputs, [&](std::unique_ptr MPart) { + std::error_code EC; + std::unique_ptr Out(new tool_output_file( + OutputFilename + utostr(I++), EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; + exit(1); + } + + WriteBitcodeToFile(MPart.get(), Out->os()); + + Out->keep(); + }); + } else { + SplitModule(std::move(M), NumOutputs, [&](std::unique_ptr MPart) { + std::error_code EC; + std::unique_ptr Out(new tool_output_file( + OutputFilename + utostr(I++), EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; + exit(1); + } + + WriteBitcodeToFile(MPart.get(), Out->os()); + + // Declare success. + Out->keep(); + }); + } + return 0; }