forked from OSchip/llvm-project
refactor COFF linker to use new LTO API
Summary: The COFF linker previously implemented link-time optimization using an API which has now been marked as legacy. This change refactors the COFF linker to use the new LTO API, which is also used by the ELF linker. Reviewers: pcc, ruiu Reviewed By: pcc Subscribers: mgorny, mehdi_amini Differential Revision: https://reviews.llvm.org/D29059 llvm-svn: 293967
This commit is contained in:
parent
fbc229dc8f
commit
cde5e5b600
|
@ -15,6 +15,7 @@ add_lld_library(lldCOFF
|
|||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
Librarian.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
ModuleDef.cpp
|
||||
|
|
|
@ -114,6 +114,8 @@ struct Configuration {
|
|||
|
||||
// Used for /opt:lldltojobs=N
|
||||
unsigned LTOJobs = 1;
|
||||
// Used for /opt:lldltopartitions=N
|
||||
unsigned LTOPartitions = 1;
|
||||
|
||||
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
||||
std::map<StringRef, StringRef> Merge;
|
||||
|
|
|
@ -609,6 +609,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
fatal("/opt:lldltojobs: invalid job count: " + Jobs);
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldltopartitions=")) {
|
||||
StringRef N = StringRef(S).substr(17);
|
||||
if (N.getAsInteger(10, Config->LTOPartitions) ||
|
||||
Config->LTOPartitions == 0)
|
||||
fatal("/opt:lldltopartitions: invalid partition count: " + N);
|
||||
continue;
|
||||
}
|
||||
if (S != "ref" && S != "lbr" && S != "nolbr")
|
||||
fatal("/opt: unknown option: " + S);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOModule.h"
|
||||
#include "llvm/Bitcode/BitcodeReader.h"
|
||||
#include "llvm/Object/Binary.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/COFF.h"
|
||||
|
@ -45,7 +44,19 @@ using llvm::support::ulittle32_t;
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
LLVMContext BitcodeFile::Context;
|
||||
/// Checks that Source is compatible with being a weak alias to Target.
|
||||
/// If Source is Undefined and has no weak alias set, makes it a weak
|
||||
/// alias to Target.
|
||||
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
|
||||
SymbolBody *Source, SymbolBody *Target) {
|
||||
auto *U = dyn_cast<Undefined>(Source);
|
||||
if (!U)
|
||||
return;
|
||||
else if (!U->WeakAlias)
|
||||
U->WeakAlias = Target;
|
||||
else if (U->WeakAlias != Target)
|
||||
Symtab->reportDuplicate(Source->symbol(), F);
|
||||
}
|
||||
|
||||
ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
|
||||
|
||||
|
@ -175,15 +186,9 @@ void ObjectFile::initializeSymbols() {
|
|||
I += Sym.getNumberOfAuxSymbols();
|
||||
LastSectionNumber = Sym.getSectionNumber();
|
||||
}
|
||||
for (auto WeakAlias : WeakAliases) {
|
||||
auto *U = dyn_cast<Undefined>(WeakAlias.first);
|
||||
if (!U)
|
||||
continue;
|
||||
// Report an error if two undefined symbols have different weak aliases.
|
||||
if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second])
|
||||
Symtab->reportDuplicate(U->symbol(), this);
|
||||
U->WeakAlias = SparseSymbolBodies[WeakAlias.second];
|
||||
}
|
||||
for (auto WeakAlias : WeakAliases)
|
||||
checkAndSetWeakAlias(Symtab, this, WeakAlias.first,
|
||||
SparseSymbolBodies[WeakAlias.second]);
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
|
||||
|
@ -198,7 +203,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
|||
if (Sym.isCommon()) {
|
||||
auto *C = new (Alloc) CommonChunk(Sym);
|
||||
Chunks.push_back(C);
|
||||
return Symtab->addCommon(this, Sym, C)->body();
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
|
||||
return S->body();
|
||||
}
|
||||
if (Sym.isAbsolute()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
|
@ -245,10 +253,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
|||
}
|
||||
|
||||
DefinedRegular *B;
|
||||
if (Sym.isExternal())
|
||||
B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body());
|
||||
else
|
||||
B = new (Alloc) DefinedRegular(this, Sym, SC);
|
||||
if (Sym.isExternal()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
|
||||
B = cast<DefinedRegular>(S->body());
|
||||
} else
|
||||
B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(),
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
|
||||
SC->setSymbol(B);
|
||||
|
||||
|
@ -327,39 +339,38 @@ void ImportFile::parse() {
|
|||
}
|
||||
|
||||
void BitcodeFile::parse() {
|
||||
Context.enableDebugTypeODRUniquing();
|
||||
ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer(
|
||||
Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions());
|
||||
M = check(std::move(ModOrErr), "could not create LTO module");
|
||||
|
||||
StringSaver Saver(Alloc);
|
||||
for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
|
||||
lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
|
||||
if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
|
||||
continue;
|
||||
|
||||
StringRef SymName = Saver.save(M->getSymbolName(I));
|
||||
int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
|
||||
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
|
||||
SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body());
|
||||
Obj = check(lto::InputFile::create(
|
||||
MemoryBufferRef(MB.getBuffer(), Saver.save(MB.getBufferIdentifier()))));
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
|
||||
StringRef SymName = Saver.save(ObjSym.getName());
|
||||
auto Flags = ObjSym.getFlags();
|
||||
Symbol *Sym;
|
||||
if (Flags & object::BasicSymbolRef::SF_Undefined) {
|
||||
Sym = Symtab->addUndefined(SymName, this, false);
|
||||
} else if (Flags & object::BasicSymbolRef::SF_Common) {
|
||||
Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
|
||||
} else if ((Flags & object::BasicSymbolRef::SF_Weak) &&
|
||||
(Flags & object::BasicSymbolRef::SF_Indirect)) {
|
||||
// Weak external.
|
||||
Sym = Symtab->addUndefined(SymName, this, true);
|
||||
std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
|
||||
SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
|
||||
checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
|
||||
} else {
|
||||
bool Replaceable =
|
||||
(SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
|
||||
(Attrs & LTO_SYMBOL_COMDAT) || // comdat
|
||||
(SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
|
||||
(Attrs & LTO_SYMBOL_ALIAS)));
|
||||
SymbolBodies.push_back(
|
||||
Symtab->addBitcode(this, SymName, Replaceable)->body());
|
||||
Expected<int> ComdatIndex = ObjSym.getComdatIndex();
|
||||
bool IsCOMDAT = ComdatIndex && *ComdatIndex != -1;
|
||||
Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
|
||||
}
|
||||
SymbolBodies.push_back(Sym->body());
|
||||
}
|
||||
|
||||
Directives = M->getLinkerOpts();
|
||||
Directives = check(Obj->getLinkerOpts());
|
||||
}
|
||||
|
||||
MachineTypes BitcodeFile::getMachineType() {
|
||||
if (!M)
|
||||
Expected<std::string> ET = getBitcodeTargetTriple(MB);
|
||||
if (!ET)
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
switch (Triple(M->getTargetTriple()).getArch()) {
|
||||
switch (Triple(*ET).getArch()) {
|
||||
case Triple::x86_64:
|
||||
return AMD64;
|
||||
case Triple::x86:
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOModule.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
|
@ -25,7 +24,6 @@
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
using llvm::LTOModule;
|
||||
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::object::Archive;
|
||||
|
@ -191,16 +189,13 @@ public:
|
|||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
MachineTypes getMachineType() override;
|
||||
std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
|
||||
|
||||
static llvm::LLVMContext Context;
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
std::unique_ptr<LTOModule> M;
|
||||
};
|
||||
} // namespace coff
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
//===- LTO.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/LTO/Config.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/SymbolicFile.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
SmallString<128> ErrStorage;
|
||||
raw_svector_ostream OS(ErrStorage);
|
||||
DiagnosticPrinterRawOStream DP(OS);
|
||||
DI.print(DP);
|
||||
warn(ErrStorage);
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
lto::Config Conf;
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
Conf.DisableVerify = true;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOOptLevel;
|
||||
lto::ThinBackend Backend;
|
||||
if (Config->LTOJobs != -1u)
|
||||
Backend = lto::createInProcessThinBackend(Config->LTOJobs);
|
||||
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
|
||||
Config->LTOPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName());
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<SymbolBody *> SymBodies = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(SymBodies.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
SymbolBody *B = SymBodies[SymNum];
|
||||
Symbol *Sym = B->symbol();
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing =
|
||||
!(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
|
||||
B->getFile() == &F;
|
||||
R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
|
||||
// Merge all the bitcode files we have seen, codegen the result
|
||||
// and return the resulting ObjectFile(s).
|
||||
std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
std::vector<InputFile *> Ret;
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.resize(MaxTasks);
|
||||
|
||||
checkError(LTOObj->run([&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
}));
|
||||
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
if (Buff[I].empty())
|
||||
continue;
|
||||
Ret.push_back(make<ObjectFile>(MemoryBufferRef(Buff[I], "lto.tmp")));
|
||||
}
|
||||
return Ret;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one COFF
|
||||
// file by compiling them using LLVM.
|
||||
//
|
||||
// If LTO is in use, your input files are not in regular COFF files
|
||||
// but instead LLVM bitcode files. In that case, the linker has to
|
||||
// convert bitcode files into the native format so that we can create
|
||||
// a COFF file that contains native code. This file provides that
|
||||
// functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_LTO_H
|
||||
#define LLD_COFF_LTO_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace lto {
|
||||
class LTO;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
|
||||
class BitcodeCompiler {
|
||||
public:
|
||||
BitcodeCompiler();
|
||||
~BitcodeCompiler();
|
||||
|
||||
void add(BitcodeFile &F);
|
||||
std::vector<InputFile *> compile();
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> LTOObj;
|
||||
std::vector<SmallString<0>> Buff;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -11,10 +11,10 @@
|
|||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "LTO.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <utility>
|
||||
|
@ -24,6 +24,36 @@ using namespace llvm;
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
enum SymbolPreference {
|
||||
SP_EXISTING = -1,
|
||||
SP_CONFLICT = 0,
|
||||
SP_NEW = 1,
|
||||
};
|
||||
|
||||
/// Checks if an existing symbol S should be kept or replaced by a new symbol.
|
||||
/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
|
||||
/// should be kept, and SP_CONFLICT if no valid resolution exists.
|
||||
static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
|
||||
bool NewIsCOMDAT) {
|
||||
// If the symbol wasn't previously known, the new symbol wins by default.
|
||||
if (WasInserted || !isa<Defined>(S->body()))
|
||||
return SP_NEW;
|
||||
|
||||
// If the existing symbol is a DefinedRegular, both it and the new symbol
|
||||
// must be comdats. In that case, we have no reason to prefer one symbol
|
||||
// over the other, and we keep the existing one. If one of the symbols
|
||||
// is not a comdat, we report a conflict.
|
||||
if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (NewIsCOMDAT && R->isCOMDAT())
|
||||
return SP_EXISTING;
|
||||
else
|
||||
return SP_CONFLICT;
|
||||
}
|
||||
|
||||
// Existing symbol is not a DefinedRegular; new symbol wins.
|
||||
return SP_NEW;
|
||||
}
|
||||
|
||||
SymbolTable *Symtab;
|
||||
|
||||
void SymbolTable::addFile(InputFile *File) {
|
||||
|
@ -204,59 +234,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
|
|||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym,
|
||||
Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
const coff_symbol_generic *Sym,
|
||||
SectionChunk *C) {
|
||||
StringRef Name;
|
||||
F->getCOFFObj()->getSymbolName(Sym, Name);
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
else if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (!C->isCOMDAT() || !R->isCOMDAT())
|
||||
reportDuplicate(S, F);
|
||||
} else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) {
|
||||
if (B->IsReplaceable)
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
else if (!C->isCOMDAT())
|
||||
reportDuplicate(S, F);
|
||||
} else
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) {
|
||||
replaceBody<DefinedBitcode>(S, F, N, IsReplaceable);
|
||||
return S;
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
|
||||
if (SP == SP_CONFLICT) {
|
||||
reportDuplicate(S, F);
|
||||
} else if (SP == SP_NEW) {
|
||||
replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
|
||||
}
|
||||
if (isa<DefinedCommon>(S->body()))
|
||||
return S;
|
||||
if (IsReplaceable)
|
||||
if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body()))
|
||||
return S;
|
||||
reportDuplicate(S, F);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym,
|
||||
CommonChunk *C) {
|
||||
StringRef Name;
|
||||
F->getCOFFObj()->getSymbolName(Sym, Name);
|
||||
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *Sym, CommonChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || !isa<DefinedCOFF>(S->body()))
|
||||
replaceBody<DefinedCommon>(S, F, Sym, C);
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
|
||||
if (Sym.getValue() > DC->getSize())
|
||||
replaceBody<DefinedCommon>(S, F, Sym, C);
|
||||
if (Size > DC->getSize())
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
return S;
|
||||
}
|
||||
|
||||
|
@ -349,61 +355,15 @@ void SymbolTable::addCombinedLTOObjects() {
|
|||
if (BitcodeFiles.empty())
|
||||
return;
|
||||
|
||||
// Create an object file and add it to the symbol table by replacing any
|
||||
// DefinedBitcode symbols with the definitions in the object file.
|
||||
LTOCodeGenerator CG(BitcodeFile::Context);
|
||||
CG.setOptLevel(Config->LTOOptLevel);
|
||||
for (ObjectFile *Obj : createLTOObjects(&CG))
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
LTO->add(*F);
|
||||
|
||||
for (auto *File : LTO->compile()) {
|
||||
auto *Obj = cast<ObjectFile>(File);
|
||||
ObjectFiles.push_back(Obj);
|
||||
Obj->parse();
|
||||
}
|
||||
|
||||
// Combine and compile bitcode files and then return the result
|
||||
// as a vector of regular COFF object files.
|
||||
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
|
||||
// All symbols referenced by non-bitcode objects, including GC roots, must be
|
||||
// preserved. We must also replace bitcode symbols with undefined symbols so
|
||||
// that they may be replaced with real definitions without conflicting.
|
||||
for (BitcodeFile *File : BitcodeFiles)
|
||||
for (SymbolBody *Body : File->getSymbols()) {
|
||||
if (!isa<DefinedBitcode>(Body))
|
||||
continue;
|
||||
if (Body->symbol()->IsUsedInRegularObj)
|
||||
CG->addMustPreserveSymbol(Body->getName());
|
||||
replaceBody<Undefined>(Body->symbol(), Body->getName());
|
||||
}
|
||||
|
||||
CG->setModule(BitcodeFiles[0]->takeModule());
|
||||
for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
|
||||
CG->addModule(BitcodeFiles[I]->takeModule().get());
|
||||
|
||||
bool DisableVerify = true;
|
||||
#ifdef NDEBUG
|
||||
DisableVerify = false;
|
||||
#endif
|
||||
if (!CG->optimize(DisableVerify, false, false, false))
|
||||
fatal(""); // optimize() should have emitted any error message.
|
||||
|
||||
Objs.resize(Config->LTOJobs);
|
||||
// Use std::list to avoid invalidation of pointers in OSPtrs.
|
||||
std::list<raw_svector_ostream> OSs;
|
||||
std::vector<raw_pwrite_stream *> OSPtrs;
|
||||
for (SmallString<0> &Obj : Objs) {
|
||||
OSs.emplace_back(Obj);
|
||||
OSPtrs.push_back(&OSs.back());
|
||||
}
|
||||
|
||||
if (!CG->compileOptimized(OSPtrs))
|
||||
fatal(""); // compileOptimized() should have emitted any error message.
|
||||
|
||||
std::vector<ObjectFile *> ObjFiles;
|
||||
for (SmallString<0> &Obj : Objs) {
|
||||
auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>"));
|
||||
ObjectFiles.push_back(ObjFile);
|
||||
ObjFiles.push_back(ObjFile);
|
||||
}
|
||||
|
||||
return ObjFiles;
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define LLD_COFF_SYMBOL_TABLE_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "LTO.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
|
@ -90,9 +91,12 @@ public:
|
|||
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
|
||||
void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
|
||||
Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
|
||||
Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C);
|
||||
Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable);
|
||||
Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C);
|
||||
Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr);
|
||||
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr);
|
||||
Symbol *addImportData(StringRef N, ImportFile *F);
|
||||
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
|
||||
uint16_t Machine);
|
||||
|
@ -110,12 +114,11 @@ private:
|
|||
StringRef findByPrefix(StringRef Prefix);
|
||||
|
||||
void addCombinedLTOObject(ObjectFile *Obj);
|
||||
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
|
||||
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
std::vector<SmallString<0>> Objs;
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
||||
extern SymbolTable *Symtab;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace lld {
|
|||
namespace coff {
|
||||
|
||||
StringRef SymbolBody::getName() {
|
||||
// DefinedCOFF names are read lazily for a performance reason.
|
||||
// COFF symbol names are read lazily for a performance reason.
|
||||
// Non-external symbol names are never used by the linker except for logging
|
||||
// or debugging. Their internal references are resolved not by name but by
|
||||
// symbol index. And because they are not external, no one can refer them by
|
||||
|
@ -39,7 +39,7 @@ StringRef SymbolBody::getName() {
|
|||
// is a waste of time.
|
||||
if (Name.empty()) {
|
||||
auto *D = cast<DefinedCOFF>(this);
|
||||
D->File->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
|
@ -47,15 +47,14 @@ StringRef SymbolBody::getName() {
|
|||
InputFile *SymbolBody::getFile() {
|
||||
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<DefinedBitcode>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<Lazy>(this))
|
||||
return Sym->File;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize();
|
||||
size_t SymSize =
|
||||
cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
if (SymSize == sizeof(coff_symbol16))
|
||||
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
|
||||
assert(SymSize == sizeof(coff_symbol32));
|
||||
|
|
|
@ -30,7 +30,6 @@ using llvm::object::coff_import_header;
|
|||
using llvm::object::coff_symbol_generic;
|
||||
|
||||
class ArchiveFile;
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
class ObjectFile;
|
||||
struct Symbol;
|
||||
|
@ -52,13 +51,12 @@ public:
|
|||
DefinedImportDataKind,
|
||||
DefinedAbsoluteKind,
|
||||
DefinedRelativeKind,
|
||||
DefinedBitcodeKind,
|
||||
|
||||
UndefinedKind,
|
||||
LazyKind,
|
||||
|
||||
LastDefinedCOFFKind = DefinedCommonKind,
|
||||
LastDefinedKind = DefinedBitcodeKind,
|
||||
LastDefinedKind = DefinedRelativeKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
@ -81,7 +79,7 @@ protected:
|
|||
friend SymbolTable;
|
||||
explicit SymbolBody(Kind K, StringRef N = "")
|
||||
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
|
||||
IsReplaceable(false), WrittenToSymtab(false), Name(N) {}
|
||||
WrittenToSymtab(false), Name(N) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
unsigned IsExternal : 1;
|
||||
|
@ -89,11 +87,9 @@ protected:
|
|||
// This bit is used by the \c DefinedRegular subclass.
|
||||
unsigned IsCOMDAT : 1;
|
||||
|
||||
// This bit is used by the \c DefinedBitcode subclass.
|
||||
unsigned IsReplaceable : 1;
|
||||
|
||||
public:
|
||||
// This bit is used by Writer::createSymbolAndStringTable().
|
||||
// This bit is used by Writer::createSymbolAndStringTable() to prevent
|
||||
// symbols from being written to the symbol table more than once.
|
||||
unsigned WrittenToSymtab : 1;
|
||||
|
||||
protected:
|
||||
|
@ -104,7 +100,7 @@ protected:
|
|||
// etc.
|
||||
class Defined : public SymbolBody {
|
||||
public:
|
||||
Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {}
|
||||
Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedKind;
|
||||
|
@ -127,22 +123,25 @@ public:
|
|||
bool isExecutable();
|
||||
};
|
||||
|
||||
// Symbols defined via a COFF object file.
|
||||
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
|
||||
// stores a coff_symbol_generic*, and names of internal symbols are lazily
|
||||
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
|
||||
// as a StringRef.
|
||||
class DefinedCOFF : public Defined {
|
||||
friend SymbolBody;
|
||||
public:
|
||||
DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S)
|
||||
: Defined(K), File(F), Sym(S.getGeneric()) {}
|
||||
DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
|
||||
: Defined(K, N), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedCOFFKind;
|
||||
}
|
||||
|
||||
ObjectFile *getFile() { return File; }
|
||||
InputFile *getFile() { return File; }
|
||||
|
||||
COFFSymbolRef getCOFFSymbol();
|
||||
|
||||
ObjectFile *File;
|
||||
InputFile *File;
|
||||
|
||||
protected:
|
||||
const coff_symbol_generic *Sym;
|
||||
|
@ -151,10 +150,13 @@ protected:
|
|||
// Regular defined symbols read from object file symbol tables.
|
||||
class DefinedRegular : public DefinedCOFF {
|
||||
public:
|
||||
DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C)
|
||||
: DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) {
|
||||
IsExternal = S.isExternal();
|
||||
IsCOMDAT = C->isCOMDAT();
|
||||
DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
bool IsExternal = false,
|
||||
const coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr)
|
||||
: DefinedCOFF(DefinedRegularKind, F, N, S), Data(&C->Repl) {
|
||||
this->IsExternal = IsExternal;
|
||||
this->IsCOMDAT = IsCOMDAT;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
|
@ -172,9 +174,11 @@ private:
|
|||
|
||||
class DefinedCommon : public DefinedCOFF {
|
||||
public:
|
||||
DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C)
|
||||
: DefinedCOFF(DefinedCommonKind, F, S), Data(C) {
|
||||
IsExternal = S.isExternal();
|
||||
DefinedCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr)
|
||||
: DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) {
|
||||
this->IsExternal = true;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
|
@ -185,8 +189,9 @@ public:
|
|||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
uint64_t getSize() { return Sym->Value; }
|
||||
uint64_t getSize() const { return Size; }
|
||||
CommonChunk *Data;
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
// Absolute symbols.
|
||||
|
@ -340,26 +345,6 @@ private:
|
|||
LocalImportChunk *Data;
|
||||
};
|
||||
|
||||
class DefinedBitcode : public Defined {
|
||||
friend SymbolBody;
|
||||
public:
|
||||
DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
|
||||
: Defined(DefinedBitcodeKind, N), File(F) {
|
||||
// IsReplaceable tracks whether the bitcode symbol may be replaced with some
|
||||
// other (defined, common or bitcode) symbol. This is the case for common,
|
||||
// comdat and weak external symbols. We try to replace bitcode symbols with
|
||||
// "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the
|
||||
// result against the real symbol from the combined LTO object.
|
||||
this->IsReplaceable = IsReplaceable;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == DefinedBitcodeKind;
|
||||
}
|
||||
|
||||
BitcodeFile *File;
|
||||
};
|
||||
|
||||
inline uint64_t Defined::getRVA() {
|
||||
switch (kind()) {
|
||||
case DefinedAbsoluteKind:
|
||||
|
@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() {
|
|||
return cast<DefinedCommon>(this)->getRVA();
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getRVA();
|
||||
case DefinedBitcodeKind:
|
||||
llvm_unreachable("There is no address for a bitcode symbol.");
|
||||
case LazyKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||
|
@ -401,10 +384,9 @@ struct Symbol {
|
|||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<DefinedRegular, DefinedCommon, DefinedAbsolute,
|
||||
DefinedRelative, Lazy, Undefined,
|
||||
DefinedImportData, DefinedImportThunk,
|
||||
DefinedLocalImport, DefinedBitcode>
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy,
|
||||
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
; RUN: rm -f %T/comdat.lib
|
||||
; RUN: llvm-ar cru %T/comdat.lib %T/comdat1.obj %T/comdat2.obj
|
||||
|
||||
; Check that, when we use an LTO main with LTO objects, we optimize away all
|
||||
; of f1, f2, and comdat.
|
||||
; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.lto.obj %T/comdat2.lto.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s
|
||||
|
@ -17,6 +19,9 @@
|
|||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s
|
||||
|
||||
; Check that, when we use a non-LTO main with LTO objects, we pick the comdat
|
||||
; implementation in LTO, elide calls to it from inside LTO, and retain the
|
||||
; call to comdat from main.
|
||||
; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat1.lto.obj %T/comdat2.lto.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s
|
||||
|
@ -24,6 +29,9 @@
|
|||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s
|
||||
|
||||
; Check that, when we use an LTO main with non-LTO objects, we pick the comdat
|
||||
; implementation in LTO, elide the call to it from inside LTO, and keep the
|
||||
; calls to comdat from the non-LTO objects.
|
||||
; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.obj %T/comdat2.obj
|
||||
; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s
|
||||
; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s
|
||||
|
@ -46,70 +54,39 @@
|
|||
; TEXT-01-NEXT: callq 13
|
||||
; TEXT-01-NEXT: xorl %eax, %eax
|
||||
; TEXT-01-NEXT: addq $40, %rsp
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: int3
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01-NEXT: nopw %cs:(%rax,%rax)
|
||||
; TEXT-01-NEXT: retq
|
||||
; TEXT-01: retq
|
||||
; TEXT-01-NOT: callq
|
||||
; TEXT-01: retq
|
||||
; TEXT-01-NOT: callq
|
||||
; TEXT-01: retq
|
||||
; TEXT-01-NOT: callq
|
||||
; TEXT-01: retq
|
||||
; TEXT-01-NOT: {{.}}
|
||||
|
||||
; HEADERS-10: AddressOfEntryPoint: 0x2030
|
||||
; HEADERS-10: AddressOfEntryPoint: 0x2020
|
||||
; TEXT-10: Disassembly of section .text:
|
||||
; TEXT-10-NEXT: .text:
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq 7
|
||||
; TEXT-10-NEXT: nop
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq -25
|
||||
; TEXT-10-NEXT: callq 55
|
||||
; TEXT-10-NEXT: nop
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq -57
|
||||
; TEXT-10-NEXT: callq 39
|
||||
; TEXT-10-NEXT: nop
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NEXT: int3
|
||||
; TEXT-10-NEXT: subq $40, %rsp
|
||||
; TEXT-10-NEXT: callq -41
|
||||
; TEXT-10-NEXT: callq -30
|
||||
; TEXT-10-NEXT: xorl %eax, %eax
|
||||
; TEXT-10-NEXT: addq $40, %rsp
|
||||
; TEXT-10-NEXT: retq
|
||||
; TEXT-10-NOT: callq
|
||||
; TEXT-10: retq
|
||||
; TEXT-10-NOT: {{.}}
|
||||
|
||||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
; RUN: llvm-as -o %t.obj %s
|
||||
; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltojobs=2 /subsystem:console /lldmap:%t.map %t.obj
|
||||
; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltopartitions=2 /subsystem:console /lldmap:%t.map %t.obj
|
||||
; RUN: FileCheck %s < %t.map
|
||||
|
||||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
; CHECK: <lto object>
|
||||
; CHECK: lto.tmp
|
||||
; CHECK-NEXT: foo
|
||||
define void @foo() {
|
||||
call void @bar()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: <lto object>
|
||||
; CHECK-NEXT: bar
|
||||
; CHECK: lto.tmp
|
||||
; CHECK: bar
|
||||
define void @bar() {
|
||||
call void @foo()
|
||||
ret void
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# RUN: lld-link /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
||||
# RUN: FileCheck %s < %t2.map
|
||||
|
||||
# CHECK: <lto object>
|
||||
# CHECK: lto.tmp
|
||||
# CHECK-NEXT: 0 g
|
||||
|
||||
--- !COFF
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# RUN: lld-link /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj
|
||||
# RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map
|
||||
|
||||
# CHECK1: <lto object>
|
||||
# CHECK1: lto.tmp
|
||||
# CHECK1-NEXT: 0 g
|
||||
|
||||
# CHECK2: weak-external3.test.tmp.obj
|
||||
|
|
Loading…
Reference in New Issue