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:
parent
602583e93c
commit
bd574a6d8f
172
include/llvm/LTO/Config.h
Normal file
172
include/llvm/LTO/Config.h
Normal 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
|
@ -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
|
||||
|
51
include/llvm/LTO/LTOBackend.h
Normal file
51
include/llvm/LTO/LTOBackend.h
Normal 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
|
@ -49,6 +49,7 @@ endif()
|
||||
|
||||
add_llvm_library(LLVMLTO
|
||||
LTO.cpp
|
||||
LTOBackend.cpp
|
||||
LTOModule.cpp
|
||||
LTOCodeGenerator.cpp
|
||||
UpdateCompilerUsed.cpp
|
||||
|
@ -34,4 +34,4 @@ required_libraries =
|
||||
Scalar
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
TransformUtils
|
||||
|
536
lib/LTO/LTO.cpp
536
lib/LTO/LTO.cpp
@ -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
284
lib/LTO/LTOBackend.cpp
Normal 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();
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ set(LLVM_TEST_DEPENDS
|
||||
llvm-extract
|
||||
llvm-lib
|
||||
llvm-link
|
||||
llvm-lto2
|
||||
llvm-mc
|
||||
llvm-mcmarkup
|
||||
llvm-nm
|
||||
|
4
test/LTO/Resolution/X86/Inputs/alias-1.ll
Normal file
4
test/LTO/Resolution/X86/Inputs/alias-1.ll
Normal 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
|
28
test/LTO/Resolution/X86/Inputs/comdat.ll
Normal file
28
test/LTO/Resolution/X86/Inputs/comdat.ll
Normal 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
|
22
test/LTO/Resolution/X86/alias.ll
Normal file
22
test/LTO/Resolution/X86/alias.ll
Normal 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
|
86
test/LTO/Resolution/X86/comdat.ll
Normal file
86
test/LTO/Resolution/X86/comdat.ll
Normal 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: }
|
2
test/LTO/Resolution/X86/lit.local.cfg
Normal file
2
test/LTO/Resolution/X86/lit.local.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
if not 'X86' in config.root.targets:
|
||||
config.unsupported = True
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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: ]
|
||||
|
@ -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
|
||||
|
@ -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{{$}}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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}}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
11
test/tools/llvm-lto2/errors.ll
Normal file
11
test/tools/llvm-lto2/errors.ll
Normal 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
10
tools/llvm-lto2/CMakeLists.txt
Normal file
10
tools/llvm-lto2/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
LTO
|
||||
Object
|
||||
Support
|
||||
)
|
||||
|
||||
add_llvm_tool(llvm-lto2
|
||||
llvm-lto2.cpp
|
||||
)
|
22
tools/llvm-lto2/LLVMBuild.txt
Normal file
22
tools/llvm-lto2/LLVMBuild.txt
Normal 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
|
168
tools/llvm-lto2/llvm-lto2.cpp
Normal file
168
tools/llvm-lto2/llvm-lto2.cpp
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user