328 changes: 327 additions & 1 deletion llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,27 @@
#ifndef LLVM_LTO_LTO_H
#define LLVM_LTO_LTO_H

#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/LTO/Config.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/thread.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO/FunctionImport.h"

namespace llvm {

class Error;
class LLVMContext;
class MemoryBufferRef;
class Module;
class Target;
class raw_pwrite_stream;

/// Helper to load a module from bitcode.
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
Expand Down Expand Up @@ -69,6 +82,319 @@ void thinLTOResolveWeakForLinkerInIndex(
void thinLTOInternalizeAndPromoteInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported);
}

namespace lto {

class LTO;
struct SymbolResolution;
class ThinBackendProc;

/// An input file. This is a wrapper for IRObjectFile that exposes only the
/// information that an LTO client should need in order to do symbol resolution.
class InputFile {
// FIXME: Remove LTO class friendship once we have bitcode symbol tables.
friend LTO;
InputFile() = default;

// FIXME: Remove the LLVMContext once we have bitcode symbol tables.
LLVMContext Ctx;
std::unique_ptr<object::IRObjectFile> Obj;

public:
/// Create an InputFile.
static Expected<std::unique_ptr<InputFile>> create(MemoryBufferRef Object);

class symbol_iterator;

/// This is a wrapper for object::basic_symbol_iterator that exposes only the
/// information that an LTO client should need in order to do symbol
/// resolution.
///
/// This object is ephemeral; it is only valid as long as an iterator obtained
/// from symbols() refers to it.
class Symbol {
friend symbol_iterator;
friend LTO;

object::basic_symbol_iterator I;
const GlobalValue *GV;
uint32_t Flags;
SmallString<64> Name;

bool shouldSkip() {
return !(Flags & object::BasicSymbolRef::SF_Global) ||
(Flags & object::BasicSymbolRef::SF_FormatSpecific);
}

void skip() {
const object::SymbolicFile *Obj = I->getObject();
auto E = Obj->symbol_end();
while (I != E) {
Flags = I->getFlags();
if (!shouldSkip())
break;
++I;
}
if (I == E)
return;

Name.clear();
{
raw_svector_ostream OS(Name);
I->printName(OS);
}
GV = cast<object::IRObjectFile>(Obj)->getSymbolGV(I->getRawDataRefImpl());
}

public:
Symbol(object::basic_symbol_iterator I) : I(I) { skip(); }

StringRef getName() const { return Name; }
StringRef getIRName() const {
if (GV)
return GV->getName();
return StringRef();
}
uint32_t getFlags() const { return Flags; }
GlobalValue::VisibilityTypes getVisibility() const {
if (GV)
return GV->getVisibility();
return GlobalValue::DefaultVisibility;
}
bool canBeOmittedFromSymbolTable() const {
return GV && llvm::canBeOmittedFromSymbolTable(GV);
}
Expected<const Comdat *> getComdat() const {
const GlobalObject *GO;
if (auto *GA = dyn_cast<GlobalAlias>(GV)) {
GO = GA->getBaseObject();
if (!GO)
return make_error<StringError>("Unable to determine comdat of alias!",
inconvertibleErrorCode());
} else {
GO = cast<GlobalObject>(GV);
}
if (GV)
return GV->getComdat();
return nullptr;
}
size_t getCommonSize() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getParent()->getDataLayout().getTypeAllocSize(
GV->getType()->getElementType());
}
unsigned getCommonAlignment() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getAlignment();
}
};

class symbol_iterator {
Symbol Sym;

public:
symbol_iterator(object::basic_symbol_iterator I) : Sym(I) {}

symbol_iterator &operator++() {
++Sym.I;
Sym.skip();
return *this;
}

symbol_iterator operator++(int) {
symbol_iterator I = *this;
++*this;
return I;
}

const Symbol &operator*() const { return Sym; }
const Symbol *operator->() const { return &Sym; }

bool operator!=(const symbol_iterator &Other) const {
return Sym.I != Other.Sym.I;
}
};

/// A range over the symbols in this InputFile.
iterator_range<symbol_iterator> symbols() {
return llvm::make_range(symbol_iterator(Obj->symbol_begin()),
symbol_iterator(Obj->symbol_end()));
}

StringRef getSourceFileName() const {
return Obj->getModule().getSourceFileName();
}
};

/// A ThinBackend defines what happens after the thin-link phase during ThinLTO.
/// The details of this type definition aren't important; clients can only
/// create a ThinBackend using one of the create*ThinBackend() functions below.
typedef std::function<std::unique_ptr<ThinBackendProc>(
Config &C, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream)>
ThinBackend;

/// This ThinBackend runs the individual backend jobs in-process.
ThinBackend createInProcessThinBackend(unsigned ParallelismLevel);

/// This ThinBackend writes individual module indexes to files, instead of
/// running the individual backend jobs. This backend is for distributed builds
/// where separate processes will invoke the real backends.
///
/// To find the path to write the index to, the backend checks if the path has a
/// prefix of OldPrefix; if so, it replaces that prefix with NewPrefix. It then
/// appends ".thinlto.bc" and writes the index to that path. If
/// ShouldEmitImportsFiles is true it also writes a list of imported files to a
/// similar path with ".imports" appended instead.
ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
std::string NewPrefix,
bool ShouldEmitImportsFiles,
std::string LinkedObjectsFile);

/// This class implements a resolution-based interface to LLVM's LTO
/// functionality. It supports regular LTO, parallel LTO code generation and
/// ThinLTO. You can use it from a linker in the following way:
/// - Set hooks and code generation options (see lto::Config struct defined in
/// Config.h), and use the lto::Config object to create an lto::LTO object.
/// - Create lto::InputFile objects using lto::InputFile::create(), then use
/// the symbols() function to enumerate its symbols and compute a resolution
/// for each symbol (see SymbolResolution below).
/// - After the linker has visited each input file (and each regular object
/// file) and computed a resolution for each symbol, take each lto::InputFile
/// and pass it and an array of symbol resolutions to the add() function.
/// - Call the getMaxTasks() function to get an upper bound on the number of
/// native object files that LTO may add to the link.
/// - Call the run() function. This function will use the supplied AddStream
/// function to add up to getMaxTasks() native object files to the link.
class LTO {
friend InputFile;

public:
/// Create an LTO object. A default constructed LTO object has a reasonable
/// production configuration, but you can customize it by passing arguments to
/// this constructor.
/// FIXME: We do currently require the DiagHandler field to be set in Conf.
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
unsigned ParallelCodeGenParallelismLevel = 1);

/// Add an input file to the LTO link, using the provided symbol resolutions.
/// The symbol resolutions must appear in the enumeration order given by
/// InputFile::symbols().
Error add(std::unique_ptr<InputFile> Obj, ArrayRef<SymbolResolution> Res);

/// Returns an upper bound on the number of tasks that the client may expect.
/// This may only be called after all IR object files have been added. For a
/// full description of tasks see LTOBackend.h.
size_t getMaxTasks() const;

/// Runs the LTO pipeline. This function calls the supplied AddStream function
/// to add native object files to the link.
Error run(AddStreamFn AddStream);

private:
Config Conf;

struct RegularLTOState {
RegularLTOState(unsigned ParallelCodeGenParallelismLevel, Config &Conf);

unsigned ParallelCodeGenParallelismLevel;
LTOLLVMContext Ctx;
bool HasModule = false;
std::unique_ptr<Module> CombinedModule;
IRMover Mover;
} RegularLTO;

struct ThinLTOState {
ThinLTOState(ThinBackend Backend);

ThinBackend Backend;
ModuleSummaryIndex CombinedIndex;
MapVector<StringRef, MemoryBufferRef> ModuleMap;
DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
} ThinLTO;

// The global resolution for a particular (mangled) symbol name. This is in
// particular necessary to track whether each symbol can be internalized.
// Because any input file may introduce a new cross-partition reference, we
// cannot make any final internalization decisions until all input files have
// been added and the client has called run(). During run() we apply
// internalization decisions either directly to the module (for regular LTO)
// or to the combined index (for ThinLTO).
struct GlobalResolution {
/// The unmangled name of the global.
std::string IRName;

bool UnnamedAddr = true;

/// This field keeps track of the partition number of this global. The
/// regular LTO object is partition 0, while each ThinLTO object has its own
/// partition number from 1 onwards.
///
/// Any global that is defined or used by more than one partition, or that
/// is referenced externally, may not be internalized.
///
/// Partitions generally have a one-to-one correspondence with tasks, except
/// that we use partition 0 for all parallel LTO code generation partitions.
/// Any partitioning of the combined LTO object is done internally by the
/// LTO backend.
size_t Partition = Unknown;

/// Special partition numbers.
enum {
/// A partition number has not yet been assigned to this global.
Unknown = -1ull,

/// This global is either used by more than one partition or has an
/// external reference, and therefore cannot be internalized.
External = -2ull,
};
};

// Global mapping from mangled symbol names to resolutions.
StringMap<GlobalResolution> GlobalResolutions;

void writeToResolutionFile(InputFile *Input, ArrayRef<SymbolResolution> Res);

void addSymbolToGlobalRes(object::IRObjectFile *Obj,
SmallPtrSet<GlobalValue *, 8> &Used,
const InputFile::Symbol &Sym, SymbolResolution Res,
size_t Partition);

Error addRegularLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);
Error addThinLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);

Error runRegularLTO(AddStreamFn AddStream);
Error runThinLTO(AddStreamFn AddStream);

mutable bool CalledGetMaxTasks = false;
};

/// The resolution for a symbol. The linker must provide a SymbolResolution for
/// each global symbol based on its internal resolution of that symbol.
struct SymbolResolution {
SymbolResolution()
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0) {
}
/// The linker has chosen this definition of the symbol.
unsigned Prevailing : 1;

/// The definition of this symbol is unpreemptable at runtime and is known to
/// be in this linkage unit.
unsigned FinalDefinitionInLinkageUnit : 1;

/// The definition of this symbol is visible outside of the LTO unit.
unsigned VisibleToRegularObj : 1;
};

} // namespace lto
} // namespace llvm

#endif
51 changes: 51 additions & 0 deletions llvm/include/llvm/LTO/LTOBackend.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions llvm/lib/LTO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ endif()

add_llvm_library(LLVMLTO
LTO.cpp
LTOBackend.cpp
LTOModule.cpp
LTOCodeGenerator.cpp
UpdateCompilerUsed.cpp
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/LTO/LLVMBuild.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ required_libraries =
Scalar
Support
Target
TransformUtils
TransformUtils
536 changes: 531 additions & 5 deletions llvm/lib/LTO/LTO.cpp

Large diffs are not rendered by default.

277 changes: 277 additions & 0 deletions llvm/lib/LTO/LTOBackend.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
//===-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) {
ShouldDiscardValueNames = false;

std::error_code EC;
ResolutionFile = 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;
PathPrefix = OutputFileName;
if (Task != 0)
PathPrefix += "." + utostr(Task);
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();
}
2 changes: 1 addition & 1 deletion llvm/lib/Object/IRObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,5 +324,5 @@ llvm::object::IRObjectFile::create(MemoryBufferRef Object,
return EC;

std::unique_ptr<Module> &M = MOrErr.get();
return llvm::make_unique<IRObjectFile>(Object, std::move(M));
return llvm::make_unique<IRObjectFile>(BCOrErr.get(), std::move(M));
}
1 change: 1 addition & 0 deletions llvm/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(LLVM_TEST_DEPENDS
llvm-extract
llvm-lib
llvm-link
llvm-lto2
llvm-mc
llvm-mcmarkup
llvm-nm
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/LTO/Resolution/X86/Inputs/alias-1.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 28 additions & 0 deletions llvm/test/LTO/Resolution/X86/Inputs/comdat.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions llvm/test/LTO/Resolution/X86/alias.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 86 additions & 0 deletions llvm/test/LTO/Resolution/X86/comdat.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions llvm/test/LTO/Resolution/X86/lit.local.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
if not 'X86' in config.root.targets:
config.unsupported = True
1 change: 1 addition & 0 deletions llvm/test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ for pattern in [r"\bbugpoint\b(?!-)",
r"\bllvm-lib\b",
r"\bllvm-link\b",
r"\bllvm-lto\b",
r"\bllvm-lto2\b",
r"\bllvm-mc\b",
r"\bllvm-mcmarkup\b",
r"\bllvm-nm\b",
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/coff.ll
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ define hidden void @g() {
ret void
}

; CHECK: define internal void @h() local_unnamed_addr {
; CHECK: define internal void @h() {
define linkonce_odr void @h() local_unnamed_addr {
ret void
}
80 changes: 38 additions & 42 deletions llvm/test/tools/gold/X86/comdat.ll
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %p/Inputs/comdat.ll -o %t2.o
; RUN: %gold -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t.o %t2.o \
; RUN: %gold -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t1.o %t2.o \
; RUN: -plugin-opt=save-temps
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
; RUN: llvm-readobj -t %t3.o | FileCheck --check-prefix=OBJ %s

$c1 = comdat any

Expand All @@ -24,42 +25,37 @@ bb11:
@a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*)
@a15 = alias i16, i16* @a14

; CHECK: $c1 = comdat any
; CHECK: $c2 = comdat any

; 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 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: }
; gold's resolutions should tell us that our $c1 wins, and the other input's $c2
; wins. f1 is also local due to having protected visibility in the other object.

; 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{{$}}

; RES: 2.o,f1,l{{$}}
; RES: 2.o,will_be_undefined,{{$}}
; RES: 2.o,v1,{{$}}
; RES: 2.o,r21,px{{$}}
; RES: 2.o,r22,px{{$}}
; RES: 2.o,a21,px{{$}}
; RES: 2.o,a22,px{{$}}
; RES: 2.o,a23,px{{$}}
; RES: 2.o,a24,px{{$}}
; RES: 2.o,a25,px{{$}}

; f1's protected visibility should be reflected in the DSO.

; OBJ: Name: f1 (
; OBJ-NEXT: Value:
; OBJ-NEXT: Size:
; OBJ-NEXT: Binding:
; OBJ-NEXT: Type:
; OBJ-NEXT: Other [
; OBJ-NEXT: STV_PROTECTED
; OBJ-NEXT: ]
10 changes: 5 additions & 5 deletions llvm/test/tools/gold/X86/common.ll
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=A

; Shared library case, we merge @a as common and keep it for the symbol table.
; A: @a = common global i32 0, align 8
; A: @a = common global [4 x i8] zeroinitializer, align 8

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
; RUN: -shared %t1.o %t2b.o -o %t3.o
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=B

; (i16 align 8) + (i8 align 16) = i16 align 16
; B: @a = common global i16 0, align 16
; B: @a = common global [2 x i8] zeroinitializer, align 16

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
; RUN: -shared %t1.o %t2c.o -o %t3.o
; RUN: llvm-dis %t3.o -o - | FileCheck %s --check-prefix=C

; (i16 align 8) + (i8 align 1) = i16 align 8.
; C: @a = common global i16 0, align 8
; C: @a = common global [2 x i8] zeroinitializer, align 8

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
; RUN: %t1.o %t2.o -o %t3.o
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=EXEC %s

; All IR case, we internalize a after merging.
; EXEC: @a = internal global i32 0, align 8
; EXEC: @a = internal global [4 x i8] zeroinitializer, align 8

; RUN: llc %p/Inputs/common.ll -o %t2native.o -filetype=obj
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
Expand All @@ -44,4 +44,4 @@
; RUN: llvm-dis %t3.o -o - | FileCheck --check-prefix=MIXED %s

; Mixed ELF and IR. We keep ours as common so the linker will finish the merge.
; MIXED: @a = common global i16 0, align 8
; MIXED: @a = common global [2 x i8] zeroinitializer, align 8
51 changes: 28 additions & 23 deletions llvm/test/tools/gold/X86/emit-llvm.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
; RUN: --plugin-opt=generate-api-file \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o -o - | FileCheck %s
; RUN: FileCheck --check-prefix=API %s < %T/../apifile.txt

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: -m elf_x86_64 --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t3.o
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t3.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t3.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-nm %t3.o.o | FileCheck --check-prefix=NM %s

; RUN: rm -f %t4.o
Expand All @@ -25,19 +24,19 @@

target triple = "x86_64-unknown-linux-gnu"

; CHECK-DAG: @g1 = linkonce_odr constant i32 32
; CHECK-DAG: @g1 = weak_odr constant i32 32
@g1 = linkonce_odr constant i32 32

; CHECK-DAG: @g2 = internal local_unnamed_addr constant i32 32
; CHECK-DAG: @g2 = internal constant i32 32
@g2 = linkonce_odr local_unnamed_addr constant i32 32

; CHECK-DAG: @g3 = internal unnamed_addr constant i32 32
@g3 = linkonce_odr unnamed_addr constant i32 32

; CHECK-DAG: @g4 = linkonce_odr global i32 32
; CHECK-DAG: @g4 = weak_odr global i32 32
@g4 = linkonce_odr global i32 32

; CHECK-DAG: @g5 = linkonce_odr local_unnamed_addr global i32 32
; CHECK-DAG: @g5 = weak_odr global i32 32
@g5 = linkonce_odr local_unnamed_addr global i32 32

; CHECK-DAG: @g6 = internal unnamed_addr global i32 32
Expand Down Expand Up @@ -75,8 +74,8 @@ define linkonce_odr void @f4() local_unnamed_addr {
ret void
}

; CHECK-DAG: define linkonce_odr void @f5()
; OPT-DAG: define linkonce_odr void @f5()
; CHECK-DAG: define weak_odr void @f5()
; OPT-DAG: define weak_odr void @f5()
define linkonce_odr void @f5() {
ret void
}
Expand All @@ -97,15 +96,21 @@ define i32* @f8() {
ret i32* @g8
}

; API: f1 PREVAILING_DEF_IRONLY
; API: f2 PREVAILING_DEF_IRONLY
; API: f3 PREVAILING_DEF_IRONLY_EXP
; API: f4 PREVAILING_DEF_IRONLY_EXP
; API: f5 PREVAILING_DEF_IRONLY_EXP
; API: f6 PREVAILING_DEF_IRONLY_EXP
; API: f7 PREVAILING_DEF_IRONLY_EXP
; API: f8 PREVAILING_DEF_IRONLY_EXP
; API: g7 UNDEF
; API: g8 UNDEF
; API: g9 PREVAILING_DEF_IRONLY_EXP
; API: g10 PREVAILING_DEF_IRONLY_EXP
; RES: .o,f1,pl{{$}}
; RES: .o,f2,pl{{$}}
; RES: .o,f3,px{{$}}
; RES: .o,f4,p{{$}}
; RES: .o,f5,px{{$}}
; RES: .o,f6,p{{$}}
; RES: .o,f7,px{{$}}
; RES: .o,f8,px{{$}}
; RES: .o,g1,px{{$}}
; RES: .o,g2,p{{$}}
; RES: .o,g3,p{{$}}
; RES: .o,g4,px{{$}}
; RES: .o,g5,px{{$}}
; RES: .o,g6,p{{$}}
; RES: .o,g7,{{$}}
; RES: .o,g8,{{$}}
; RES: .o,g9,px{{$}}
; RES: .o,g10,px{{$}}
6 changes: 3 additions & 3 deletions llvm/test/tools/gold/X86/opt-level.ll
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
; RUN: llvm-as -o %t.bc %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O0 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O0 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O0 %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O1 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O1 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O1 %s
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so -plugin-opt=save-temps \
; RUN: -plugin-opt=O2 -r -o %t.o %t.bc
; RUN: llvm-dis < %t.o.opt.bc -o - | FileCheck --check-prefix=CHECK-O2 %s
; RUN: llvm-dis < %t.o.4.opt.bc -o - | FileCheck --check-prefix=CHECK-O2 %s

; CHECK-O0: define internal void @foo(
; CHECK-O1: define internal void @foo(
Expand Down
5 changes: 3 additions & 2 deletions llvm/test/tools/gold/X86/parallel.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
; RUN: llvm-as -o %t.bc %s
; RUN: rm -f %t.opt.bc0 %t.opt.bc1 %t.o0 %t.o1
; RUN: env LD_PRELOAD=%llvmshlibdir/LLVMgold.so %gold -plugin %llvmshlibdir/LLVMgold.so -u foo -u bar -plugin-opt jobs=2 -plugin-opt save-temps -m elf_x86_64 -o %t %t.bc
; RUN: llvm-dis %t.opt.bc0 -o - | FileCheck --check-prefix=CHECK-BC0 %s
; RUN: llvm-dis %t.opt.bc1 -o - | FileCheck --check-prefix=CHECK-BC1 %s
; RUN: llvm-dis %t.5.precodegen.bc -o - | FileCheck --check-prefix=CHECK-BC0 %s
; RUN: llvm-dis %t.1.5.precodegen.bc -o - | FileCheck --check-prefix=CHECK-BC1 %s
; RUN: llvm-nm %t.o0 | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t.o1 | FileCheck --check-prefix=CHECK1 %s

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/slp-vectorize.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s

; test that the vectorizer is run.
; CHECK: fadd <4 x float>
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/start-lib-common.ll
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@

; Check that the common symbol is not dropped completely, which was a regression
; in r262676.
; CHECK: @x = common global i32 0
; CHECK: @x = common global [4 x i8] zeroinitializer
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/strip_names.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.2.internalize.bc -o - | FileCheck %s

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=emit-llvm \
Expand Down
10 changes: 6 additions & 4 deletions llvm/test/tools/gold/X86/thinlto.ll
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,23 @@
; RUN: llvm-bcanalyzer -dump %t2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: not test -e %t3

; Ensure gold generates an index as well as a binary by default in ThinLTO mode.
; Ensure gold generates an index as well as a binary with save-temps in ThinLTO mode.
; First force single-threaded mode
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: --plugin-opt=thinlto \
; RUN: --plugin-opt=jobs=1 \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-bcanalyzer -dump %t4.thinlto.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-bcanalyzer -dump %t4.index.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM

; Next force multi-threaded mode
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: --plugin-opt=thinlto \
; RUN: --plugin-opt=jobs=2 \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-bcanalyzer -dump %t4.thinlto.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-bcanalyzer -dump %t4.index.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM

; Test --plugin-opt=obj-path to ensure unique object files generated.
Expand All @@ -48,8 +50,8 @@
; RUN: --plugin-opt=jobs=2 \
; RUN: --plugin-opt=obj-path=%t5.o \
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-nm %t5.o0 | FileCheck %s --check-prefix=NM2
; RUN: llvm-nm %t5.o1 | FileCheck %s --check-prefix=NM2
; RUN: llvm-nm %t5.o2 | FileCheck %s --check-prefix=NM2

; NM: T f
; NM2: T {{f|g}}
Expand Down
10 changes: 8 additions & 2 deletions llvm/test/tools/gold/X86/thinlto_alias.ll
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
; RUN: --plugin-opt=save-temps \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-nm %t3.o | FileCheck %s
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s

; This does not currently pass because the gold plugin now uses the
; combined summary rather than the IRMover to change the module's linkage
; during the ThinLTO backend. The internalization step implemented by IRMover
; for preempted symbols has not yet been implemented for the combined summary.
; XFAIL: *

; CHECK-NOT: U f
; OPT: define hidden void @weakfunc.llvm.0()
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/thinlto_internalize.ll
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
; RUN: --plugin-opt=-import-instr-limit=0 \
; RUN: --plugin-opt=save-temps \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck %s

; f() should be internalized and eliminated after inlining
; CHECK-NOT: @f()
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/tools/gold/X86/thinlto_linkonceresolution.ll
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
; RUN: -shared \
; RUN: -o %t3.o %t2.o %t.o
; RUN: llvm-nm %t3.o | FileCheck %s
; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck --check-prefix=OPT2 %s

; Ensure that f() is defined in resulting object file, and also
; confirm the weak linkage directly in the saved opt bitcode files.
; CHECK-NOT: U f
; OPT: declare hidden void @f()
; OPT-NOT: @f()
; OPT2: define weak_odr hidden void @f()

target triple = "x86_64-unknown-linux-gnu"
Expand Down
13 changes: 9 additions & 4 deletions llvm/test/tools/gold/X86/thinlto_weak_resolution.ll
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@
; RUN: llvm-nm %t3.o | FileCheck %s
; CHECK: weakfunc

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

; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s
; RUN: llvm-dis %t.o.4.opt.bc -o - | FileCheck --check-prefix=OPT %s

target triple = "x86_64-unknown-linux-gnu"

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/type-merge2.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o %t2.o -o %t3.o
; RUN: llvm-dis %t3.o.bc -o - | FileCheck %s
; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s

%zed = type { i8 }
define void @foo() {
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/gold/X86/vectorize.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck %s
; RUN: llvm-dis %t2.o.4.opt.bc -o - | FileCheck %s

; test that the vectorizer is run.
; CHECK: fadd <4 x float>
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/tools/gold/X86/visibility.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
; RUN: --plugin-opt=save-temps \
; RUN: -shared %t.o %t2.o -o %t.so
; RUN: llvm-readobj -t %t.so | FileCheck %s
; RUN: llvm-dis %t.so.bc -o - | FileCheck --check-prefix=IR %s
; RUN: llvm-dis %t.so.2.internalize.bc -o - | FileCheck --check-prefix=IR %s

; CHECK: Name: foo
; CHECK-NEXT: Value:
Expand All @@ -16,7 +16,7 @@
; CHECK-NEXT: STV_PROTECTED
; CHECK-NEXT: ]

; IR: define protected void @foo
; IR: define void @foo

define weak protected void @foo() {
ret void
Expand Down
11 changes: 11 additions & 0 deletions llvm/test/tools/llvm-lto2/errors.ll
Original file line number Diff line number Diff line change
@@ -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
1,171 changes: 227 additions & 944 deletions llvm/tools/gold/gold-plugin.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions llvm/tools/llvm-lto2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
LTO
Object
Support
)

add_llvm_tool(llvm-lto2
llvm-lto2.cpp
)
22 changes: 22 additions & 0 deletions llvm/tools/llvm-lto2/LLVMBuild.txt
Original file line number Diff line number Diff line change
@@ -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 changes: 168 additions & 0 deletions llvm/tools/llvm-lto2/llvm-lto2.cpp
Original file line number Diff line number Diff line change
@@ -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 = make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
check(EC, Path);
return S;
};

check(Lto.run(AddStream), "LTO::run failed");
}