[lld] ef8c913 - [WebAssembly] Allow import and export of TLS symbols between DSOs
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 14 06:47:54 PDT 2021
Author: Sam Clegg
Date: 2021-09-14T06:47:37-07:00
New Revision: ef8c9135efcb3847fc0e5bbdb55eae18751090df
URL: https://github.com/llvm/llvm-project/commit/ef8c9135efcb3847fc0e5bbdb55eae18751090df
DIFF: https://github.com/llvm/llvm-project/commit/ef8c9135efcb3847fc0e5bbdb55eae18751090df.diff
LOG: [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
Added:
Modified:
lld/test/wasm/pie.ll
lld/test/wasm/shared-needed.s
lld/test/wasm/shared.s
lld/test/wasm/shared64.s
lld/test/wasm/tls-export.s
lld/test/wasm/tls-non-shared-memory.s
lld/wasm/Relocations.cpp
lld/wasm/Symbols.cpp
lld/wasm/Symbols.h
lld/wasm/SyntheticSections.cpp
lld/wasm/SyntheticSections.h
lld/wasm/Writer.cpp
llvm/include/llvm/BinaryFormat/Wasm.h
llvm/include/llvm/MC/MCExpr.h
llvm/include/llvm/ObjectYAML/WasmYAML.h
llvm/lib/MC/MCExpr.cpp
llvm/lib/MC/MCWasmStreamer.cpp
llvm/lib/MC/WasmObjectWriter.cpp
llvm/lib/Object/WasmObjectFile.cpp
llvm/lib/ObjectYAML/WasmYAML.cpp
llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
llvm/test/MC/WebAssembly/tls.s
llvm/tools/obj2yaml/wasm2yaml.cpp
Removed:
lld/test/wasm/tls-import.s
################################################################################
diff --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll
index ee2f02777fda9..0dba0aab02d6f 100644
--- a/lld/test/wasm/pie.ll
+++ b/lld/test/wasm/pie.ll
@@ -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
diff --git a/lld/test/wasm/shared-needed.s b/lld/test/wasm/shared-needed.s
index 12c4597190a3b..10ac40855d240 100644
--- a/lld/test/wasm/shared-needed.s
+++ b/lld/test/wasm/shared-needed.s
@@ -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
diff --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s
index 7861485470ffa..a3f82cebad2b8 100644
--- a/lld/test/wasm/shared.s
+++ b/lld/test/wasm/shared.s
@@ -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
diff --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s
index 86d5a521ab334..42fc8298a6da4 100644
--- a/lld/test/wasm/shared64.s
+++ b/lld/test/wasm/shared64.s
@@ -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
diff --git a/lld/test/wasm/tls-export.s b/lld/test/wasm/tls-export.s
index b8a36aa55aa1b..619f9d2df312a 100644
--- a/lld/test/wasm/tls-export.s
+++ b/lld/test/wasm/tls-export.s
@@ -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
diff --git a/lld/test/wasm/tls-import.s b/lld/test/wasm/tls-import.s
deleted file mode 100644
index cce45f5fbaac5..0000000000000
--- a/lld/test/wasm/tls-import.s
+++ /dev/null
@@ -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 at 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"
diff --git a/lld/test/wasm/tls-non-shared-memory.s b/lld/test/wasm/tls-non-shared-memory.s
index 38f95e5310b77..222db777df29c 100644
--- a/lld/test/wasm/tls-non-shared-memory.s
+++ b/lld/test/wasm/tls-non-shared-memory.s
@@ -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 at 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
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 06d7f5d6752b5..a6128e2443fcb 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -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:
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index fd13b70f8c6da..319d60c6a81d4 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -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;
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 415881e0c1982..eebe4fd573c7e 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -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;
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 3e6e9fe56d9f9..549af5b3b460a 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -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");
diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index d26f89b6fd5f1..421bbabe0e1b2 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -287,9 +287,9 @@ class GlobalSection : public SyntheticSection {
// 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;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 2a81fd31bf7e3..41d26e98ec148 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -63,6 +63,7 @@ class Writer {
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();
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 966dc1bbc886d..736280cfc602b 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -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
diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h
index 38cca2413e1eb..d16e917a2d5b4 100644
--- a/llvm/include/llvm/MC/MCExpr.h
+++ b/llvm/include/llvm/MC/MCExpr.h
@@ -328,6 +328,7 @@ class MCSymbolRefExpr : public MCExpr {
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 at gotpcrel32@lo
VK_AMDGPU_GOTPCREL32_HI, // symbol at gotpcrel32@hi
diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h
index 8b827bf7f408c..e1b48835dbd6f 100644
--- a/llvm/include/llvm/ObjectYAML/WasmYAML.h
+++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h
@@ -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
diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
index 84ec0f6bb57ba..bfe393e975cac 100644
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -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 at TLS";
case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32 at lo";
case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32 at hi";
case VK_AMDGPU_REL32_LO: return "rel32 at 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 at tls", VK_WASM_GOT_TLS)
.Case("gotpcrel32 at lo", VK_AMDGPU_GOTPCREL32_LO)
.Case("gotpcrel32 at hi", VK_AMDGPU_GOTPCREL32_HI)
.Case("rel32 at lo", VK_AMDGPU_REL32_LO)
diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp
index 47951bd5e8726..54e0b79f2b8ef 100644
--- a/llvm/lib/MC/MCWasmStreamer.cpp
+++ b/llvm/lib/MC/MCWasmStreamer.cpp
@@ -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;
}
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 2d16c3747180f..25400c31b95cf 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -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");
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 3b1392e418edc..300d08fd39d7c 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -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);
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index b798987e28905..59da56891f66a 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -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)
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
index ce8a15bfd9e3c..ea58292199383 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
@@ -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;
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 99defb42e3807..d07bfce9abc16 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -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.
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index f67fab9467467..405712906c400 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -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;
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 622fdfb47b594..b5dad0257da44 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -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,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index f6adb279aa307..c61a9cd0b967f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -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)),
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index 4d5d6bd06b21e..8c6f57168341d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -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;
diff --git a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
index 285a44e9197ce..b6ede2bb97fe2 100644
--- a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
+++ b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
@@ -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 at 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 at 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 at 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 at 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
+ at 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()
diff --git a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
index f863c70be64ad..583f15a4fabde 100644
--- a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
+++ b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
@@ -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 at 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
+ at tls_external = external thread_local(localexec) global i32, align 4
+
declare i32 @llvm.wasm.tls.size.i32()
diff --git a/llvm/test/MC/WebAssembly/tls.s b/llvm/test/MC/WebAssembly/tls.s
index fc7c7c1b28b4d..340cd5ac6a183 100644
--- a/llvm/test/MC/WebAssembly/tls.s
+++ b/llvm/test/MC/WebAssembly/tls.s
@@ -20,7 +20,7 @@ tls_store:
tls_get_undefined:
.functype tls_get_undefined (i32) -> (i32)
- i32.const tls_undefined at TLSREL
+ global.get tls_undefined at 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
diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp
index 9555cb2dea5be..0cbf02118818f 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -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 =
More information about the llvm-commits
mailing list