COFF: Implement parallel LTO code generation.
This is exposed via a new flag /opt:lldltojobs=N, where N is the number of code generation threads. Differential Revision: http://reviews.llvm.org/D12309 llvm-svn: 246342
This commit is contained in:
parent
adbcf12029
commit
df5783b7a5
|
@ -91,6 +91,9 @@ struct Configuration {
|
||||||
// Used for /opt:lldlto=N
|
// Used for /opt:lldlto=N
|
||||||
unsigned LTOOptLevel = 2;
|
unsigned LTOOptLevel = 2;
|
||||||
|
|
||||||
|
// Used for /opt:lldltojobs=N
|
||||||
|
unsigned LTOJobs = 1;
|
||||||
|
|
||||||
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
||||||
std::map<StringRef, StringRef> Merge;
|
std::map<StringRef, StringRef> Merge;
|
||||||
|
|
||||||
|
|
|
@ -372,9 +372,14 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
|
||||||
if (StringRef(S).startswith("lldlto=")) {
|
if (StringRef(S).startswith("lldlto=")) {
|
||||||
StringRef OptLevel = StringRef(S).substr(7);
|
StringRef OptLevel = StringRef(S).substr(7);
|
||||||
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
|
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
|
||||||
Config->LTOOptLevel > 3) {
|
Config->LTOOptLevel > 3)
|
||||||
error("/opt:lldlto: invalid optimization level: " + OptLevel);
|
error("/opt:lldlto: invalid optimization level: " + OptLevel);
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
if (StringRef(S).startswith("lldltojobs=")) {
|
||||||
|
StringRef Jobs = StringRef(S).substr(11);
|
||||||
|
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
|
||||||
|
error("/opt:lldltojobs: invalid job count: " + Jobs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (S != "ref" && S != "icf" && S != "noicf" &&
|
if (S != "ref" && S != "icf" && S != "noicf" &&
|
||||||
|
@ -594,9 +599,9 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
|
||||||
Symtab.run();
|
Symtab.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do LTO by compiling bitcode input files to a native COFF file
|
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||||
// then link that file.
|
// link those files.
|
||||||
Symtab.addCombinedLTOObject();
|
Symtab.addCombinedLTOObjects();
|
||||||
|
|
||||||
// Make sure we have resolved all symbols.
|
// Make sure we have resolved all symbols.
|
||||||
Symtab.reportRemainingUndefines(/*Resolve=*/true);
|
Symtab.reportRemainingUndefines(/*Resolve=*/true);
|
||||||
|
|
|
@ -331,21 +331,7 @@ void SymbolTable::printMap(llvm::raw_ostream &OS) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::addCombinedLTOObject() {
|
void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
|
||||||
if (BitcodeFiles.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Diagnose any undefined symbols early, but do not resolve weak externals,
|
|
||||||
// as resolution breaks the invariant that each Symbol points to a unique
|
|
||||||
// SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
|
|
||||||
reportRemainingUndefines(/*Resolve=*/false);
|
|
||||||
|
|
||||||
// Create an object file and add it to the symbol table by replacing any
|
|
||||||
// DefinedBitcode symbols with the definitions in the object file.
|
|
||||||
LTOCodeGenerator CG;
|
|
||||||
CG.setOptLevel(Config->LTOOptLevel);
|
|
||||||
ObjectFile *Obj = createLTOObject(&CG);
|
|
||||||
|
|
||||||
for (SymbolBody *Body : Obj->getSymbols()) {
|
for (SymbolBody *Body : Obj->getSymbols()) {
|
||||||
if (!Body->isExternal())
|
if (!Body->isExternal())
|
||||||
continue;
|
continue;
|
||||||
|
@ -371,6 +357,25 @@ void SymbolTable::addCombinedLTOObject() {
|
||||||
if (Comp < 0)
|
if (Comp < 0)
|
||||||
Sym->Body = Body;
|
Sym->Body = Body;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolTable::addCombinedLTOObjects() {
|
||||||
|
if (BitcodeFiles.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Diagnose any undefined symbols early, but do not resolve weak externals,
|
||||||
|
// as resolution breaks the invariant that each Symbol points to a unique
|
||||||
|
// SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
|
||||||
|
reportRemainingUndefines(/*Resolve=*/false);
|
||||||
|
|
||||||
|
// Create an object file and add it to the symbol table by replacing any
|
||||||
|
// DefinedBitcode symbols with the definitions in the object file.
|
||||||
|
LTOCodeGenerator CG;
|
||||||
|
CG.setOptLevel(Config->LTOOptLevel);
|
||||||
|
std::vector<ObjectFile *> Objs = createLTOObjects(&CG);
|
||||||
|
|
||||||
|
for (ObjectFile *Obj : Objs)
|
||||||
|
addCombinedLTOObject(Obj);
|
||||||
|
|
||||||
size_t NumBitcodeFiles = BitcodeFiles.size();
|
size_t NumBitcodeFiles = BitcodeFiles.size();
|
||||||
run();
|
run();
|
||||||
|
@ -379,8 +384,8 @@ void SymbolTable::addCombinedLTOObject() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine and compile bitcode files and then return the result
|
// Combine and compile bitcode files and then return the result
|
||||||
// as a regular COFF object file.
|
// as a vector of regular COFF object files.
|
||||||
ObjectFile *SymbolTable::createLTOObject(LTOCodeGenerator *CG) {
|
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
|
||||||
// All symbols referenced by non-bitcode objects must be preserved.
|
// All symbols referenced by non-bitcode objects must be preserved.
|
||||||
for (ObjectFile *File : ObjectFiles)
|
for (ObjectFile *File : ObjectFiles)
|
||||||
for (SymbolBody *Body : File->getSymbols())
|
for (SymbolBody *Body : File->getSymbols())
|
||||||
|
@ -406,14 +411,32 @@ ObjectFile *SymbolTable::createLTOObject(LTOCodeGenerator *CG) {
|
||||||
CG->addModule(BitcodeFiles[I]->getModule());
|
CG->addModule(BitcodeFiles[I]->getModule());
|
||||||
|
|
||||||
std::string ErrMsg;
|
std::string ErrMsg;
|
||||||
LTOMB = CG->compile(false, false, false, ErrMsg); // take MB ownership
|
if (!CG->optimize(false, false, false, ErrMsg))
|
||||||
if (!LTOMB)
|
|
||||||
error(ErrMsg);
|
error(ErrMsg);
|
||||||
auto *Obj = new ObjectFile(LTOMB->getMemBufferRef());
|
|
||||||
Files.emplace_back(Obj);
|
Objs.resize(Config->LTOJobs);
|
||||||
ObjectFiles.push_back(Obj);
|
// Use std::list to avoid invalidation of pointers in OSPtrs.
|
||||||
Obj->parse();
|
std::list<raw_svector_ostream> OSs;
|
||||||
return Obj;
|
std::vector<raw_pwrite_stream *> OSPtrs;
|
||||||
|
for (SmallVector<char, 0> &Obj : Objs) {
|
||||||
|
OSs.emplace_back(Obj);
|
||||||
|
OSPtrs.push_back(&OSs.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CG->compileOptimized(OSPtrs, ErrMsg))
|
||||||
|
error(ErrMsg);
|
||||||
|
|
||||||
|
std::vector<ObjectFile *> ObjFiles;
|
||||||
|
for (SmallVector<char, 0> &Obj : Objs) {
|
||||||
|
auto *ObjFile = new ObjectFile(
|
||||||
|
MemoryBufferRef(StringRef(Obj.data(), Obj.size()), "<LTO object>"));
|
||||||
|
Files.emplace_back(ObjFile);
|
||||||
|
ObjectFiles.push_back(ObjFile);
|
||||||
|
ObjFile->parse();
|
||||||
|
ObjFiles.push_back(ObjFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
|
@ -68,10 +68,10 @@ public:
|
||||||
// Print a layout map to OS.
|
// Print a layout map to OS.
|
||||||
void printMap(llvm::raw_ostream &OS);
|
void printMap(llvm::raw_ostream &OS);
|
||||||
|
|
||||||
// Build a COFF object representing the combined contents of BitcodeFiles
|
// Build a set of COFF objects representing the combined contents of
|
||||||
// and add it to the symbol table. Called after all files are added and
|
// BitcodeFiles and add them to the symbol table. Called after all files are
|
||||||
// before the writer writes results to a file.
|
// added and before the writer writes results to a file.
|
||||||
void addCombinedLTOObject();
|
void addCombinedLTOObjects();
|
||||||
|
|
||||||
// The writer needs to handle DLL import libraries specially in
|
// The writer needs to handle DLL import libraries specially in
|
||||||
// order to create the import descriptor table.
|
// order to create the import descriptor table.
|
||||||
|
@ -98,7 +98,8 @@ private:
|
||||||
StringRef findByPrefix(StringRef Prefix);
|
StringRef findByPrefix(StringRef Prefix);
|
||||||
|
|
||||||
void addMemberFile(Lazy *Body);
|
void addMemberFile(Lazy *Body);
|
||||||
ObjectFile *createLTOObject(llvm::LTOCodeGenerator *CG);
|
void addCombinedLTOObject(ObjectFile *Obj);
|
||||||
|
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
|
||||||
|
|
||||||
llvm::DenseMap<StringRef, Symbol *> Symtab;
|
llvm::DenseMap<StringRef, Symbol *> Symtab;
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ private:
|
||||||
std::vector<InputFile *> ObjectQueue;
|
std::vector<InputFile *> ObjectQueue;
|
||||||
|
|
||||||
std::vector<BitcodeFile *> BitcodeFiles;
|
std::vector<BitcodeFile *> BitcodeFiles;
|
||||||
std::unique_ptr<MemoryBuffer> LTOMB;
|
std::vector<SmallVector<char, 0>> Objs;
|
||||||
llvm::BumpPtrAllocator Alloc;
|
llvm::BumpPtrAllocator Alloc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
; RUN: llvm-as -o %t.obj %s
|
||||||
|
; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltojobs=2 /subsystem:console /lldmap:%t.map %t.obj
|
||||||
|
; RUN: FileCheck %s < %t.map
|
||||||
|
|
||||||
|
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64-pc-windows-msvc"
|
||||||
|
|
||||||
|
; CHECK: <lto object>:
|
||||||
|
; CHECK: foo
|
||||||
|
define void @foo() {
|
||||||
|
call void @bar()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: <lto object>:
|
||||||
|
; CHECK: bar
|
||||||
|
define void @bar() {
|
||||||
|
call void @foo()
|
||||||
|
ret void
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
# RUN: lld-link /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
# RUN: lld-link /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
||||||
# RUN: FileCheck %s < %t2.map
|
# RUN: FileCheck %s < %t2.map
|
||||||
|
|
||||||
# CHECK: lto-llvm{{.*}}:
|
# CHECK: <lto object>:
|
||||||
# CHECK-NOT: :
|
# CHECK-NOT: :
|
||||||
# CHECK: {{ g$}}
|
# CHECK: {{ g$}}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# RUN: lld-link /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
# RUN: lld-link /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
||||||
# RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map
|
# RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map
|
||||||
|
|
||||||
# CHECK1: lto-llvm{{.*}}:
|
# CHECK1: <lto object>:
|
||||||
# CHECK1-NOT: :
|
# CHECK1-NOT: :
|
||||||
# CHECK1: {{ g$}}
|
# CHECK1: {{ g$}}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue