mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-22 13:32:09 +00: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
|
#ifndef LLVM_LTO_LTO_H
|
||||||
#define LLVM_LTO_LTO_H
|
#define LLVM_LTO_LTO_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/MapVector.h"
|
||||||
#include "llvm/ADT/StringMap.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/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 {
|
namespace llvm {
|
||||||
|
|
||||||
|
class Error;
|
||||||
class LLVMContext;
|
class LLVMContext;
|
||||||
class MemoryBufferRef;
|
class MemoryBufferRef;
|
||||||
class Module;
|
class Module;
|
||||||
|
class Target;
|
||||||
|
class raw_pwrite_stream;
|
||||||
|
|
||||||
/// Helper to load a module from bitcode.
|
/// Helper to load a module from bitcode.
|
||||||
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
|
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
|
||||||
@ -69,6 +82,319 @@ void thinLTOResolveWeakForLinkerInIndex(
|
|||||||
void thinLTOInternalizeAndPromoteInIndex(
|
void thinLTOInternalizeAndPromoteInIndex(
|
||||||
ModuleSummaryIndex &Index,
|
ModuleSummaryIndex &Index,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported);
|
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
|
#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
|
add_llvm_library(LLVMLTO
|
||||||
LTO.cpp
|
LTO.cpp
|
||||||
|
LTOBackend.cpp
|
||||||
LTOModule.cpp
|
LTOModule.cpp
|
||||||
LTOCodeGenerator.cpp
|
LTOCodeGenerator.cpp
|
||||||
UpdateCompilerUsed.cpp
|
UpdateCompilerUsed.cpp
|
||||||
|
@ -34,4 +34,4 @@ required_libraries =
|
|||||||
Scalar
|
Scalar
|
||||||
Support
|
Support
|
||||||
Target
|
Target
|
||||||
TransformUtils
|
TransformUtils
|
||||||
|
536
lib/LTO/LTO.cpp
536
lib/LTO/LTO.cpp
@ -12,16 +12,39 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "llvm/LTO/LTO.h"
|
#include "llvm/LTO/LTO.h"
|
||||||
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||||
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||||
#include "llvm/Bitcode/ReaderWriter.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/MemoryBuffer.h"
|
||||||
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/SourceMgr.h"
|
#include "llvm/Support/SourceMgr.h"
|
||||||
|
#include "llvm/Support/TargetRegistry.h"
|
||||||
|
#include "llvm/Support/ThreadPool.h"
|
||||||
#include "llvm/Support/raw_ostream.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
|
// Simple helper to load a module from bitcode
|
||||||
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
|
std::unique_ptr<Module>
|
||||||
LLVMContext &Context, bool Lazy) {
|
llvm::loadModuleFromBuffer(const MemoryBufferRef &Buffer, LLVMContext &Context,
|
||||||
|
bool Lazy) {
|
||||||
SMDiagnostic Err;
|
SMDiagnostic Err;
|
||||||
ErrorOr<std::unique_ptr<Module>> ModuleOrErr(nullptr);
|
ErrorOr<std::unique_ptr<Module>> ModuleOrErr(nullptr);
|
||||||
if (Lazy) {
|
if (Lazy) {
|
||||||
@ -76,7 +99,7 @@ static void thinLTOResolveWeakForLinkerGUID(
|
|||||||
// current module. However there is a chance that another module is still
|
// 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
|
// referencing them because of the import. We make sure we always emit at least
|
||||||
// one copy.
|
// one copy.
|
||||||
void thinLTOResolveWeakForLinkerInIndex(
|
void llvm::thinLTOResolveWeakForLinkerInIndex(
|
||||||
ModuleSummaryIndex &Index,
|
ModuleSummaryIndex &Index,
|
||||||
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
||||||
isPrevailing,
|
isPrevailing,
|
||||||
@ -110,10 +133,513 @@ static void thinLTOInternalizeAndPromoteGUID(
|
|||||||
|
|
||||||
// Update the linkages in the given \p Index to mark exported values
|
// Update the linkages in the given \p Index to mark exported values
|
||||||
// as external and non-exported values as internal.
|
// as external and non-exported values as internal.
|
||||||
void thinLTOInternalizeAndPromoteInIndex(
|
void llvm::thinLTOInternalizeAndPromoteInIndex(
|
||||||
ModuleSummaryIndex &Index,
|
ModuleSummaryIndex &Index,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported) {
|
function_ref<bool(StringRef, GlobalValue::GUID)> isExported) {
|
||||||
for (auto &I : Index)
|
for (auto &I : Index)
|
||||||
thinLTOInternalizeAndPromoteGUID(I.second, I.first, isExported);
|
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;
|
return EC;
|
||||||
|
|
||||||
std::unique_ptr<Module> &M = MOrErr.get();
|
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-extract
|
||||||
llvm-lib
|
llvm-lib
|
||||||
llvm-link
|
llvm-link
|
||||||
|
llvm-lto2
|
||||||
llvm-mc
|
llvm-mc
|
||||||
llvm-mcmarkup
|
llvm-mcmarkup
|
||||||
llvm-nm
|
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-lib\b",
|
||||||
r"\bllvm-link\b",
|
r"\bllvm-link\b",
|
||||||
r"\bllvm-lto\b",
|
r"\bllvm-lto\b",
|
||||||
|
r"\bllvm-lto2\b",
|
||||||
r"\bllvm-mc\b",
|
r"\bllvm-mc\b",
|
||||||
r"\bllvm-mcmarkup\b",
|
r"\bllvm-mcmarkup\b",
|
||||||
r"\bllvm-nm\b",
|
r"\bllvm-nm\b",
|
||||||
|
@ -16,7 +16,7 @@ define hidden void @g() {
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK: define internal void @h() local_unnamed_addr {
|
; CHECK: define internal void @h() {
|
||||||
define linkonce_odr void @h() local_unnamed_addr {
|
define linkonce_odr void @h() local_unnamed_addr {
|
||||||
ret void
|
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: 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: -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
|
$c1 = comdat any
|
||||||
|
|
||||||
@ -24,42 +25,37 @@ bb11:
|
|||||||
@a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
|
@a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
|
||||||
@a15 = alias i16, i16* @a14
|
@a15 = alias i16, i16* @a14
|
||||||
|
|
||||||
; CHECK: $c1 = comdat any
|
; gold's resolutions should tell us that our $c1 wins, and the other input's $c2
|
||||||
; CHECK: $c2 = comdat any
|
; 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{{$}}
|
; RES: 2.o,f1,l{{$}}
|
||||||
; CHECK-DAG: @r12 = global i32 (i8*)* @f1{{$}}
|
; 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{{$}}
|
; f1's protected visibility should be reflected in the DSO.
|
||||||
; CHECK-DAG: @r22 = global i32 (i8*)* @f1{{$}}
|
|
||||||
|
|
||||||
; CHECK-DAG: @v1.1 = internal global i32 41, comdat($c2)
|
; OBJ: Name: f1 (
|
||||||
|
; OBJ-NEXT: Value:
|
||||||
; CHECK-DAG: @a11 = alias i32, i32* @v1{{$}}
|
; OBJ-NEXT: Size:
|
||||||
; CHECK-DAG: @a12 = alias i16, bitcast (i32* @v1 to i16*)
|
; OBJ-NEXT: Binding:
|
||||||
|
; OBJ-NEXT: Type:
|
||||||
; CHECK-DAG: @a13 = alias i32 (i8*), i32 (i8*)* @f1{{$}}
|
; OBJ-NEXT: Other [
|
||||||
; CHECK-DAG: @a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
|
; OBJ-NEXT: STV_PROTECTED
|
||||||
|
; OBJ-NEXT: ]
|
||||||
; 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: }
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=A
|
; 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.
|
; 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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=emit-llvm \
|
; RUN: --plugin-opt=emit-llvm \
|
||||||
@ -19,7 +19,7 @@
|
|||||||
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=B
|
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=B
|
||||||
|
|
||||||
; (i16 align 8) + (i8 align 16) = i16 align 16
|
; (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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=emit-llvm \
|
; RUN: --plugin-opt=emit-llvm \
|
||||||
@ -27,7 +27,7 @@
|
|||||||
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=C
|
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=C
|
||||||
|
|
||||||
; (i16 align 8) + (i8 align 1) = i16 align 8.
|
; (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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=emit-llvm \
|
; RUN: --plugin-opt=emit-llvm \
|
||||||
@ -35,7 +35,7 @@
|
|||||||
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=EXEC %s
|
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=EXEC %s
|
||||||
|
|
||||||
; All IR case, we internalize a after merging.
|
; 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: llc %p/Inputs/common.ll -o %t2native.o -filetype=obj
|
||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
@ -44,4 +44,4 @@
|
|||||||
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=MIXED %s
|
; 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 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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=emit-llvm \
|
; RUN: --plugin-opt=emit-llvm \
|
||||||
; RUN: --plugin-opt=generate-api-file \
|
|
||||||
; RUN: -shared %t.o -o %t2.o
|
; RUN: -shared %t.o -o %t2.o
|
||||||
; RUN: llvm-dis %t2.o -o - | FileCheck %s
|
; RUN: llvm-dis %t2.o -o - | FileCheck %s
|
||||||
; RUN: FileCheck --check-prefix=API %s < %T/../apifile.txt
|
|
||||||
|
|
||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: -m elf_x86_64 --plugin-opt=save-temps \
|
; RUN: -m elf_x86_64 --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o -o %t3.o
|
; RUN: -shared %t.o -o %t3.o
|
||||||
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
|
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
|
||||||
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
|
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s
|
||||||
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %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: llvm-nm %t3.o.o | FileCheck --check-prefix=NM %s
|
||||||
|
|
||||||
; RUN: rm -f %t4.o
|
; RUN: rm -f %t4.o
|
||||||
@ -25,19 +24,19 @@
|
|||||||
|
|
||||||
target triple = "x86_64-unknown-linux-gnu"
|
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
|
@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
|
@g2 = linkonce_odr local_unnamed_addr constant i32 32
|
||||||
|
|
||||||
; CHECK-DAG: @g3 = internal unnamed_addr constant i32 32
|
; CHECK-DAG: @g3 = internal unnamed_addr constant i32 32
|
||||||
@g3 = linkonce_odr 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
|
@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
|
@g5 = linkonce_odr local_unnamed_addr global i32 32
|
||||||
|
|
||||||
; CHECK-DAG: @g6 = internal 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
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK-DAG: define linkonce_odr void @f5()
|
; CHECK-DAG: define weak_odr void @f5()
|
||||||
; OPT-DAG: define linkonce_odr void @f5()
|
; OPT-DAG: define weak_odr void @f5()
|
||||||
define linkonce_odr void @f5() {
|
define linkonce_odr void @f5() {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
@ -97,15 +96,21 @@ define i32* @f8() {
|
|||||||
ret i32* @g8
|
ret i32* @g8
|
||||||
}
|
}
|
||||||
|
|
||||||
; API: f1 PREVAILING_DEF_IRONLY
|
; RES: .o,f1,pl{{$}}
|
||||||
; API: f2 PREVAILING_DEF_IRONLY
|
; RES: .o,f2,pl{{$}}
|
||||||
; API: f3 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f3,px{{$}}
|
||||||
; API: f4 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f4,p{{$}}
|
||||||
; API: f5 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f5,px{{$}}
|
||||||
; API: f6 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f6,p{{$}}
|
||||||
; API: f7 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f7,px{{$}}
|
||||||
; API: f8 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,f8,px{{$}}
|
||||||
; API: g7 UNDEF
|
; RES: .o,g1,px{{$}}
|
||||||
; API: g8 UNDEF
|
; RES: .o,g2,p{{$}}
|
||||||
; API: g9 PREVAILING_DEF_IRONLY_EXP
|
; RES: .o,g3,p{{$}}
|
||||||
; API: g10 PREVAILING_DEF_IRONLY_EXP
|
; 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: llvm-as -o %t.bc %s
|
||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
|
||||||
; RUN: -plugin-opt=O0 -r -o %t.o %t.bc
|
; 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: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
|
||||||
; RUN: -plugin-opt=O1 -r -o %t.o %t.bc
|
; 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: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
|
||||||
; RUN: -plugin-opt=O2 -r -o %t.o %t.bc
|
; 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-O0: define internal void @foo(
|
||||||
; CHECK-O1: define internal void @foo(
|
; CHECK-O1: define internal void @foo(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
; RUN: llvm-as -o %t.bc %s
|
; 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: 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.5.precodegen.bc -o - | FileCheck --check-prefix=CHECK-BC0 %s
|
||||||
; RUN: llvm-dis %t.opt.bc1 -o - | FileCheck --check-prefix=CHECK-BC1 %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.o0 | FileCheck --check-prefix=CHECK0 %s
|
||||||
; RUN: llvm-nm %t.o1 | FileCheck --check-prefix=CHECK1 %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: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o -o %t2.o
|
; 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.
|
; test that the vectorizer is run.
|
||||||
; CHECK: fadd <4 x float>
|
; CHECK: fadd <4 x float>
|
||||||
|
@ -19,4 +19,4 @@
|
|||||||
|
|
||||||
; Check that the common symbol is not dropped completely, which was a regression
|
; Check that the common symbol is not dropped completely, which was a regression
|
||||||
; in r262676.
|
; 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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o -o %t2.o
|
; 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: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=emit-llvm \
|
; RUN: --plugin-opt=emit-llvm \
|
||||||
|
@ -25,21 +25,23 @@
|
|||||||
; RUN: llvm-bcanalyzer -dump %t2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
|
; RUN: llvm-bcanalyzer -dump %t2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
|
||||||
; RUN: not test -e %t3
|
; 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
|
; First force single-threaded mode
|
||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=thinlto \
|
; RUN: --plugin-opt=thinlto \
|
||||||
; RUN: --plugin-opt=jobs=1 \
|
; RUN: --plugin-opt=jobs=1 \
|
||||||
; RUN: -shared %t.o %t2.o -o %t4
|
; 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
|
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
|
||||||
|
|
||||||
; Next force multi-threaded mode
|
; Next force multi-threaded mode
|
||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: --plugin-opt=thinlto \
|
; RUN: --plugin-opt=thinlto \
|
||||||
; RUN: --plugin-opt=jobs=2 \
|
; RUN: --plugin-opt=jobs=2 \
|
||||||
; RUN: -shared %t.o %t2.o -o %t4
|
; 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
|
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
|
||||||
|
|
||||||
; Test --plugin-opt=obj-path to ensure unique object files generated.
|
; Test --plugin-opt=obj-path to ensure unique object files generated.
|
||||||
@ -48,8 +50,8 @@
|
|||||||
; RUN: --plugin-opt=jobs=2 \
|
; RUN: --plugin-opt=jobs=2 \
|
||||||
; RUN: --plugin-opt=obj-path=%t5.o \
|
; RUN: --plugin-opt=obj-path=%t5.o \
|
||||||
; RUN: -shared %t.o %t2.o -o %t4
|
; 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.o1 | FileCheck %s --check-prefix=NM2
|
||||||
|
; RUN: llvm-nm %t5.o2 | FileCheck %s --check-prefix=NM2
|
||||||
|
|
||||||
; NM: T f
|
; NM: T f
|
||||||
; NM2: T {{f|g}}
|
; NM2: T {{f|g}}
|
||||||
|
@ -14,8 +14,14 @@
|
|||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -o %t3.o %t2.o %t.o
|
; RUN: -o %t3.o %t2.o %t.o
|
||||||
; RUN: llvm-nm %t3.o | FileCheck %s
|
; RUN: llvm-nm %t3.o | FileCheck %s
|
||||||
; 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
|
||||||
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %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
|
; CHECK-NOT: U f
|
||||||
; OPT: define hidden void @weakfunc.llvm.0()
|
; OPT: define hidden void @weakfunc.llvm.0()
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
; RUN: --plugin-opt=-import-instr-limit=0 \
|
; RUN: --plugin-opt=-import-instr-limit=0 \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -o %t3.o %t2.o %t.o
|
; 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
|
; f() should be internalized and eliminated after inlining
|
||||||
; CHECK-NOT: @f()
|
; CHECK-NOT: @f()
|
||||||
|
@ -14,13 +14,13 @@
|
|||||||
; RUN: -shared \
|
; RUN: -shared \
|
||||||
; RUN: -o %t3.o %t2.o %t.o
|
; RUN: -o %t3.o %t2.o %t.o
|
||||||
; RUN: llvm-nm %t3.o | FileCheck %s
|
; RUN: llvm-nm %t3.o | FileCheck %s
|
||||||
; 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
|
||||||
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %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
|
; Ensure that f() is defined in resulting object file, and also
|
||||||
; confirm the weak linkage directly in the saved opt bitcode files.
|
; confirm the weak linkage directly in the saved opt bitcode files.
|
||||||
; CHECK-NOT: U f
|
; CHECK-NOT: U f
|
||||||
; OPT: declare hidden void @f()
|
; OPT-NOT: @f()
|
||||||
; OPT2: define weak_odr hidden void @f()
|
; OPT2: define weak_odr hidden void @f()
|
||||||
|
|
||||||
target triple = "x86_64-unknown-linux-gnu"
|
target triple = "x86_64-unknown-linux-gnu"
|
||||||
|
@ -13,12 +13,17 @@
|
|||||||
; RUN: llvm-nm %t3.o | FileCheck %s
|
; RUN: llvm-nm %t3.o | FileCheck %s
|
||||||
; CHECK: weakfunc
|
; CHECK: weakfunc
|
||||||
|
|
||||||
; All of the preempted functions should have been eliminated (the plugin will
|
; Most of the preempted functions should have been eliminated (the plugin will
|
||||||
; not link them in).
|
; set linkage of odr functions to available_externally and linkonce functions
|
||||||
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
|
; 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: @
|
; 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"
|
target triple = "x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o %t2.o -o %t3.o
|
; 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 }
|
%zed = type { i8 }
|
||||||
define void @foo() {
|
define void @foo() {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
|
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
|
||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o -o %t2.o
|
; 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.
|
; test that the vectorizer is run.
|
||||||
; CHECK: fadd <4 x float>
|
; CHECK: fadd <4 x float>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
; RUN: --plugin-opt=save-temps \
|
; RUN: --plugin-opt=save-temps \
|
||||||
; RUN: -shared %t.o %t2.o -o %t.so
|
; RUN: -shared %t.o %t2.o -o %t.so
|
||||||
; RUN: llvm-readobj -t %t.so | FileCheck %s
|
; 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: Name: foo
|
||||||
; CHECK-NEXT: Value:
|
; CHECK-NEXT: Value:
|
||||||
@ -16,7 +16,7 @@
|
|||||||
; CHECK-NEXT: STV_PROTECTED
|
; CHECK-NEXT: STV_PROTECTED
|
||||||
; CHECK-NEXT: ]
|
; CHECK-NEXT: ]
|
||||||
|
|
||||||
; IR: define protected void @foo
|
; IR: define void @foo
|
||||||
|
|
||||||
define weak protected void @foo() {
|
define weak protected void @foo() {
|
||||||
ret void
|
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…
x
Reference in New Issue
Block a user