forked from OSchip/llvm-project
[lld][WebAssembly] Allow references to __tls_base without shared memory
Previously we limited the use of atomics and TLS to programs linked with `--shared-memory`. However, as of https://reviews.llvm.org/D79530 we now allow programs that use atomic to be linked without `--shared-memory`. For this to be useful we also want to all TLS usage in such programs. In this case, since we know we are single threaded we simply include the TLS data as a regular active segment and create an immutable `__tls_base` global that point to the start of this segment. Fixes: https://github.com/emscripten-core/emscripten/issues/12489 Differential Revision: https://reviews.llvm.org/D91115
This commit is contained in:
parent
e1eeb026e6
commit
29a3056bb5
|
@ -52,4 +52,4 @@ _start:
|
|||
# CHECK-NEXT: Mutable: false
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1
|
||||
# CHECK-NEXT: Value: 0
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# Test that linking without shared memory causes __tls_base to be
|
||||
# interlized
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
|
||||
|
||||
.globaltype __tls_base, i32
|
||||
|
||||
.globl get_tls1
|
||||
get_tls1:
|
||||
.functype get_tls1 () -> (i32)
|
||||
global.get __tls_base
|
||||
i32.const tls1
|
||||
i32.add
|
||||
end_function
|
||||
|
||||
.section .data.no_tls,"",@
|
||||
.globl no_tls
|
||||
.p2align 2
|
||||
no_tls:
|
||||
.int32 42
|
||||
.size no_tls, 4
|
||||
|
||||
.section .tdata.tls1,"",@
|
||||
.globl tls1
|
||||
.p2align 2
|
||||
tls1:
|
||||
.int32 43
|
||||
.size tls1, 2
|
||||
|
||||
.section .custom_section.target_features,"",@
|
||||
.int8 2
|
||||
.int8 43
|
||||
.int8 7
|
||||
.ascii "atomics"
|
||||
.int8 43
|
||||
.int8 11
|
||||
.ascii "bulk-memory"
|
||||
|
||||
# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
|
||||
# RUN: obj2yaml %t.wasm | FileCheck %s
|
||||
|
||||
# CHECK: - Type: GLOBAL
|
||||
# __stack_pointer
|
||||
# CHECK-NEXT: Globals:
|
||||
# CHECK-NEXT: - Index: 0
|
||||
# CHECK-NEXT: Type: I32
|
||||
# CHECK-NEXT: Mutable: true
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 66576
|
||||
# __tls_base
|
||||
# CHECK-NEXT: - Index: 1
|
||||
# CHECK-NEXT: Type: I32
|
||||
# CHECK-NEXT: Mutable: false
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1028
|
||||
# CHECK-NEXT: - Type: EXPORT
|
||||
|
||||
# CHECK: - Type: DATA
|
||||
# .data
|
||||
# CHECK-NEXT: Segments:
|
||||
# CHECK-NEXT: - SectionOffset: 7
|
||||
# CHECK-NEXT: InitFlags: 0
|
||||
# CHECK-NEXT: Offset:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1024
|
||||
# CHECK-NEXT: Content: 2A000000
|
||||
# .tdata
|
||||
# CHECK-NEXT: - SectionOffset: 17
|
||||
# CHECK-NEXT: InitFlags: 0
|
||||
# CHECK-NEXT: Offset:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1028
|
||||
# CHECK-NEXT: Content: 2B000000
|
|
@ -541,21 +541,30 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
|
|||
return sym;
|
||||
}
|
||||
|
||||
static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable,
|
||||
int value) {
|
||||
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
|
||||
llvm::wasm::WasmGlobal wasmGlobal;
|
||||
if (config->is64.getValueOr(false)) {
|
||||
wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
|
||||
wasmGlobal.InitExpr.Value.Int64 = value;
|
||||
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
|
||||
wasmGlobal.InitExpr.Value.Int64 = 0;
|
||||
} else {
|
||||
wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
|
||||
wasmGlobal.InitExpr.Value.Int32 = value;
|
||||
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
|
||||
wasmGlobal.InitExpr.Value.Int32 = 0;
|
||||
}
|
||||
wasmGlobal.SymbolName = name;
|
||||
return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
make<InputGlobal>(wasmGlobal, nullptr));
|
||||
return make<InputGlobal>(wasmGlobal, nullptr);
|
||||
}
|
||||
|
||||
static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) {
|
||||
InputGlobal *g = createGlobal(name, isMutable);
|
||||
return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN, g);
|
||||
}
|
||||
|
||||
static GlobalSymbol *createOptionalGlobal(StringRef name, bool isMutable) {
|
||||
InputGlobal *g = createGlobal(name, isMutable);
|
||||
return symtab->addOptionalGlobalSymbols(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
g);
|
||||
}
|
||||
|
||||
// Create ABI-defined synthetic symbols
|
||||
|
@ -602,7 +611,7 @@ static void createSyntheticSymbols() {
|
|||
WasmSym::tableBase->markLive();
|
||||
} else {
|
||||
// For non-PIC code
|
||||
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true, 0);
|
||||
WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
|
||||
WasmSym::stackPointer->markLive();
|
||||
}
|
||||
|
||||
|
@ -616,9 +625,9 @@ static void createSyntheticSymbols() {
|
|||
WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol(
|
||||
"__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN);
|
||||
assert(WasmSym::initMemoryFlag);
|
||||
WasmSym::tlsBase = createGlobalVariable("__tls_base", true, 0);
|
||||
WasmSym::tlsSize = createGlobalVariable("__tls_size", false, 0);
|
||||
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1);
|
||||
WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
|
||||
WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
|
||||
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
|
||||
WasmSym::initTLS = symtab->addSyntheticFunction(
|
||||
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
make<SyntheticFunction>(
|
||||
|
@ -642,6 +651,18 @@ static void createOptionalSymbols() {
|
|||
WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
|
||||
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
|
||||
}
|
||||
|
||||
// For non-shared memory programs we still need to define __tls_base since we
|
||||
// allow object files built with TLS to be linked into single threaded
|
||||
// programs, and such object files can contains refernced to this symbol.
|
||||
//
|
||||
// However, in this case __tls_base is immutable and points directly to the
|
||||
// start of the `.tdata` static segment.
|
||||
//
|
||||
// __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 (!config->sharedMemory)
|
||||
WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
|
||||
}
|
||||
|
||||
// Reconstructs command line arguments so that so that you can re-run
|
||||
|
|
|
@ -206,7 +206,7 @@ DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name,
|
|||
flags, nullptr, function);
|
||||
}
|
||||
|
||||
// Adds an optional, linker generated, data symbols. The symbol will only be
|
||||
// Adds an optional, linker generated, data symbol. The symbol will only be
|
||||
// added if there is an undefine reference to it, or if it is explicitly
|
||||
// exported via the --export flag. Otherwise we don't add the symbol and return
|
||||
// nullptr.
|
||||
|
@ -241,6 +241,18 @@ DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
|
|||
nullptr, global);
|
||||
}
|
||||
|
||||
DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name,
|
||||
uint32_t flags,
|
||||
InputGlobal *global) {
|
||||
LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbols: " << name << " -> " << global
|
||||
<< "\n");
|
||||
Symbol *s = find(name);
|
||||
if (!s || s->isDefined())
|
||||
return nullptr;
|
||||
syntheticGlobals.emplace_back(global);
|
||||
return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global);
|
||||
}
|
||||
|
||||
static bool shouldReplace(const Symbol *existing, InputFile *newFile,
|
||||
uint32_t newFlags) {
|
||||
// If existing symbol is undefined, replace it.
|
||||
|
|
|
@ -83,6 +83,8 @@ public:
|
|||
DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags,
|
||||
InputFunction *function);
|
||||
DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
|
||||
DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags,
|
||||
InputGlobal *global);
|
||||
|
||||
void handleSymbolVariants();
|
||||
void handleWeakUndefines();
|
||||
|
|
|
@ -204,6 +204,16 @@ void Writer::writeSections() {
|
|||
});
|
||||
}
|
||||
|
||||
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
|
||||
if (config->is64.getValueOr(false)) {
|
||||
assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I64_CONST);
|
||||
g->global->global.InitExpr.Value.Int64 = memoryPtr;
|
||||
} else {
|
||||
assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
|
||||
g->global->global.InitExpr.Value.Int32 = memoryPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix the memory layout of the output binary. This assigns memory offsets
|
||||
// to each of the input data sections as well as the explicit stack region.
|
||||
// The default memory layout is as follows, from low to high.
|
||||
|
@ -267,18 +277,21 @@ void Writer::layoutMemory() {
|
|||
seg->startVA = memoryPtr;
|
||||
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
|
||||
memoryPtr, seg->size, seg->alignment));
|
||||
memoryPtr += seg->size;
|
||||
|
||||
if (WasmSym::tlsSize && seg->name == ".tdata") {
|
||||
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
|
||||
assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
|
||||
tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
|
||||
if (seg->name == ".tdata") {
|
||||
if (config->sharedMemory) {
|
||||
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
|
||||
setGlobalPtr(tlsSize, seg->size);
|
||||
|
||||
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
|
||||
assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
|
||||
tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1}
|
||||
<< seg->alignment;
|
||||
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
|
||||
setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
|
||||
} else {
|
||||
auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
|
||||
setGlobalPtr(tlsBase, memoryPtr);
|
||||
}
|
||||
}
|
||||
|
||||
memoryPtr += seg->size;
|
||||
}
|
||||
|
||||
// Make space for the memory initialization flag
|
||||
|
@ -768,7 +781,7 @@ void Writer::createOutputSegments() {
|
|||
if (s == nullptr) {
|
||||
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
|
||||
s = make<OutputSegment>(name);
|
||||
if (config->sharedMemory || name == ".tdata")
|
||||
if (config->sharedMemory)
|
||||
s->initFlags = WASM_SEGMENT_IS_PASSIVE;
|
||||
// Exported memories are guaranteed to be zero-initialized, so no need
|
||||
// to emit data segments for bss sections.
|
||||
|
|
Loading…
Reference in New Issue