[WebAssembly] Allow import and export of TLS symbols between DSOs
We previously had a limitation that TLS variables could not be exported (and therefore could also not be imported). This change removed that limitation. Differential Revision: https://reviews.llvm.org/D108877
This commit is contained in:
@@ -91,7 +91,7 @@ declare void @external_func()
|
||||
; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM
|
||||
|
||||
; SHMEM: - Type: CODE
|
||||
; SHMEM: - Index: 6
|
||||
; SHMEM: - Index: 7
|
||||
; SHMEM-NEXT: Locals: []
|
||||
; SHMEM-NEXT: Body: 100310050B
|
||||
|
||||
@@ -109,11 +109,13 @@ declare void @external_func()
|
||||
; SHMEM-NEXT: - Index: 5
|
||||
; SHMEM-NEXT: Name: __wasm_apply_global_relocs
|
||||
; SHMEM-NEXT: - Index: 6
|
||||
; SHMEM-NEXT: Name: __wasm_start
|
||||
; SHMEM-NEXT: Name: __wasm_apply_global_tls_relocs
|
||||
; SHMEM-NEXT: - Index: 7
|
||||
; SHMEM-NEXT: Name: foo
|
||||
; SHMEM-NEXT: Name: __wasm_start
|
||||
; SHMEM-NEXT: - Index: 8
|
||||
; SHMEM-NEXT: Name: get_data_address
|
||||
; SHMEM-NEXT: Name: foo
|
||||
; SHMEM-NEXT: - Index: 9
|
||||
; SHMEM-NEXT: Name: get_data_address
|
||||
; SHMEM-NEXT: - Index: 10
|
||||
; SHMEM-NEXT: Name: _start
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ data:
|
||||
# SO1-NEXT: TableSize: 0
|
||||
# SO1-NEXT: TableAlignment: 0
|
||||
# SO1-NEXT: Needed: []
|
||||
# SO1-NEXT: ExportInfo: []
|
||||
# SO1-NEXT: - Type: TYPE
|
||||
|
||||
# SO2: Sections:
|
||||
@@ -40,4 +41,5 @@ data:
|
||||
# SO2-NEXT: TableAlignment: 0
|
||||
# SO2-NEXT: Needed:
|
||||
# SO2-NEXT: - shared-needed.s.tmp1.so
|
||||
# SO2-NEXT: ExportInfo: []
|
||||
# SO2-NEXT: - Type: TYPE
|
||||
|
||||
@@ -133,6 +133,7 @@ get_local_func_address:
|
||||
# CHECK-NEXT: TableSize: 2
|
||||
# CHECK-NEXT: TableAlignment: 0
|
||||
# CHECK-NEXT: Needed: []
|
||||
# CHECK-NEXT: ExportInfo: []
|
||||
# CHECK-NEXT: - Type: TYPE
|
||||
|
||||
# check for import of __table_base and __memory_base globals
|
||||
|
||||
@@ -134,6 +134,7 @@ get_local_func_address:
|
||||
# CHECK-NEXT: TableSize: 2
|
||||
# CHECK-NEXT: TableAlignment: 0
|
||||
# CHECK-NEXT: Needed: []
|
||||
# CHECK-NEXT: ExportInfo: []
|
||||
# CHECK-NEXT: - Type: TYPE
|
||||
|
||||
# check for import of __table_base and __memory_base globals
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
|
||||
# RUN: wasm-ld -no-gc-sections --shared-memory --no-entry -o %t.wasm %t.o
|
||||
# RUN: not wasm-ld --shared-memory --no-entry --export=tls1 -o %t.wasm %t.o 2>&1 | FileCheck %s
|
||||
# With --export-all we ignore TLS symbols so we don't expect an error here
|
||||
# RUN: wasm-ld --shared-memory --no-entry --export-all -o %t.wasm %t.o
|
||||
|
||||
# CHECK: error: TLS symbols cannot yet be exported: `tls1`
|
||||
# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o
|
||||
# RUN: obj2yaml %t.so | FileCheck %s
|
||||
|
||||
.section .tdata.tls1,"",@
|
||||
.globl tls1
|
||||
@@ -24,3 +20,26 @@ tls1:
|
||||
.int8 43
|
||||
.int8 15
|
||||
.ascii "mutable-globals"
|
||||
|
||||
# CHECK: ExportInfo:
|
||||
# CHECK-NEXT: - Name: tls1
|
||||
# CHECK-NEXT: Flags: [ TLS ]
|
||||
# CHECK-NEXT: - Type: TYPE
|
||||
|
||||
# CHECK: - Type: GLOBAL
|
||||
# CHECK-NEXT: Globals:
|
||||
# CHECK-NEXT: - Index: 2
|
||||
# CHECK-NEXT: Type: I32
|
||||
# CHECK-NEXT: Mutable: false
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 0
|
||||
|
||||
# CHECK: - Type: EXPORT
|
||||
# CHECK-NEXT: Exports:
|
||||
# CHECK-NEXT: - Name: __wasm_call_ctors
|
||||
# CHECK-NEXT: Kind: FUNCTION
|
||||
# CHECK-NEXT: Index: 0
|
||||
# CHECK-NEXT: - Name: tls1
|
||||
# CHECK-NEXT: Kind: GLOBAL
|
||||
# CHECK-NEXT: Index: 2
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
|
||||
# RUN: not wasm-ld -shared --experimental-pic --shared-memory -o %t.wasm %t.o 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK: error: {{.*}}.o: TLS symbol is undefined, but TLS symbols cannot yet be imported: `foo`
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
.functype _start () -> ()
|
||||
i32.const foo@TLSREL
|
||||
drop
|
||||
end_function
|
||||
|
||||
.section .custom_section.target_features,"",@
|
||||
.int8 3
|
||||
.int8 43
|
||||
.int8 7
|
||||
.ascii "atomics"
|
||||
.int8 43
|
||||
.int8 11
|
||||
.ascii "bulk-memory"
|
||||
.int8 43
|
||||
.int8 15
|
||||
.ascii "mutable-globals"
|
||||
@@ -1,5 +1,5 @@
|
||||
# Test that linking without shared memory causes __tls_base to be
|
||||
# internalized
|
||||
# internalized.
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
|
||||
|
||||
@@ -13,6 +13,12 @@ get_tls1:
|
||||
i32.add
|
||||
end_function
|
||||
|
||||
.globl get_tls1_got
|
||||
get_tls1_got:
|
||||
.functype get_tls1_got () -> (i32)
|
||||
global.get tls1@GOT@TLS
|
||||
end_function
|
||||
|
||||
.section .data.no_tls,"",@
|
||||
.globl no_tls
|
||||
.p2align 2
|
||||
@@ -20,7 +26,7 @@ no_tls:
|
||||
.int32 42
|
||||
.size no_tls, 4
|
||||
|
||||
.section .tdata.tls1,"",@
|
||||
.section .tdata.tls1,"T",@
|
||||
.globl tls1
|
||||
.p2align 2
|
||||
tls1:
|
||||
@@ -58,6 +64,13 @@ tls1:
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1024
|
||||
# GOT.data.internal.tls1
|
||||
# CHECK-NEXT: - Index: 2
|
||||
# CHECK-NEXT: Type: I32
|
||||
# CHECK-NEXT: Mutable: false
|
||||
# CHECK-NEXT: InitExpr:
|
||||
# CHECK-NEXT: Opcode: I32_CONST
|
||||
# CHECK-NEXT: Value: 1024
|
||||
# CHECK-NEXT: - Type: EXPORT
|
||||
|
||||
# CHECK: - Type: DATA
|
||||
|
||||
@@ -139,12 +139,6 @@ void scanRelocations(InputChunk *chunk) {
|
||||
}
|
||||
|
||||
if (config->isPic) {
|
||||
if (sym->isTLS() && sym->isUndefined()) {
|
||||
error(toString(file) +
|
||||
": TLS symbol is undefined, but TLS symbols cannot yet be "
|
||||
"imported: `" +
|
||||
toString(*sym) + "`");
|
||||
}
|
||||
switch (reloc.Type) {
|
||||
case R_WASM_TABLE_INDEX_SLEB:
|
||||
case R_WASM_TABLE_INDEX_SLEB64:
|
||||
|
||||
@@ -74,6 +74,7 @@ DefinedFunction *WasmSym::callDtors;
|
||||
DefinedFunction *WasmSym::initMemory;
|
||||
DefinedFunction *WasmSym::applyDataRelocs;
|
||||
DefinedFunction *WasmSym::applyGlobalRelocs;
|
||||
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
|
||||
DefinedFunction *WasmSym::initTLS;
|
||||
DefinedFunction *WasmSym::startFunction;
|
||||
DefinedData *WasmSym::dsoHandle;
|
||||
|
||||
@@ -552,10 +552,15 @@ struct WasmSym {
|
||||
static DefinedFunction *applyDataRelocs;
|
||||
|
||||
// __wasm_apply_global_relocs
|
||||
// Function that applies relocations to data segment post-instantiation.
|
||||
// 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_global_tls_relocs
|
||||
// Like applyGlobalRelocs but for globals that hold TLS addresess. 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;
|
||||
|
||||
@@ -73,6 +73,37 @@ void DylinkSection::writeBody() {
|
||||
writeStr(sub.os, llvm::sys::path::filename(so->getName()), "so name");
|
||||
sub.writeTo(os);
|
||||
}
|
||||
|
||||
// Under certain circumstances we need to include extra information about the
|
||||
// exports we are providing to the dynamic linker. Currently this is only the
|
||||
// case for TLS symbols where the exported value is relative to __tls_base
|
||||
// rather than __memory_base.
|
||||
std::vector<const Symbol *> exportInfo;
|
||||
for (const Symbol *sym : symtab->getSymbols()) {
|
||||
if (sym->isExported() && sym->isLive() && sym->isTLS() &&
|
||||
isa<DefinedData>(sym)) {
|
||||
exportInfo.push_back(sym);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exportInfo.empty()) {
|
||||
SubSection sub(WASM_DYLINK_EXPORT_INFO);
|
||||
writeUleb128(sub.os, exportInfo.size(), "num exports");
|
||||
|
||||
for (const Symbol *sym : exportInfo) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "export info: " << toString(*sym) << "\n");
|
||||
StringRef name = sym->getName();
|
||||
if (auto *f = dyn_cast<DefinedFunction>(sym)) {
|
||||
if (Optional<StringRef> exportName = f->function->getExportName()) {
|
||||
name = *exportName;
|
||||
}
|
||||
}
|
||||
writeStr(sub.os, name, "sym name");
|
||||
writeUleb128(sub.os, sym->flags, "sym flags");
|
||||
}
|
||||
|
||||
sub.writeTo(os);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TypeSection::registerType(const WasmSignature &sig) {
|
||||
@@ -345,7 +376,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
|
||||
internalGotSymbols.push_back(sym);
|
||||
}
|
||||
|
||||
void GlobalSection::generateRelocationCode(raw_ostream &os) const {
|
||||
void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
|
||||
bool is64 = config->is64.getValueOr(false);
|
||||
unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
|
||||
: WASM_OPCODE_I32_CONST;
|
||||
@@ -353,10 +384,17 @@ void GlobalSection::generateRelocationCode(raw_ostream &os) const {
|
||||
: WASM_OPCODE_I32_ADD;
|
||||
|
||||
for (const Symbol *sym : internalGotSymbols) {
|
||||
if (TLS != sym->isTLS())
|
||||
continue;
|
||||
|
||||
if (auto *d = dyn_cast<DefinedData>(sym)) {
|
||||
// Get __memory_base
|
||||
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
|
||||
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base");
|
||||
if (sym->isTLS())
|
||||
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base");
|
||||
else
|
||||
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
|
||||
"__memory_base");
|
||||
|
||||
// Add the virtual address of the data symbol
|
||||
writeU8(os, opcode_ptr_const, "CONST");
|
||||
|
||||
@@ -287,9 +287,9 @@ public:
|
||||
// transform a `global.get` to an `i32.const`.
|
||||
void addInternalGOTEntry(Symbol *sym);
|
||||
bool needsRelocations() { return internalGotSymbols.size(); }
|
||||
void generateRelocationCode(raw_ostream &os) const;
|
||||
void generateRelocationCode(raw_ostream &os, bool TLS) const;
|
||||
|
||||
std::vector<const DefinedData *> dataAddressGlobals;
|
||||
std::vector<DefinedData *> dataAddressGlobals;
|
||||
std::vector<InputGlobal *> inputGlobals;
|
||||
std::vector<Symbol *> internalGotSymbols;
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ private:
|
||||
void createStartFunction();
|
||||
void createApplyDataRelocationsFunction();
|
||||
void createApplyGlobalRelocationsFunction();
|
||||
void createApplyGlobalTLSRelocationsFunction();
|
||||
void createCallCtorsFunction();
|
||||
void createInitTLSFunction();
|
||||
void createCommandExportWrappers();
|
||||
@@ -639,12 +640,6 @@ void Writer::calculateExports() {
|
||||
} else if (auto *t = dyn_cast<DefinedTag>(sym)) {
|
||||
export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()};
|
||||
} else if (auto *d = dyn_cast<DefinedData>(sym)) {
|
||||
if (sym->isTLS()) {
|
||||
// We can't currenly export TLS data symbols.
|
||||
if (sym->isExportedExplicit())
|
||||
error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`");
|
||||
continue;
|
||||
}
|
||||
out.globalSec->dataAddressGlobals.push_back(d);
|
||||
export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
|
||||
} else {
|
||||
@@ -991,6 +986,13 @@ void Writer::createSyntheticInitFunctions() {
|
||||
"__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
|
||||
WasmSym::applyGlobalRelocs->markLive();
|
||||
if (config->sharedMemory) {
|
||||
WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction(
|
||||
"__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
make<SyntheticFunction>(nullSignature,
|
||||
"__wasm_apply_global_tls_relocs"));
|
||||
WasmSym::applyGlobalTLSRelocs->markLive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1235,13 +1237,29 @@ void Writer::createApplyGlobalRelocationsFunction() {
|
||||
{
|
||||
raw_string_ostream os(bodyContent);
|
||||
writeUleb128(os, 0, "num locals");
|
||||
out.globalSec->generateRelocationCode(os);
|
||||
out.globalSec->generateRelocationCode(os, false);
|
||||
writeU8(os, WASM_OPCODE_END, "END");
|
||||
}
|
||||
|
||||
createFunction(WasmSym::applyGlobalRelocs, bodyContent);
|
||||
}
|
||||
|
||||
// Similar to createApplyGlobalRelocationsFunction but for
|
||||
// TLS symbols. This cannot be run during the start function
|
||||
// but must be delayed until __wasm_init_tls is called.
|
||||
void Writer::createApplyGlobalTLSRelocationsFunction() {
|
||||
// First write the body's contents to a string.
|
||||
std::string bodyContent;
|
||||
{
|
||||
raw_string_ostream os(bodyContent);
|
||||
writeUleb128(os, 0, "num locals");
|
||||
out.globalSec->generateRelocationCode(os, true);
|
||||
writeU8(os, WASM_OPCODE_END, "END");
|
||||
}
|
||||
|
||||
createFunction(WasmSym::applyGlobalTLSRelocs, bodyContent);
|
||||
}
|
||||
|
||||
// Create synthetic "__wasm_call_ctors" function based on ctor functions
|
||||
// in input object.
|
||||
void Writer::createCallCtorsFunction() {
|
||||
@@ -1352,6 +1370,12 @@ void Writer::createInitTLSFunction() {
|
||||
writeUleb128(os, tlsSeg->index, "segment index immediate");
|
||||
writeU8(os, 0, "memory index immediate");
|
||||
}
|
||||
|
||||
if (WasmSym::applyGlobalTLSRelocs) {
|
||||
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
||||
writeUleb128(os, WasmSym::applyGlobalTLSRelocs->getFunctionIndex(),
|
||||
"function index");
|
||||
}
|
||||
writeU8(os, WASM_OPCODE_END, "end function");
|
||||
}
|
||||
|
||||
@@ -1486,6 +1510,8 @@ void Writer::run() {
|
||||
createApplyDataRelocationsFunction();
|
||||
if (WasmSym::applyGlobalRelocs)
|
||||
createApplyGlobalRelocationsFunction();
|
||||
if (WasmSym::applyGlobalTLSRelocs)
|
||||
createApplyGlobalTLSRelocationsFunction();
|
||||
if (WasmSym::initMemory)
|
||||
createInitMemoryFunction();
|
||||
createStartFunction();
|
||||
|
||||
@@ -36,12 +36,18 @@ struct WasmObjectHeader {
|
||||
uint32_t Version;
|
||||
};
|
||||
|
||||
struct WasmDylinkExport {
|
||||
StringRef Name;
|
||||
uint32_t Flags;
|
||||
};
|
||||
|
||||
struct WasmDylinkInfo {
|
||||
uint32_t MemorySize; // Memory size in bytes
|
||||
uint32_t MemoryAlignment; // P2 alignment of memory
|
||||
uint32_t TableSize; // Table size in elements
|
||||
uint32_t TableAlignment; // P2 alignment of table
|
||||
std::vector<StringRef> Needed; // Shared library dependencies
|
||||
std::vector<WasmDylinkExport> ExportInfo; // Shared library dependencies
|
||||
};
|
||||
|
||||
struct WasmProducerInfo {
|
||||
@@ -345,6 +351,7 @@ enum : unsigned {
|
||||
enum : unsigned {
|
||||
WASM_DYLINK_MEM_INFO = 0x1,
|
||||
WASM_DYLINK_NEEDED = 0x2,
|
||||
WASM_DYLINK_EXPORT_INFO = 0x3,
|
||||
};
|
||||
|
||||
// Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO
|
||||
|
||||
@@ -328,6 +328,7 @@ public:
|
||||
VK_WASM_TLSREL, // Memory address relative to __tls_base
|
||||
VK_WASM_MBREL, // Memory address relative to __memory_base
|
||||
VK_WASM_TBREL, // Table index relative to __table_base
|
||||
VK_WASM_GOT_TLS, // Wasm global index of TLS symbol.
|
||||
|
||||
VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo
|
||||
VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi
|
||||
|
||||
@@ -199,6 +199,11 @@ struct CustomSection : Section {
|
||||
yaml::BinaryRef Payload;
|
||||
};
|
||||
|
||||
struct DylinkExport {
|
||||
StringRef Name;
|
||||
SymbolFlags Flags;
|
||||
};
|
||||
|
||||
struct DylinkSection : CustomSection {
|
||||
DylinkSection() : CustomSection("dylink.0") {}
|
||||
|
||||
@@ -212,6 +217,7 @@ struct DylinkSection : CustomSection {
|
||||
uint32_t TableSize;
|
||||
uint32_t TableAlignment;
|
||||
std::vector<StringRef> Needed;
|
||||
std::vector<DylinkExport> ExportInfo;
|
||||
};
|
||||
|
||||
struct NameSection : CustomSection {
|
||||
@@ -426,6 +432,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ComdatEntry)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Comdat)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Tag)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExport)
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
@@ -574,6 +581,10 @@ template <> struct MappingTraits<WasmYAML::Tag> {
|
||||
static void mapping(IO &IO, WasmYAML::Tag &Tag);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<WasmYAML::DylinkExport> {
|
||||
static void mapping(IO &IO, WasmYAML::DylinkExport &Export);
|
||||
};
|
||||
|
||||
} // end namespace yaml
|
||||
} // end namespace llvm
|
||||
|
||||
|
||||
@@ -358,6 +358,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
|
||||
case VK_WASM_MBREL: return "MBREL";
|
||||
case VK_WASM_TLSREL: return "TLSREL";
|
||||
case VK_WASM_TBREL: return "TBREL";
|
||||
case VK_WASM_GOT_TLS: return "GOT@TLS";
|
||||
case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo";
|
||||
case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi";
|
||||
case VK_AMDGPU_REL32_LO: return "rel32@lo";
|
||||
@@ -499,6 +500,7 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
|
||||
.Case("tbrel", VK_WASM_TBREL)
|
||||
.Case("mbrel", VK_WASM_MBREL)
|
||||
.Case("tlsrel", VK_WASM_TLSREL)
|
||||
.Case("got@tls", VK_WASM_GOT_TLS)
|
||||
.Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO)
|
||||
.Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI)
|
||||
.Case("rel32@lo", VK_AMDGPU_REL32_LO)
|
||||
|
||||
@@ -232,9 +232,14 @@ void MCWasmStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) {
|
||||
|
||||
case MCExpr::SymbolRef: {
|
||||
const MCSymbolRefExpr &symRef = *cast<MCSymbolRefExpr>(expr);
|
||||
if (symRef.getKind() == MCSymbolRefExpr::VK_WASM_TLSREL) {
|
||||
switch (symRef.getKind()) {
|
||||
case MCSymbolRefExpr::VK_WASM_TLSREL:
|
||||
case MCSymbolRefExpr::VK_WASM_GOT_TLS:
|
||||
getAssembler().registerSymbol(symRef.getSymbol());
|
||||
cast<MCSymbolWasm>(symRef.getSymbol()).setTLS();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -565,8 +565,14 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
|
||||
SymA->setUsedInReloc();
|
||||
}
|
||||
|
||||
if (RefA->getKind() == MCSymbolRefExpr::VK_GOT)
|
||||
switch (RefA->getKind()) {
|
||||
case MCSymbolRefExpr::VK_GOT:
|
||||
case MCSymbolRefExpr::VK_WASM_GOT_TLS:
|
||||
SymA->setUsedInGOT();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection);
|
||||
LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n");
|
||||
|
||||
@@ -384,6 +384,13 @@ Error WasmObjectFile::parseDylink0Section(ReadContext &Ctx) {
|
||||
DylinkInfo.Needed.push_back(readString(Ctx));
|
||||
}
|
||||
break;
|
||||
case wasm::WASM_DYLINK_EXPORT_INFO: {
|
||||
uint32_t Count = readVaruint32(Ctx);
|
||||
while (Count--) {
|
||||
DylinkInfo.ExportInfo.push_back({readString(Ctx), readVaruint32(Ctx)});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return make_error<GenericBinaryError>("unknown dylink.0 sub-section",
|
||||
object_error::parse_failed);
|
||||
|
||||
@@ -55,6 +55,7 @@ static void sectionMapping(IO &IO, WasmYAML::DylinkSection &Section) {
|
||||
IO.mapRequired("TableSize", Section.TableSize);
|
||||
IO.mapRequired("TableAlignment", Section.TableAlignment);
|
||||
IO.mapRequired("Needed", Section.Needed);
|
||||
IO.mapRequired("ExportInfo", Section.ExportInfo);
|
||||
}
|
||||
|
||||
static void sectionMapping(IO &IO, WasmYAML::NameSection &Section) {
|
||||
@@ -531,6 +532,12 @@ void MappingTraits<WasmYAML::Tag>::mapping(IO &IO, WasmYAML::Tag &Tag) {
|
||||
IO.mapRequired("SigIndex", Tag.SigIndex);
|
||||
}
|
||||
|
||||
void MappingTraits<WasmYAML::DylinkExport>::mapping(
|
||||
IO &IO, WasmYAML::DylinkExport &Export) {
|
||||
IO.mapRequired("Name", Export.Name);
|
||||
IO.mapRequired("Flags", Export.Flags);
|
||||
}
|
||||
|
||||
void ScalarBitSetTraits<WasmYAML::LimitFlags>::bitset(
|
||||
IO &IO, WasmYAML::LimitFlags &Value) {
|
||||
#define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X)
|
||||
|
||||
@@ -155,8 +155,12 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
|
||||
break;
|
||||
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
|
||||
case wasm::WASM_SYMBOL_TYPE_DATA:
|
||||
if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) {
|
||||
switch (SymRef->getKind()) {
|
||||
case MCSymbolRefExpr::VK_GOT:
|
||||
case MCSymbolRefExpr::VK_WASM_GOT_TLS:
|
||||
Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32;
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
|
||||
@@ -95,6 +95,9 @@ enum TOF {
|
||||
// platforms.
|
||||
MO_GOT,
|
||||
|
||||
// Same as MO_GOT but the address stored in the global is a TLS address.
|
||||
MO_GOT_TLS,
|
||||
|
||||
// On a symbol operand this indicates that the immediate is the symbol
|
||||
// address relative the __memory_base wasm global.
|
||||
// Only applicable to data symbols.
|
||||
|
||||
@@ -74,6 +74,7 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
|
||||
|
||||
switch (Modifier) {
|
||||
case MCSymbolRefExpr::VK_GOT:
|
||||
case MCSymbolRefExpr::VK_WASM_GOT_TLS:
|
||||
return wasm::R_WASM_GLOBAL_INDEX_LEB;
|
||||
case MCSymbolRefExpr::VK_WASM_TBREL:
|
||||
assert(SymA.isFunction());
|
||||
@@ -88,7 +89,10 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
|
||||
: wasm::R_WASM_MEMORY_ADDR_REL_SLEB;
|
||||
case MCSymbolRefExpr::VK_WASM_TYPEINDEX:
|
||||
return wasm::R_WASM_TYPE_INDEX_LEB;
|
||||
case MCSymbolRefExpr::VK_None:
|
||||
break;
|
||||
default:
|
||||
report_fatal_error("unknown VariantKind");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1539,7 +1539,6 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
SDLoc DL(Op);
|
||||
const auto *GA = cast<GlobalAddressSDNode>(Op);
|
||||
MVT PtrVT = getPointerTy(DAG.getDataLayout());
|
||||
|
||||
MachineFunction &MF = DAG.getMachineFunction();
|
||||
if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
|
||||
@@ -1561,21 +1560,43 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
|
||||
false);
|
||||
}
|
||||
|
||||
auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
|
||||
: WebAssembly::GLOBAL_GET_I32;
|
||||
const char *BaseName = MF.createExternalSymbolName("__tls_base");
|
||||
auto model = GV->getThreadLocalMode();
|
||||
|
||||
SDValue BaseAddr(
|
||||
DAG.getMachineNode(GlobalGet, DL, PtrVT,
|
||||
DAG.getTargetExternalSymbol(BaseName, PtrVT)),
|
||||
0);
|
||||
// Unsupported TLS modes
|
||||
assert(model != GlobalValue::NotThreadLocal);
|
||||
assert(model != GlobalValue::InitialExecTLSModel);
|
||||
|
||||
SDValue TLSOffset = DAG.getTargetGlobalAddress(
|
||||
GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
|
||||
SDValue SymOffset =
|
||||
DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
|
||||
if (model == GlobalValue::LocalExecTLSModel ||
|
||||
model == GlobalValue::LocalDynamicTLSModel ||
|
||||
(model == GlobalValue::GeneralDynamicTLSModel &&
|
||||
getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV))) {
|
||||
// For DSO-local TLS variables we use offset from __tls_base
|
||||
|
||||
return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
|
||||
MVT PtrVT = getPointerTy(DAG.getDataLayout());
|
||||
auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
|
||||
: WebAssembly::GLOBAL_GET_I32;
|
||||
const char *BaseName = MF.createExternalSymbolName("__tls_base");
|
||||
|
||||
SDValue BaseAddr(
|
||||
DAG.getMachineNode(GlobalGet, DL, PtrVT,
|
||||
DAG.getTargetExternalSymbol(BaseName, PtrVT)),
|
||||
0);
|
||||
|
||||
SDValue TLSOffset = DAG.getTargetGlobalAddress(
|
||||
GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
|
||||
SDValue SymOffset =
|
||||
DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
|
||||
|
||||
return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
|
||||
}
|
||||
|
||||
assert(model == GlobalValue::GeneralDynamicTLSModel);
|
||||
|
||||
EVT VT = Op.getValueType();
|
||||
return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
|
||||
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
|
||||
GA->getOffset(),
|
||||
WebAssemblyII::MO_GOT_TLS));
|
||||
}
|
||||
|
||||
SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
|
||||
|
||||
@@ -398,6 +398,11 @@ def : Pat<(i32 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
|
||||
def : Pat<(i64 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
|
||||
(CONST_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
|
||||
|
||||
def : Pat<(i32 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
|
||||
(GLOBAL_GET_I32 tglobaltlsaddr:$addr)>, Requires<[HasAddr32]>;
|
||||
def : Pat<(i64 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
|
||||
(GLOBAL_GET_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
|
||||
|
||||
def : Pat<(i32 (WebAssemblyWrapper texternalsym:$addr)),
|
||||
(GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC, HasAddr32]>;
|
||||
def : Pat<(i64 (WebAssemblyWrapper texternalsym:$addr)),
|
||||
|
||||
@@ -102,6 +102,9 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
|
||||
switch (TargetFlags) {
|
||||
case WebAssemblyII::MO_NO_FLAG:
|
||||
break;
|
||||
case WebAssemblyII::MO_GOT_TLS:
|
||||
Kind = MCSymbolRefExpr::VK_WASM_GOT_TLS;
|
||||
break;
|
||||
case WebAssemblyII::MO_GOT:
|
||||
Kind = MCSymbolRefExpr::VK_GOT;
|
||||
break;
|
||||
|
||||
@@ -20,6 +20,17 @@ define i32 @address_of_tls() {
|
||||
ret i32 ptrtoint(i32* @tls to i32)
|
||||
}
|
||||
|
||||
; CHECK-LABEL: address_of_tls_external:
|
||||
; CHECK-NEXT: .functype address_of_tls_external () -> (i32)
|
||||
define i32 @address_of_tls_external() {
|
||||
; TLS-DAG: global.get tls_external@GOT@TLS
|
||||
; TLS-NEXT: return
|
||||
|
||||
; NO-TLS-NEXT: i32.const tls_external
|
||||
; NO-TLS-NEXT: return
|
||||
ret i32 ptrtoint(i32* @tls_external to i32)
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ptr_to_tls:
|
||||
; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
|
||||
define i32* @ptr_to_tls() {
|
||||
@@ -33,6 +44,17 @@ define i32* @ptr_to_tls() {
|
||||
ret i32* @tls
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ptr_to_tls_external:
|
||||
; CHECK-NEXT: .functype ptr_to_tls_external () -> (i32)
|
||||
define i32* @ptr_to_tls_external() {
|
||||
; TLS-DAG: global.get tls_external@GOT@TLS
|
||||
; TLS-NEXT: return
|
||||
|
||||
; NO-TLS-NEXT: i32.const tls_external
|
||||
; NO-TLS-NEXT: return
|
||||
ret i32* @tls_external
|
||||
}
|
||||
|
||||
; CHECK-LABEL: tls_load:
|
||||
; CHECK-NEXT: .functype tls_load () -> (i32)
|
||||
define i32 @tls_load() {
|
||||
@@ -49,6 +71,20 @@ define i32 @tls_load() {
|
||||
ret i32 %tmp
|
||||
}
|
||||
|
||||
; CHECK-LABEL: tls_load_external:
|
||||
; CHECK-NEXT: .functype tls_load_external () -> (i32)
|
||||
define i32 @tls_load_external() {
|
||||
; TLS-DAG: global.get tls_external@GOT@TLS
|
||||
; TLS-NEXT: i32.load 0
|
||||
; TLS-NEXT: return
|
||||
|
||||
; NO-TLS-NEXT: i32.const 0
|
||||
; NO-TLS-NEXT: i32.load tls_external
|
||||
; NO-TLS-NEXT: return
|
||||
%tmp = load i32, i32* @tls_external, align 4
|
||||
ret i32 %tmp
|
||||
}
|
||||
|
||||
; CHECK-LABEL: tls_store:
|
||||
; CHECK-NEXT: .functype tls_store (i32) -> ()
|
||||
define void @tls_store(i32 %x) {
|
||||
@@ -65,6 +101,20 @@ define void @tls_store(i32 %x) {
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: tls_store_external:
|
||||
; CHECK-NEXT: .functype tls_store_external (i32) -> ()
|
||||
define void @tls_store_external(i32 %x) {
|
||||
; TLS-DAG: global.get tls_external@GOT@TLS
|
||||
; TLS-NEXT: i32.store 0
|
||||
; TLS-NEXT: return
|
||||
|
||||
; NO-TLS-NEXT: i32.const 0
|
||||
; NO-TLS-NEXT: i32.store tls_external
|
||||
; NO-TLS-NEXT: return
|
||||
store i32 %x, i32* @tls_external, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: tls_size:
|
||||
; CHECK-NEXT: .functype tls_size () -> (i32)
|
||||
define i32 @tls_size() {
|
||||
@@ -111,6 +161,8 @@ define void @tls_base_write(i8** %output) {
|
||||
; CHECK-NEXT: .int32 0
|
||||
@tls = internal thread_local global i32 0
|
||||
|
||||
@tls_external = external thread_local global i32, align 4
|
||||
|
||||
declare i32 @llvm.wasm.tls.size.i32()
|
||||
declare i32 @llvm.wasm.tls.align.i32()
|
||||
declare i8* @llvm.wasm.tls.base()
|
||||
|
||||
@@ -16,6 +16,19 @@ define i32 @address_of_tls() {
|
||||
ret i32 ptrtoint(i32* @tls to i32)
|
||||
}
|
||||
|
||||
; CHECK-LABEL: address_of_tls_external:
|
||||
; CHECK-NEXT: .functype address_of_tls_external () -> (i32)
|
||||
define i32 @address_of_tls_external() {
|
||||
; TLS-DAG: global.get __tls_base
|
||||
; TLS-DAG: i32.const tls_external@TLSREL
|
||||
; TLS-NEXT: i32.add
|
||||
; TLS-NEXT: return
|
||||
|
||||
; NO-TLS-NEXT: i32.const tls_external
|
||||
; NO-TLS-NEXT: return
|
||||
ret i32 ptrtoint(i32* @tls_external to i32)
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ptr_to_tls:
|
||||
; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
|
||||
define i32* @ptr_to_tls() {
|
||||
@@ -78,4 +91,6 @@ define i32 @tls_size() {
|
||||
; CHECK-NEXT: .int32 0
|
||||
@tls = internal thread_local(localexec) global i32 0
|
||||
|
||||
@tls_external = external thread_local(localexec) global i32, align 4
|
||||
|
||||
declare i32 @llvm.wasm.tls.size.i32()
|
||||
|
||||
@@ -20,7 +20,7 @@ tls_store:
|
||||
|
||||
tls_get_undefined:
|
||||
.functype tls_get_undefined (i32) -> (i32)
|
||||
i32.const tls_undefined@TLSREL
|
||||
global.get tls_undefined@GOT@TLS
|
||||
end_function
|
||||
|
||||
.section .tls.foo,"T",@
|
||||
@@ -43,6 +43,9 @@ tls2:
|
||||
# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_TLS_SLEB
|
||||
# CHECK-OBJ-NEXT: Index: 2
|
||||
# CHECK-OBJ-NEXT: Offset: 0xA
|
||||
# CHECK-OBJ-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
|
||||
# CHECK-OBJ-NEXT: Index: 4
|
||||
# CHECK-OBJ-NEXT: Offset: 0x19
|
||||
|
||||
# CHECK-OBJ: - Type: CUSTOM
|
||||
# CHECK-OBJ-NEXT: Name: linking
|
||||
|
||||
@@ -60,6 +60,8 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
|
||||
DylinkSec->TableSize = Info.TableSize;
|
||||
DylinkSec->TableAlignment = Info.TableAlignment;
|
||||
DylinkSec->Needed = Info.Needed;
|
||||
for (const auto &Exp : Info.ExportInfo)
|
||||
DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags});
|
||||
CustomSec = std::move(DylinkSec);
|
||||
} else if (WasmSec.Name == "name") {
|
||||
std::unique_ptr<WasmYAML::NameSection> NameSec =
|
||||
|
||||
Reference in New Issue
Block a user