llvm-project/lld/wasm/MarkLive.cpp
Andy Wingo 38dfce706f [WebAssembly] Add support for table linking to wasm-ld
This patch adds support to wasm-ld for linking multiple table references
together, in a manner similar to wasm globals. The indirect function
table is synthesized as needed.

To manage the transitional period in which the compiler doesn't yet
produce TABLE_NUMBER relocations and doesn't residualize table symbols,
the linker will detect object files which have table imports or
definitions, but no table symbols. In that case it will synthesize
symbols for the defined and imported tables.

As a change, relocatable objects are now written with table symbols,
which can cause symbol renumbering in some of the tests. If no object
file requires an indirect function table, none will be written to the
file. Note that for legacy ObjFile inputs, this test is conservative: as
we don't have relocs for each use of the indirecy function table, we
just assume that any incoming indirect function table should be
propagated to the output.

Differential Revision: https://reviews.llvm.org/D91870
2021-01-15 09:21:52 +01:00

212 lines
6.5 KiB
C++

//===- MarkLive.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements --gc-sections, which is a feature to remove unused
// chunks from the output. Unused chunks are those that are not reachable from
// known root symbols or chunks. This feature is implemented as a mark-sweep
// garbage collector.
//
// Here's how it works. Each InputChunk has a "Live" bit. The bit is off by
// default. Starting with the GC-roots, visit all reachable chunks and set their
// Live bits. The Writer will then ignore chunks whose Live bits are off, so
// that such chunk are not appear in the output.
//
//===----------------------------------------------------------------------===//
#include "MarkLive.h"
#include "Config.h"
#include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h"
#include "InputTable.h"
#include "SymbolTable.h"
#include "Symbols.h"
#define DEBUG_TYPE "lld"
using namespace llvm;
using namespace llvm::wasm;
namespace lld {
namespace wasm {
namespace {
class MarkLive {
public:
void run();
private:
void enqueue(Symbol *sym);
void enqueueInitFunctions(const ObjFile *sym);
void mark();
bool isCallCtorsLive();
// A list of chunks to visit.
SmallVector<InputChunk *, 256> queue;
};
} // namespace
void MarkLive::enqueue(Symbol *sym) {
if (!sym || sym->isLive())
return;
LLVM_DEBUG(dbgs() << "markLive: " << sym->getName() << "\n");
InputFile *file = sym->getFile();
bool needInitFunctions = file && !file->isLive() && sym->isDefined();
sym->markLive();
// Mark ctor functions in the object that defines this symbol live.
// The ctor functions are all referenced by the synthetic callCtors
// function. However, this function does not contain relocations so we
// have to manually mark the ctors as live.
if (needInitFunctions)
enqueueInitFunctions(cast<ObjFile>(file));
if (InputChunk *chunk = sym->getChunk())
queue.push_back(chunk);
}
// The ctor functions are all referenced by the synthetic callCtors
// function. However, this function does not contain relocations so we
// have to manually mark the ctors as live.
void MarkLive::enqueueInitFunctions(const ObjFile *obj) {
const WasmLinkingData &l = obj->getWasmObj()->linkingData();
for (const WasmInitFunc &f : l.InitFunctions) {
auto *initSym = obj->getFunctionSymbol(f.Symbol);
if (!initSym->isDiscarded())
enqueue(initSym);
}
}
void MarkLive::run() {
// Add GC root symbols.
if (!config->entry.empty())
enqueue(symtab->find(config->entry));
// We need to preserve any no-strip or exported symbol
for (Symbol *sym : symtab->getSymbols())
if (sym->isNoStrip() || sym->isExported())
enqueue(sym);
if (WasmSym::callDtors)
enqueue(WasmSym::callDtors);
// Enqueue constructors in objects explicitly live from the command-line.
for (const ObjFile *obj : symtab->objectFiles)
if (obj->isLive())
enqueueInitFunctions(obj);
mark();
// If we have any non-discarded init functions, mark `__wasm_call_ctors` as
// live so that we assign it an index and call it.
if (isCallCtorsLive())
WasmSym::callCtors->markLive();
}
void MarkLive::mark() {
// Follow relocations to mark all reachable chunks.
while (!queue.empty()) {
InputChunk *c = queue.pop_back_val();
for (const WasmRelocation reloc : c->getRelocations()) {
if (reloc.Type == R_WASM_TYPE_INDEX_LEB)
continue;
Symbol *sym = c->file->getSymbol(reloc.Index);
// If the function has been assigned the special index zero in the table,
// the relocation doesn't pull in the function body, since the function
// won't actually go in the table (the runtime will trap attempts to call
// that index, since we don't use it). A function with a table index of
// zero is only reachable via "call", not via "call_indirect". The stub
// functions used for weak-undefined symbols have this behaviour (compare
// equal to null pointer, only reachable via direct call).
if (reloc.Type == R_WASM_TABLE_INDEX_SLEB ||
reloc.Type == R_WASM_TABLE_INDEX_SLEB64 ||
reloc.Type == R_WASM_TABLE_INDEX_I32 ||
reloc.Type == R_WASM_TABLE_INDEX_I64) {
auto *funcSym = cast<FunctionSymbol>(sym);
if (funcSym->isStub)
continue;
}
enqueue(sym);
}
}
}
void markLive() {
if (!config->gcSections)
return;
LLVM_DEBUG(dbgs() << "markLive\n");
MarkLive marker;
marker.run();
// Report garbage-collected sections.
if (config->printGcSections) {
for (const ObjFile *obj : symtab->objectFiles) {
for (InputChunk *c : obj->functions)
if (!c->live)
message("removing unused section " + toString(c));
for (InputChunk *c : obj->segments)
if (!c->live)
message("removing unused section " + toString(c));
for (InputGlobal *g : obj->globals)
if (!g->live)
message("removing unused section " + toString(g));
for (InputEvent *e : obj->events)
if (!e->live)
message("removing unused section " + toString(e));
for (InputTable *t : obj->tables)
if (!t->live)
message("removing unused section " + toString(t));
}
for (InputChunk *c : symtab->syntheticFunctions)
if (!c->live)
message("removing unused section " + toString(c));
for (InputGlobal *g : symtab->syntheticGlobals)
if (!g->live)
message("removing unused section " + toString(g));
for (InputTable *t : symtab->syntheticTables)
if (!t->live)
message("removing unused section " + toString(t));
}
}
bool MarkLive::isCallCtorsLive() {
// In a reloctable link, we don't call `__wasm_call_ctors`.
if (config->relocatable)
return false;
// In Emscripten-style PIC, we call `__wasm_call_ctors` which calls
// `__wasm_apply_relocs`.
if (config->isPic)
return true;
// If there are any init functions, mark `__wasm_call_ctors` live so that
// it can call them.
for (const ObjFile *file : symtab->objectFiles) {
const WasmLinkingData &l = file->getWasmObj()->linkingData();
for (const WasmInitFunc &f : l.InitFunctions) {
auto *sym = file->getFunctionSymbol(f.Symbol);
if (!sym->isDiscarded() && sym->isLive())
return true;
}
}
return false;
}
} // namespace wasm
} // namespace lld