2016-03-09 09:37:22 +08:00
|
|
|
//===-ThinLTOCodeGenerator.cpp - LLVM Link Time Optimizer -----------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file implements the Thin Link Time Optimization library. This library is
|
|
|
|
// intended to be used by linker to optimize code at link time.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2016-07-15 05:21:16 +08:00
|
|
|
#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
|
2016-03-15 05:18:10 +08:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
2016-03-15 08:04:37 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2016-04-11 21:58:45 +08:00
|
|
|
#include "llvm/Analysis/ModuleSummaryAnalysis.h"
|
[thinlto] Basic thinlto fdo heuristic
Summary:
This patch improves thinlto importer
by importing 3x larger functions that are called from hot block.
I compared performance with the trunk on spec, and there
were about 2% on povray and 3.33% on milc. These results seems
to be consistant and match the results Teresa got with her simple
heuristic. Some benchmarks got slower but I think they are just
noisy (mcf, xalancbmki, omnetpp)- running the benchmarks again with
more iterations to confirm. Geomean of all benchmarks including the noisy ones
were about +0.02%.
I see much better improvement on google branch with Easwaran patch
for pgo callsite inlining (the inliner actually inline those big functions)
Over all I see +0.5% improvement, and I get +8.65% on povray.
So I guess we will see much bigger change when Easwaran patch will land
(it depends on new pass manager), but it is still worth putting this to trunk
before it.
Implementation details changes:
- Removed CallsiteCount.
- ProfileCount got replaced by Hotness
- hot-import-multiplier is set to 3.0 for now,
didn't have time to tune it up, but I see that we get most of the interesting
functions with 3, so there is no much performance difference with higher, and
binary size doesn't grow as much as with 10.0.
Reviewers: eraman, mehdi_amini, tejohnson
Subscribers: mehdi_amini, llvm-commits
Differential Revision: https://reviews.llvm.org/D24638
llvm-svn: 282437
2016-09-27 04:37:32 +08:00
|
|
|
#include "llvm/Analysis/ProfileSummaryInfo.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
2016-11-11 13:34:58 +08:00
|
|
|
#include "llvm/Bitcode/BitcodeReader.h"
|
|
|
|
#include "llvm/Bitcode/BitcodeWriter.h"
|
2016-03-15 05:18:10 +08:00
|
|
|
#include "llvm/Bitcode/BitcodeWriterPass.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/ExecutionEngine/ObjectMemoryBuffer.h"
|
2017-05-20 08:00:08 +08:00
|
|
|
#include "llvm/IR/DebugInfo.h"
|
2017-06-06 19:49:48 +08:00
|
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
2016-03-15 08:04:37 +08:00
|
|
|
#include "llvm/IR/LLVMContext.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
|
|
#include "llvm/IR/Mangler.h"
|
2017-05-20 08:00:08 +08:00
|
|
|
#include "llvm/IR/Verifier.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/IRReader/IRReader.h"
|
2016-05-24 06:54:06 +08:00
|
|
|
#include "llvm/LTO/LTO.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/MC/SubtargetFeature.h"
|
2016-04-24 11:18:01 +08:00
|
|
|
#include "llvm/Object/IRObjectFile.h"
|
2016-04-21 13:54:23 +08:00
|
|
|
#include "llvm/Support/CachePruning.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
2016-11-20 02:20:05 +08:00
|
|
|
#include "llvm/Support/Error.h"
|
2016-04-21 13:54:23 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "llvm/Support/SHA1.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
|
|
#include "llvm/Support/ThreadPool.h"
|
2016-10-20 01:35:01 +08:00
|
|
|
#include "llvm/Support/Threading.h"
|
2016-11-20 02:20:05 +08:00
|
|
|
#include "llvm/Support/ToolOutputFile.h"
|
2017-04-13 09:26:12 +08:00
|
|
|
#include "llvm/Support/VCSRevision.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
#include "llvm/Transforms/IPO.h"
|
|
|
|
#include "llvm/Transforms/IPO/FunctionImport.h"
|
2016-04-24 11:18:01 +08:00
|
|
|
#include "llvm/Transforms/IPO/Internalize.h"
|
2016-03-09 09:37:22 +08:00
|
|
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
|
|
|
#include "llvm/Transforms/ObjCARC.h"
|
|
|
|
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
|
|
|
|
|
2016-05-17 03:33:07 +08:00
|
|
|
#include <numeric>
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
2016-04-16 15:02:16 +08:00
|
|
|
#define DEBUG_TYPE "thinlto"
|
|
|
|
|
Add a flag to the LLVMContext to disable name for Value other than GlobalValue
Summary:
This is intended to be a performance flag, on the same level as clang
cc1 option "--disable-free". LLVM will never initialize it by default,
it will be up to the client creating the LLVMContext to request this
behavior. Clang will do it by default in Release build (just like
--disable-free).
"opt" and "llc" can opt-in using -disable-named-value command line
option.
When performing LTO on llvm-tblgen, the initial merging of IR peaks
at 92MB without this patch, and 86MB after this patch,setNameImpl()
drops from 6.5MB to 0.5MB.
The total link time goes from ~29.5s to ~27.8s.
Compared to a compile-time flag (like the IRBuilder one), it performs
very close. I profiled on SROA and obtain these results:
420ms with IRBuilder that preserve name
372ms with IRBuilder that strip name
375ms with IRBuilder that preserve name, and a runtime flag to strip
Reviewers: chandlerc, dexonsmith, bogner
Subscribers: joker.eph, llvm-commits
Differential Revision: http://reviews.llvm.org/D17946
From: Mehdi Amini <mehdi.amini@apple.com>
llvm-svn: 263086
2016-03-10 09:28:54 +08:00
|
|
|
namespace llvm {
|
|
|
|
// Flags -discard-value-names, defined in LTOCodeGenerator.cpp
|
|
|
|
extern cl::opt<bool> LTODiscardValueNames;
|
2016-11-20 02:20:05 +08:00
|
|
|
extern cl::opt<std::string> LTORemarksFilename;
|
2016-12-03 01:53:56 +08:00
|
|
|
extern cl::opt<bool> LTOPassRemarksWithHotness;
|
2017-05-20 08:00:08 +08:00
|
|
|
extern cl::opt<bool> LTOStripInvalidDebugInfo;
|
Add a flag to the LLVMContext to disable name for Value other than GlobalValue
Summary:
This is intended to be a performance flag, on the same level as clang
cc1 option "--disable-free". LLVM will never initialize it by default,
it will be up to the client creating the LLVMContext to request this
behavior. Clang will do it by default in Release build (just like
--disable-free).
"opt" and "llc" can opt-in using -disable-named-value command line
option.
When performing LTO on llvm-tblgen, the initial merging of IR peaks
at 92MB without this patch, and 86MB after this patch,setNameImpl()
drops from 6.5MB to 0.5MB.
The total link time goes from ~29.5s to ~27.8s.
Compared to a compile-time flag (like the IRBuilder one), it performs
very close. I profiled on SROA and obtain these results:
420ms with IRBuilder that preserve name
372ms with IRBuilder that strip name
375ms with IRBuilder that preserve name, and a runtime flag to strip
Reviewers: chandlerc, dexonsmith, bogner
Subscribers: joker.eph, llvm-commits
Differential Revision: http://reviews.llvm.org/D17946
From: Mehdi Amini <mehdi.amini@apple.com>
llvm-svn: 263086
2016-03-10 09:28:54 +08:00
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
namespace {
|
|
|
|
|
2016-10-20 01:35:01 +08:00
|
|
|
static cl::opt<int>
|
|
|
|
ThreadCount("threads", cl::init(llvm::heavyweight_hardware_concurrency()));
|
2016-03-09 09:37:22 +08:00
|
|
|
|
|
|
|
// Simple helper to save temporary files for debug.
|
|
|
|
static void saveTempBitcode(const Module &TheModule, StringRef TempDir,
|
|
|
|
unsigned count, StringRef Suffix) {
|
|
|
|
if (TempDir.empty())
|
|
|
|
return;
|
|
|
|
// User asked to save temps, let dump the bitcode file after import.
|
2016-08-16 07:24:57 +08:00
|
|
|
std::string SaveTempPath = (TempDir + llvm::utostr(count) + Suffix).str();
|
2016-03-09 09:37:22 +08:00
|
|
|
std::error_code EC;
|
2016-08-16 07:24:57 +08:00
|
|
|
raw_fd_ostream OS(SaveTempPath, EC, sys::fs::F_None);
|
2016-03-09 09:37:22 +08:00
|
|
|
if (EC)
|
|
|
|
report_fatal_error(Twine("Failed to open ") + SaveTempPath +
|
|
|
|
" to save optimized bitcode\n");
|
2016-04-05 05:19:31 +08:00
|
|
|
WriteBitcodeToFile(&TheModule, OS, /* ShouldPreserveUseListOrder */ true);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
static const GlobalValueSummary *
|
|
|
|
getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) {
|
|
|
|
// If there is any strong definition anywhere, get it.
|
|
|
|
auto StrongDefForLinker = llvm::find_if(
|
|
|
|
GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
|
|
|
|
auto Linkage = Summary->linkage();
|
|
|
|
return !GlobalValue::isAvailableExternallyLinkage(Linkage) &&
|
|
|
|
!GlobalValue::isWeakForLinker(Linkage);
|
|
|
|
});
|
|
|
|
if (StrongDefForLinker != GVSummaryList.end())
|
|
|
|
return StrongDefForLinker->get();
|
2016-04-02 05:53:50 +08:00
|
|
|
// Get the first *linker visible* definition for this global in the summary
|
|
|
|
// list.
|
|
|
|
auto FirstDefForLinker = llvm::find_if(
|
2016-04-24 22:57:11 +08:00
|
|
|
GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
|
|
|
|
auto Linkage = Summary->linkage();
|
2016-04-02 05:53:50 +08:00
|
|
|
return !GlobalValue::isAvailableExternallyLinkage(Linkage);
|
|
|
|
});
|
2016-05-25 01:24:25 +08:00
|
|
|
// Extern templates can be emitted as available_externally.
|
|
|
|
if (FirstDefForLinker == GVSummaryList.end())
|
|
|
|
return nullptr;
|
|
|
|
return FirstDefForLinker->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate map of GUID to the prevailing copy for any multiply defined
|
|
|
|
// symbols. Currently assume first copy is prevailing, or any strong
|
|
|
|
// definition. Can be refined with Linker information in the future.
|
|
|
|
static void computePrevailingCopies(
|
|
|
|
const ModuleSummaryIndex &Index,
|
|
|
|
DenseMap<GlobalValue::GUID, const GlobalValueSummary *> &PrevailingCopy) {
|
2016-04-24 22:57:11 +08:00
|
|
|
auto HasMultipleCopies = [&](const GlobalValueSummaryList &GVSummaryList) {
|
|
|
|
return GVSummaryList.size() > 1;
|
|
|
|
};
|
2016-04-02 05:53:50 +08:00
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
for (auto &I : Index) {
|
2017-05-05 02:03:25 +08:00
|
|
|
if (HasMultipleCopies(I.second.SummaryList))
|
|
|
|
PrevailingCopy[I.first] =
|
|
|
|
getFirstDefinitionForLinker(I.second.SummaryList);
|
2016-04-02 05:53:50 +08:00
|
|
|
}
|
2016-05-25 01:24:25 +08:00
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
static StringMap<MemoryBufferRef>
|
2017-02-14 10:20:51 +08:00
|
|
|
generateModuleMap(const std::vector<ThinLTOBuffer> &Modules) {
|
2016-03-09 09:37:22 +08:00
|
|
|
StringMap<MemoryBufferRef> ModuleMap;
|
|
|
|
for (auto &ModuleBuffer : Modules) {
|
|
|
|
assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) ==
|
|
|
|
ModuleMap.end() &&
|
|
|
|
"Expect unique Buffer Identifier");
|
2017-02-14 10:20:51 +08:00
|
|
|
ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer.getMemBuffer();
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
return ModuleMap;
|
|
|
|
}
|
|
|
|
|
2016-03-15 08:04:37 +08:00
|
|
|
static void promoteModule(Module &TheModule, const ModuleSummaryIndex &Index) {
|
2016-03-09 09:37:22 +08:00
|
|
|
if (renameModuleForThinLTO(TheModule, Index))
|
|
|
|
report_fatal_error("renameModuleForThinLTO failed");
|
|
|
|
}
|
|
|
|
|
2017-05-20 08:00:08 +08:00
|
|
|
namespace {
|
|
|
|
class ThinLTODiagnosticInfo : public DiagnosticInfo {
|
|
|
|
const Twine &Msg;
|
|
|
|
public:
|
|
|
|
ThinLTODiagnosticInfo(const Twine &DiagMsg,
|
|
|
|
DiagnosticSeverity Severity = DS_Error)
|
|
|
|
: DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {}
|
|
|
|
void print(DiagnosticPrinter &DP) const override { DP << Msg; }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verify the module and strip broken debug info.
|
|
|
|
static void verifyLoadedModule(Module &TheModule) {
|
|
|
|
bool BrokenDebugInfo = false;
|
|
|
|
if (verifyModule(TheModule, &dbgs(),
|
|
|
|
LTOStripInvalidDebugInfo ? &BrokenDebugInfo : nullptr))
|
|
|
|
report_fatal_error("Broken module found, compilation aborted!");
|
|
|
|
if (BrokenDebugInfo) {
|
|
|
|
TheModule.getContext().diagnose(ThinLTODiagnosticInfo(
|
|
|
|
"Invalid debug info found, debug info will be stripped", DS_Warning));
|
|
|
|
StripDebugInfo(TheModule);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 13:52:32 +08:00
|
|
|
static std::unique_ptr<Module>
|
|
|
|
loadModuleFromBuffer(const MemoryBufferRef &Buffer, LLVMContext &Context,
|
2016-12-17 05:25:01 +08:00
|
|
|
bool Lazy, bool IsImporting) {
|
2016-12-01 13:52:32 +08:00
|
|
|
SMDiagnostic Err;
|
|
|
|
Expected<std::unique_ptr<Module>> ModuleOrErr =
|
2016-12-17 05:25:01 +08:00
|
|
|
Lazy
|
|
|
|
? getLazyBitcodeModule(Buffer, Context,
|
|
|
|
/* ShouldLazyLoadMetadata */ true, IsImporting)
|
|
|
|
: parseBitcodeFile(Buffer, Context);
|
2016-12-01 13:52:32 +08:00
|
|
|
if (!ModuleOrErr) {
|
|
|
|
handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
|
|
|
|
SMDiagnostic Err = SMDiagnostic(Buffer.getBufferIdentifier(),
|
|
|
|
SourceMgr::DK_Error, EIB.message());
|
|
|
|
Err.print("ThinLTO", errs());
|
|
|
|
});
|
|
|
|
report_fatal_error("Can't load module, abort.");
|
|
|
|
}
|
2017-05-20 08:00:08 +08:00
|
|
|
if (!Lazy)
|
|
|
|
verifyLoadedModule(*ModuleOrErr.get());
|
2016-12-01 13:52:32 +08:00
|
|
|
return std::move(ModuleOrErr.get());
|
|
|
|
}
|
|
|
|
|
2016-03-26 13:40:34 +08:00
|
|
|
static void
|
|
|
|
crossImportIntoModule(Module &TheModule, const ModuleSummaryIndex &Index,
|
|
|
|
StringMap<MemoryBufferRef> &ModuleMap,
|
|
|
|
const FunctionImporter::ImportMapTy &ImportList) {
|
2016-12-01 13:52:32 +08:00
|
|
|
auto Loader = [&](StringRef Identifier) {
|
|
|
|
return loadModuleFromBuffer(ModuleMap[Identifier], TheModule.getContext(),
|
2016-12-17 05:25:01 +08:00
|
|
|
/*Lazy=*/true, /*IsImporting*/ true);
|
2016-12-01 13:52:32 +08:00
|
|
|
};
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
FunctionImporter Importer(Index, Loader);
|
2017-05-20 07:32:21 +08:00
|
|
|
Expected<bool> Result = Importer.importFunctions(TheModule, ImportList);
|
2017-01-08 08:30:27 +08:00
|
|
|
if (!Result) {
|
|
|
|
handleAllErrors(Result.takeError(), [&](ErrorInfoBase &EIB) {
|
|
|
|
SMDiagnostic Err = SMDiagnostic(TheModule.getModuleIdentifier(),
|
|
|
|
SourceMgr::DK_Error, EIB.message());
|
|
|
|
Err.print("ThinLTO", errs());
|
|
|
|
});
|
2016-11-10 01:49:19 +08:00
|
|
|
report_fatal_error("importFunctions failed");
|
2017-01-08 08:30:27 +08:00
|
|
|
}
|
2017-05-20 08:00:08 +08:00
|
|
|
// Verify again after cross-importing.
|
|
|
|
verifyLoadedModule(TheModule);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
2016-12-29 03:37:16 +08:00
|
|
|
static void optimizeModule(Module &TheModule, TargetMachine &TM,
|
2017-03-29 02:55:44 +08:00
|
|
|
unsigned OptLevel, bool Freestanding) {
|
2016-03-09 09:37:22 +08:00
|
|
|
// Populate the PassManager
|
|
|
|
PassManagerBuilder PMB;
|
|
|
|
PMB.LibraryInfo = new TargetLibraryInfoImpl(TM.getTargetTriple());
|
2017-03-29 02:55:44 +08:00
|
|
|
if (Freestanding)
|
|
|
|
PMB.LibraryInfo->disableAllFunctions();
|
2016-03-09 09:37:22 +08:00
|
|
|
PMB.Inliner = createFunctionInliningPass();
|
|
|
|
// FIXME: should get it from the bitcode?
|
2016-12-29 03:37:16 +08:00
|
|
|
PMB.OptLevel = OptLevel;
|
2016-03-09 09:37:22 +08:00
|
|
|
PMB.LoopVectorize = true;
|
|
|
|
PMB.SLPVectorize = true;
|
2017-05-20 08:00:08 +08:00
|
|
|
// Already did this in verifyLoadedModule().
|
|
|
|
PMB.VerifyInput = false;
|
2016-03-09 09:37:22 +08:00
|
|
|
PMB.VerifyOutput = false;
|
|
|
|
|
|
|
|
legacy::PassManager PM;
|
|
|
|
|
|
|
|
// Add the TTI (required to inform the vectorizer about register size for
|
|
|
|
// instance)
|
|
|
|
PM.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
|
|
|
|
|
|
|
|
// Add optimizations
|
|
|
|
PMB.populateThinLTOPassManager(PM);
|
|
|
|
|
|
|
|
PM.run(TheModule);
|
|
|
|
}
|
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
// Convert the PreservedSymbols map from "Name" based to "GUID" based.
|
|
|
|
static DenseSet<GlobalValue::GUID>
|
2017-02-03 15:41:43 +08:00
|
|
|
computeGUIDPreservedSymbols(const StringSet<> &PreservedSymbols,
|
|
|
|
const Triple &TheTriple) {
|
|
|
|
DenseSet<GlobalValue::GUID> GUIDPreservedSymbols(PreservedSymbols.size());
|
|
|
|
for (auto &Entry : PreservedSymbols) {
|
2016-04-24 11:18:01 +08:00
|
|
|
StringRef Name = Entry.first();
|
|
|
|
if (TheTriple.isOSBinFormatMachO() && Name.size() > 0 && Name[0] == '_')
|
|
|
|
Name = Name.drop_front();
|
2017-02-03 15:41:43 +08:00
|
|
|
GUIDPreservedSymbols.insert(GlobalValue::getGUID(Name));
|
2016-04-24 11:18:01 +08:00
|
|
|
}
|
2017-02-03 15:41:43 +08:00
|
|
|
return GUIDPreservedSymbols;
|
2016-04-24 11:18:01 +08:00
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
std::unique_ptr<MemoryBuffer> codegenModule(Module &TheModule,
|
|
|
|
TargetMachine &TM) {
|
|
|
|
SmallVector<char, 128> OutputBuffer;
|
|
|
|
|
|
|
|
// CodeGen
|
|
|
|
{
|
|
|
|
raw_svector_ostream OS(OutputBuffer);
|
|
|
|
legacy::PassManager PM;
|
2016-04-01 16:22:59 +08:00
|
|
|
|
|
|
|
// If the bitcode files contain ARC code and were compiled with optimization,
|
|
|
|
// the ObjCARCContractPass must be run, so do it unconditionally here.
|
|
|
|
PM.add(createObjCARCContractPass());
|
|
|
|
|
|
|
|
// Setup the codegen now.
|
2016-03-09 09:37:22 +08:00
|
|
|
if (TM.addPassesToEmitFile(PM, OS, TargetMachine::CGFT_ObjectFile,
|
|
|
|
/* DisableVerify */ true))
|
|
|
|
report_fatal_error("Failed to setup codegen");
|
|
|
|
|
|
|
|
// Run codegen now. resulting binary is in OutputBuffer.
|
|
|
|
PM.run(TheModule);
|
|
|
|
}
|
|
|
|
return make_unique<ObjectMemoryBuffer>(std::move(OutputBuffer));
|
|
|
|
}
|
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
/// Manage caching for a single Module.
|
|
|
|
class ModuleCacheEntry {
|
|
|
|
SmallString<128> EntryPath;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// Create a cache entry. This compute a unique hash for the Module considering
|
|
|
|
// the current list of export/import, and offer an interface to query to
|
|
|
|
// access the content in the cache.
|
|
|
|
ModuleCacheEntry(
|
|
|
|
StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID,
|
|
|
|
const FunctionImporter::ImportMapTy &ImportList,
|
|
|
|
const FunctionImporter::ExportSetTy &ExportList,
|
|
|
|
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
|
2016-04-26 05:09:51 +08:00
|
|
|
const GVSummaryMapTy &DefinedFunctions,
|
2017-01-10 08:55:47 +08:00
|
|
|
const DenseSet<GlobalValue::GUID> &PreservedSymbols, unsigned OptLevel,
|
2017-03-29 02:55:44 +08:00
|
|
|
bool Freestanding, const TargetMachineBuilder &TMBuilder) {
|
2016-04-21 13:54:23 +08:00
|
|
|
if (CachePath.empty())
|
|
|
|
return;
|
|
|
|
|
2016-10-08 12:44:18 +08:00
|
|
|
if (!Index.modulePaths().count(ModuleID))
|
|
|
|
// The module does not have an entry, it can't have a hash at all
|
|
|
|
return;
|
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
// Compute the unique hash for this entry
|
|
|
|
// This is based on the current compiler version, the module itself, the
|
|
|
|
// export list, the hash for every single module in the import list, the
|
|
|
|
// list of ResolvedODR for the module, and the list of preserved symbols.
|
|
|
|
|
2016-10-08 12:44:23 +08:00
|
|
|
// Include the hash for the current module
|
|
|
|
auto ModHash = Index.getModuleHash(ModuleID);
|
|
|
|
|
|
|
|
if (all_of(ModHash, [](uint32_t V) { return V == 0; }))
|
|
|
|
// No hash entry, no caching!
|
|
|
|
return;
|
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
SHA1 Hasher;
|
|
|
|
|
2017-01-10 08:55:47 +08:00
|
|
|
// Include the parts of the LTO configuration that affect code generation.
|
|
|
|
auto AddString = [&](StringRef Str) {
|
|
|
|
Hasher.update(Str);
|
|
|
|
Hasher.update(ArrayRef<uint8_t>{0});
|
|
|
|
};
|
|
|
|
auto AddUnsigned = [&](unsigned I) {
|
|
|
|
uint8_t Data[4];
|
|
|
|
Data[0] = I;
|
|
|
|
Data[1] = I >> 8;
|
|
|
|
Data[2] = I >> 16;
|
|
|
|
Data[3] = I >> 24;
|
|
|
|
Hasher.update(ArrayRef<uint8_t>{Data, 4});
|
|
|
|
};
|
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
// Start with the compiler revision
|
|
|
|
Hasher.update(LLVM_VERSION_STRING);
|
2017-04-13 09:26:12 +08:00
|
|
|
#ifdef LLVM_REVISION
|
2016-04-21 13:54:23 +08:00
|
|
|
Hasher.update(LLVM_REVISION);
|
|
|
|
#endif
|
|
|
|
|
2017-01-10 08:55:47 +08:00
|
|
|
// Hash the optimization level and the target machine settings.
|
|
|
|
AddString(TMBuilder.MCpu);
|
|
|
|
// FIXME: Hash more of Options. For now all clients initialize Options from
|
|
|
|
// command-line flags (which is unsupported in production), but may set
|
|
|
|
// RelaxELFRelocations. The clang driver can also pass FunctionSections,
|
|
|
|
// DataSections and DebuggerTuning via command line flags.
|
|
|
|
AddUnsigned(TMBuilder.Options.RelaxELFRelocations);
|
|
|
|
AddUnsigned(TMBuilder.Options.FunctionSections);
|
|
|
|
AddUnsigned(TMBuilder.Options.DataSections);
|
|
|
|
AddUnsigned((unsigned)TMBuilder.Options.DebuggerTuning);
|
|
|
|
AddString(TMBuilder.MAttr);
|
|
|
|
if (TMBuilder.RelocModel)
|
|
|
|
AddUnsigned(*TMBuilder.RelocModel);
|
|
|
|
AddUnsigned(TMBuilder.CGOptLevel);
|
|
|
|
AddUnsigned(OptLevel);
|
2017-03-29 02:55:44 +08:00
|
|
|
AddUnsigned(Freestanding);
|
2017-01-10 08:55:47 +08:00
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
|
|
|
for (auto F : ExportList)
|
|
|
|
// The export list can impact the internalization, be conservative here
|
|
|
|
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&F, sizeof(F)));
|
|
|
|
|
|
|
|
// Include the hash for every module we import functions from
|
|
|
|
for (auto &Entry : ImportList) {
|
|
|
|
auto ModHash = Index.getModuleHash(Entry.first());
|
|
|
|
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Include the hash for the resolved ODR.
|
|
|
|
for (auto &Entry : ResolvedODR) {
|
2016-04-28 02:35:02 +08:00
|
|
|
Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.first,
|
2016-04-21 13:54:23 +08:00
|
|
|
sizeof(GlobalValue::GUID)));
|
2016-04-28 02:35:02 +08:00
|
|
|
Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.second,
|
2016-04-21 13:54:23 +08:00
|
|
|
sizeof(GlobalValue::LinkageTypes)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Include the hash for the preserved symbols.
|
|
|
|
for (auto &Entry : PreservedSymbols) {
|
|
|
|
if (DefinedFunctions.count(Entry))
|
|
|
|
Hasher.update(
|
2016-04-28 02:35:02 +08:00
|
|
|
ArrayRef<uint8_t>((const uint8_t *)&Entry, sizeof(GlobalValue::GUID)));
|
2016-04-21 13:54:23 +08:00
|
|
|
}
|
|
|
|
|
2017-03-21 00:41:57 +08:00
|
|
|
// This choice of file name allows the cache to be pruned (see pruneCache()
|
|
|
|
// in include/llvm/Support/CachePruning.h).
|
|
|
|
sys::path::append(EntryPath, CachePath,
|
|
|
|
"llvmcache-" + toHex(Hasher.result()));
|
2016-04-21 13:54:23 +08:00
|
|
|
}
|
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
// Access the path to this entry in the cache.
|
|
|
|
StringRef getEntryPath() { return EntryPath; }
|
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
// Try loading the buffer for this cache entry.
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() {
|
|
|
|
if (EntryPath.empty())
|
|
|
|
return std::error_code();
|
|
|
|
return MemoryBuffer::getFile(EntryPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the Produced object file
|
2016-12-14 12:56:42 +08:00
|
|
|
void write(const MemoryBuffer &OutputBuffer) {
|
2016-04-21 13:54:23 +08:00
|
|
|
if (EntryPath.empty())
|
2016-12-14 12:56:42 +08:00
|
|
|
return;
|
2016-04-21 13:54:23 +08:00
|
|
|
|
|
|
|
// Write to a temporary to avoid race condition
|
|
|
|
SmallString<128> TempFilename;
|
|
|
|
int TempFD;
|
|
|
|
std::error_code EC =
|
|
|
|
sys::fs::createTemporaryFile("Thin", "tmp.o", TempFD, TempFilename);
|
|
|
|
if (EC) {
|
|
|
|
errs() << "Error: " << EC.message() << "\n";
|
|
|
|
report_fatal_error("ThinLTO: Can't get a temporary file");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
raw_fd_ostream OS(TempFD, /* ShouldClose */ true);
|
2016-12-14 12:56:42 +08:00
|
|
|
OS << OutputBuffer.getBuffer();
|
2016-04-21 13:54:23 +08:00
|
|
|
}
|
|
|
|
// Rename to final destination (hopefully race condition won't matter here)
|
2016-05-14 12:58:38 +08:00
|
|
|
EC = sys::fs::rename(TempFilename, EntryPath);
|
|
|
|
if (EC) {
|
2016-05-14 13:16:35 +08:00
|
|
|
sys::fs::remove(TempFilename);
|
|
|
|
raw_fd_ostream OS(EntryPath, EC, sys::fs::F_None);
|
|
|
|
if (EC)
|
|
|
|
report_fatal_error(Twine("Failed to open ") + EntryPath +
|
|
|
|
" to save cached entry\n");
|
2016-12-14 12:56:42 +08:00
|
|
|
OS << OutputBuffer.getBuffer();
|
2016-05-14 12:58:38 +08:00
|
|
|
}
|
2016-04-21 13:54:23 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
static std::unique_ptr<MemoryBuffer>
|
|
|
|
ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index,
|
|
|
|
StringMap<MemoryBufferRef> &ModuleMap, TargetMachine &TM,
|
|
|
|
const FunctionImporter::ImportMapTy &ImportList,
|
|
|
|
const FunctionImporter::ExportSetTy &ExportList,
|
|
|
|
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
|
|
|
|
const GVSummaryMapTy &DefinedGlobals,
|
2016-06-09 03:09:22 +08:00
|
|
|
const ThinLTOCodeGenerator::CachingOptions &CacheOptions,
|
2016-05-25 01:24:25 +08:00
|
|
|
bool DisableCodeGen, StringRef SaveTempsDir,
|
2017-03-29 02:55:44 +08:00
|
|
|
bool Freestanding, unsigned OptLevel, unsigned count) {
|
2016-04-24 11:18:01 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// "Benchmark"-like optimization: single-source case
|
|
|
|
bool SingleModule = (ModuleMap.size() == 1);
|
|
|
|
|
|
|
|
if (!SingleModule) {
|
|
|
|
promoteModule(TheModule, Index);
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
// Apply summary-based LinkOnce/Weak resolution decisions.
|
|
|
|
thinLTOResolveWeakForLinkerModule(TheModule, DefinedGlobals);
|
2016-04-02 05:53:50 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Save temps: after promotion.
|
2016-05-05 13:14:16 +08:00
|
|
|
saveTempBitcode(TheModule, SaveTempsDir, count, ".1.promoted.bc");
|
2016-04-24 11:18:01 +08:00
|
|
|
}
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
// Be friendly and don't nuke totally the module when the client didn't
|
|
|
|
// supply anything to preserve.
|
|
|
|
if (!ExportList.empty() || !GUIDPreservedSymbols.empty()) {
|
|
|
|
// Apply summary-based internalization decisions.
|
|
|
|
thinLTOInternalizeModule(TheModule, DefinedGlobals);
|
|
|
|
}
|
2016-03-09 09:37:22 +08:00
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
// Save internalized bitcode
|
2016-05-05 13:14:16 +08:00
|
|
|
saveTempBitcode(TheModule, SaveTempsDir, count, ".2.internalized.bc");
|
2016-04-24 11:18:01 +08:00
|
|
|
|
|
|
|
if (!SingleModule) {
|
2016-03-26 13:40:34 +08:00
|
|
|
crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
|
2016-03-09 09:37:22 +08:00
|
|
|
|
|
|
|
// Save temps: after cross-module import.
|
2016-05-05 13:14:16 +08:00
|
|
|
saveTempBitcode(TheModule, SaveTempsDir, count, ".3.imported.bc");
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
2017-03-29 02:55:44 +08:00
|
|
|
optimizeModule(TheModule, TM, OptLevel, Freestanding);
|
2016-03-09 09:37:22 +08:00
|
|
|
|
2016-05-05 13:14:16 +08:00
|
|
|
saveTempBitcode(TheModule, SaveTempsDir, count, ".4.opt.bc");
|
2016-03-09 09:37:22 +08:00
|
|
|
|
2016-04-01 14:47:02 +08:00
|
|
|
if (DisableCodeGen) {
|
|
|
|
// Configured to stop before CodeGen, serialize the bitcode and return.
|
|
|
|
SmallVector<char, 128> OutputBuffer;
|
|
|
|
{
|
|
|
|
raw_svector_ostream OS(OutputBuffer);
|
2016-09-29 05:00:58 +08:00
|
|
|
ProfileSummaryInfo PSI(TheModule);
|
2017-05-11 02:52:16 +08:00
|
|
|
auto Index = buildModuleSummaryIndex(TheModule, nullptr, &PSI);
|
2016-08-19 15:49:19 +08:00
|
|
|
WriteBitcodeToFile(&TheModule, OS, true, &Index);
|
2016-04-01 14:47:02 +08:00
|
|
|
}
|
|
|
|
return make_unique<ObjectMemoryBuffer>(std::move(OutputBuffer));
|
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
return codegenModule(TheModule, TM);
|
|
|
|
}
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
/// Resolve LinkOnce/Weak symbols. Record resolutions in the \p ResolvedODR map
|
|
|
|
/// for caching, and in the \p Index for application during the ThinLTO
|
|
|
|
/// backends. This is needed for correctness for exported symbols (ensure
|
|
|
|
/// at least one copy kept) and a compile-time optimization (to drop duplicate
|
|
|
|
/// copies when possible).
|
|
|
|
static void resolveWeakForLinkerInIndex(
|
|
|
|
ModuleSummaryIndex &Index,
|
|
|
|
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>>
|
|
|
|
&ResolvedODR) {
|
|
|
|
|
|
|
|
DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
|
|
|
|
computePrevailingCopies(Index, PrevailingCopy);
|
|
|
|
|
|
|
|
auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
|
|
|
|
const auto &Prevailing = PrevailingCopy.find(GUID);
|
|
|
|
// Not in map means that there was only one copy, which must be prevailing.
|
|
|
|
if (Prevailing == PrevailingCopy.end())
|
|
|
|
return true;
|
|
|
|
return Prevailing->second == S;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto recordNewLinkage = [&](StringRef ModuleIdentifier,
|
|
|
|
GlobalValue::GUID GUID,
|
|
|
|
GlobalValue::LinkageTypes NewLinkage) {
|
|
|
|
ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
|
|
|
|
};
|
|
|
|
|
2016-07-08 02:31:51 +08:00
|
|
|
thinLTOResolveWeakForLinkerInIndex(Index, isPrevailing, recordNewLinkage);
|
2016-05-25 01:24:25 +08:00
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Initialize the TargetMachine builder for a given Triple
|
|
|
|
static void initTMBuilder(TargetMachineBuilder &TMBuilder,
|
|
|
|
const Triple &TheTriple) {
|
|
|
|
// Set a default CPU for Darwin triples (copied from LTOCodeGenerator).
|
|
|
|
// FIXME this looks pretty terrible...
|
|
|
|
if (TMBuilder.MCpu.empty() && TheTriple.isOSDarwin()) {
|
|
|
|
if (TheTriple.getArch() == llvm::Triple::x86_64)
|
|
|
|
TMBuilder.MCpu = "core2";
|
|
|
|
else if (TheTriple.getArch() == llvm::Triple::x86)
|
|
|
|
TMBuilder.MCpu = "yonah";
|
|
|
|
else if (TheTriple.getArch() == llvm::Triple::aarch64)
|
|
|
|
TMBuilder.MCpu = "cyclone";
|
|
|
|
}
|
|
|
|
TMBuilder.TheTriple = std::move(TheTriple);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
void ThinLTOCodeGenerator::addModule(StringRef Identifier, StringRef Data) {
|
2017-02-14 10:20:51 +08:00
|
|
|
ThinLTOBuffer Buffer(Data, Identifier);
|
2017-05-18 11:52:29 +08:00
|
|
|
LLVMContext Context;
|
|
|
|
StringRef TripleStr;
|
|
|
|
ErrorOr<std::string> TripleOrErr = expectedToErrorOrAndEmitErrors(
|
|
|
|
Context, getBitcodeTargetTriple(Buffer.getMemBuffer()));
|
|
|
|
|
|
|
|
if (TripleOrErr)
|
|
|
|
TripleStr = *TripleOrErr;
|
|
|
|
|
|
|
|
Triple TheTriple(TripleStr);
|
|
|
|
|
|
|
|
if (Modules.empty())
|
2016-03-09 09:37:22 +08:00
|
|
|
initTMBuilder(TMBuilder, Triple(TheTriple));
|
2017-05-18 11:52:29 +08:00
|
|
|
else if (TMBuilder.TheTriple != TheTriple) {
|
|
|
|
if (!TMBuilder.TheTriple.isCompatibleWith(TheTriple))
|
|
|
|
report_fatal_error("ThinLTO modules with incompatible triples not "
|
|
|
|
"supported");
|
|
|
|
initTMBuilder(TMBuilder, Triple(TMBuilder.TheTriple.merge(TheTriple)));
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
2017-05-18 11:52:29 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
Modules.push_back(Buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThinLTOCodeGenerator::preserveSymbol(StringRef Name) {
|
|
|
|
PreservedSymbols.insert(Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThinLTOCodeGenerator::crossReferenceSymbol(StringRef Name) {
|
2017-02-03 15:41:43 +08:00
|
|
|
// FIXME: At the moment, we don't take advantage of this extra information,
|
|
|
|
// we're conservatively considering cross-references as preserved.
|
|
|
|
// CrossReferencedSymbols.insert(Name);
|
|
|
|
PreservedSymbols.insert(Name);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// TargetMachine factory
|
|
|
|
std::unique_ptr<TargetMachine> TargetMachineBuilder::create() const {
|
|
|
|
std::string ErrMsg;
|
|
|
|
const Target *TheTarget =
|
|
|
|
TargetRegistry::lookupTarget(TheTriple.str(), ErrMsg);
|
|
|
|
if (!TheTarget) {
|
|
|
|
report_fatal_error("Can't load target for this Triple: " + ErrMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use MAttr as the default set of features.
|
|
|
|
SubtargetFeatures Features(MAttr);
|
|
|
|
Features.getDefaultSubtargetFeatures(TheTriple);
|
|
|
|
std::string FeatureStr = Features.getString();
|
2016-12-29 03:37:16 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
|
|
|
|
TheTriple.str(), MCpu, FeatureStr, Options, RelocModel,
|
|
|
|
CodeModel::Default, CGOptLevel));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-03-15 08:04:37 +08:00
|
|
|
* Produce the combined summary index from all the bitcode files:
|
2016-03-09 09:37:22 +08:00
|
|
|
* "thin-link".
|
|
|
|
*/
|
2016-03-15 08:04:37 +08:00
|
|
|
std::unique_ptr<ModuleSummaryIndex> ThinLTOCodeGenerator::linkCombinedIndex() {
|
2017-05-02 06:04:36 +08:00
|
|
|
std::unique_ptr<ModuleSummaryIndex> CombinedIndex =
|
|
|
|
llvm::make_unique<ModuleSummaryIndex>();
|
2016-03-09 09:37:22 +08:00
|
|
|
uint64_t NextModuleId = 0;
|
|
|
|
for (auto &ModuleBuffer : Modules) {
|
2017-05-02 06:04:36 +08:00
|
|
|
if (Error Err = readModuleSummaryIndex(ModuleBuffer.getMemBuffer(),
|
|
|
|
*CombinedIndex, NextModuleId++)) {
|
2016-03-09 09:37:22 +08:00
|
|
|
// FIXME diagnose
|
2016-11-12 03:50:39 +08:00
|
|
|
logAllUnhandledErrors(
|
2017-05-02 06:04:36 +08:00
|
|
|
std::move(Err), errs(),
|
2017-05-02 04:42:32 +08:00
|
|
|
"error: can't create module summary index for buffer: ");
|
2016-03-09 09:37:22 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return CombinedIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform promotion and renaming of exported internal functions.
|
2016-05-25 01:24:25 +08:00
|
|
|
* Index is updated to reflect linkage changes from weak resolution.
|
2016-03-09 09:37:22 +08:00
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::promote(Module &TheModule,
|
2016-03-15 08:04:37 +08:00
|
|
|
ModuleSummaryIndex &Index) {
|
2016-04-21 13:47:17 +08:00
|
|
|
auto ModuleCount = Index.modulePaths().size();
|
2016-04-16 15:02:16 +08:00
|
|
|
auto ModuleIdentifier = TheModule.getModuleIdentifier();
|
2016-11-15 03:21:41 +08:00
|
|
|
|
2016-04-16 15:02:16 +08:00
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
2016-04-26 05:09:51 +08:00
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
|
2016-04-16 15:02:16 +08:00
|
|
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
2016-04-02 05:53:50 +08:00
|
|
|
|
2017-01-06 05:34:18 +08:00
|
|
|
// Convert the preserved symbols set from string to GUID
|
2017-02-03 15:41:43 +08:00
|
|
|
auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
|
2017-01-06 05:34:18 +08:00
|
|
|
PreservedSymbols, Triple(TheModule.getTargetTriple()));
|
|
|
|
|
|
|
|
// Compute "dead" symbols, we don't want to import/export these!
|
2017-06-02 04:30:06 +08:00
|
|
|
computeDeadSymbols(Index, GUIDPreservedSymbols);
|
2017-01-06 05:34:18 +08:00
|
|
|
|
2016-04-21 13:47:17 +08:00
|
|
|
// Generate import/export list
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
|
|
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
2017-06-02 04:30:06 +08:00
|
|
|
ExportLists);
|
2016-04-21 13:47:17 +08:00
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
// Resolve LinkOnce/Weak symbols.
|
|
|
|
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
|
2016-07-08 02:31:51 +08:00
|
|
|
resolveWeakForLinkerInIndex(Index, ResolvedODR);
|
2016-05-25 01:24:25 +08:00
|
|
|
|
|
|
|
thinLTOResolveWeakForLinkerModule(
|
|
|
|
TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
|
2016-04-02 05:53:50 +08:00
|
|
|
|
2016-11-15 03:21:41 +08:00
|
|
|
// Promote the exported values in the index, so that they are promoted
|
|
|
|
// in the module.
|
2017-02-03 15:41:43 +08:00
|
|
|
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
2016-11-15 03:21:41 +08:00
|
|
|
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
2017-02-03 15:41:43 +08:00
|
|
|
return (ExportList != ExportLists.end() &&
|
|
|
|
ExportList->second.count(GUID)) ||
|
|
|
|
GUIDPreservedSymbols.count(GUID);
|
2016-11-15 03:21:41 +08:00
|
|
|
};
|
|
|
|
thinLTOInternalizeAndPromoteInIndex(Index, isExported);
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
promoteModule(TheModule, Index);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform cross-module importing for the module identified by ModuleIdentifier.
|
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::crossModuleImport(Module &TheModule,
|
2016-03-15 08:04:37 +08:00
|
|
|
ModuleSummaryIndex &Index) {
|
2016-03-09 09:37:22 +08:00
|
|
|
auto ModuleMap = generateModuleMap(Modules);
|
2016-04-16 15:02:16 +08:00
|
|
|
auto ModuleCount = Index.modulePaths().size();
|
|
|
|
|
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
2016-04-26 05:09:51 +08:00
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
2016-04-16 15:02:16 +08:00
|
|
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
2016-03-26 13:40:34 +08:00
|
|
|
|
2017-01-06 05:34:18 +08:00
|
|
|
// Convert the preserved symbols set from string to GUID
|
2017-02-03 15:41:43 +08:00
|
|
|
auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
|
2017-01-06 05:34:18 +08:00
|
|
|
PreservedSymbols, Triple(TheModule.getTargetTriple()));
|
|
|
|
|
|
|
|
// Compute "dead" symbols, we don't want to import/export these!
|
2017-06-02 04:30:06 +08:00
|
|
|
computeDeadSymbols(Index, GUIDPreservedSymbols);
|
2017-01-06 05:34:18 +08:00
|
|
|
|
2016-03-26 13:40:34 +08:00
|
|
|
// Generate import/export list
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
2016-04-16 15:02:16 +08:00
|
|
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
2017-06-02 04:30:06 +08:00
|
|
|
ExportLists);
|
2016-03-26 13:40:34 +08:00
|
|
|
auto &ImportList = ImportLists[TheModule.getModuleIdentifier()];
|
|
|
|
|
|
|
|
crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
2016-05-10 21:48:23 +08:00
|
|
|
/**
|
|
|
|
* Compute the list of summaries needed for importing into module.
|
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::gatherImportedSummariesForModule(
|
|
|
|
StringRef ModulePath, ModuleSummaryIndex &Index,
|
|
|
|
std::map<std::string, GVSummaryMapTy> &ModuleToSummariesForIndex) {
|
|
|
|
auto ModuleCount = Index.modulePaths().size();
|
|
|
|
|
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
|
|
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
|
|
|
|
|
|
|
// Generate import/export list
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
|
|
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
|
|
|
ExportLists);
|
|
|
|
|
|
|
|
llvm::gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
|
2016-08-16 13:46:05 +08:00
|
|
|
ImportLists[ModulePath],
|
2016-05-10 21:48:23 +08:00
|
|
|
ModuleToSummariesForIndex);
|
|
|
|
}
|
|
|
|
|
2016-05-10 23:54:09 +08:00
|
|
|
/**
|
|
|
|
* Emit the list of files needed for importing into module.
|
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::emitImports(StringRef ModulePath,
|
|
|
|
StringRef OutputName,
|
|
|
|
ModuleSummaryIndex &Index) {
|
|
|
|
auto ModuleCount = Index.modulePaths().size();
|
|
|
|
|
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
|
|
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
|
|
|
|
|
|
|
// Generate import/export list
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
|
|
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
|
|
|
ExportLists);
|
|
|
|
|
|
|
|
std::error_code EC;
|
2016-08-16 13:46:05 +08:00
|
|
|
if ((EC = EmitImportsFiles(ModulePath, OutputName, ImportLists[ModulePath])))
|
2016-05-10 23:54:09 +08:00
|
|
|
report_fatal_error(Twine("Failed to open ") + OutputName +
|
|
|
|
" to save imports lists\n");
|
|
|
|
}
|
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
/**
|
2016-05-25 01:24:25 +08:00
|
|
|
* Perform internalization. Index is updated to reflect linkage changes.
|
2016-04-24 11:18:01 +08:00
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::internalize(Module &TheModule,
|
|
|
|
ModuleSummaryIndex &Index) {
|
|
|
|
initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple()));
|
|
|
|
auto ModuleCount = Index.modulePaths().size();
|
|
|
|
auto ModuleIdentifier = TheModule.getModuleIdentifier();
|
|
|
|
|
|
|
|
// Convert the preserved symbols set from string to GUID
|
|
|
|
auto GUIDPreservedSymbols =
|
2017-02-03 15:41:43 +08:00
|
|
|
computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
|
2016-04-24 11:18:01 +08:00
|
|
|
|
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
2016-04-26 05:09:51 +08:00
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
2016-04-24 11:18:01 +08:00
|
|
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
|
|
|
|
2017-01-06 05:34:18 +08:00
|
|
|
// Compute "dead" symbols, we don't want to import/export these!
|
2017-06-02 04:30:06 +08:00
|
|
|
computeDeadSymbols(Index, GUIDPreservedSymbols);
|
2017-01-06 05:34:18 +08:00
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
// Generate import/export list
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
|
|
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
2017-06-02 04:30:06 +08:00
|
|
|
ExportLists);
|
2016-04-24 11:18:01 +08:00
|
|
|
auto &ExportList = ExportLists[ModuleIdentifier];
|
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
// Be friendly and don't nuke totally the module when the client didn't
|
|
|
|
// supply anything to preserve.
|
|
|
|
if (ExportList.empty() && GUIDPreservedSymbols.empty())
|
|
|
|
return;
|
|
|
|
|
2016-04-24 11:18:01 +08:00
|
|
|
// Internalization
|
2017-02-03 15:41:43 +08:00
|
|
|
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
2016-05-25 01:24:25 +08:00
|
|
|
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
2017-02-03 15:41:43 +08:00
|
|
|
return (ExportList != ExportLists.end() &&
|
|
|
|
ExportList->second.count(GUID)) ||
|
|
|
|
GUIDPreservedSymbols.count(GUID);
|
2016-05-25 01:24:25 +08:00
|
|
|
};
|
|
|
|
thinLTOInternalizeAndPromoteInIndex(Index, isExported);
|
|
|
|
thinLTOInternalizeModule(TheModule,
|
|
|
|
ModuleToDefinedGVSummaries[ModuleIdentifier]);
|
2016-04-24 11:18:01 +08:00
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
/**
|
|
|
|
* Perform post-importing ThinLTO optimizations.
|
|
|
|
*/
|
|
|
|
void ThinLTOCodeGenerator::optimize(Module &TheModule) {
|
|
|
|
initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple()));
|
2016-04-24 11:18:01 +08:00
|
|
|
|
|
|
|
// Optimize now
|
2017-03-29 02:55:44 +08:00
|
|
|
optimizeModule(TheModule, *TMBuilder.create(), OptLevel, Freestanding);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform ThinLTO CodeGen.
|
|
|
|
*/
|
|
|
|
std::unique_ptr<MemoryBuffer> ThinLTOCodeGenerator::codegen(Module &TheModule) {
|
|
|
|
initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple()));
|
|
|
|
return codegenModule(TheModule, *TMBuilder.create());
|
|
|
|
}
|
|
|
|
|
2016-12-14 12:56:42 +08:00
|
|
|
/// Write out the generated object file, either from CacheEntryPath or from
|
|
|
|
/// OutputBuffer, preferring hard-link when possible.
|
|
|
|
/// Returns the path to the generated file in SavedObjectsDirectoryPath.
|
|
|
|
static std::string writeGeneratedObject(int count, StringRef CacheEntryPath,
|
|
|
|
StringRef SavedObjectsDirectoryPath,
|
|
|
|
const MemoryBuffer &OutputBuffer) {
|
|
|
|
SmallString<128> OutputPath(SavedObjectsDirectoryPath);
|
|
|
|
llvm::sys::path::append(OutputPath, Twine(count) + ".thinlto.o");
|
|
|
|
OutputPath.c_str(); // Ensure the string is null terminated.
|
|
|
|
if (sys::fs::exists(OutputPath))
|
|
|
|
sys::fs::remove(OutputPath);
|
|
|
|
|
|
|
|
// We don't return a memory buffer to the linker, just a list of files.
|
|
|
|
if (!CacheEntryPath.empty()) {
|
|
|
|
// Cache is enabled, hard-link the entry (or copy if hard-link fails).
|
|
|
|
auto Err = sys::fs::create_hard_link(CacheEntryPath, OutputPath);
|
|
|
|
if (!Err)
|
|
|
|
return OutputPath.str();
|
|
|
|
// Hard linking failed, try to copy.
|
|
|
|
Err = sys::fs::copy_file(CacheEntryPath, OutputPath);
|
|
|
|
if (!Err)
|
|
|
|
return OutputPath.str();
|
|
|
|
// Copy failed (could be because the CacheEntry was removed from the cache
|
|
|
|
// in the meantime by another process), fall back and try to write down the
|
|
|
|
// buffer to the output.
|
|
|
|
errs() << "error: can't link or copy from cached entry '" << CacheEntryPath
|
|
|
|
<< "' to '" << OutputPath << "'\n";
|
|
|
|
}
|
|
|
|
// No cache entry, just write out the buffer.
|
|
|
|
std::error_code Err;
|
|
|
|
raw_fd_ostream OS(OutputPath, Err, sys::fs::F_None);
|
|
|
|
if (Err)
|
|
|
|
report_fatal_error("Can't open output '" + OutputPath + "'\n");
|
|
|
|
OS << OutputBuffer.getBuffer();
|
|
|
|
return OutputPath.str();
|
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Main entry point for the ThinLTO processing
|
|
|
|
void ThinLTOCodeGenerator::run() {
|
2017-01-21 06:45:34 +08:00
|
|
|
// Prepare the resulting object vector
|
|
|
|
assert(ProducedBinaries.empty() && "The generator should not be reused");
|
|
|
|
if (SavedObjectsDirectoryPath.empty())
|
|
|
|
ProducedBinaries.resize(Modules.size());
|
|
|
|
else {
|
|
|
|
sys::fs::create_directories(SavedObjectsDirectoryPath);
|
|
|
|
bool IsDir;
|
|
|
|
sys::fs::is_directory(SavedObjectsDirectoryPath, IsDir);
|
|
|
|
if (!IsDir)
|
|
|
|
report_fatal_error("Unexistent dir: '" + SavedObjectsDirectoryPath + "'");
|
|
|
|
ProducedBinaryFiles.resize(Modules.size());
|
|
|
|
}
|
|
|
|
|
2016-04-01 14:47:02 +08:00
|
|
|
if (CodeGenOnly) {
|
|
|
|
// Perform only parallel codegen and return.
|
|
|
|
ThreadPool Pool;
|
|
|
|
int count = 0;
|
|
|
|
for (auto &ModuleBuffer : Modules) {
|
|
|
|
Pool.async([&](int count) {
|
|
|
|
LLVMContext Context;
|
|
|
|
Context.setDiscardValueNames(LTODiscardValueNames);
|
|
|
|
|
|
|
|
// Parse module now
|
2017-02-14 10:20:51 +08:00
|
|
|
auto TheModule =
|
|
|
|
loadModuleFromBuffer(ModuleBuffer.getMemBuffer(), Context, false,
|
|
|
|
/*IsImporting*/ false);
|
2016-04-01 14:47:02 +08:00
|
|
|
|
|
|
|
// CodeGen
|
2017-01-21 06:45:34 +08:00
|
|
|
auto OutputBuffer = codegen(*TheModule);
|
|
|
|
if (SavedObjectsDirectoryPath.empty())
|
|
|
|
ProducedBinaries[count] = std::move(OutputBuffer);
|
|
|
|
else
|
|
|
|
ProducedBinaryFiles[count] = writeGeneratedObject(
|
|
|
|
count, "", SavedObjectsDirectoryPath, *OutputBuffer);
|
2016-04-01 14:47:02 +08:00
|
|
|
}, count++);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Sequential linking phase
|
|
|
|
auto Index = linkCombinedIndex();
|
|
|
|
|
|
|
|
// Save temps: index.
|
|
|
|
if (!SaveTempsDir.empty()) {
|
|
|
|
auto SaveTempPath = SaveTempsDir + "index.bc";
|
|
|
|
std::error_code EC;
|
|
|
|
raw_fd_ostream OS(SaveTempPath, EC, sys::fs::F_None);
|
|
|
|
if (EC)
|
|
|
|
report_fatal_error(Twine("Failed to open ") + SaveTempPath +
|
|
|
|
" to save optimized bitcode\n");
|
[ThinLTO] Support for reference graph in per-module and combined summary.
Summary:
This patch adds support for including a full reference graph including
call graph edges and other GV references in the summary.
The reference graph edges can be used to make importing decisions
without materializing any source modules, can be used in the plugin
to make file staging decisions for distributed build systems, and is
expected to have other uses.
The call graph edges are recorded in each function summary in the
bitcode via a list of <CalleeValueIds, StaticCount> tuples when no PGO
data exists, or <CalleeValueId, StaticCount, ProfileCount> pairs when
there is PGO, where the ValueId can be mapped to the function GUID via
the ValueSymbolTable. In the function index in memory, the call graph
edges reference the target via the CalleeGUID instead of the
CalleeValueId.
The reference graph edges are recorded in each summary record with a
list of referenced value IDs, which can be mapped to value GUID via the
ValueSymbolTable.
Addtionally, a new summary record type is added to record references
from global variable initializers. A number of bitcode records and data
structures have been renamed to reflect the newly expanded scope of the
summary beyond functions. More cleanup will follow.
Reviewers: joker.eph, davidxl
Subscribers: joker.eph, llvm-commits
Differential Revision: http://reviews.llvm.org/D17212
llvm-svn: 263275
2016-03-12 02:52:24 +08:00
|
|
|
WriteIndexToFile(*Index, OS);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the module map.
|
|
|
|
auto ModuleMap = generateModuleMap(Modules);
|
2016-03-26 13:40:34 +08:00
|
|
|
auto ModuleCount = Modules.size();
|
|
|
|
|
2016-04-16 15:02:16 +08:00
|
|
|
// Collect for each module the list of function it defines (GUID -> Summary).
|
2016-04-26 05:09:51 +08:00
|
|
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
2016-04-16 15:02:16 +08:00
|
|
|
Index->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
|
|
|
|
2017-01-06 05:34:18 +08:00
|
|
|
// Convert the preserved symbols set from string to GUID, this is needed for
|
|
|
|
// computing the caching hash and the internalization.
|
|
|
|
auto GUIDPreservedSymbols =
|
2017-02-03 15:41:43 +08:00
|
|
|
computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
|
2017-01-06 05:34:18 +08:00
|
|
|
|
|
|
|
// Compute "dead" symbols, we don't want to import/export these!
|
2017-06-02 04:30:06 +08:00
|
|
|
computeDeadSymbols(*Index, GUIDPreservedSymbols);
|
2017-01-06 05:34:18 +08:00
|
|
|
|
2016-03-26 13:40:34 +08:00
|
|
|
// Collect the import/export lists for all modules from the call-graph in the
|
|
|
|
// combined index.
|
|
|
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
|
|
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
2016-04-16 15:02:16 +08:00
|
|
|
ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists,
|
2017-06-02 04:30:06 +08:00
|
|
|
ExportLists);
|
2016-04-21 13:54:23 +08:00
|
|
|
|
2016-05-25 01:24:25 +08:00
|
|
|
// We use a std::map here to be able to have a defined ordering when
|
|
|
|
// producing a hash for the cache entry.
|
|
|
|
// FIXME: we should be able to compute the caching hash for the entry based
|
|
|
|
// on the index, and nuke this map.
|
|
|
|
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
|
|
|
|
|
|
|
|
// Resolve LinkOnce/Weak symbols, this has to be computed early because it
|
|
|
|
// impacts the caching.
|
2016-07-08 02:31:51 +08:00
|
|
|
resolveWeakForLinkerInIndex(*Index, ResolvedODR);
|
2016-05-25 01:24:25 +08:00
|
|
|
|
2017-02-03 15:41:43 +08:00
|
|
|
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
2016-05-25 01:24:25 +08:00
|
|
|
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
2017-02-03 15:41:43 +08:00
|
|
|
return (ExportList != ExportLists.end() &&
|
|
|
|
ExportList->second.count(GUID)) ||
|
|
|
|
GUIDPreservedSymbols.count(GUID);
|
2016-05-25 01:24:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Use global summary-based analysis to identify symbols that can be
|
|
|
|
// internalized (because they aren't exported or preserved as per callback).
|
|
|
|
// Changes are made in the index, consumed in the ThinLTO backends.
|
|
|
|
thinLTOInternalizeAndPromoteInIndex(*Index, isExported);
|
|
|
|
|
2016-05-25 02:44:01 +08:00
|
|
|
// Make sure that every module has an entry in the ExportLists and
|
|
|
|
// ResolvedODR maps to enable threaded access to these maps below.
|
|
|
|
for (auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) {
|
2016-05-15 13:49:47 +08:00
|
|
|
ExportLists[DefinedGVSummaries.first()];
|
2016-05-25 02:44:01 +08:00
|
|
|
ResolvedODR[DefinedGVSummaries.first()];
|
|
|
|
}
|
2016-05-15 13:49:47 +08:00
|
|
|
|
2016-05-17 03:33:07 +08:00
|
|
|
// Compute the ordering we will process the inputs: the rough heuristic here
|
|
|
|
// is to sort them per size so that the largest module get schedule as soon as
|
|
|
|
// possible. This is purely a compile-time optimization.
|
|
|
|
std::vector<int> ModulesOrdering;
|
|
|
|
ModulesOrdering.resize(Modules.size());
|
|
|
|
std::iota(ModulesOrdering.begin(), ModulesOrdering.end(), 0);
|
|
|
|
std::sort(ModulesOrdering.begin(), ModulesOrdering.end(),
|
|
|
|
[&](int LeftIndex, int RightIndex) {
|
2017-02-14 10:20:51 +08:00
|
|
|
auto LSize = Modules[LeftIndex].getBuffer().size();
|
|
|
|
auto RSize = Modules[RightIndex].getBuffer().size();
|
2016-05-17 03:33:07 +08:00
|
|
|
return LSize > RSize;
|
|
|
|
});
|
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Parallel optimizer + codegen
|
|
|
|
{
|
|
|
|
ThreadPool Pool(ThreadCount);
|
2016-05-17 03:33:07 +08:00
|
|
|
for (auto IndexCount : ModulesOrdering) {
|
|
|
|
auto &ModuleBuffer = Modules[IndexCount];
|
2016-03-09 09:37:22 +08:00
|
|
|
Pool.async([&](int count) {
|
2016-04-16 15:02:16 +08:00
|
|
|
auto ModuleIdentifier = ModuleBuffer.getBufferIdentifier();
|
2016-04-21 13:47:17 +08:00
|
|
|
auto &ExportList = ExportLists[ModuleIdentifier];
|
2016-04-16 15:02:16 +08:00
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
auto &DefinedFunctions = ModuleToDefinedGVSummaries[ModuleIdentifier];
|
|
|
|
|
|
|
|
// The module may be cached, this helps handling it.
|
2016-04-24 11:18:01 +08:00
|
|
|
ModuleCacheEntry CacheEntry(CacheOptions.Path, *Index, ModuleIdentifier,
|
|
|
|
ImportLists[ModuleIdentifier], ExportList,
|
2016-05-25 01:24:25 +08:00
|
|
|
ResolvedODR[ModuleIdentifier],
|
2017-01-10 08:55:47 +08:00
|
|
|
DefinedFunctions, GUIDPreservedSymbols,
|
2017-03-29 02:55:44 +08:00
|
|
|
OptLevel, Freestanding, TMBuilder);
|
2016-12-14 12:56:42 +08:00
|
|
|
auto CacheEntryPath = CacheEntry.getEntryPath();
|
2016-04-21 13:54:23 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
auto ErrOrBuffer = CacheEntry.tryLoadingBuffer();
|
2016-04-24 11:18:01 +08:00
|
|
|
DEBUG(dbgs() << "Cache " << (ErrOrBuffer ? "hit" : "miss") << " '"
|
2016-12-14 12:56:42 +08:00
|
|
|
<< CacheEntryPath << "' for buffer " << count << " "
|
|
|
|
<< ModuleIdentifier << "\n");
|
2016-04-24 11:18:01 +08:00
|
|
|
|
2016-04-21 13:54:23 +08:00
|
|
|
if (ErrOrBuffer) {
|
|
|
|
// Cache Hit!
|
2016-12-14 12:56:42 +08:00
|
|
|
if (SavedObjectsDirectoryPath.empty())
|
|
|
|
ProducedBinaries[count] = std::move(ErrOrBuffer.get());
|
|
|
|
else
|
|
|
|
ProducedBinaryFiles[count] = writeGeneratedObject(
|
|
|
|
count, CacheEntryPath, SavedObjectsDirectoryPath,
|
|
|
|
*ErrOrBuffer.get());
|
2016-04-21 13:54:23 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LLVMContext Context;
|
|
|
|
Context.setDiscardValueNames(LTODiscardValueNames);
|
|
|
|
Context.enableDebugTypeODRUniquing();
|
2017-02-11 07:49:38 +08:00
|
|
|
auto DiagFileOrErr = lto::setupOptimizationRemarks(
|
|
|
|
Context, LTORemarksFilename, LTOPassRemarksWithHotness, count);
|
2016-11-20 02:20:05 +08:00
|
|
|
if (!DiagFileOrErr) {
|
|
|
|
errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n";
|
|
|
|
report_fatal_error("ThinLTO: Can't get an output file for the "
|
|
|
|
"remarks");
|
|
|
|
}
|
2016-04-21 13:54:23 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// Parse module now
|
2017-02-14 10:20:51 +08:00
|
|
|
auto TheModule =
|
|
|
|
loadModuleFromBuffer(ModuleBuffer.getMemBuffer(), Context, false,
|
|
|
|
/*IsImporting*/ false);
|
2016-03-09 09:37:22 +08:00
|
|
|
|
|
|
|
// Save temps: original file.
|
2016-04-24 11:18:01 +08:00
|
|
|
saveTempBitcode(*TheModule, SaveTempsDir, count, ".0.original.bc");
|
2016-03-09 09:37:22 +08:00
|
|
|
|
2016-04-16 15:02:16 +08:00
|
|
|
auto &ImportList = ImportLists[ModuleIdentifier];
|
2016-04-24 11:18:01 +08:00
|
|
|
// Run the main process now, and generates a binary
|
2016-04-21 13:54:23 +08:00
|
|
|
auto OutputBuffer = ProcessThinLTOModule(
|
2016-03-26 13:40:34 +08:00
|
|
|
*TheModule, *Index, ModuleMap, *TMBuilder.create(), ImportList,
|
2016-05-25 01:24:25 +08:00
|
|
|
ExportList, GUIDPreservedSymbols,
|
|
|
|
ModuleToDefinedGVSummaries[ModuleIdentifier], CacheOptions,
|
2017-03-29 02:55:44 +08:00
|
|
|
DisableCodeGen, SaveTempsDir, Freestanding, OptLevel, count);
|
2016-04-21 13:54:23 +08:00
|
|
|
|
2016-12-14 12:56:42 +08:00
|
|
|
// Commit to the cache (if enabled)
|
|
|
|
CacheEntry.write(*OutputBuffer);
|
|
|
|
|
|
|
|
if (SavedObjectsDirectoryPath.empty()) {
|
|
|
|
// We need to generated a memory buffer for the linker.
|
|
|
|
if (!CacheEntryPath.empty()) {
|
|
|
|
// Cache is enabled, reload from the cache
|
|
|
|
// We do this to lower memory pressuree: the buffer is on the heap
|
|
|
|
// and releasing it frees memory that can be used for the next input
|
|
|
|
// file. The final binary link will read from the VFS cache
|
|
|
|
// (hopefully!) or from disk if the memory pressure wasn't too high.
|
|
|
|
auto ReloadedBufferOrErr = CacheEntry.tryLoadingBuffer();
|
|
|
|
if (auto EC = ReloadedBufferOrErr.getError()) {
|
|
|
|
// On error, keeping the preexisting buffer and printing a
|
|
|
|
// diagnostic is more friendly than just crashing.
|
|
|
|
errs() << "error: can't reload cached file '" << CacheEntryPath
|
|
|
|
<< "': " << EC.message() << "\n";
|
|
|
|
} else {
|
|
|
|
OutputBuffer = std::move(*ReloadedBufferOrErr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ProducedBinaries[count] = std::move(OutputBuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ProducedBinaryFiles[count] = writeGeneratedObject(
|
|
|
|
count, CacheEntryPath, SavedObjectsDirectoryPath, *OutputBuffer);
|
2016-05-17 03:33:07 +08:00
|
|
|
}, IndexCount);
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 06:54:18 +08:00
|
|
|
pruneCache(CacheOptions.Path, CacheOptions.Policy);
|
2016-04-21 13:54:23 +08:00
|
|
|
|
2016-03-09 09:37:22 +08:00
|
|
|
// If statistics were requested, print them out now.
|
|
|
|
if (llvm::AreStatisticsEnabled())
|
|
|
|
llvm::PrintStatistics();
|
2017-05-16 17:43:21 +08:00
|
|
|
reportAndResetTimings();
|
2016-03-09 09:37:22 +08:00
|
|
|
}
|