blob: 995ebacacb870c1352a7bde857f08acf202a1352 [file] [log] [blame]
//===- llvm-lto: a simple command-line program to link modules with LTO ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This program takes in a list of bitcode files, links them, performs link-time
// optimization, and outputs an object file.
//
//===----------------------------------------------------------------------===//
#include "llvm-c/lto.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
#include "llvm/LTO/legacy/LTOModule.h"
#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetOptions.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <system_error>
#include <tuple>
#include <utility>
#include <vector>
using namespace llvm;
static codegen::RegisterCodeGenFlags CGF;
static cl::OptionCategory LTOCategory("LTO Options");
static cl::opt<char>
OptLevel("O",
cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
"(default = '-O2')"),
cl::Prefix, cl::ZeroOrMore, cl::init('2'), cl::cat(LTOCategory));
static cl::opt<bool>
IndexStats("thinlto-index-stats",
cl::desc("Print statistic for the index in every input files"),
cl::init(false), cl::cat(LTOCategory));
static cl::opt<bool> DisableVerify(
"disable-verify", cl::init(false),
cl::desc("Do not run the verifier during the optimization pipeline"),
cl::cat(LTOCategory));
static cl::opt<bool> EnableFreestanding(
"lto-freestanding", cl::init(false),
cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
cl::cat(LTOCategory));
static cl::opt<bool> UseDiagnosticHandler(
"use-diagnostic-handler", cl::init(false),
cl::desc("Use a diagnostic handler to test the handler interface"),
cl::cat(LTOCategory));
static cl::opt<bool>
ThinLTO("thinlto", cl::init(false),
cl::desc("Only write combined global index for ThinLTO backends"),
cl::cat(LTOCategory));
enum ThinLTOModes {
THINLINK,
THINDISTRIBUTE,
THINEMITIMPORTS,
THINPROMOTE,
THINIMPORT,
THININTERNALIZE,
THINOPT,
THINCODEGEN,
THINALL
};
cl::opt<ThinLTOModes> ThinLTOMode(
"thinlto-action", cl::desc("Perform a single ThinLTO stage:"),
cl::values(
clEnumValN(
THINLINK, "thinlink",
"ThinLink: produces the index by linking only the summaries."),
clEnumValN(THINDISTRIBUTE, "distributedindexes",
"Produces individual indexes for distributed backends."),
clEnumValN(THINEMITIMPORTS, "emitimports",
"Emit imports files for distributed backends."),
clEnumValN(THINPROMOTE, "promote",
"Perform pre-import promotion (requires -thinlto-index)."),
clEnumValN(THINIMPORT, "import",
"Perform both promotion and "
"cross-module importing (requires "
"-thinlto-index)."),
clEnumValN(THININTERNALIZE, "internalize",
"Perform internalization driven by -exported-symbol "
"(requires -thinlto-index)."),
clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."),
clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"),
clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end")),
cl::cat(LTOCategory));
static cl::opt<std::string>
ThinLTOIndex("thinlto-index",
cl::desc("Provide the index produced by a ThinLink, required "
"to perform the promotion and/or importing."),
cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOPrefixReplace(
"thinlto-prefix-replace",
cl::desc("Control where files for distributed backends are "
"created. Expects 'oldprefix;newprefix' and if path "
"prefix of output file is oldprefix it will be "
"replaced with newprefix."),
cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOModuleId(
"thinlto-module-id",
cl::desc("For the module ID for the file to process, useful to "
"match what is in the index."),
cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir",
cl::desc("Enable ThinLTO caching."),
cl::cat(LTOCategory));
static cl::opt<int> ThinLTOCachePruningInterval(
"thinlto-cache-pruning-interval", cl::init(1200),
cl::desc("Set ThinLTO cache pruning interval."), cl::cat(LTOCategory));
static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes(
"thinlto-cache-max-size-bytes",
cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."),
cl::cat(LTOCategory));
static cl::opt<int> ThinLTOCacheMaxSizeFiles(
"thinlto-cache-max-size-files", cl::init(1000000),
cl::desc("Set ThinLTO cache pruning directory maximum number of files."),
cl::cat(LTOCategory));
static cl::opt<unsigned> ThinLTOCacheEntryExpiration(
"thinlto-cache-entry-expiration", cl::init(604800) /* 1w */,
cl::desc("Set ThinLTO cache entry expiration time."), cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOSaveTempsPrefix(
"thinlto-save-temps",
cl::desc("Save ThinLTO temp files using filenames created by adding "
"suffixes to the given file path prefix."),
cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOGeneratedObjectsDir(
"thinlto-save-objects",
cl::desc("Save ThinLTO generated object files using filenames created in "
"the given directory."),
cl::cat(LTOCategory));
static cl::opt<bool> SaveLinkedModuleFile(
"save-linked-module", cl::init(false),
cl::desc("Write linked LTO module to file before optimize"),
cl::cat(LTOCategory));
static cl::opt<bool>
SaveModuleFile("save-merged-module", cl::init(false),
cl::desc("Write merged LTO module to file before CodeGen"),
cl::cat(LTOCategory));
static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input bitcode files>"),
cl::cat(LTOCategory));
static cl::opt<std::string> OutputFilename("o", cl::init(""),
cl::desc("Override output filename"),
cl::value_desc("filename"),
cl::cat(LTOCategory));
static cl::list<std::string> ExportedSymbols(
"exported-symbol",
cl::desc("List of symbols to export from the resulting object file"),
cl::ZeroOrMore, cl::cat(LTOCategory));
static cl::list<std::string>
DSOSymbols("dso-symbol",
cl::desc("Symbol to put in the symtab in the resulting dso"),
cl::ZeroOrMore, cl::cat(LTOCategory));
static cl::opt<bool> ListSymbolsOnly(
"list-symbols-only", cl::init(false),
cl::desc("Instead of running LTO, list the symbols in each IR file"),
cl::cat(LTOCategory));
static cl::opt<bool> ListDependentLibrariesOnly(
"list-dependent-libraries-only", cl::init(false),
cl::desc(
"Instead of running LTO, list the dependent libraries in each IR file"),
cl::cat(LTOCategory));
static cl::opt<bool> QueryHasCtorDtor(
"query-hasCtorDtor", cl::init(false),
cl::desc("Queries LTOModule::hasCtorDtor() on each IR file"));
static cl::opt<bool>
SetMergedModule("set-merged-module", cl::init(false),
cl::desc("Use the first input module as the merged module"),
cl::cat(LTOCategory));
static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1),
cl::desc("Number of backend threads"),
cl::cat(LTOCategory));
static cl::opt<bool> RestoreGlobalsLinkage(
"restore-linkage", cl::init(false),
cl::desc("Restore original linkage of globals prior to CodeGen"),
cl::cat(LTOCategory));
static cl::opt<bool> CheckHasObjC(
"check-for-objc", cl::init(false),
cl::desc("Only check if the module has objective-C defined in it"),
cl::cat(LTOCategory));
static cl::opt<bool> PrintMachOCPUOnly(
"print-macho-cpu-only", cl::init(false),
cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"),
cl::cat(LTOCategory));
static cl::opt<bool> UseNewPM(
"use-new-pm", cl::desc("Run LTO passes using the new pass manager"),
cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden, cl::cat(LTOCategory));
static cl::opt<bool>
DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
cl::desc("Print pass management debugging information"),
cl::cat(LTOCategory));
namespace {
struct ModuleInfo {
std::vector<bool> CanBeHidden;
};
} // end anonymous namespace
static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity,
const char *Msg, void *) {
errs() << "llvm-lto: ";
switch (Severity) {
case LTO_DS_NOTE:
errs() << "note: ";
break;
case LTO_DS_REMARK:
errs() << "remark: ";
break;
case LTO_DS_ERROR:
errs() << "error: ";
break;
case LTO_DS_WARNING:
errs() << "warning: ";
break;
}
errs() << Msg << "\n";
}
static std::string CurrentActivity;
namespace {
struct LLVMLTODiagnosticHandler : public DiagnosticHandler {
bool handleDiagnostics(const DiagnosticInfo &DI) override {
raw_ostream &OS = errs();
OS << "llvm-lto: ";
switch (DI.getSeverity()) {
case DS_Error:
OS << "error";
break;
case DS_Warning:
OS << "warning";
break;
case DS_Remark:
OS << "remark";
break;
case DS_Note:
OS << "note";
break;
}
if (!CurrentActivity.empty())
OS << ' ' << CurrentActivity;
OS << ": ";
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
OS << '\n';
if (DI.getSeverity() == DS_Error)
exit(1);
return true;
}
};
}
static void error(const Twine &Msg) {
errs() << "llvm-lto: " << Msg << '\n';
exit(1);
}
static void error(std::error_code EC, const Twine &Prefix) {
if (EC)
error(Prefix + ": " + EC.message());
}
template <typename T>
static void error(const ErrorOr<T> &V, const Twine &Prefix) {
error(V.getError(), Prefix);
}
static void maybeVerifyModule(const Module &Mod) {
if (!DisableVerify && verifyModule(Mod, &errs()))
error("Broken Module");
}
static std::unique_ptr<LTOModule>
getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
const TargetOptions &Options) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFile(Path);
error(BufferOrErr, "error loading file '" + Path + "'");
Buffer = std::move(BufferOrErr.get());
CurrentActivity = ("loading file '" + Path + "'").str();
std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>();
Context->setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
true);
ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext(
std::move(Context), Buffer->getBufferStart(), Buffer->getBufferSize(),
Options, Path);
CurrentActivity = "";
maybeVerifyModule((*Ret)->getModule());
return std::move(*Ret);
}
/// Print some statistics on the index for each input files.
static void printIndexStats() {
for (auto &Filename : InputFilenames) {
ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");
std::unique_ptr<ModuleSummaryIndex> Index =
ExitOnErr(getModuleSummaryIndexForFile(Filename));
// Skip files without a module summary.
if (!Index)
report_fatal_error(Twine(Filename) + " does not contain an index");
unsigned Calls = 0, Refs = 0, Functions = 0, Alias = 0, Globals = 0;
for (auto &Summaries : *Index) {
for (auto &Summary : Summaries.second.SummaryList) {
Refs += Summary->refs().size();
if (auto *FuncSummary = dyn_cast<FunctionSummary>(Summary.get())) {
Functions++;
Calls += FuncSummary->calls().size();
} else if (isa<AliasSummary>(Summary.get()))
Alias++;
else
Globals++;
}
}
outs() << "Index " << Filename << " contains "
<< (Alias + Globals + Functions) << " nodes (" << Functions
<< " functions, " << Alias << " alias, " << Globals
<< " globals) and " << (Calls + Refs) << " edges (" << Refs
<< " refs and " << Calls << " calls)\n";
}
}
/// Load each IR file and dump certain information based on active flags.
///
/// The main point here is to provide lit-testable coverage for the LTOModule
/// functionality that's exposed by the C API. Moreover, this provides testing
/// coverage for modules that have been created in their own contexts.
static void testLTOModule(const TargetOptions &Options) {
for (auto &Filename : InputFilenames) {
std::unique_ptr<MemoryBuffer> Buffer;
std::unique_ptr<LTOModule> Module =
getLocalLTOModule(Filename, Buffer, Options);
if (ListSymbolsOnly) {
// List the symbols.
outs() << Filename << ":\n";
for (int I = 0, E = Module->getSymbolCount(); I != E; ++I)
outs() << Module->getSymbolName(I) << "\n";
}
if (QueryHasCtorDtor)
outs() << Filename
<< ": hasCtorDtor = " << (Module->hasCtorDtor() ? "true" : "false")
<< "\n";
}
}
static std::unique_ptr<MemoryBuffer> loadFile(StringRef Filename) {
ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() +
"': ");
return ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename)));
}
static void listDependentLibraries() {
for (auto &Filename : InputFilenames) {
auto Buffer = loadFile(Filename);
std::string E;
std::unique_ptr<lto::InputFile> Input(LTOModule::createInputFile(
Buffer->getBufferStart(), Buffer->getBufferSize(), Filename.c_str(),
E));
if (!Input)
error(E);
// List the dependent libraries.
outs() << Filename << ":\n";
for (size_t I = 0, C = LTOModule::getDependentLibraryCount(Input.get());
I != C; ++I) {
size_t L = 0;
const char *S = LTOModule::getDependentLibrary(Input.get(), I, &L);
assert(S);
outs() << StringRef(S, L) << "\n";
}
}
}
static void printMachOCPUOnly() {
LLVMContext Context;
Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
true);
TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
for (auto &Filename : InputFilenames) {
ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
LTOModule::createFromFile(Context, Filename, Options);
if (!ModuleOrErr)
error(ModuleOrErr, "llvm-lto: ");
Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType();
Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType();
if (!CPUType)
error("Error while printing mach-o cputype: " +
toString(CPUType.takeError()));
if (!CPUSubType)
error("Error while printing mach-o cpusubtype: " +
toString(CPUSubType.takeError()));
outs() << llvm::format("%s:\ncputype: %u\ncpusubtype: %u\n",
Filename.c_str(), *CPUType, *CPUSubType);
}
}
/// Create a combined index file from the input IR files and write it.
///
/// This is meant to enable testing of ThinLTO combined index generation,
/// currently available via the gold plugin via -thinlto.
static void createCombinedModuleSummaryIndex() {
ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false);
uint64_t NextModuleId = 0;
for (auto &Filename : InputFilenames) {
ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");
std::unique_ptr<MemoryBuffer> MB =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename)));
ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, NextModuleId++));
}
// In order to use this index for testing, specifically import testing, we
// need to update any indirect call edges created from SamplePGO, so that they
// point to the correct GUIDs.
updateIndirectCalls(CombinedIndex);
std::error_code EC;
assert(!OutputFilename.empty());
raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC,
sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'");
WriteIndexToFile(CombinedIndex, OS);
OS.close();
}
/// Parse the thinlto_prefix_replace option into the \p OldPrefix and
/// \p NewPrefix strings, if it was specified.
static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
std::string &NewPrefix) {
assert(ThinLTOPrefixReplace.empty() ||
ThinLTOPrefixReplace.find(';') != StringRef::npos);
StringRef PrefixReplace = ThinLTOPrefixReplace;
std::pair<StringRef, StringRef> Split = PrefixReplace.split(";");
OldPrefix = Split.first.str();
NewPrefix = Split.second.str();
}
/// Given the original \p Path to an output file, replace any path
/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
/// resulting directory if it does not yet exist.
static std::string getThinLTOOutputFile(const std::string &Path,
const std::string &OldPrefix,
const std::string &NewPrefix) {
if (OldPrefix.empty() && NewPrefix.empty())
return Path;
SmallString<128> NewPath(Path);
llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix);
StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str());
if (!ParentPath.empty()) {
// Make sure the new directory exists, creating it if necessary.
if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
error(EC, "error creating the directory '" + ParentPath + "'");
}
return std::string(NewPath.str());
}
namespace thinlto {
std::vector<std::unique_ptr<MemoryBuffer>>
loadAllFilesForIndex(const ModuleSummaryIndex &Index) {
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (auto &ModPath : Index.modulePaths()) {
const auto &Filename = ModPath.first();
std::string CurrentActivity = ("loading file '" + Filename + "'").str();
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
}
return InputBuffers;
}
std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() {
if (ThinLTOIndex.empty())
report_fatal_error("Missing -thinlto-index for ThinLTO promotion stage");
ExitOnError ExitOnErr("llvm-lto: error loading file '" + ThinLTOIndex +
"': ");
return ExitOnErr(getModuleSummaryIndexForFile(ThinLTOIndex));
}
static std::unique_ptr<lto::InputFile> loadInputFile(MemoryBufferRef Buffer) {
ExitOnError ExitOnErr("llvm-lto: error loading input '" +
Buffer.getBufferIdentifier().str() + "': ");
return ExitOnErr(lto::InputFile::create(Buffer));
}
static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File,
LLVMContext &CTX) {
auto &Mod = File.getSingleBitcodeModule();
auto ModuleOrErr = Mod.parseModule(CTX);
if (!ModuleOrErr) {
handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(),
SourceMgr::DK_Error, EIB.message());
Err.print("llvm-lto", errs());
});
report_fatal_error("Can't load module, abort.");
}
maybeVerifyModule(**ModuleOrErr);
if (ThinLTOModuleId.getNumOccurrences()) {
if (InputFilenames.size() != 1)
report_fatal_error("Can't override the module id for multiple files");
(*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId);
}
return std::move(*ModuleOrErr);
}
static void writeModuleToFile(Module &TheModule, StringRef Filename) {
std::error_code EC;
raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + Filename + "'");
maybeVerifyModule(TheModule);
WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true);
}
class ThinLTOProcessing {
public:
ThinLTOCodeGenerator ThinGenerator;
ThinLTOProcessing(const TargetOptions &Options) {
ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel());
ThinGenerator.setTargetOptions(Options);
ThinGenerator.setCacheDir(ThinLTOCacheDir);
ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval);
ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration);
ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles);
ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes);
ThinGenerator.setFreestanding(EnableFreestanding);
ThinGenerator.setUseNewPM(UseNewPM);
ThinGenerator.setDebugPassManager(DebugPassManager);
// Add all the exported symbols to the table of symbols to preserve.
for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
ThinGenerator.preserveSymbol(ExportedSymbols[i]);
}
void run() {
switch (ThinLTOMode) {
case THINLINK:
return thinLink();
case THINDISTRIBUTE:
return distributedIndexes();
case THINEMITIMPORTS:
return emitImports();
case THINPROMOTE:
return promote();
case THINIMPORT:
return import();
case THININTERNALIZE:
return internalize();
case THINOPT:
return optimize();
case THINCODEGEN:
return codegen();
case THINALL:
return runAll();
}
}
private:
/// Load the input files, create the combined index, and write it out.
void thinLink() {
// Perform "ThinLink": just produce the index
if (OutputFilename.empty())
report_fatal_error(
"OutputFilename is necessary to store the combined index.\n");
LLVMContext Ctx;
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (unsigned i = 0; i < InputFilenames.size(); ++i) {
auto &Filename = InputFilenames[i];
std::string CurrentActivity = "loading file '" + Filename + "'";
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
}
auto CombinedIndex = ThinGenerator.linkCombinedIndex();
if (!CombinedIndex)
report_fatal_error("ThinLink didn't create an index");
std::error_code EC;
raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + OutputFilename + "'");
WriteIndexToFile(*CombinedIndex, OS);
}
/// Load the combined index from disk, then compute and generate
/// individual index files suitable for ThinLTO distributed backend builds
/// on the files mentioned on the command line (these must match the index
/// content).
void distributedIndexes() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
std::string OldPrefix, NewPrefix;
getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
auto Index = loadCombinedIndex();
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
// Build a map of module to the GUIDs and summary objects that should
// be written to its index.
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
ThinGenerator.gatherImportedSummariesForModule(
*TheModule, *Index, ModuleToSummariesForIndex, *Input);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".thinlto.bc";
}
OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
std::error_code EC;
raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + OutputName + "'");
WriteIndexToFile(*Index, OS, &ModuleToSummariesForIndex);
}
}
/// Load the combined index from disk, compute the imports, and emit
/// the import file lists for each module to disk.
void emitImports() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
std::string OldPrefix, NewPrefix;
getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
auto Index = loadCombinedIndex();
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".imports";
}
OutputName =
getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
ThinGenerator.emitImports(*TheModule, OutputName, *Index, *Input);
}
}
/// Load the combined index from disk, then load every file referenced by
/// the index and add them to the generator, finally perform the promotion
/// on the files mentioned on the command line (these must match the index
/// content).
void promote() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
auto Index = loadCombinedIndex();
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
ThinGenerator.promote(*TheModule, *Index, *Input);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".thinlto.promoted.bc";
}
writeModuleToFile(*TheModule, OutputName);
}
}
/// Load the combined index from disk, then load every file referenced by
/// the index and add them to the generator, then performs the promotion and
/// cross module importing on the files mentioned on the command line
/// (these must match the index content).
void import() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
auto Index = loadCombinedIndex();
auto InputBuffers = loadAllFilesForIndex(*Index);
for (auto &MemBuffer : InputBuffers)
ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
MemBuffer->getBuffer());
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
ThinGenerator.crossModuleImport(*TheModule, *Index, *Input);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".thinlto.imported.bc";
}
writeModuleToFile(*TheModule, OutputName);
}
}
void internalize() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
if (ExportedSymbols.empty())
errs() << "Warning: -internalize will not perform without "
"-exported-symbol\n";
auto Index = loadCombinedIndex();
auto InputBuffers = loadAllFilesForIndex(*Index);
for (auto &MemBuffer : InputBuffers)
ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
MemBuffer->getBuffer());
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
ThinGenerator.internalize(*TheModule, *Index, *Input);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".thinlto.internalized.bc";
}
writeModuleToFile(*TheModule, OutputName);
}
}
void optimize() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
if (!ThinLTOIndex.empty())
errs() << "Warning: -thinlto-index ignored for optimize stage";
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto Buffer = loadFile(Filename);
auto Input = loadInputFile(Buffer->getMemBufferRef());
auto TheModule = loadModuleFromInput(*Input, Ctx);
ThinGenerator.optimize(*TheModule);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".thinlto.imported.bc";
}
writeModuleToFile(*TheModule, OutputName);
}
}
void codegen() {
if (InputFilenames.size() != 1 && !OutputFilename.empty())
report_fatal_error("Can't handle a single output filename and multiple "
"input files, do not provide an output filename and "
"the output files will be suffixed from the input "
"ones.");
if (!ThinLTOIndex.empty())
errs() << "Warning: -thinlto-index ignored for codegen stage";
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (auto &Filename : InputFilenames) {
LLVMContext Ctx;
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
}
ThinGenerator.setCodeGenOnly(true);
ThinGenerator.run();
for (auto BinName :
zip(ThinGenerator.getProducedBinaries(), InputFilenames)) {
std::string OutputName = OutputFilename;
if (OutputName.empty())
OutputName = std::get<1>(BinName) + ".thinlto.o";
else if (OutputName == "-") {
outs() << std::get<0>(BinName)->getBuffer();
return;
}
std::error_code EC;
raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + OutputName + "'");
OS << std::get<0>(BinName)->getBuffer();
}
}
/// Full ThinLTO process
void runAll() {
if (!OutputFilename.empty())
report_fatal_error("Do not provide an output filename for ThinLTO "
" processing, the output files will be suffixed from "
"the input ones.");
if (!ThinLTOIndex.empty())
errs() << "Warning: -thinlto-index ignored for full ThinLTO process";
LLVMContext Ctx;
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (unsigned i = 0; i < InputFilenames.size(); ++i) {
auto &Filename = InputFilenames[i];
std::string CurrentActivity = "loading file '" + Filename + "'";
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
}
if (!ThinLTOSaveTempsPrefix.empty())
ThinGenerator.setSaveTempsDir(ThinLTOSaveTempsPrefix);
if (!ThinLTOGeneratedObjectsDir.empty()) {
ThinGenerator.setGeneratedObjectsDirectory(ThinLTOGeneratedObjectsDir);
ThinGenerator.run();
return;
}
ThinGenerator.run();
auto &Binaries = ThinGenerator.getProducedBinaries();
if (Binaries.size() != InputFilenames.size())
report_fatal_error("Number of output objects does not match the number "
"of inputs");
for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) {
auto OutputName = InputFilenames[BufID] + ".thinlto.o";
std::error_code EC;
raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
error(EC, "error opening the file '" + OutputName + "'");
OS << Binaries[BufID]->getBuffer();
}
}
/// Load the combined index from disk, then load every file referenced by
};
} // end namespace thinlto
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::HideUnrelatedOptions({&LTOCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n");
if (OptLevel < '0' || OptLevel > '3')
error("optimization level must be between 0 and 3");
// Initialize the configured targets.
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
// set up the TargetOptions for the machine
TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
if (ListSymbolsOnly || QueryHasCtorDtor) {
testLTOModule(Options);
return 0;
}
if (ListDependentLibrariesOnly) {
listDependentLibraries();
return 0;
}
if (IndexStats) {
printIndexStats();
return 0;
}
if (CheckHasObjC) {
for (auto &Filename : InputFilenames) {
ExitOnError ExitOnErr(std::string(*argv) + ": error loading file '" +
Filename + "': ");
std::unique_ptr<MemoryBuffer> BufferOrErr =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(Filename)));
auto Buffer = std::move(BufferOrErr.get());
if (ExitOnErr(isBitcodeContainingObjCCategory(*Buffer)))
outs() << "Bitcode " << Filename << " contains ObjC\n";
else
outs() << "Bitcode " << Filename << " does not contain ObjC\n";
}
return 0;
}
if (PrintMachOCPUOnly) {
printMachOCPUOnly();
return 0;
}
if (ThinLTOMode.getNumOccurrences()) {
if (ThinLTOMode.getNumOccurrences() > 1)
report_fatal_error("You can't specify more than one -thinlto-action");
thinlto::ThinLTOProcessing ThinLTOProcessor(Options);
ThinLTOProcessor.run();
return 0;
}
if (ThinLTO) {
createCombinedModuleSummaryIndex();
return 0;
}
unsigned BaseArg = 0;
LLVMContext Context;
Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
true);
LTOCodeGenerator CodeGen(Context);
CodeGen.setDisableVerify(DisableVerify);
if (UseDiagnosticHandler)
CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr);
CodeGen.setCodePICModel(codegen::getExplicitRelocModel());
CodeGen.setFreestanding(EnableFreestanding);
CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF);
CodeGen.setTargetOptions(Options);
CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage);
StringSet<MallocAllocator> DSOSymbolsSet;
for (unsigned i = 0; i < DSOSymbols.size(); ++i)
DSOSymbolsSet.insert(DSOSymbols[i]);
std::vector<std::string> KeptDSOSyms;
for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) {
CurrentActivity = "loading file '" + InputFilenames[i] + "'";
ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
LTOModule::createFromFile(Context, InputFilenames[i], Options);
std::unique_ptr<LTOModule> &Module = *ModuleOrErr;
CurrentActivity = "";
unsigned NumSyms = Module->getSymbolCount();
for (unsigned I = 0; I < NumSyms; ++I) {
StringRef Name = Module->getSymbolName(I);
if (!DSOSymbolsSet.count(Name))
continue;
lto_symbol_attributes Attrs = Module->getSymbolAttributes(I);
unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK;
if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN)
KeptDSOSyms.push_back(std::string(Name));
}
// We use the first input module as the destination module when
// SetMergedModule is true.
if (SetMergedModule && i == BaseArg) {
// Transfer ownership to the code generator.
CodeGen.setModule(std::move(Module));
} else if (!CodeGen.addModule(Module.get())) {
// Print a message here so that we know addModule() did not abort.
error("error adding file '" + InputFilenames[i] + "'");
}
}
// Add all the exported symbols to the table of symbols to preserve.
for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
CodeGen.addMustPreserveSymbol(ExportedSymbols[i]);
// Add all the dso symbols to the table of symbols to expose.
for (unsigned i = 0; i < KeptDSOSyms.size(); ++i)
CodeGen.addMustPreserveSymbol(KeptDSOSyms[i]);
// Set cpu and attrs strings for the default target/subtarget.
CodeGen.setCpu(codegen::getMCPU());
CodeGen.setOptLevel(OptLevel - '0');
CodeGen.setAttrs(codegen::getMAttrs());
CodeGen.setUseNewPM(UseNewPM);
if (auto FT = codegen::getExplicitFileType())
CodeGen.setFileType(FT.getValue());
if (!OutputFilename.empty()) {
if (SaveLinkedModuleFile) {
std::string ModuleFilename = OutputFilename;
ModuleFilename += ".linked.bc";
std::string ErrMsg;
if (!CodeGen.writeMergedModules(ModuleFilename))
error("writing linked module failed.");
}
if (!CodeGen.optimize()) {
// Diagnostic messages should have been printed by the handler.
error("error optimizing the code");
}
if (SaveModuleFile) {
std::string ModuleFilename = OutputFilename;
ModuleFilename += ".merged.bc";
std::string ErrMsg;
if (!CodeGen.writeMergedModules(ModuleFilename))
error("writing merged module failed.");
}
auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
std::string PartFilename = OutputFilename;
if (Parallelism != 1)
PartFilename += "." + utostr(Task);
std::error_code EC;
auto S =
std::make_unique<raw_fd_ostream>(PartFilename, EC, sys::fs::OF_None);
if (EC)
error("error opening the file '" + PartFilename + "': " + EC.message());
return std::make_unique<CachedFileStream>(std::move(S));
};
if (!CodeGen.compileOptimized(AddStream, Parallelism))
// Diagnostic messages should have been printed by the handler.
error("error compiling the code");
} else {
if (Parallelism != 1)
error("-j must be specified together with -o");
if (SaveModuleFile)
error(": -save-merged-module must be specified with -o");
const char *OutputName = nullptr;
if (!CodeGen.compile_to_file(&OutputName))
error("error compiling the code");
// Diagnostic messages should have been printed by the handler.
outs() << "Wrote native object file '" << OutputName << "'\n";
}
return 0;
}