64 changes: 58 additions & 6 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"

#define DEBUG_TYPE "lld"

Expand All @@ -48,6 +49,17 @@ enum {
#undef OPTION
};

// This function is called on startup. We need this for LTO since
// LTO calls LLVM functions to compile bitcode files to native code.
// Technically this can be delayed until we read bitcode files, but
// we don't bother to do lazily because the initialization is fast.
static void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
}

class LinkerDriver {
public:
void link(ArrayRef<const char *> ArgsArr);
Expand All @@ -72,6 +84,7 @@ bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
Config = make<Configuration>();
Symtab = make<SymbolTable>();

initLLVM();
LinkerDriver().link(Args);

// Exit immediately if we don't need to return to the caller.
Expand Down Expand Up @@ -173,7 +186,8 @@ void LinkerDriver::addFile(StringRef Path) {
return;
MemoryBufferRef MBRef = *Buffer;

if (identify_magic(MBRef.getBuffer()) == file_magic::archive) {
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::archive: {
SmallString<128> ImportFile = Path;
path::replace_extension(ImportFile, ".imports");
if (fs::exists(ImportFile))
Expand All @@ -182,8 +196,12 @@ void LinkerDriver::addFile(StringRef Path) {
Files.push_back(make<ArchiveFile>(MBRef));
return;
}

Files.push_back(make<ObjFile>(MBRef));
case file_magic::bitcode:
Files.push_back(make<BitcodeFile>(MBRef));
break;
default:
Files.push_back(make<ObjFile>(MBRef));
}
}

// Add a given library by searching it from input search paths.
Expand Down Expand Up @@ -261,6 +279,17 @@ static void handleWeakUndefines() {
}
}

// Force Sym to be entered in the output. Used for -u or equivalent.
static Symbol *addUndefined(StringRef Name) {
Symbol *S = Symtab->addUndefinedFunction(Name, 0, nullptr, nullptr);

// Since symbol S may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
S->IsUsedInRegularObj = true;

return S;
}

void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
WasmOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
Expand Down Expand Up @@ -288,12 +317,15 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {

Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
Config->ExportTable = Args.hasArg(OPT_export_table);
errorHandler().FatalWarnings =
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
Config->ImportMemory = Args.hasArg(OPT_import_memory);
Config->ImportTable = Args.hasArg(OPT_import_table);
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
Config->Optimize = args::getInteger(Args, OPT_O, 0);
Config->OutputFile = Args.getLastArgValue(OPT_o);
Config->Relocatable = Args.hasArg(OPT_relocatable);
Expand All @@ -304,10 +336,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
!Config->Relocatable);
Config->PrintGcSections =
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
Config->SearchPaths = args::getStrings(Args, OPT_L);
Config->StripAll = Args.hasArg(OPT_strip_all);
Config->StripDebug = Args.hasArg(OPT_strip_debug);
Config->StackFirst = Args.hasArg(OPT_stack_first);
Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
Config->ThinLTOCachePolicy = CHECK(
parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
errorHandler().Verbose = Args.hasArg(OPT_verbose);
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);

Expand All @@ -319,6 +357,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {

Config->CompressRelocTargets = Config->Optimize > 0 && !Config->Relocatable;

if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + Twine(Config->LTOO));
if (Config->LTOPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");

if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
readImportFile(Arg->getValue());

Expand Down Expand Up @@ -372,12 +417,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// For now, since we don't actually use the start function as the
// wasm start symbol, we don't need to care about it signature.
if (!Config->Entry.empty())
EntrySym =
Symtab->addUndefinedFunction(Config->Entry, 0, nullptr, nullptr);
EntrySym = addUndefined(Config->Entry);

// Handle the `--undefined <sym>` options.
for (auto *Arg : Args.filtered(OPT_undefined))
Symtab->addUndefinedFunction(Arg->getValue(), 0, nullptr, nullptr);
addUndefined(Arg->getValue());
}

createFiles(Args);
Expand All @@ -388,11 +432,19 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// symbols that we need to the symbol table.
for (InputFile *F : Files)
Symtab->addFile(F);
if (errorCount())
return;

// Add synthetic dummies for weak undefined functions.
if (!Config->Relocatable)
handleWeakUndefines();

// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
Symtab->addCombinedLTOObject();
if (errorCount())
return;

// Make sure we have resolved all symbols.
if (!Config->Relocatable && !Config->AllowUndefined) {
Symtab->reportRemainingUndefines();
Expand Down
42 changes: 42 additions & 0 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,48 @@ void ArchiveFile::addMember(const Archive::Symbol *Sym) {
Symtab->addFile(Obj);
}

static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
switch (GvVisibility) {
case GlobalValue::DefaultVisibility:
return WASM_SYMBOL_VISIBILITY_DEFAULT;
case GlobalValue::HiddenVisibility:
case GlobalValue::ProtectedVisibility:
return WASM_SYMBOL_VISIBILITY_HIDDEN;
}
llvm_unreachable("unknown visibility");
}

static Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &ObjSym,
BitcodeFile &F) {
StringRef Name = Saver.save(ObjSym.getName());

uint32_t Flags = ObjSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0;
Flags |= mapVisibility(ObjSym.getVisibility());

if (ObjSym.isUndefined()) {
if (ObjSym.isExecutable())
return Symtab->addUndefinedFunction(Name, Flags, &F, nullptr);
return Symtab->addUndefinedData(Name, Flags, &F);
}

if (ObjSym.isExecutable())
return Symtab->addDefinedFunction(Name, Flags, &F, nullptr);
return Symtab->addDefinedData(Name, Flags, &F, nullptr, 0, 0);
}

void BitcodeFile::parse() {
Obj = check(lto::InputFile::create(MemoryBufferRef(
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
Triple T(Obj->getTargetTriple());
if (T.getArch() != Triple::wasm32) {
error(toString(MB.getBufferIdentifier()) + ": machine type must be wasm32");
return;
}

for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
Symbols.push_back(createBitcodeSymbol(ObjSym, *this));
}

// Returns a string in the format of "foo.o" or "foo.a(bar.o)".
std::string lld::toString(const wasm::InputFile *File) {
if (!File)
Expand Down
26 changes: 22 additions & 4 deletions lld/wasm/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/MemoryBuffer.h"
Expand All @@ -28,6 +29,12 @@ using llvm::wasm::WasmImport;
using llvm::wasm::WasmRelocation;
using llvm::wasm::WasmSignature;

namespace llvm {
namespace lto {
class InputFile;
}
} // namespace llvm

namespace lld {
namespace wasm {

Expand All @@ -42,6 +49,7 @@ class InputFile {
enum Kind {
ObjectKind,
ArchiveKind,
BitcodeKind,
};

virtual ~InputFile() {}
Expand All @@ -57,10 +65,15 @@ class InputFile {
// An archive file name if this file is created from an archive.
StringRef ParentName;

ArrayRef<Symbol *> getSymbols() const { return Symbols; }

protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
MemoryBufferRef MB;

// List of all symbols referenced or defined by this file.
std::vector<Symbol *> Symbols;

private:
const Kind FileKind;
};
Expand Down Expand Up @@ -113,7 +126,6 @@ class ObjFile : public InputFile {
std::vector<InputSection *> CustomSections;
llvm::DenseMap<uint32_t, InputSection *> CustomSectionsByIndex;

ArrayRef<Symbol *> getSymbols() const { return Symbols; }
Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; }
FunctionSymbol *getFunctionSymbol(uint32_t Index) const;
DataSymbol *getDataSymbol(uint32_t Index) const;
Expand All @@ -126,12 +138,18 @@ class ObjFile : public InputFile {

bool isExcludedByComdat(InputChunk *Chunk) const;

// List of all symbols referenced or defined by this file.
std::vector<Symbol *> Symbols;

std::unique_ptr<WasmObjectFile> WasmObj;
};

class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }

void parse() override;
std::unique_ptr<llvm::lto::InputFile> Obj;
};

// Opens a given file.
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);

Expand Down
151 changes: 151 additions & 0 deletions lld/wasm/LTO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//===- 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 "InputFiles.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "lld/Common/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/Caching.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::wasm;

static std::unique_ptr<lto::LTO> createLTO() {
lto::Config C;
C.Options = InitTargetOptionsFromCodeGenFlags();

// Always emit a section per function/datum with LTO.
C.Options.FunctionSections = true;
C.Options.DataSections = true;

C.DisableVerify = Config->DisableVerify;
C.DiagHandler = diagnosticHandler;
C.OptLevel = Config->LTOO;

if (Config->SaveTemps)
checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
/*UseInputModulePath*/ true));

lto::ThinBackend Backend;
if (Config->ThinLTOJobs != -1U)
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
return llvm::make_unique<lto::LTO>(std::move(C), Backend,
Config->LTOPartitions);
}

BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}

BitcodeCompiler::~BitcodeCompiler() = default;

static void undefine(Symbol *S) {
if (isa<DefinedFunction>(S))
replaceSymbol<UndefinedFunction>(S, S->getName(), 0);
else if (isa<DefinedData>(S))
replaceSymbol<UndefinedData>(S, S->getName(), 0);
else
llvm_unreachable("unexpected symbol kind");
}

void BitcodeCompiler::add(BitcodeFile &F) {
lto::InputFile &Obj = *F.Obj;
unsigned SymNum = 0;
ArrayRef<Symbol *> Syms = F.getSymbols();
std::vector<lto::SymbolResolution> Resols(Syms.size());

// Provide a resolution to the LTO API for each symbol.
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
Symbol *Sym = Syms[SymNum];
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.isUndefined() && Sym->getFile() == &F;
R.VisibleToRegularObj = Config->Relocatable || 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 objects.
std::vector<StringRef> BitcodeCompiler::compile() {
unsigned MaxTasks = LTOObj->getMaxTasks();
Buf.resize(MaxTasks);
Files.resize(MaxTasks);

// The --thinlto-cache-dir option specifies the path to a directory in which
// to cache native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
lto::NativeObjectCache Cache;
if (!Config->ThinLTOCacheDir.empty())
Cache = check(
lto::localCache(Config->ThinLTOCacheDir,
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
Files[Task] = std::move(MB);
}));

checkError(LTOObj->run(
[&](size_t Task) {
return llvm::make_unique<lto::NativeObjectStream>(
llvm::make_unique<raw_svector_ostream>(Buf[Task]));
},
Cache));

if (!Config->ThinLTOCacheDir.empty())
pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);

std::vector<StringRef> Ret;
for (unsigned I = 0; I != MaxTasks; ++I) {
if (Buf[I].empty())
continue;
if (Config->SaveTemps) {
if (I == 0)
saveBuffer(Buf[I], Config->OutputFile + ".lto.o");
else
saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o");
}
Ret.emplace_back(Buf[I].data(), Buf[I].size());
}

for (std::unique_ptr<MemoryBuffer> &File : Files)
if (File)
Ret.push_back(File->getBuffer());

return Ret;
}
57 changes: 57 additions & 0 deletions lld/wasm/LTO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===- 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 wasm
// file by compiling them using LLVM.
//
// If LTO is in use, your input files are not in regular wasm 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 wasm file that contains native code. This file provides that
// functionality.
//
//===----------------------------------------------------------------------===//

#ifndef LLD_WASM_LTO_H
#define LLD_WASM_LTO_H

#include "lld/Common/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include <memory>
#include <vector>

namespace llvm {
namespace lto {
class LTO;
}
} // namespace llvm

namespace lld {
namespace wasm {

class BitcodeFile;
class InputFile;

class BitcodeCompiler {
public:
BitcodeCompiler();
~BitcodeCompiler();

void add(BitcodeFile &F);
std::vector<StringRef> compile();

private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
std::vector<SmallString<0>> Buf;
std::vector<std::unique_ptr<MemoryBuffer>> Files;
};
} // namespace wasm
} // namespace lld

#endif
13 changes: 13 additions & 0 deletions lld/wasm/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,16 @@ def alias_initial_memory_i: Flag<["-"], "i">, Alias<initial_memory>;
def alias_max_memory_m: Flag<["-"], "m">, Alias<max_memory>;
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;

// LTO-related options.
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def lto_partitions: J<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
def disable_verify: F<"disable-verify">;
def save_temps: F<"save-temps">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: Eq<"thinlto-cache-policy">,
HelpText<"Pruning policy for the ThinLTO cache">;
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
58 changes: 52 additions & 6 deletions lld/wasm/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,46 @@ void SymbolTable::addFile(InputFile *File) {
log("Processing: " + toString(File));
File->parse();

if (auto *F = dyn_cast<ObjFile>(File))
// LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(File))
BitcodeFiles.push_back(F);
else if (auto *F = dyn_cast<ObjFile>(File))
ObjectFiles.push_back(F);
}

// This function is where all the optimizations of link-time
// optimization happens. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that the program consists of are passed
// to the compiler at once, it can do whole-program optimization.
void SymbolTable::addCombinedLTOObject() {
if (BitcodeFiles.empty())
return;

// Compile bitcode files and replace bitcode symbols.
LTO.reset(new BitcodeCompiler);
for (BitcodeFile *F : BitcodeFiles)
LTO->add(*F);

for (StringRef Filename : LTO->compile()) {
auto *Obj = make<ObjFile>(MemoryBufferRef(Filename, "lto.tmp"));
Obj->parse();
ObjectFiles.push_back(Obj);
}
}

void SymbolTable::reportRemainingUndefines() {
SetVector<Symbol *> Undefs;
for (Symbol *Sym : SymVector) {
if (Sym->isUndefined() && !Sym->isWeak() &&
Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) {
Undefs.insert(Sym);
}
if (!Sym->isUndefined() || Sym->isWeak())
continue;
if (Config->AllowUndefinedSymbols.count(Sym->getName()) != 0)
continue;
if (!Sym->IsUsedInRegularObj)
continue;
Undefs.insert(Sym);
}

if (Undefs.empty())
Expand All @@ -64,6 +93,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
if (Sym)
return {Sym, false};
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Sym->IsUsedInRegularObj = false;
SymVector.emplace_back(Sym);
return {Sym, true};
}
Expand Down Expand Up @@ -178,12 +208,16 @@ Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags,
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);

if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;

if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
return S;
}

checkFunctionType(S, File, &Function->Signature);
if (Function)
checkFunctionType(S, File, &Function->Signature);

if (shouldReplace(S, File, Flags))
replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
Expand All @@ -199,6 +233,9 @@ Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags,
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);

if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;

if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size);
return S;
Expand All @@ -218,6 +255,9 @@ Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);

if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;

if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global);
return S;
Expand All @@ -239,6 +279,9 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags,
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);

if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;

if (WasInserted)
replaceSymbol<UndefinedFunction>(S, Name, Flags, File, Sig);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
Expand Down Expand Up @@ -274,6 +317,9 @@ Symbol *SymbolTable::addUndefinedGlobal(StringRef Name, uint32_t Flags,
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);

if (!File || File->kind() == InputFile::ObjectKind)
S->IsUsedInRegularObj = true;

if (WasInserted)
replaceSymbol<UndefinedGlobal>(S, Name, Flags, File, Type);
else if (auto *Lazy = dyn_cast<LazySymbol>(S))
Expand Down
6 changes: 6 additions & 0 deletions lld/wasm/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define LLD_WASM_SYMBOL_TABLE_H

#include "InputFiles.h"
#include "LTO.h"
#include "Symbols.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
Expand Down Expand Up @@ -39,8 +40,10 @@ class InputSegment;
class SymbolTable {
public:
void addFile(InputFile *File);
void addCombinedLTOObject();

std::vector<ObjFile *> ObjectFiles;
std::vector<BitcodeFile *> BitcodeFiles;
std::vector<InputFunction *> SyntheticFunctions;
std::vector<InputGlobal *> SyntheticGlobals;

Expand Down Expand Up @@ -80,6 +83,9 @@ class SymbolTable {
std::vector<Symbol *> SymVector;

llvm::DenseSet<llvm::CachedHashStringRef> Comdats;

// For LTO.
std::unique_ptr<BitcodeCompiler> LTO;
};

extern SymbolTable *Symtab;
Expand Down
14 changes: 11 additions & 3 deletions lld/wasm/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,13 @@ class Symbol {

WasmSymbolType getWasmType() const;

// True if this symbol was referenced by a regular (non-bitcode) object.
unsigned IsUsedInRegularObj : 1;

protected:
Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
: Name(Name), SymbolKind(K), Flags(Flags), File(F),
Referenced(!Config->GcSections) {}
: IsUsedInRegularObj(false), Name(Name), SymbolKind(K), Flags(Flags),
File(F), Referenced(!Config->GcSections) {}

StringRef Name;
Kind SymbolKind;
Expand Down Expand Up @@ -332,7 +335,12 @@ T *replaceSymbol(Symbol *S, ArgT &&... Arg) {
"SymbolUnion not aligned enough");
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
"Not a Symbol");
return new (S) T(std::forward<ArgT>(Arg)...);

Symbol SymCopy = *S;

T *S2 = new (S) T(std::forward<ArgT>(Arg)...);
S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj;
return S2;
}

} // namespace wasm
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,8 @@ void Writer::calculateImports() {
continue;
if (!Sym->isLive())
continue;
if (!Sym->IsUsedInRegularObj)
continue;

LLVM_DEBUG(dbgs() << "import: " << Sym->getName() << "\n");
ImportedSymbols.emplace_back(Sym);
Expand Down