Skip to content

release/20.x: [wasm-ld] Refactor WasmSym from static globals to per-link context (#134970) #137620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
@@ -32,6 +32,11 @@ class InputTable;
class InputGlobal;
class InputFunction;
class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
class UndefinedGlobal;
class TableSymbol;

// For --unresolved-symbols.
enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
@@ -139,6 +144,107 @@ struct Ctx {
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
llvm::SmallVector<InputTable *, 0> syntheticTables;

// linker-generated symbols
struct WasmSym {
// __global_base
// Symbol marking the start of the global section.
DefinedData *globalBase;

// __stack_pointer/__stack_low/__stack_high
// Global that holds current value of stack pointer and data symbols marking
// the start and end of the stack region. stackPointer is initialized to
// stackHigh and grows downwards towards stackLow
GlobalSymbol *stackPointer;
DefinedData *stackLow;
DefinedData *stackHigh;

// __tls_base
// Global that holds the address of the base of the current thread's
// TLS block.
GlobalSymbol *tlsBase;

// __tls_size
// Symbol whose value is the size of the TLS block.
GlobalSymbol *tlsSize;

// __tls_size
// Symbol whose value is the alignment of the TLS block.
GlobalSymbol *tlsAlign;

// __data_end
// Symbol marking the end of the data and bss.
DefinedData *dataEnd;

// __heap_base/__heap_end
// Symbols marking the beginning and end of the "heap". It starts at the end
// of the data, bss and explicit stack, and extends to the end of the linear
// memory allocated by wasm-ld. This region of memory is not used by the
// linked code, so it may be used as a backing store for `sbrk` or `malloc`
// implementations.
DefinedData *heapBase;
DefinedData *heapEnd;

// __wasm_init_memory_flag
// Symbol whose contents are nonzero iff memory has already been
// initialized.
DefinedData *initMemoryFlag;

// __wasm_init_memory
// Function that initializes passive data segments during instantiation.
DefinedFunction *initMemory;

// __wasm_call_ctors
// Function that directly calls all ctors in priority order.
DefinedFunction *callCtors;

// __wasm_call_dtors
// Function that calls the libc/etc. cleanup function.
DefinedFunction *callDtors;

// __wasm_apply_global_relocs
// Function that applies relocations to wasm globals post-instantiation.
// Unlike __wasm_apply_data_relocs this needs to run on every thread.
DefinedFunction *applyGlobalRelocs;

// __wasm_apply_tls_relocs
// Like __wasm_apply_data_relocs but for TLS section. These must be
// delayed until __wasm_init_tls.
DefinedFunction *applyTLSRelocs;

// __wasm_apply_global_tls_relocs
// Like applyGlobalRelocs but for globals that hold TLS addresses. These
// must be delayed until __wasm_init_tls.
DefinedFunction *applyGlobalTLSRelocs;

// __wasm_init_tls
// Function that allocates thread-local storage and initializes it.
DefinedFunction *initTLS;

// Pointer to the function that is to be used in the start section.
// (normally an alias of initMemory, or applyGlobalRelocs).
DefinedFunction *startFunction;

// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
DefinedData *dsoHandle;

// __table_base
// Used in PIC code for offset of indirect function table
UndefinedGlobal *tableBase;
DefinedData *definedTableBase;

// __memory_base
// Used in PIC code for offset of global data
UndefinedGlobal *memoryBase;
DefinedData *definedMemoryBase;

// __indirect_function_table
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
};
WasmSym sym;

// True if we are creating position-independent code.
bool isPic = false;

60 changes: 30 additions & 30 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@ void Ctx::reset() {
isPic = false;
legacyFunctionTable = false;
emitBssSegments = false;
sym = WasmSym{};
}

namespace {
@@ -941,14 +942,14 @@ static void createSyntheticSymbols() {
true};
static llvm::wasm::WasmGlobalType mutableGlobalTypeI64 = {WASM_TYPE_I64,
true};
WasmSym::callCtors = symtab->addSyntheticFunction(
ctx.sym.callCtors = symtab->addSyntheticFunction(
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));

bool is64 = ctx.arg.is64.value_or(false);

if (ctx.isPic) {
WasmSym::stackPointer =
ctx.sym.stackPointer =
createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
? &mutableGlobalTypeI64
: &mutableGlobalTypeI32);
@@ -958,45 +959,44 @@ static void createSyntheticSymbols() {
// See:
// https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
WasmSym::memoryBase->markLive();
WasmSym::tableBase->markLive();
ctx.sym.memoryBase = createUndefinedGlobal("__memory_base", globalType);
ctx.sym.tableBase = createUndefinedGlobal("__table_base", globalType);
ctx.sym.memoryBase->markLive();
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
WasmSym::stackPointer->markLive();
ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
ctx.sym.stackPointer->markLive();
}

if (ctx.arg.sharedMemory) {
WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
WasmSym::initTLS = symtab->addSyntheticFunction(
ctx.sym.tlsBase = createGlobalVariable("__tls_base", true);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false);
ctx.sym.initTLS = symtab->addSyntheticFunction(
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(
is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
}
}

static void createOptionalSymbols() {
if (ctx.arg.relocatable)
return;

WasmSym::dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");
ctx.sym.dsoHandle = symtab->addOptionalDataSymbol("__dso_handle");

if (!ctx.arg.shared)
WasmSym::dataEnd = symtab->addOptionalDataSymbol("__data_end");
ctx.sym.dataEnd = symtab->addOptionalDataSymbol("__data_end");

if (!ctx.isPic) {
WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low");
WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high");
WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end");
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
ctx.sym.stackLow = symtab->addOptionalDataSymbol("__stack_low");
ctx.sym.stackHigh = symtab->addOptionalDataSymbol("__stack_high");
ctx.sym.globalBase = symtab->addOptionalDataSymbol("__global_base");
ctx.sym.heapBase = symtab->addOptionalDataSymbol("__heap_base");
ctx.sym.heapEnd = symtab->addOptionalDataSymbol("__heap_end");
ctx.sym.definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
ctx.sym.definedTableBase = symtab->addOptionalDataSymbol("__table_base");
}

// For non-shared memory programs we still need to define __tls_base since we
@@ -1009,7 +1009,7 @@ static void createOptionalSymbols() {
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
if (!ctx.arg.sharedMemory)
WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}

static void processStubLibrariesPreLTO() {
@@ -1384,9 +1384,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// by libc/etc., because destructors are registered dynamically with
// `__cxa_atexit` and friends.
if (!ctx.arg.relocatable && !ctx.arg.shared &&
!WasmSym::callCtors->isUsedInRegularObj &&
WasmSym::callCtors->getName() != ctx.arg.entry &&
!ctx.arg.exportedSymbols.count(WasmSym::callCtors->getName())) {
!ctx.sym.callCtors->isUsedInRegularObj &&
ctx.sym.callCtors->getName() != ctx.arg.entry &&
!ctx.arg.exportedSymbols.count(ctx.sym.callCtors->getName())) {
if (Symbol *callDtors =
handleUndefined("__wasm_call_dtors", "<internal>")) {
if (auto *callDtorsFunc = dyn_cast<DefinedFunction>(callDtors)) {
@@ -1395,7 +1395,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
!callDtorsFunc->signature->Returns.empty())) {
error("__wasm_call_dtors must have no argument or return values");
}
WasmSym::callDtors = callDtorsFunc;
ctx.sym.callDtors = callDtorsFunc;
} else {
error("__wasm_call_dtors must be a function");
}
@@ -1488,7 +1488,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
markLive();

// Provide the indirect function table if needed.
WasmSym::indirectFunctionTable =
ctx.sym.indirectFunctionTable =
symtab->resolveIndirectFunctionTable(/*required =*/false);

if (errorCount())
10 changes: 5 additions & 5 deletions lld/wasm/InputChunks.cpp
Original file line number Diff line number Diff line change
@@ -397,9 +397,9 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
if (ctx.isPic) {
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (isTLS())
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base");
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "tls_base");
else
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "memory_base");
writeU8(os, opcode_ptr_add, "ADD");
}

@@ -422,12 +422,12 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const {
}
} else {
assert(ctx.isPic);
const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
const GlobalSymbol *baseSymbol = ctx.sym.memoryBase;
if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
rel.Type == R_WASM_TABLE_INDEX_I64)
baseSymbol = WasmSym::tableBase;
baseSymbol = ctx.sym.tableBase;
else if (sym->isTLS())
baseSymbol = WasmSym::tlsBase;
baseSymbol = ctx.sym.tlsBase;
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
writeU8(os, opcode_reloc_const, "CONST");
6 changes: 3 additions & 3 deletions lld/wasm/MarkLive.cpp
Original file line number Diff line number Diff line change
@@ -114,8 +114,8 @@ void MarkLive::run() {
if (sym->isNoStrip() || sym->isExported())
enqueue(sym);

if (WasmSym::callDtors)
enqueue(WasmSym::callDtors);
if (ctx.sym.callDtors)
enqueue(ctx.sym.callDtors);

for (const ObjFile *obj : ctx.objectFiles)
if (obj->isLive()) {
@@ -131,7 +131,7 @@ void MarkLive::run() {
// 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();
ctx.sym.callCtors->markLive();
}

void MarkLive::mark() {
4 changes: 2 additions & 2 deletions lld/wasm/OutputSections.cpp
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ void DataSection::finalizeContents() {
if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
if (ctx.isPic && ctx.arg.extendedConst) {
writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(),
"literal (global index)");
if (segment->startVA) {
writePtrConst(os, segment->startVA, is64, "offset");
@@ -136,7 +136,7 @@ void DataSection::finalizeContents() {
if (ctx.isPic) {
assert(segment->startVA == 0);
initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex();
initExpr.Inst.Value.Global = ctx.sym.memoryBase->getGlobalIndex();
} else {
initExpr = intConst(segment->startVA, is64);
}
25 changes: 0 additions & 25 deletions lld/wasm/Symbols.cpp
Original file line number Diff line number Diff line change
@@ -77,31 +77,6 @@ std::string toString(wasm::Symbol::Kind kind) {
}

namespace wasm {
DefinedFunction *WasmSym::callCtors;
DefinedFunction *WasmSym::callDtors;
DefinedFunction *WasmSym::initMemory;
DefinedFunction *WasmSym::applyGlobalRelocs;
DefinedFunction *WasmSym::applyTLSRelocs;
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
DefinedFunction *WasmSym::initTLS;
DefinedFunction *WasmSym::startFunction;
DefinedData *WasmSym::dsoHandle;
DefinedData *WasmSym::dataEnd;
DefinedData *WasmSym::globalBase;
DefinedData *WasmSym::heapBase;
DefinedData *WasmSym::heapEnd;
DefinedData *WasmSym::initMemoryFlag;
GlobalSymbol *WasmSym::stackPointer;
DefinedData *WasmSym::stackLow;
DefinedData *WasmSym::stackHigh;
GlobalSymbol *WasmSym::tlsBase;
GlobalSymbol *WasmSym::tlsSize;
GlobalSymbol *WasmSym::tlsAlign;
UndefinedGlobal *WasmSym::tableBase;
DefinedData *WasmSym::definedTableBase;
UndefinedGlobal *WasmSym::memoryBase;
DefinedData *WasmSym::definedMemoryBase;
TableSymbol *WasmSym::indirectFunctionTable;

WasmSymbolType Symbol::getWasmType() const {
if (isa<FunctionSymbol>(this))
99 changes: 0 additions & 99 deletions lld/wasm/Symbols.h
Original file line number Diff line number Diff line change
@@ -537,105 +537,6 @@ class LazySymbol : public Symbol {
const WasmSignature *signature = nullptr;
};

// linker-generated symbols
struct WasmSym {
// __global_base
// Symbol marking the start of the global section.
static DefinedData *globalBase;

// __stack_pointer/__stack_low/__stack_high
// Global that holds current value of stack pointer and data symbols marking
// the start and end of the stack region. stackPointer is initialized to
// stackHigh and grows downwards towards stackLow
static GlobalSymbol *stackPointer;
static DefinedData *stackLow;
static DefinedData *stackHigh;

// __tls_base
// Global that holds the address of the base of the current thread's
// TLS block.
static GlobalSymbol *tlsBase;

// __tls_size
// Symbol whose value is the size of the TLS block.
static GlobalSymbol *tlsSize;

// __tls_size
// Symbol whose value is the alignment of the TLS block.
static GlobalSymbol *tlsAlign;

// __data_end
// Symbol marking the end of the data and bss.
static DefinedData *dataEnd;

// __heap_base/__heap_end
// Symbols marking the beginning and end of the "heap". It starts at the end
// of the data, bss and explicit stack, and extends to the end of the linear
// memory allocated by wasm-ld. This region of memory is not used by the
// linked code, so it may be used as a backing store for `sbrk` or `malloc`
// implementations.
static DefinedData *heapBase;
static DefinedData *heapEnd;

// __wasm_init_memory_flag
// Symbol whose contents are nonzero iff memory has already been initialized.
static DefinedData *initMemoryFlag;

// __wasm_init_memory
// Function that initializes passive data segments during instantiation.
static DefinedFunction *initMemory;

// __wasm_call_ctors
// Function that directly calls all ctors in priority order.
static DefinedFunction *callCtors;

// __wasm_call_dtors
// Function that calls the libc/etc. cleanup function.
static DefinedFunction *callDtors;

// __wasm_apply_global_relocs
// Function that applies relocations to wasm globals post-instantiation.
// Unlike __wasm_apply_data_relocs this needs to run on every thread.
static DefinedFunction *applyGlobalRelocs;

// __wasm_apply_tls_relocs
// Like __wasm_apply_data_relocs but for TLS section. These must be
// delayed until __wasm_init_tls.
static DefinedFunction *applyTLSRelocs;

// __wasm_apply_global_tls_relocs
// Like applyGlobalRelocs but for globals that hold TLS addresses. These
// must be delayed until __wasm_init_tls.
static DefinedFunction *applyGlobalTLSRelocs;

// __wasm_init_tls
// Function that allocates thread-local storage and initializes it.
static DefinedFunction *initTLS;

// Pointer to the function that is to be used in the start section.
// (normally an alias of initMemory, or applyGlobalRelocs).
static DefinedFunction *startFunction;

// __dso_handle
// Symbol used in calls to __cxa_atexit to determine current DLL
static DefinedData *dsoHandle;

// __table_base
// Used in PIC code for offset of indirect function table
static UndefinedGlobal *tableBase;
static DefinedData *definedTableBase;

// __memory_base
// Used in PIC code for offset of global data
static UndefinedGlobal *memoryBase;
static DefinedData *definedMemoryBase;

// __indirect_function_table
// Used as an address space for function pointers, with each function that is
// used as a function pointer being allocated a slot.
static TableSymbol *indirectFunctionTable;
};

// A buffer class that is large enough to hold any Symbol-derived
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
32 changes: 14 additions & 18 deletions lld/wasm/SyntheticSections.cpp
Original file line number Diff line number Diff line change
@@ -319,8 +319,8 @@ void TableSection::addTable(InputTable *table) {
// Some inputs require that the indirect function table be assigned to table
// number 0.
if (ctx.legacyFunctionTable &&
isa<DefinedTable>(WasmSym::indirectFunctionTable) &&
cast<DefinedTable>(WasmSym::indirectFunctionTable)->table == table) {
isa<DefinedTable>(ctx.sym.indirectFunctionTable) &&
cast<DefinedTable>(ctx.sym.indirectFunctionTable)->table == table) {
if (out.importSec->getNumImportedTables()) {
// Alack! Some other input imported a table, meaning that we are unable
// to assign table number 0 to the indirect function table.
@@ -395,8 +395,8 @@ void GlobalSection::assignIndexes() {
}

static void ensureIndirectFunctionTable() {
if (!WasmSym::indirectFunctionTable)
WasmSym::indirectFunctionTable =
if (!ctx.sym.indirectFunctionTable)
ctx.sym.indirectFunctionTable =
symtab->resolveIndirectFunctionTable(/*required =*/true);
}

@@ -430,10 +430,9 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base");
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
else
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
"__memory_base");
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");

// Add the virtual address of the data symbol
writeU8(os, opcode_ptr_const, "CONST");
@@ -443,7 +442,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
continue;
// Get __table_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, WasmSym::tableBase->getGlobalIndex(), "__table_base");
writeUleb128(os, ctx.sym.tableBase->getGlobalIndex(), "__table_base");

// Add the table index to __table_base
writeU8(os, opcode_ptr_const, "CONST");
@@ -490,13 +489,13 @@ void GlobalSection::writeBody() {
if (ctx.arg.extendedConst && ctx.isPic) {
if (auto *d = dyn_cast<DefinedData>(sym)) {
if (!sym->isTLS()) {
globalIdx = WasmSym::memoryBase->getGlobalIndex();
globalIdx = ctx.sym.memoryBase->getGlobalIndex();
offset = d->getVA();
useExtendedConst = true;
}
} else if (auto *f = dyn_cast<FunctionSymbol>(sym)) {
if (!sym->isStub) {
globalIdx = WasmSym::tableBase->getGlobalIndex();
globalIdx = ctx.sym.tableBase->getGlobalIndex();
offset = f->getTableIndex();
useExtendedConst = true;
}
@@ -550,14 +549,11 @@ void ExportSection::writeBody() {
writeExport(os, export_);
}

bool StartSection::isNeeded() const {
return WasmSym::startFunction != nullptr;
}
bool StartSection::isNeeded() const { return ctx.sym.startFunction != nullptr; }

void StartSection::writeBody() {
raw_ostream &os = bodyOutputStream;
writeUleb128(os, WasmSym::startFunction->getFunctionIndex(),
"function index");
writeUleb128(os, ctx.sym.startFunction->getFunctionIndex(), "function index");
}

void ElemSection::addEntry(FunctionSymbol *sym) {
@@ -573,9 +569,9 @@ void ElemSection::addEntry(FunctionSymbol *sym) {
void ElemSection::writeBody() {
raw_ostream &os = bodyOutputStream;

assert(WasmSym::indirectFunctionTable);
assert(ctx.sym.indirectFunctionTable);
writeUleb128(os, 1, "segment count");
uint32_t tableNumber = WasmSym::indirectFunctionTable->getTableNumber();
uint32_t tableNumber = ctx.sym.indirectFunctionTable->getTableNumber();
uint32_t flags = 0;
if (tableNumber)
flags |= WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER;
@@ -587,7 +583,7 @@ void ElemSection::writeBody() {
initExpr.Extended = false;
if (ctx.isPic) {
initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
initExpr.Inst.Value.Global = WasmSym::tableBase->getGlobalIndex();
initExpr.Inst.Value.Global = ctx.sym.tableBase->getGlobalIndex();
} else {
bool is64 = ctx.arg.is64.value_or(false);
initExpr = intConst(ctx.arg.tableBase, is64);
185 changes: 93 additions & 92 deletions lld/wasm/Writer.cpp

Large diffs are not rendered by default.