1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 04:32:44 +01:00

Restore "Resolution-based LTO API."

This restores commit r278330, with fixes for a few bot failures:
- Fix a late change I had made to the save temps output file that I
  missed due to existing files sitting on my disk
- Fix a bunch of Windows bot failures with "ambiguous call to overloaded
  function" due to confusion between llvm::make_unique vs
  std::make_unique (preface the new make_unique calls with "llvm::")
- Attempt to fix a modules bot failure by adding a missing include
  to LTO/Config.h.

Original change:

Resolution-based LTO API.

Summary:
This introduces a resolution-based LTO API. The main advantage of this API over
existing APIs is that it allows the linker to supply a resolution for each
symbol in each object, rather than the combined object as a whole. This will
become increasingly important for use cases such as ThinLTO which require us
to process symbol resolutions in a more complicated way than just adjusting
linkage.

Patch by Peter Collingbourne.

Reviewers: rafael, tejohnson, mehdi_amini

Subscribers: lhames, tejohnson, mehdi_amini, llvm-commits

Differential Revision: https://reviews.llvm.org/D20268

llvm-svn: 278338
This commit is contained in:
Teresa Johnson 2016-08-11 14:58:12 +00:00
parent 602583e93c
commit bd574a6d8f
37 changed files with 2065 additions and 1052 deletions

172
include/llvm/LTO/Config.h Normal file
View File

@ -0,0 +1,172 @@
//===-Config.h - LLVM Link Time Optimizer Configuration -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the lto::Config data structure, which allows clients to
// configure LTO.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LTO_CONFIG_H
#define LLVM_LTO_CONFIG_H
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetOptions.h"
#include <functional>
namespace llvm {
class Error;
class Module;
class ModuleSummaryIndex;
class raw_pwrite_stream;
namespace lto {
/// LTO configuration. A linker can configure LTO by setting fields in this data
/// structure and passing it to the lto::LTO constructor.
struct Config {
std::string CPU;
std::string Features;
TargetOptions Options;
std::vector<std::string> MAttrs;
Reloc::Model RelocModel = Reloc::PIC_;
CodeModel::Model CodeModel = CodeModel::Default;
CodeGenOpt::Level CGOptLevel = CodeGenOpt::Default;
unsigned OptLevel = 2;
bool DisableVerify = false;
/// Setting this field will replace target triples in input files with this
/// triple.
std::string OverrideTriple;
/// Setting this field will replace unspecified target triples in input files
/// with this triple.
std::string DefaultTriple;
bool ShouldDiscardValueNames = true;
DiagnosticHandlerFunction DiagHandler;
/// If this field is set, LTO will write input file paths and symbol
/// resolutions here in llvm-lto2 command line flag format. This can be
/// used for testing and for running the LTO pipeline outside of the linker
/// with llvm-lto2.
std::unique_ptr<raw_ostream> ResolutionFile;
/// The following callbacks deal with tasks, which normally represent the
/// entire optimization and code generation pipeline for what will become a
/// single native object file. Each task has a unique identifier between 0 and
/// getMaxTasks()-1, which is supplied to the callback via the Task parameter.
/// A task represents the entire pipeline for ThinLTO and regular
/// (non-parallel) LTO, but a parallel code generation task will be split into
/// N tasks before code generation, where N is the parallelism level.
///
/// LTO may decide to stop processing a task at any time, for example if the
/// module is empty or if a module hook (see below) returns false. For this
/// reason, the client should not expect to receive exactly getMaxTasks()
/// native object files.
/// A module hook may be used by a linker to perform actions during the LTO
/// pipeline. For example, a linker may use this function to implement
/// -save-temps, or to add its own resolved symbols to the module. If this
/// function returns false, any further processing for that task is aborted.
///
/// Module hooks must be thread safe with respect to the linker's internal
/// data structures. A module hook will never be called concurrently from
/// multiple threads with the same task ID, or the same module.
///
/// Note that in out-of-process backend scenarios, none of the hooks will be
/// called for ThinLTO tasks.
typedef std::function<bool(size_t Task, Module &)> ModuleHookFn;
/// This module hook is called after linking (regular LTO) or loading
/// (ThinLTO) the module, before modifying it.
ModuleHookFn PreOptModuleHook;
/// This hook is called after promoting any internal functions
/// (ThinLTO-specific).
ModuleHookFn PostPromoteModuleHook;
/// This hook is called after internalizing the module.
ModuleHookFn PostInternalizeModuleHook;
/// This hook is called after importing from other modules (ThinLTO-specific).
ModuleHookFn PostImportModuleHook;
/// This module hook is called after optimization is complete.
ModuleHookFn PostOptModuleHook;
/// This module hook is called before code generation. It is similar to the
/// PostOptModuleHook, but for parallel code generation it is called after
/// splitting the module.
ModuleHookFn PreCodeGenModuleHook;
/// A combined index hook is called after all per-module indexes have been
/// combined (ThinLTO-specific). It can be used to implement -save-temps for
/// the combined index.
///
/// If this function returns false, any further processing for ThinLTO tasks
/// is aborted.
///
/// It is called regardless of whether the backend is in-process, although it
/// is not called from individual backend processes.
typedef std::function<bool(const ModuleSummaryIndex &Index)>
CombinedIndexHookFn;
CombinedIndexHookFn CombinedIndexHook;
/// This is a convenience function that configures this Config object to write
/// temporary files named after the given OutputFileName for each of the LTO
/// phases to disk. A client can use this function to implement -save-temps.
///
/// FIXME: Temporary files derived from ThinLTO backends are currently named
/// after the input file name, rather than the output file name, when
/// UseInputModulePath is set to true.
///
/// Specifically, it (1) sets each of the above module hooks and the combined
/// index hook to a function that calls the hook function (if any) that was
/// present in the appropriate field when the addSaveTemps function was
/// called, and writes the module to a bitcode file with a name prefixed by
/// the given output file name, and (2) creates a resolution file whose name
/// is prefixed by the given output file name and sets ResolutionFile to its
/// file handle.
Error addSaveTemps(std::string OutputFileName,
bool UseInputModulePath = false);
};
/// This type defines a stream callback. A stream callback is used to add a
/// native object that is generated on the fly. The callee must set up and
/// return a output stream to write the native object to.
///
/// Stream callbacks must be thread safe.
typedef std::function<std::unique_ptr<raw_pwrite_stream>(size_t Task)>
AddStreamFn;
/// A derived class of LLVMContext that initializes itself according to a given
/// Config object. The purpose of this class is to tie ownership of the
/// diagnostic handler to the context, as opposed to the Config object (which
/// may be ephemeral).
struct LTOLLVMContext : LLVMContext {
static void funcDiagHandler(const DiagnosticInfo &DI, void *Context) {
auto *Fn = static_cast<DiagnosticHandlerFunction *>(Context);
(*Fn)(DI);
}
LTOLLVMContext(const Config &C) : DiagHandler(C.DiagHandler) {
setDiscardValueNames(C.ShouldDiscardValueNames);
enableDebugTypeODRUniquing();
setDiagnosticHandler(funcDiagHandler, &DiagHandler, true);
}
DiagnosticHandlerFunction DiagHandler;
};
}
}
#endif

View File

@ -16,14 +16,27 @@
#ifndef LLVM_LTO_LTO_H
#define LLVM_LTO_LTO_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/LTO/Config.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/thread.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
namespace llvm {
class Error;
class LLVMContext;
class MemoryBufferRef;
class Module;
class Target;
class raw_pwrite_stream;
/// Helper to load a module from bitcode.
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
@ -69,6 +82,319 @@ void thinLTOResolveWeakForLinkerInIndex(
void thinLTOInternalizeAndPromoteInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported);
}
namespace lto {
class LTO;
struct SymbolResolution;
class ThinBackendProc;
/// An input file. This is a wrapper for IRObjectFile that exposes only the
/// information that an LTO client should need in order to do symbol resolution.
class InputFile {
// FIXME: Remove LTO class friendship once we have bitcode symbol tables.
friend LTO;
InputFile() = default;
// FIXME: Remove the LLVMContext once we have bitcode symbol tables.
LLVMContext Ctx;
std::unique_ptr<object::IRObjectFile> Obj;
public:
/// Create an InputFile.
static Expected<std::unique_ptr<InputFile>> create(MemoryBufferRef Object);
class symbol_iterator;
/// This is a wrapper for object::basic_symbol_iterator that exposes only the
/// information that an LTO client should need in order to do symbol
/// resolution.
///
/// This object is ephemeral; it is only valid as long as an iterator obtained
/// from symbols() refers to it.
class Symbol {
friend symbol_iterator;
friend LTO;
object::basic_symbol_iterator I;
const GlobalValue *GV;
uint32_t Flags;
SmallString<64> Name;
bool shouldSkip() {
return !(Flags & object::BasicSymbolRef::SF_Global) ||
(Flags & object::BasicSymbolRef::SF_FormatSpecific);
}
void skip() {
const object::SymbolicFile *Obj = I->getObject();
auto E = Obj->symbol_end();
while (I != E) {
Flags = I->getFlags();
if (!shouldSkip())
break;
++I;
}
if (I == E)
return;
Name.clear();
{
raw_svector_ostream OS(Name);
I->printName(OS);
}
GV = cast<object::IRObjectFile>(Obj)->getSymbolGV(I->getRawDataRefImpl());
}
public:
Symbol(object::basic_symbol_iterator I) : I(I) { skip(); }
StringRef getName() const { return Name; }
StringRef getIRName() const {
if (GV)
return GV->getName();
return StringRef();
}
uint32_t getFlags() const { return Flags; }
GlobalValue::VisibilityTypes getVisibility() const {
if (GV)
return GV->getVisibility();
return GlobalValue::DefaultVisibility;
}
bool canBeOmittedFromSymbolTable() const {
return GV && llvm::canBeOmittedFromSymbolTable(GV);
}
Expected<const Comdat *> getComdat() const {
const GlobalObject *GO;
if (auto *GA = dyn_cast<GlobalAlias>(GV)) {
GO = GA->getBaseObject();
if (!GO)
return make_error<StringError>("Unable to determine comdat of alias!",
inconvertibleErrorCode());
} else {
GO = cast<GlobalObject>(GV);
}
if (GV)
return GV->getComdat();
return nullptr;
}
size_t getCommonSize() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getParent()->getDataLayout().getTypeAllocSize(
GV->getType()->getElementType());
}
unsigned getCommonAlignment() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getAlignment();
}
};
class symbol_iterator {
Symbol Sym;
public:
symbol_iterator(object::basic_symbol_iterator I) : Sym(I) {}
symbol_iterator &operator++() {
++Sym.I;
Sym.skip();
return *this;
}
symbol_iterator operator++(int) {
symbol_iterator I = *this;
++*this;
return I;
}
const Symbol &operator*() const { return Sym; }
const Symbol *operator->() const { return &Sym; }
bool operator!=(const symbol_iterator &Other) const {
return Sym.I != Other.Sym.I;
}
};
/// A range over the symbols in this InputFile.
iterator_range<symbol_iterator> symbols() {
return llvm::make_range(symbol_iterator(Obj->symbol_begin()),
symbol_iterator(Obj->symbol_end()));
}
StringRef getSourceFileName() const {
return Obj->getModule().getSourceFileName();
}
};
/// A ThinBackend defines what happens after the thin-link phase during ThinLTO.
/// The details of this type definition aren't important; clients can only
/// create a ThinBackend using one of the create*ThinBackend() functions below.
typedef std::function<std::unique_ptr<ThinBackendProc>(
Config &C, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream)>
ThinBackend;
/// This ThinBackend runs the individual backend jobs in-process.
ThinBackend createInProcessThinBackend(unsigned ParallelismLevel);
/// This ThinBackend writes individual module indexes to files, instead of
/// running the individual backend jobs. This backend is for distributed builds
/// where separate processes will invoke the real backends.
///
/// To find the path to write the index to, the backend checks if the path has a
/// prefix of OldPrefix; if so, it replaces that prefix with NewPrefix. It then
/// appends ".thinlto.bc" and writes the index to that path. If
/// ShouldEmitImportsFiles is true it also writes a list of imported files to a
/// similar path with ".imports" appended instead.
ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
std::string NewPrefix,
bool ShouldEmitImportsFiles,
std::string LinkedObjectsFile);
/// This class implements a resolution-based interface to LLVM's LTO
/// functionality. It supports regular LTO, parallel LTO code generation and
/// ThinLTO. You can use it from a linker in the following way:
/// - Set hooks and code generation options (see lto::Config struct defined in
/// Config.h), and use the lto::Config object to create an lto::LTO object.
/// - Create lto::InputFile objects using lto::InputFile::create(), then use
/// the symbols() function to enumerate its symbols and compute a resolution
/// for each symbol (see SymbolResolution below).
/// - After the linker has visited each input file (and each regular object
/// file) and computed a resolution for each symbol, take each lto::InputFile
/// and pass it and an array of symbol resolutions to the add() function.
/// - Call the getMaxTasks() function to get an upper bound on the number of
/// native object files that LTO may add to the link.
/// - Call the run() function. This function will use the supplied AddStream
/// function to add up to getMaxTasks() native object files to the link.
class LTO {
friend InputFile;
public:
/// Create an LTO object. A default constructed LTO object has a reasonable
/// production configuration, but you can customize it by passing arguments to
/// this constructor.
/// FIXME: We do currently require the DiagHandler field to be set in Conf.
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
unsigned ParallelCodeGenParallelismLevel = 1);
/// Add an input file to the LTO link, using the provided symbol resolutions.
/// The symbol resolutions must appear in the enumeration order given by
/// InputFile::symbols().
Error add(std::unique_ptr<InputFile> Obj, ArrayRef<SymbolResolution> Res);
/// Returns an upper bound on the number of tasks that the client may expect.
/// This may only be called after all IR object files have been added. For a
/// full description of tasks see LTOBackend.h.
size_t getMaxTasks() const;
/// Runs the LTO pipeline. This function calls the supplied AddStream function
/// to add native object files to the link.
Error run(AddStreamFn AddStream);
private:
Config Conf;
struct RegularLTOState {
RegularLTOState(unsigned ParallelCodeGenParallelismLevel, Config &Conf);
unsigned ParallelCodeGenParallelismLevel;
LTOLLVMContext Ctx;
bool HasModule = false;
std::unique_ptr<Module> CombinedModule;
IRMover Mover;
} RegularLTO;
struct ThinLTOState {
ThinLTOState(ThinBackend Backend);
ThinBackend Backend;
ModuleSummaryIndex CombinedIndex;
MapVector<StringRef, MemoryBufferRef> ModuleMap;
DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
} ThinLTO;
// The global resolution for a particular (mangled) symbol name. This is in
// particular necessary to track whether each symbol can be internalized.
// Because any input file may introduce a new cross-partition reference, we
// cannot make any final internalization decisions until all input files have
// been added and the client has called run(). During run() we apply
// internalization decisions either directly to the module (for regular LTO)
// or to the combined index (for ThinLTO).
struct GlobalResolution {
/// The unmangled name of the global.
std::string IRName;
bool UnnamedAddr = true;
/// This field keeps track of the partition number of this global. The
/// regular LTO object is partition 0, while each ThinLTO object has its own
/// partition number from 1 onwards.
///
/// Any global that is defined or used by more than one partition, or that
/// is referenced externally, may not be internalized.
///
/// Partitions generally have a one-to-one correspondence with tasks, except
/// that we use partition 0 for all parallel LTO code generation partitions.
/// Any partitioning of the combined LTO object is done internally by the
/// LTO backend.
size_t Partition = Unknown;
/// Special partition numbers.
enum {
/// A partition number has not yet been assigned to this global.
Unknown = -1ull,
/// This global is either used by more than one partition or has an
/// external reference, and therefore cannot be internalized.
External = -2ull,
};
};
// Global mapping from mangled symbol names to resolutions.
StringMap<GlobalResolution> GlobalResolutions;
void writeToResolutionFile(InputFile *Input, ArrayRef<SymbolResolution> Res);
void addSymbolToGlobalRes(object::IRObjectFile *Obj,
SmallPtrSet<GlobalValue *, 8> &Used,
const InputFile::Symbol &Sym, SymbolResolution Res,
size_t Partition);
Error addRegularLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);
Error addThinLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);
Error runRegularLTO(AddStreamFn AddStream);
Error runThinLTO(AddStreamFn AddStream);
mutable bool CalledGetMaxTasks = false;
};
/// The resolution for a symbol. The linker must provide a SymbolResolution for
/// each global symbol based on its internal resolution of that symbol.
struct SymbolResolution {
SymbolResolution()
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0) {
}
/// The linker has chosen this definition of the symbol.
unsigned Prevailing : 1;
/// The definition of this symbol is unpreemptable at runtime and is known to
/// be in this linkage unit.
unsigned FinalDefinitionInLinkageUnit : 1;
/// The definition of this symbol is visible outside of the LTO unit.
unsigned VisibleToRegularObj : 1;
};
} // namespace lto
} // namespace llvm
#endif

View File

@ -0,0 +1,51 @@
//===-LTOBackend.h - LLVM Link Time Optimizer Backend ---------------------===//
//
// 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 "backend" phase of LTO, i.e. it performs
// optimization and code generation on a loaded module. It is generally used
// internally by the LTO class but can also be used independently, for example
// to implement a standalone ThinLTO backend.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LTO_LTOBACKEND_H
#define LLVM_LTO_LTOBACKEND_H
#include "llvm/ADT/MapVector.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/LTO/Config.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
namespace llvm {
class Error;
class Module;
class Target;
namespace lto {
/// Runs a regular LTO backend.
Error backend(Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> M);
/// Runs a ThinLTO backend.
Error thinBackend(Config &C, size_t Task, AddStreamFn AddStream, Module &M,
ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap);
}
}
#endif

View File

@ -49,6 +49,7 @@ endif()
add_llvm_library(LLVMLTO
LTO.cpp
LTOBackend.cpp
LTOModule.cpp
LTOCodeGenerator.cpp
UpdateCompilerUsed.cpp

View File

@ -12,16 +12,39 @@
//===----------------------------------------------------------------------===//
#include "llvm/LTO/LTO.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/LTO/LTOBackend.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Object/ModuleSummaryIndexObjectFile.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/SplitModule.h"
namespace llvm {
#include <set>
using namespace llvm;
using namespace lto;
using namespace object;
// Simple helper to load a module from bitcode
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
LLVMContext &Context, bool Lazy) {
std::unique_ptr<Module>
llvm::loadModuleFromBuffer(const MemoryBufferRef &Buffer, LLVMContext &Context,
bool Lazy) {
SMDiagnostic Err;
ErrorOr<std::unique_ptr<Module>> ModuleOrErr(nullptr);
if (Lazy) {
@ -76,7 +99,7 @@ static void thinLTOResolveWeakForLinkerGUID(
// current module. However there is a chance that another module is still
// referencing them because of the import. We make sure we always emit at least
// one copy.
void thinLTOResolveWeakForLinkerInIndex(
void llvm::thinLTOResolveWeakForLinkerInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing,
@ -110,10 +133,513 @@ static void thinLTOInternalizeAndPromoteGUID(
// Update the linkages in the given \p Index to mark exported values
// as external and non-exported values as internal.
void thinLTOInternalizeAndPromoteInIndex(
void llvm::thinLTOInternalizeAndPromoteInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported) {
for (auto &I : Index)
thinLTOInternalizeAndPromoteGUID(I.second, I.first, isExported);
}
Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
std::unique_ptr<InputFile> File(new InputFile);
std::string Msg;
auto DiagHandler = [](const DiagnosticInfo &DI, void *MsgP) {
auto *Msg = reinterpret_cast<std::string *>(MsgP);
raw_string_ostream OS(*Msg);
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
};
File->Ctx.setDiagnosticHandler(DiagHandler, static_cast<void *>(&Msg));
ErrorOr<std::unique_ptr<object::IRObjectFile>> IRObj =
IRObjectFile::create(Object, File->Ctx);
if (!Msg.empty())
return make_error<StringError>(Msg, inconvertibleErrorCode());
if (!IRObj)
return errorCodeToError(IRObj.getError());
File->Obj = std::move(*IRObj);
File->Ctx.setDiagnosticHandler(nullptr, nullptr);
return std::move(File);
}
LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
Config &Conf)
: ParallelCodeGenParallelismLevel(ParallelCodeGenParallelismLevel),
Ctx(Conf), CombinedModule(llvm::make_unique<Module>("ld-temp.o", Ctx)),
Mover(*CombinedModule) {}
LTO::ThinLTOState::ThinLTOState(ThinBackend Backend) : Backend(Backend) {
if (!Backend)
this->Backend = createInProcessThinBackend(thread::hardware_concurrency());
}
LTO::LTO(Config Conf, ThinBackend Backend,
unsigned ParallelCodeGenParallelismLevel)
: Conf(std::move(Conf)),
RegularLTO(ParallelCodeGenParallelismLevel, this->Conf),
ThinLTO(Backend) {}
// Add the given symbol to the GlobalResolutions map, and resolve its partition.
void LTO::addSymbolToGlobalRes(IRObjectFile *Obj,
SmallPtrSet<GlobalValue *, 8> &Used,
const InputFile::Symbol &Sym,
SymbolResolution Res, size_t Partition) {
GlobalValue *GV = Obj->getSymbolGV(Sym.I->getRawDataRefImpl());
auto &GlobalRes = GlobalResolutions[Sym.getName()];
if (GV) {
GlobalRes.UnnamedAddr &= GV->hasGlobalUnnamedAddr();
if (Res.Prevailing)
GlobalRes.IRName = GV->getName();
}
if (Res.VisibleToRegularObj || (GV && Used.count(GV)) ||
(GlobalRes.Partition != GlobalResolution::Unknown &&
GlobalRes.Partition != Partition))
GlobalRes.Partition = GlobalResolution::External;
else
GlobalRes.Partition = Partition;
}
void LTO::writeToResolutionFile(InputFile *Input,
ArrayRef<SymbolResolution> Res) {
StringRef Path = Input->Obj->getMemoryBufferRef().getBufferIdentifier();
*Conf.ResolutionFile << Path << '\n';
auto ResI = Res.begin();
for (const InputFile::Symbol &Sym : Input->symbols()) {
assert(ResI != Res.end());
SymbolResolution Res = *ResI++;
*Conf.ResolutionFile << "-r=" << Path << ',' << Sym.getName() << ',';
if (Res.Prevailing)
*Conf.ResolutionFile << 'p';
if (Res.FinalDefinitionInLinkageUnit)
*Conf.ResolutionFile << 'l';
if (Res.VisibleToRegularObj)
*Conf.ResolutionFile << 'x';
*Conf.ResolutionFile << '\n';
}
assert(ResI == Res.end());
}
Error LTO::add(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res) {
assert(!CalledGetMaxTasks);
if (Conf.ResolutionFile)
writeToResolutionFile(Input.get(), Res);
Module &M = Input->Obj->getModule();
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
if (!Conf.OverrideTriple.empty())
M.setTargetTriple(Conf.OverrideTriple);
else if (M.getTargetTriple().empty())
M.setTargetTriple(Conf.DefaultTriple);
MemoryBufferRef MBRef = Input->Obj->getMemoryBufferRef();
bool HasThinLTOSummary = hasGlobalValueSummary(MBRef, Conf.DiagHandler);
if (HasThinLTOSummary)
return addThinLTO(std::move(Input), Res);
else
return addRegularLTO(std::move(Input), Res);
}
// Add a regular LTO object to the link.
Error LTO::addRegularLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res) {
RegularLTO.HasModule = true;
ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr =
IRObjectFile::create(Input->Obj->getMemoryBufferRef(), RegularLTO.Ctx);
if (!ObjOrErr)
return errorCodeToError(ObjOrErr.getError());
std::unique_ptr<object::IRObjectFile> Obj = std::move(*ObjOrErr);
Module &M = Obj->getModule();
M.materializeMetadata();
UpgradeDebugInfo(M);
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
std::vector<GlobalValue *> Keep;
for (GlobalVariable &GV : M.globals())
if (GV.hasAppendingLinkage())
Keep.push_back(&GV);
auto ResI = Res.begin();
for (const InputFile::Symbol &Sym :
make_range(InputFile::symbol_iterator(Obj->symbol_begin()),
InputFile::symbol_iterator(Obj->symbol_end()))) {
assert(ResI != Res.end());
SymbolResolution Res = *ResI++;
addSymbolToGlobalRes(Obj.get(), Used, Sym, Res, 0);
GlobalValue *GV = Obj->getSymbolGV(Sym.I->getRawDataRefImpl());
if (Res.Prevailing && GV) {
Keep.push_back(GV);
switch (GV->getLinkage()) {
default:
break;
case GlobalValue::LinkOnceAnyLinkage:
GV->setLinkage(GlobalValue::WeakAnyLinkage);
break;
case GlobalValue::LinkOnceODRLinkage:
GV->setLinkage(GlobalValue::WeakODRLinkage);
break;
}
}
// FIXME: use proposed local attribute for FinalDefinitionInLinkageUnit.
}
assert(ResI == Res.end());
return RegularLTO.Mover.move(Obj->takeModule(), Keep,
[](GlobalValue &, IRMover::ValueAdder) {});
}
// Add a ThinLTO object to the link.
Error LTO::addThinLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res) {
Module &M = Input->Obj->getModule();
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
// We need to initialize the target info for the combined regular LTO module
// in case we have no regular LTO objects. In that case we still need to build
// it as usual because the client may want to add symbol definitions to it.
if (RegularLTO.CombinedModule->getTargetTriple().empty()) {
RegularLTO.CombinedModule->setTargetTriple(M.getTargetTriple());
RegularLTO.CombinedModule->setDataLayout(M.getDataLayout());
}
MemoryBufferRef MBRef = Input->Obj->getMemoryBufferRef();
ErrorOr<std::unique_ptr<object::ModuleSummaryIndexObjectFile>>
SummaryObjOrErr =
object::ModuleSummaryIndexObjectFile::create(MBRef, Conf.DiagHandler);
if (!SummaryObjOrErr)
return errorCodeToError(SummaryObjOrErr.getError());
ThinLTO.CombinedIndex.mergeFrom((*SummaryObjOrErr)->takeIndex(),
ThinLTO.ModuleMap.size());
auto ResI = Res.begin();
for (const InputFile::Symbol &Sym : Input->symbols()) {
assert(ResI != Res.end());
SymbolResolution Res = *ResI++;
addSymbolToGlobalRes(Input->Obj.get(), Used, Sym, Res,
ThinLTO.ModuleMap.size() + 1);
GlobalValue *GV = Input->Obj->getSymbolGV(Sym.I->getRawDataRefImpl());
if (Res.Prevailing && GV)
ThinLTO.PrevailingModuleForGUID[GV->getGUID()] =
MBRef.getBufferIdentifier();
}
assert(ResI == Res.end());
ThinLTO.ModuleMap[MBRef.getBufferIdentifier()] = MBRef;
return Error();
}
size_t LTO::getMaxTasks() const {
CalledGetMaxTasks = true;
return RegularLTO.ParallelCodeGenParallelismLevel + ThinLTO.ModuleMap.size();
}
Error LTO::run(AddStreamFn AddStream) {
// Invoke regular LTO if there was a regular LTO module to start with,
// or if there are any hooks that the linker may have used to add
// its own resolved symbols to the combined module.
if (RegularLTO.HasModule || Conf.PreOptModuleHook ||
Conf.PostInternalizeModuleHook || Conf.PostOptModuleHook ||
Conf.PreCodeGenModuleHook)
if (auto E = runRegularLTO(AddStream))
return E;
return runThinLTO(AddStream);
}
Error LTO::runRegularLTO(AddStreamFn AddStream) {
if (Conf.PreOptModuleHook &&
!Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule))
return Error();
for (const auto &R : GlobalResolutions) {
if (R.second.IRName.empty())
continue;
if (R.second.Partition != 0 &&
R.second.Partition != GlobalResolution::External)
continue;
GlobalValue *GV = RegularLTO.CombinedModule->getNamedValue(R.second.IRName);
// Ignore symbols defined in other partitions.
if (!GV || GV->hasLocalLinkage())
continue;
GV->setUnnamedAddr(R.second.UnnamedAddr ? GlobalValue::UnnamedAddr::Global
: GlobalValue::UnnamedAddr::None);
if (R.second.Partition == 0)
GV->setLinkage(GlobalValue::InternalLinkage);
}
if (Conf.PostInternalizeModuleHook &&
!Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule))
return Error();
return backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
std::move(RegularLTO.CombinedModule));
}
/// This class defines the interface to the ThinLTO backend.
class lto::ThinBackendProc {
protected:
Config &Conf;
ModuleSummaryIndex &CombinedIndex;
AddStreamFn AddStream;
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries;
public:
ThinBackendProc(Config &Conf, ModuleSummaryIndex &CombinedIndex,
AddStreamFn AddStream,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries)
: Conf(Conf), CombinedIndex(CombinedIndex), AddStream(AddStream),
ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries) {}
virtual ~ThinBackendProc() {}
virtual Error start(size_t Task, MemoryBufferRef MBRef,
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) = 0;
virtual Error wait() = 0;
};
class InProcessThinBackend : public ThinBackendProc {
ThreadPool BackendThreadPool;
Optional<Error> Err;
std::mutex ErrMu;
public:
InProcessThinBackend(Config &Conf, ModuleSummaryIndex &CombinedIndex,
unsigned ThinLTOParallelismLevel,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream)
: ThinBackendProc(Conf, CombinedIndex, AddStream,
ModuleToDefinedGVSummaries),
BackendThreadPool(ThinLTOParallelismLevel) {}
Error
runThinLTOBackendThread(AddStreamFn AddStream, size_t Task,
MemoryBufferRef MBRef,
ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
LLVMContext BackendContext;
ErrorOr<std::unique_ptr<Module>> MOrErr =
parseBitcodeFile(MBRef, BackendContext);
assert(MOrErr && "Unable to load module in thread?");
return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, ModuleMap);
}
Error start(size_t Task, MemoryBufferRef MBRef,
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) override {
StringRef ModulePath = MBRef.getBufferIdentifier();
BackendThreadPool.async(
[=](MemoryBufferRef MBRef, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
Error E =
runThinLTOBackendThread(AddStream, Task, MBRef, CombinedIndex,
ImportList, DefinedGlobals, ModuleMap);
if (E) {
std::unique_lock<std::mutex> L(ErrMu);
if (Err)
Err = joinErrors(std::move(*Err), std::move(E));
else
Err = std::move(E);
}
},
MBRef, std::ref(CombinedIndex), std::ref(ImportLists[ModulePath]),
std::ref(ModuleToDefinedGVSummaries[ModulePath]), std::ref(ModuleMap));
return Error();
}
Error wait() override {
BackendThreadPool.wait();
if (Err)
return std::move(*Err);
else
return Error();
}
};
ThinBackend lto::createInProcessThinBackend(unsigned ParallelismLevel) {
return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream) {
return llvm::make_unique<InProcessThinBackend>(
Conf, CombinedIndex, ParallelismLevel, ModuleToDefinedGVSummaries,
AddStream);
};
}
class WriteIndexesThinBackend : public ThinBackendProc {
std::string OldPrefix, NewPrefix;
bool ShouldEmitImportsFiles;
std::string LinkedObjectsFileName;
std::unique_ptr<llvm::raw_fd_ostream> LinkedObjectsFile;
public:
WriteIndexesThinBackend(Config &Conf, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, std::string OldPrefix,
std::string NewPrefix, bool ShouldEmitImportsFiles,
std::string LinkedObjectsFileName)
: ThinBackendProc(Conf, CombinedIndex, AddStream,
ModuleToDefinedGVSummaries),
OldPrefix(OldPrefix), NewPrefix(NewPrefix),
ShouldEmitImportsFiles(ShouldEmitImportsFiles),
LinkedObjectsFileName(LinkedObjectsFileName) {}
/// 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.
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))
llvm::errs() << "warning: could not create directory '" << ParentPath
<< "': " << EC.message() << '\n';
}
return NewPath.str();
}
Error start(size_t Task, MemoryBufferRef MBRef,
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) override {
StringRef ModulePath = MBRef.getBufferIdentifier();
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
std::error_code EC;
if (!LinkedObjectsFileName.empty()) {
if (!LinkedObjectsFile) {
LinkedObjectsFile = llvm::make_unique<raw_fd_ostream>(
LinkedObjectsFileName, EC, sys::fs::OpenFlags::F_None);
if (EC)
return errorCodeToError(EC);
}
*LinkedObjectsFile << NewModulePath << '\n';
}
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
ImportLists, ModuleToSummariesForIndex);
raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
sys::fs::OpenFlags::F_None);
if (EC)
return errorCodeToError(EC);
WriteIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
if (ShouldEmitImportsFiles)
return errorCodeToError(EmitImportsFiles(
ModulePath, NewModulePath + ".imports", ImportLists));
return Error();
}
Error wait() override { return Error(); }
};
ThinBackend lto::createWriteIndexesThinBackend(std::string OldPrefix,
std::string NewPrefix,
bool ShouldEmitImportsFiles,
std::string LinkedObjectsFile) {
return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream) {
return llvm::make_unique<WriteIndexesThinBackend>(
Conf, CombinedIndex, ModuleToDefinedGVSummaries, AddStream, OldPrefix,
NewPrefix, ShouldEmitImportsFiles, LinkedObjectsFile);
};
}
Error LTO::runThinLTO(AddStreamFn AddStream) {
if (ThinLTO.ModuleMap.empty())
return Error();
if (Conf.CombinedIndexHook && !Conf.CombinedIndexHook(ThinLTO.CombinedIndex))
return Error();
// Collect for each module the list of function it defines (GUID ->
// Summary).
StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>>
ModuleToDefinedGVSummaries(ThinLTO.ModuleMap.size());
ThinLTO.CombinedIndex.collectDefinedGVSummariesPerModule(
ModuleToDefinedGVSummaries);
StringMap<FunctionImporter::ImportMapTy> ImportLists(
ThinLTO.ModuleMap.size());
StringMap<FunctionImporter::ExportSetTy> ExportLists(
ThinLTO.ModuleMap.size());
ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
ImportLists, ExportLists);
std::set<GlobalValue::GUID> ExportedGUIDs;
for (auto &Res : GlobalResolutions) {
if (!Res.second.IRName.empty() &&
Res.second.Partition == GlobalResolution::External)
ExportedGUIDs.insert(GlobalValue::getGUID(Res.second.IRName));
}
auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath();
};
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
const auto &ExportList = ExportLists.find(ModuleIdentifier);
return (ExportList != ExportLists.end() &&
ExportList->second.count(GUID)) ||
ExportedGUIDs.count(GUID);
};
thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported);
thinLTOResolveWeakForLinkerInIndex(
ThinLTO.CombinedIndex, isPrevailing,
[](StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes) {});
std::unique_ptr<ThinBackendProc> BackendProc = ThinLTO.Backend(
Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, AddStream);
// Partition numbers for ThinLTO jobs start at 1 (see comments for
// GlobalResolution in LTO.h). Task numbers, however, start at
// ParallelCodeGenParallelismLevel, as tasks 0 through
// ParallelCodeGenParallelismLevel-1 are reserved for parallel code generation
// partitions.
size_t Task = RegularLTO.ParallelCodeGenParallelismLevel;
size_t Partition = 1;
for (auto &Mod : ThinLTO.ModuleMap) {
if (Error E = BackendProc->start(Task, Mod.second, ImportLists,
ThinLTO.ModuleMap))
return E;
++Task;
++Partition;
}
return BackendProc->wait();
}

284
lib/LTO/LTOBackend.cpp Normal file
View File

@ -0,0 +1,284 @@
//===-LTOBackend.cpp - LLVM Link Time Optimizer Backend -------------------===//
//
// 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 "backend" phase of LTO, i.e. it performs
// optimization and code generation on a loaded module. It is generally used
// internally by the LTO class but can also be used independently, for example
// to implement a standalone ThinLTO backend.
//
//===----------------------------------------------------------------------===//
#include "llvm/LTO/LTOBackend.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/SplitModule.h"
using namespace llvm;
using namespace lto;
Error Config::addSaveTemps(std::string OutputFileName,
bool UseInputModulePath) {
ShouldDiscardValueNames = false;
std::error_code EC;
ResolutionFile = llvm::make_unique<raw_fd_ostream>(
OutputFileName + ".resolution.txt", EC, sys::fs::OpenFlags::F_Text);
if (EC)
return errorCodeToError(EC);
auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) {
// Keep track of the hook provided by the linker, which also needs to run.
ModuleHookFn LinkerHook = Hook;
Hook = [=](size_t Task, Module &M) {
// If the linker's hook returned false, we need to pass that result
// through.
if (LinkerHook && !LinkerHook(Task, M))
return false;
std::string PathPrefix;
// If this is the combined module (not a ThinLTO backend compile) or the
// user hasn't requested using the input module's path, emit to a file
// named from the provided OutputFileName with the Task ID appended.
if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) {
PathPrefix = OutputFileName;
if (Task != 0)
PathPrefix += "." + utostr(Task);
} else
PathPrefix = M.getModuleIdentifier();
std::string Path = PathPrefix + "." + PathSuffix + ".bc";
std::error_code EC;
raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
if (EC) {
// Because -save-temps is a debugging feature, we report the error
// directly and exit.
llvm::errs() << "failed to open " << Path << ": " << EC.message()
<< '\n';
exit(1);
}
WriteBitcodeToFile(&M, OS, /*ShouldPreserveUseListOrder=*/false);
return true;
};
};
setHook("0.preopt", PreOptModuleHook);
setHook("1.promote", PostPromoteModuleHook);
setHook("2.internalize", PostInternalizeModuleHook);
setHook("3.import", PostImportModuleHook);
setHook("4.opt", PostOptModuleHook);
setHook("5.precodegen", PreCodeGenModuleHook);
CombinedIndexHook = [=](const ModuleSummaryIndex &Index) {
std::string Path = OutputFileName + ".index.bc";
std::error_code EC;
raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
if (EC) {
// Because -save-temps is a debugging feature, we report the error
// directly and exit.
llvm::errs() << "failed to open " << Path << ": " << EC.message() << '\n';
exit(1);
}
WriteIndexToFile(Index, OS);
return true;
};
return Error();
}
namespace {
std::unique_ptr<TargetMachine>
createTargetMachine(Config &C, StringRef TheTriple, const Target *TheTarget) {
SubtargetFeatures Features;
Features.getDefaultSubtargetFeatures(Triple(TheTriple));
for (const std::string &A : C.MAttrs)
Features.AddFeature(A);
return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
TheTriple, C.CPU, Features.getString(), C.Options, C.RelocModel,
C.CodeModel, C.CGOptLevel));
}
bool opt(Config &C, TargetMachine *TM, size_t Task, Module &M, bool IsThinLto) {
M.setDataLayout(TM->createDataLayout());
legacy::PassManager passes;
passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
PassManagerBuilder PMB;
PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple()));
PMB.Inliner = createFunctionInliningPass();
// Unconditionally verify input since it is not verified before this
// point and has unknown origin.
PMB.VerifyInput = true;
PMB.VerifyOutput = !C.DisableVerify;
PMB.LoopVectorize = true;
PMB.SLPVectorize = true;
PMB.OptLevel = C.OptLevel;
if (IsThinLto)
PMB.populateThinLTOPassManager(passes);
else
PMB.populateLTOPassManager(passes);
passes.run(M);
if (C.PostOptModuleHook && !C.PostOptModuleHook(Task, M))
return false;
return true;
}
void codegen(Config &C, TargetMachine *TM, AddStreamFn AddStream, size_t Task,
Module &M) {
if (C.PreCodeGenModuleHook && !C.PreCodeGenModuleHook(Task, M))
return;
std::unique_ptr<raw_pwrite_stream> OS = AddStream(Task);
legacy::PassManager CodeGenPasses;
if (TM->addPassesToEmitFile(CodeGenPasses, *OS,
TargetMachine::CGFT_ObjectFile))
report_fatal_error("Failed to setup codegen");
CodeGenPasses.run(M);
}
void splitCodeGen(Config &C, TargetMachine *TM, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> M) {
ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel);
unsigned ThreadCount = 0;
const Target *T = &TM->getTarget();
SplitModule(
std::move(M), ParallelCodeGenParallelismLevel,
[&](std::unique_ptr<Module> MPart) {
// We want to clone the module in a new context to multi-thread the
// codegen. We do it by serializing partition modules to bitcode
// (while still on the main thread, in order to avoid data races) and
// spinning up new threads which deserialize the partitions into
// separate contexts.
// FIXME: Provide a more direct way to do this in LLVM.
SmallString<0> BC;
raw_svector_ostream BCOS(BC);
WriteBitcodeToFile(MPart.get(), BCOS);
// Enqueue the task
CodegenThreadPool.async(
[&](const SmallString<0> &BC, unsigned ThreadId) {
LTOLLVMContext Ctx(C);
ErrorOr<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
MemoryBufferRef(StringRef(BC.data(), BC.size()), "ld-temp.o"),
Ctx);
if (!MOrErr)
report_fatal_error("Failed to read bitcode");
std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());
std::unique_ptr<TargetMachine> TM =
createTargetMachine(C, MPartInCtx->getTargetTriple(), T);
codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx);
},
// Pass BC using std::move to ensure that it get moved rather than
// copied into the thread's context.
std::move(BC), ThreadCount++);
},
false);
}
Expected<const Target *> initAndLookupTarget(Config &C, Module &M) {
if (!C.OverrideTriple.empty())
M.setTargetTriple(C.OverrideTriple);
else if (M.getTargetTriple().empty())
M.setTargetTriple(C.DefaultTriple);
std::string Msg;
const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);
if (!T)
return make_error<StringError>(Msg, inconvertibleErrorCode());
return T;
}
}
Error lto::backend(Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> M) {
Expected<const Target *> TOrErr = initAndLookupTarget(C, *M);
if (!TOrErr)
return TOrErr.takeError();
std::unique_ptr<TargetMachine> TM =
createTargetMachine(C, M->getTargetTriple(), *TOrErr);
if (!opt(C, TM.get(), 0, *M, /*IsThinLto=*/false))
return Error();
if (ParallelCodeGenParallelismLevel == 1)
codegen(C, TM.get(), AddStream, 0, *M);
else
splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel,
std::move(M));
return Error();
}
Error lto::thinBackend(Config &C, size_t Task, AddStreamFn AddStream, Module &M,
ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
Expected<const Target *> TOrErr = initAndLookupTarget(C, M);
if (!TOrErr)
return TOrErr.takeError();
std::unique_ptr<TargetMachine> TM =
createTargetMachine(C, M.getTargetTriple(), *TOrErr);
if (C.PreOptModuleHook && !C.PreOptModuleHook(Task, M))
return Error();
thinLTOResolveWeakForLinkerModule(M, DefinedGlobals);
renameModuleForThinLTO(M, CombinedIndex);
if (C.PostPromoteModuleHook && !C.PostPromoteModuleHook(Task, M))
return Error();
if (!DefinedGlobals.empty())
thinLTOInternalizeModule(M, DefinedGlobals);
if (C.PostInternalizeModuleHook && !C.PostInternalizeModuleHook(Task, M))
return Error();
auto ModuleLoader = [&](StringRef Identifier) {
return std::move(getLazyBitcodeModule(MemoryBuffer::getMemBuffer(
ModuleMap[Identifier], false),
M.getContext(),
/*ShouldLazyLoadMetadata=*/true)
.get());
};
FunctionImporter Importer(CombinedIndex, ModuleLoader);
Importer.importFunctions(M, ImportList);
if (C.PostImportModuleHook && !C.PostImportModuleHook(Task, M))
return Error();
if (!opt(C, TM.get(), Task, M, /*IsThinLto=*/true))
return Error();
codegen(C, TM.get(), AddStream, Task, M);
return Error();
}

View File

@ -324,5 +324,5 @@ llvm::object::IRObjectFile::create(MemoryBufferRef Object,
return EC;
std::unique_ptr<Module> &M = MOrErr.get();
return llvm::make_unique<IRObjectFile>(Object, std::move(M));
return llvm::make_unique<IRObjectFile>(BCOrErr.get(), std::move(M));
}

View File

@ -43,6 +43,7 @@ set(LLVM_TEST_DEPENDS
llvm-extract
llvm-lib
llvm-link
llvm-lto2
llvm-mc
llvm-mcmarkup
llvm-nm

View File

@ -0,0 +1,4 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@a = global i32 42

View File

@ -0,0 +1,28 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
$c2 = comdat any
$c1 = comdat any
; This is only present in this file. The linker will keep $c1 from the first
; file and this will be undefined.
@will_be_undefined = global i32 1, comdat($c1)
@v1 = weak_odr global i32 41, comdat($c2)
define weak_odr protected i32 @f1(i8* %this) comdat($c2) {
bb20:
store i8* %this, i8** null
br label %bb21
bb21:
ret i32 41
}
@r21 = global i32* @v1
@r22 = global i32(i8*)* @f1
@a21 = alias i32, i32* @v1
@a22 = alias i16, bitcast (i32* @v1 to i16*)
@a23 = alias i32(i8*), i32(i8*)* @f1
@a24 = alias i16, bitcast (i32(i8*)* @f1 to i16*)
@a25 = alias i16, i16* @a24

View File

@ -0,0 +1,22 @@
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %p/Inputs/alias-1.ll -o %t2.o
; RUN: llvm-lto2 -o %t3.o %t2.o %t1.o -r %t2.o,a,px -r %t1.o,a, -r %t1.o,b,px -save-temps
; RUN: llvm-dis < %t3.o.0.preopt.bc -o - | FileCheck %s
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
; CHECK-NOT: alias
; CHECK: @a = global i32 42
; CHECK-NEXT: @b = global i32 1
; CHECK-NOT: alias
; RES: 2.o{{$}}
; RES: {{^}}-r={{.*}}2.o,a,px{{$}}
; RES: 1.o{{$}}
; RES: {{^}}-r={{.*}}1.o,b,px{{$}}
; RES: {{^}}-r={{.*}}1.o,a,{{$}}
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@a = weak alias i32, i32* @b
@b = global i32 1

View File

@ -0,0 +1,86 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/comdat.ll -o %t2.o
; RUN: llvm-lto2 -save-temps -o %t3.o %t.o %t2.o \
; RUN: -r=%t.o,f1,plx \
; RUN: -r=%t.o,v1,px \
; RUN: -r=%t.o,r11,px \
; RUN: -r=%t.o,r12,px \
; RUN: -r=%t.o,a11,px \
; RUN: -r=%t.o,a12,px \
; RUN: -r=%t.o,a13,px \
; RUN: -r=%t.o,a14,px \
; RUN: -r=%t.o,a15,px \
; RUN: -r=%t2.o,f1,l \
; RUN: -r=%t2.o,will_be_undefined, \
; RUN: -r=%t2.o,v1, \
; RUN: -r=%t2.o,r21,px \
; RUN: -r=%t2.o,r22,px \
; RUN: -r=%t2.o,a21,px \
; RUN: -r=%t2.o,a22,px \
; RUN: -r=%t2.o,a23,px \
; RUN: -r=%t2.o,a24,px \
; RUN: -r=%t2.o,a25,px
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
$c1 = comdat any
@v1 = weak_odr global i32 42, comdat($c1)
define weak_odr i32 @f1(i8*) comdat($c1) {
bb10:
br label %bb11
bb11:
ret i32 42
}
@r11 = global i32* @v1
@r12 = global i32 (i8*)* @f1
@a11 = alias i32, i32* @v1
@a12 = alias i16, bitcast (i32* @v1 to i16*)
@a13 = alias i32 (i8*), i32 (i8*)* @f1
@a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
@a15 = alias i16, i16* @a14
; CHECK: $c1 = comdat any
; CHECK: $c2 = comdat any
; CHECK-DAG: @v1 = weak_odr global i32 42, comdat($c1)
; CHECK-DAG: @r11 = global i32* @v1{{$}}
; CHECK-DAG: @r12 = global i32 (i8*)* @f1{{$}}
; CHECK-DAG: @r21 = global i32* @v1{{$}}
; CHECK-DAG: @r22 = global i32 (i8*)* @f1{{$}}
; CHECK-DAG: @v1.1 = internal global i32 41, comdat($c2)
; CHECK-DAG: @a11 = alias i32, i32* @v1{{$}}
; CHECK-DAG: @a12 = alias i16, bitcast (i32* @v1 to i16*)
; CHECK-DAG: @a13 = alias i32 (i8*), i32 (i8*)* @f1{{$}}
; CHECK-DAG: @a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
; CHECK-DAG: @a21 = alias i32, i32* @v1.1{{$}}
; CHECK-DAG: @a22 = alias i16, bitcast (i32* @v1.1 to i16*)
; CHECK-DAG: @a23 = alias i32 (i8*), i32 (i8*)* @f1.2{{$}}
; CHECK-DAG: @a24 = alias i16, bitcast (i32 (i8*)* @f1.2 to i16*)
; CHECK: define weak_odr i32 @f1(i8*) comdat($c1) {
; CHECK-NEXT: bb10:
; CHECK-NEXT: br label %bb11{{$}}
; CHECK: bb11:
; CHECK-NEXT: ret i32 42
; CHECK-NEXT: }
; CHECK: define internal i32 @f1.2(i8* %this) comdat($c2) {
; CHECK-NEXT: bb20:
; CHECK-NEXT: store i8* %this, i8** null
; CHECK-NEXT: br label %bb21
; CHECK: bb21:
; CHECK-NEXT: ret i32 41
; CHECK-NEXT: }

View File

@ -0,0 +1,2 @@
if not 'X86' in config.root.targets:
config.unsupported = True

View File

@ -295,6 +295,7 @@ for pattern in [r"\bbugpoint\b(?!-)",
r"\bllvm-lib\b",
r"\bllvm-link\b",
r"\bllvm-lto\b",
r"\bllvm-lto2\b",
r"\bllvm-mc\b",
r"\bllvm-mcmarkup\b",
r"\bllvm-nm\b",

View File

@ -16,7 +16,7 @@ define hidden void @g() {
ret void
}
; CHECK: define internal void @h() local_unnamed_addr {
; CHECK: define internal void @h() {
define linkonce_odr void @h() local_unnamed_addr {
ret void
}

View File

@ -1,8 +1,9 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %p/Inputs/comdat.ll -o %t2.o
; RUN: %gold -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t.o %t2.o \
; RUN: %gold -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t1.o %t2.o \
; RUN: -plugin-opt=save-temps
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
; RUN: llvm-readobj -t %t3.o | FileCheck --check-prefix=OBJ %s
$c1 = comdat any
@ -24,42 +25,37 @@ bb11:
@a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
@a15 = alias i16, i16* @a14
; CHECK: $c1 = comdat any
; CHECK: $c2 = comdat any
; gold's resolutions should tell us that our $c1 wins, and the other input's $c2
; wins. f1 is also local due to having protected visibility in the other object.
; CHECK-DAG: @v1 = weak_odr global i32 42, comdat($c1)
; RES: 1.o,f1,plx{{$}}
; RES: 1.o,v1,px{{$}}
; RES: 1.o,r11,px{{$}}
; RES: 1.o,r12,px{{$}}
; RES: 1.o,a11,px{{$}}
; RES: 1.o,a12,px{{$}}
; RES: 1.o,a13,px{{$}}
; RES: 1.o,a14,px{{$}}
; RES: 1.o,a15,px{{$}}
; CHECK-DAG: @r11 = global i32* @v1{{$}}
; CHECK-DAG: @r12 = global i32 (i8*)* @f1{{$}}
; RES: 2.o,f1,l{{$}}
; RES: 2.o,will_be_undefined,{{$}}
; RES: 2.o,v1,{{$}}
; RES: 2.o,r21,px{{$}}
; RES: 2.o,r22,px{{$}}
; RES: 2.o,a21,px{{$}}
; RES: 2.o,a22,px{{$}}
; RES: 2.o,a23,px{{$}}
; RES: 2.o,a24,px{{$}}
; RES: 2.o,a25,px{{$}}
; CHECK-DAG: @r21 = global i32* @v1{{$}}
; CHECK-DAG: @r22 = global i32 (i8*)* @f1{{$}}
; f1's protected visibility should be reflected in the DSO.
; CHECK-DAG: @v1.1 = internal global i32 41, comdat($c2)
; CHECK-DAG: @a11 = alias i32, i32* @v1{{$}}
; CHECK-DAG: @a12 = alias i16, bitcast (i32* @v1 to i16*)
; CHECK-DAG: @a13 = alias i32 (i8*), i32 (i8*)* @f1{{$}}
; CHECK-DAG: @a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
; CHECK-DAG: @a21 = alias i32, i32* @v1.1{{$}}
; CHECK-DAG: @a22 = alias i16, bitcast (i32* @v1.1 to i16*)
; CHECK-DAG: @a23 = alias i32 (i8*), i32 (i8*)* @f1.2{{$}}
; CHECK-DAG: @a24 = alias i16, bitcast (i32 (i8*)* @f1.2 to i16*)
; CHECK: define weak_odr protected i32 @f1(i8*) comdat($c1) {
; CHECK-NEXT: bb10:
; CHECK-NEXT: br label %bb11{{$}}
; CHECK: bb11:
; CHECK-NEXT: ret i32 42
; CHECK-NEXT: }
; CHECK: define internal i32 @f1.2(i8* %this) comdat($c2) {
; CHECK-NEXT: bb20:
; CHECK-NEXT: store i8* %this, i8** null
; CHECK-NEXT: br label %bb21
; CHECK: bb21:
; CHECK-NEXT: ret i32 41
; CHECK-NEXT: }
; OBJ: Name: f1 (
; OBJ-NEXT: Value:
; OBJ-NEXT: Size:
; OBJ-NEXT: Binding:
; OBJ-NEXT: Type:
; OBJ-NEXT: Other [
; OBJ-NEXT: STV_PROTECTED
; OBJ-NEXT: ]

View File

@ -11,7 +11,7 @@
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=A
; Shared library case, we merge @a as common and keep it for the symbol table.
; A: @a = common global i32 0, align 8
; A: @a = common global [4 x i8] zeroinitializer, align 8
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
@ -19,7 +19,7 @@
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=B
; (i16 align 8) + (i8 align 16) = i16 align 16
; B: @a = common global i16 0, align 16
; B: @a = common global [2 x i8] zeroinitializer, align 16
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
@ -27,7 +27,7 @@
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=C
; (i16 align 8) + (i8 align 1) = i16 align 8.
; C: @a = common global i16 0, align 8
; C: @a = common global [2 x i8] zeroinitializer, align 8
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
@ -35,7 +35,7 @@
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=EXEC %s
; All IR case, we internalize a after merging.
; EXEC: @a = internal global i32 0, align 8
; EXEC: @a = internal global [4 x i8] zeroinitializer, align 8
; RUN: llc %p/Inputs/common.ll -o %t2native.o -filetype=obj
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
@ -44,4 +44,4 @@
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=MIXED %s
; Mixed ELF and IR. We keep ours as common so the linker will finish the merge.
; MIXED: @a = common global i16 0, align 8
; MIXED: @a = common global [2 x i8] zeroinitializer, align 8

View File

@ -2,17 +2,16 @@
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
; RUN: --plugin-opt=generate-api-file \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o -o - | FileCheck %s
; RUN: FileCheck --check-prefix=API %s < %T/../apifile.txt
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: -m elf_x86_64 --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t3.o
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t3.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-nm %t3.o.o | FileCheck --check-prefix=NM %s
; RUN: rm -f %t4.o
@ -25,19 +24,19 @@
target triple = "x86_64-unknown-linux-gnu"
; CHECK-DAG: @g1 = linkonce_odr constant i32 32
; CHECK-DAG: @g1 = weak_odr constant i32 32
@g1 = linkonce_odr constant i32 32
; CHECK-DAG: @g2 = internal local_unnamed_addr constant i32 32
; CHECK-DAG: @g2 = internal constant i32 32
@g2 = linkonce_odr local_unnamed_addr constant i32 32
; CHECK-DAG: @g3 = internal unnamed_addr constant i32 32
@g3 = linkonce_odr unnamed_addr constant i32 32
; CHECK-DAG: @g4 = linkonce_odr global i32 32
; CHECK-DAG: @g4 = weak_odr global i32 32
@g4 = linkonce_odr global i32 32
; CHECK-DAG: @g5 = linkonce_odr local_unnamed_addr global i32 32
; CHECK-DAG: @g5 = weak_odr global i32 32
@g5 = linkonce_odr local_unnamed_addr global i32 32
; CHECK-DAG: @g6 = internal unnamed_addr global i32 32
@ -75,8 +74,8 @@ define linkonce_odr void @f4() local_unnamed_addr {
ret void
}
; CHECK-DAG: define linkonce_odr void @f5()
; OPT-DAG: define linkonce_odr void @f5()
; CHECK-DAG: define weak_odr void @f5()
; OPT-DAG: define weak_odr void @f5()
define linkonce_odr void @f5() {
ret void
}
@ -97,15 +96,21 @@ define i32* @f8() {
ret i32* @g8
}
; API: f1 PREVAILING_DEF_IRONLY
; API: f2 PREVAILING_DEF_IRONLY
; API: f3 PREVAILING_DEF_IRONLY_EXP
; API: f4 PREVAILING_DEF_IRONLY_EXP
; API: f5 PREVAILING_DEF_IRONLY_EXP
; API: f6 PREVAILING_DEF_IRONLY_EXP
; API: f7 PREVAILING_DEF_IRONLY_EXP
; API: f8 PREVAILING_DEF_IRONLY_EXP
; API: g7 UNDEF
; API: g8 UNDEF
; API: g9 PREVAILING_DEF_IRONLY_EXP
; API: g10 PREVAILING_DEF_IRONLY_EXP
; RES: .o,f1,pl{{$}}
; RES: .o,f2,pl{{$}}
; RES: .o,f3,px{{$}}
; RES: .o,f4,p{{$}}
; RES: .o,f5,px{{$}}
; RES: .o,f6,p{{$}}
; RES: .o,f7,px{{$}}
; RES: .o,f8,px{{$}}
; RES: .o,g1,px{{$}}
; RES: .o,g2,p{{$}}
; RES: .o,g3,p{{$}}
; RES: .o,g4,px{{$}}
; RES: .o,g5,px{{$}}
; RES: .o,g6,p{{$}}
; RES: .o,g7,{{$}}
; RES: .o,g8,{{$}}
; RES: .o,g9,px{{$}}
; RES: .o,g10,px{{$}}

View File

@ -1,13 +1,13 @@
; RUN: llvm-as -o %t.bc %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O0 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O0 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O0 %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O1 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O1 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O1 %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O2 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O2 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O2 %s
; CHECK-O0: define internal void @foo(
; CHECK-O1: define internal void @foo(

View File

@ -1,7 +1,8 @@
; RUN: llvm-as -o %t.bc %s
; RUN: rm -f %t.opt.bc0 %t.opt.bc1 %t.o0 %t.o1
; RUN: env LD_PRELOAD=%llvmshlibdir/LLVMgold.so %gold -plugin %llvmshlibdir/LLVMgold.so -u foo -u bar -plugin-opt jobs=2 -plugin-opt save-temps -m elf_x86_64 -o %t %t.bc
; RUN: llvm-dis %t.opt.bc0 -o - | FileCheck --check-prefix=CHECK-BC0 %s
; RUN: llvm-dis %t.opt.bc1 -o - | FileCheck --check-prefix=CHECK-BC1 %s
; RUN: llvm-dis %t.5.precodegen.bc -o - | FileCheck --check-prefix=CHECK-BC0 %s
; RUN: llvm-dis %t.1.5.precodegen.bc -o - | FileCheck --check-prefix=CHECK-BC1 %s
; RUN: llvm-nm %t.o0 | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t.o1 | FileCheck --check-prefix=CHECK1 %s

View File

@ -3,7 +3,7 @@
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s
; test that the vectorizer is run.
; CHECK: fadd <4 x float>

View File

@ -19,4 +19,4 @@
; Check that the common symbol is not dropped completely, which was a regression
; in r262676.
; CHECK: @x = common global i32 0
; CHECK: @x = common global [4 x i8] zeroinitializer

View File

@ -3,7 +3,7 @@
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.2.internalize.bc -o - | FileCheck %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \

View File

@ -25,21 +25,23 @@
; RUN: llvm-bcanalyzer -dump %t2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: not test -e %t3
; Ensure gold generates an index as well as a binary by default in ThinLTO mode.
; Ensure gold generates an index as well as a binary with save-temps in ThinLTO mode.
; First force single-threaded mode
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: --plugin-opt=thinlto \
; RUN: --plugin-opt=jobs=1 \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-bcanalyzer -dump %t4.thinlto.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-bcanalyzer -dump %t4.index.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
; Next force multi-threaded mode
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: --plugin-opt=thinlto \
; RUN: --plugin-opt=jobs=2 \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-bcanalyzer -dump %t4.thinlto.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-bcanalyzer -dump %t4.index.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
; Test --plugin-opt=obj-path to ensure unique object files generated.
@ -48,8 +50,8 @@
; RUN: --plugin-opt=jobs=2 \
; RUN: --plugin-opt=obj-path=%t5.o \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-nm %t5.o0 | FileCheck %s --check-prefix=NM2
; RUN: llvm-nm %t5.o1 | FileCheck %s --check-prefix=NM2
; RUN: llvm-nm %t5.o2 | FileCheck %s --check-prefix=NM2
; NM: T f
; NM2: T {{f|g}}

View File

@ -14,8 +14,14 @@
; RUN: --plugin-opt=save-temps \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-nm %t3.o | FileCheck %s
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; This does not currently pass because the gold plugin now uses the
; combined summary rather than the IRMover to change the module's linkage
; during the ThinLTO backend. The internalization step implemented by IRMover
; for preempted symbols has not yet been implemented for the combined summary.
; XFAIL: *
; CHECK-NOT: U f
; OPT: define hidden void @weakfunc.llvm.0()

View File

@ -6,7 +6,7 @@
; RUN: --plugin-opt=-import-instr-limit=0 \
; RUN: --plugin-opt=save-temps \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s
; f() should be internalized and eliminated after inlining
; CHECK-NOT: @f()

View File

@ -14,13 +14,13 @@
; RUN: -shared \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-nm %t3.o | FileCheck %s
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; Ensure that f() is defined in resulting object file, and also
; confirm the weak linkage directly in the saved opt bitcode files.
; CHECK-NOT: U f
; OPT: declare hidden void @f()
; OPT-NOT: @f()
; OPT2: define weak_odr hidden void @f()
target triple = "x86_64-unknown-linux-gnu"

View File

@ -13,12 +13,17 @@
; RUN: llvm-nm %t3.o | FileCheck %s
; CHECK: weakfunc
; All of the preempted functions should have been eliminated (the plugin will
; not link them in).
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; Most of the preempted functions should have been eliminated (the plugin will
; set linkage of odr functions to available_externally and linkonce functions
; are removed by globaldce). FIXME: Need to introduce combined index linkage
; that means "drop this function" so we can avoid importing linkonce functions
; and drop weak functions.
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; OPT2-NOT: @
; OPT2: @weakfunc
; OPT2-NOT: @
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
target triple = "x86_64-unknown-linux-gnu"

View File

@ -3,7 +3,7 @@
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o %t2.o -o %t3.o
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s
%zed = type { i8 }
define void @foo() {

View File

@ -3,7 +3,7 @@
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s
; test that the vectorizer is run.
; CHECK: fadd <4 x float>

View File

@ -5,7 +5,7 @@
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o %t2.o -o %t.so
; RUN: llvm-readobj -t %t.so | FileCheck %s
; RUN: llvm-dis %t.so.bc -o - | FileCheck --check-prefix=IR %s
; RUN: llvm-dis %t.so.2.internalize.bc -o - | FileCheck --check-prefix=IR %s
; CHECK: Name: foo
; CHECK-NEXT: Value:
@ -16,7 +16,7 @@
; CHECK-NEXT: STV_PROTECTED
; CHECK-NEXT: ]
; IR: define protected void @foo
; IR: define void @foo
define weak protected void @foo() {
ret void

View File

@ -0,0 +1,11 @@
; RUN: llvm-as %s -o %t.bc
; RUN: not llvm-lto2 -o %t2.o %t.bc 2>&1 | FileCheck --check-prefix=ERR1 %s
; RUN: not llvm-lto2 -o %t2.o -r %t.bc,foo,p -r %t.bc,bar,p %t.bc 2>&1 | FileCheck --check-prefix=ERR2 %s
; RUN: not llvm-lto2 -o %t2.o -r %t.bc,foo,q %t.bc 2>&1 | FileCheck --check-prefix=ERR3 %s
; RUN: not llvm-lto2 -o %t2.o -r foo %t.bc 2>&1 | FileCheck --check-prefix=ERR4 %s
; ERR1: missing symbol resolution for {{.*}}.bc,foo
; ERR2: unused symbol resolution for {{.*}}.bc,bar
; ERR3: invalid character q in resolution: {{.*}}.bc,foo
; ERR4: invalid resolution: foo
@foo = global i32 0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
LTO
Object
Support
)
add_llvm_tool(llvm-lto2
llvm-lto2.cpp
)

View File

@ -0,0 +1,22 @@
;===- ./tools/llvm-lto2/LLVMBuild.txt --------------------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
; This file is distributed under the University of Illinois Open Source
; License. See LICENSE.TXT for details.
;
;===------------------------------------------------------------------------===;
;
; This is an LLVMBuild description file for the components in this subdirectory.
;
; For more information on the LLVMBuild system, please see:
;
; http://llvm.org/docs/LLVMBuild.html
;
;===------------------------------------------------------------------------===;
[component_0]
type = Tool
name = llvm-lto2
parent = Tools
required_libraries = LTO Object all-targets

View File

@ -0,0 +1,168 @@
//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program takes in a list of bitcode files, links them and performs
// link-time optimization according to the provided symbol resolutions using the
// resolution-based LTO interface, and outputs one or more object files.
//
// This program is intended to eventually replace llvm-lto which uses the legacy
// LTO interface.
//
//===----------------------------------------------------------------------===//
#include "llvm/LTO/LTO.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
using namespace llvm;
using namespace lto;
using namespace object;
static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input bitcode files>"));
static cl::opt<std::string> OutputFilename("o", cl::Required,
cl::desc("Output filename"),
cl::value_desc("filename"));
static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
static cl::list<std::string> SymbolResolutions(
"r",
cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
"where \"resolution\" is a sequence (which may be empty) of the\n"
"following characters:\n"
" p - prevailing: the linker has chosen this definition of the\n"
" symbol\n"
" l - local: the definition of this symbol is unpreemptable at\n"
" runtime and is known to be in this linkage unit\n"
" x - externally visible: the definition of this symbol is\n"
" visible outside of the LTO unit\n"
"A resolution for each symbol must be specified."),
cl::ZeroOrMore);
static void check(Error E, std::string Msg) {
if (!E)
return;
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
errs() << "llvm-lto: " << Msg << ": " << EIB.message().c_str() << '\n';
});
exit(1);
}
template <typename T> static T check(Expected<T> E, std::string Msg) {
if (E)
return std::move(*E);
check(E.takeError(), Msg);
return T();
}
static void check(std::error_code EC, std::string Msg) {
check(errorCodeToError(EC), Msg);
}
template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
if (E)
return std::move(*E);
check(E.getError(), Msg);
return T();
}
int main(int argc, char **argv) {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");
std::map<std::pair<std::string, std::string>, SymbolResolution>
CommandLineResolutions;
for (std::string R : SymbolResolutions) {
StringRef Rest = R;
StringRef FileName, SymbolName;
std::tie(FileName, Rest) = Rest.split(',');
if (Rest.empty()) {
llvm::errs() << "invalid resolution: " << R << '\n';
return 1;
}
std::tie(SymbolName, Rest) = Rest.split(',');
SymbolResolution Res;
for (char C : Rest) {
if (C == 'p')
Res.Prevailing = true;
else if (C == 'l')
Res.FinalDefinitionInLinkageUnit = true;
else if (C == 'x')
Res.VisibleToRegularObj = true;
else
llvm::errs() << "invalid character " << C << " in resolution: " << R
<< '\n';
}
CommandLineResolutions[{FileName, SymbolName}] = Res;
}
std::vector<std::unique_ptr<MemoryBuffer>> MBs;
Config Conf;
Conf.DiagHandler = [](const DiagnosticInfo &) {
exit(1);
};
if (SaveTemps)
check(Conf.addSaveTemps(OutputFilename), "Config::addSaveTemps failed");
LTO Lto(std::move(Conf));
bool HasErrors = false;
for (std::string F : InputFilenames) {
std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
std::unique_ptr<InputFile> Input =
check(InputFile::create(MB->getMemBufferRef()), F);
std::vector<SymbolResolution> Res;
for (const InputFile::Symbol &Sym : Input->symbols()) {
auto I = CommandLineResolutions.find({F, Sym.getName()});
if (I == CommandLineResolutions.end()) {
llvm::errs() << argv[0] << ": missing symbol resolution for " << F
<< ',' << Sym.getName() << '\n';
HasErrors = true;
} else {
Res.push_back(I->second);
CommandLineResolutions.erase(I);
}
}
if (HasErrors)
continue;
MBs.push_back(std::move(MB));
check(Lto.add(std::move(Input), Res), F);
}
if (!CommandLineResolutions.empty()) {
HasErrors = true;
for (auto UnusedRes : CommandLineResolutions)
llvm::errs() << argv[0] << ": unused symbol resolution for "
<< UnusedRes.first.first << ',' << UnusedRes.first.second
<< '\n';
}
if (HasErrors)
return 1;
auto AddStream = [&](size_t Task) {
std::string Path = OutputFilename + "." + utostr(Task);
std::error_code EC;
auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
check(EC, Path);
return S;
};
check(Lto.run(AddStream), "LTO::run failed");
}