[WebAssembly] Allow import and export of TLS symbols between DSOs
authorSam Clegg <sbc@chromium.org>
Thu, 26 Aug 2021 19:29:32 +0000 (15:29 -0400)
committerSam Clegg <sbc@chromium.org>
Tue, 14 Sep 2021 13:47:37 +0000 (06:47 -0700)
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

31 files changed:
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-import.s [deleted file]
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

index ee2f027..0dba0aa 100644 (file)
@@ -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
 
index 12c4597..10ac408 100644 (file)
@@ -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
index 7861485..a3f82ce 100644 (file)
@@ -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
index 86d5a52..42fc829 100644 (file)
@@ -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
index b8a36aa..619f9d2 100644 (file)
@@ -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 (file)
index cce45f5..0000000
+++ /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@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"
index 38f95e5..222db77 100644 (file)
@@ -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
index 06d7f5d..a6128e2 100644 (file)
@@ -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:
index fd13b70..319d60c 100644 (file)
@@ -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;
index 415881e..eebe4fd 100644 (file)
@@ -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;
index 3e6e9fe..549af5b 100644 (file)
@@ -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");
index d26f89b..421bbab 100644 (file)
@@ -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;
 
index 2a81fd3..41d26e9 100644 (file)
@@ -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();
index 966dc1b..736280c 100644 (file)
@@ -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
index 38cca24..d16e917 100644 (file)
@@ -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
index 8b827bf..e1b4883 100644 (file)
@@ -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
 
index 84ec0f6..bfe393e 100644 (file)
@@ -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)
index 47951bd..54e0b79 100644 (file)
@@ -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;
   }
index 2d16c37..25400c3 100644 (file)
@@ -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");
index 3b1392e..300d08f 100644 (file)
@@ -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);
index b798987..59da568 100644 (file)
@@ -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)
index ce8a15b..ea58292 100644 (file)
@@ -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;
index 99defb4..d07bfce 100644 (file)
@@ -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.
index f67fab9..4057129 100644 (file)
@@ -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;
   }
 
index 622fdfb..b5dad02 100644 (file)
@@ -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,
index f6adb27..c61a9cd 100644 (file)
@@ -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)),
index 4d5d6bd..8c6f571 100644 (file)
@@ -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;
index 285a44e..b6ede2b 100644 (file)
@@ -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()
index f863c70..583f15a 100644 (file)
@@ -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()
index fc7c7c1..340cd5a 100644 (file)
@@ -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
index 9555cb2..0cbf021 100644 (file)
@@ -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 =