[lld] 29a3056 - [lld][WebAssembly] Allow references to __tls_base without shared memory

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 10 18:07:14 PST 2020


Author: Sam Clegg
Date: 2020-11-10T17:58:06-08:00
New Revision: 29a3056bb5346d9b38e4dcb13700576efbff9425

URL: https://github.com/llvm/llvm-project/commit/29a3056bb5346d9b38e4dcb13700576efbff9425
DIFF: https://github.com/llvm/llvm-project/commit/29a3056bb5346d9b38e4dcb13700576efbff9425.diff

LOG: [lld][WebAssembly] Allow references to __tls_base without shared memory

Previously we limited the use of atomics and TLS to programs
linked with `--shared-memory`.

However, as of https://reviews.llvm.org/D79530 we now allow
programs that use atomic to be linked without `--shared-memory`.
For this to be useful we also want to all TLS usage in such
programs.  In this case, since we know we are single threaded
we simply include the TLS data as a regular active segment
and create an immutable `__tls_base` global that point to the
start of this segment.

Fixes: https://github.com/emscripten-core/emscripten/issues/12489

Differential Revision: https://reviews.llvm.org/D91115

Added: 
    lld/test/wasm/tls-no-shared.s

Modified: 
    lld/test/wasm/no-tls.s
    lld/wasm/Driver.cpp
    lld/wasm/SymbolTable.cpp
    lld/wasm/SymbolTable.h
    lld/wasm/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/test/wasm/no-tls.s b/lld/test/wasm/no-tls.s
index d1ba7663503f..74b9331eeb19 100644
--- a/lld/test/wasm/no-tls.s
+++ b/lld/test/wasm/no-tls.s
@@ -52,4 +52,4 @@ _start:
 # CHECK-NEXT:        Mutable:         false
 # CHECK-NEXT:        InitExpr:
 # CHECK-NEXT:          Opcode:          I32_CONST
-# CHECK-NEXT:          Value:           1
+# CHECK-NEXT:          Value:           0

diff  --git a/lld/test/wasm/tls-no-shared.s b/lld/test/wasm/tls-no-shared.s
new file mode 100644
index 000000000000..3fdc7057ad07
--- /dev/null
+++ b/lld/test/wasm/tls-no-shared.s
@@ -0,0 +1,75 @@
+# Test that linking without shared memory causes __tls_base to be
+# interlized
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+
+.globaltype __tls_base, i32
+
+.globl get_tls1
+get_tls1:
+  .functype get_tls1 () -> (i32)
+  global.get __tls_base
+  i32.const tls1
+  i32.add
+  end_function
+
+.section  .data.no_tls,"",@
+.globl  no_tls
+.p2align  2
+no_tls:
+  .int32  42
+  .size no_tls, 4
+
+.section  .tdata.tls1,"",@
+.globl  tls1
+.p2align  2
+tls1:
+  .int32  43
+  .size tls1, 2
+
+.section  .custom_section.target_features,"",@
+  .int8 2
+  .int8 43
+  .int8 7
+  .ascii  "atomics"
+  .int8 43
+  .int8 11
+  .ascii  "bulk-memory"
+
+# RUN: wasm-ld --no-gc-sections --no-entry -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+#      CHECK:   - Type:            GLOBAL
+# __stack_pointer
+# CHECK-NEXT:     Globals:
+# CHECK-NEXT:       - Index:           0
+# CHECK-NEXT:         Type:            I32
+# CHECK-NEXT:         Mutable:         true
+# CHECK-NEXT:         InitExpr:
+# CHECK-NEXT:           Opcode:          I32_CONST
+# CHECK-NEXT:           Value:           66576
+# __tls_base
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         Type:            I32
+# CHECK-NEXT:         Mutable:         false
+# CHECK-NEXT:         InitExpr:
+# CHECK-NEXT:           Opcode:          I32_CONST
+# CHECK-NEXT:           Value:           1028
+# CHECK-NEXT:   - Type:            EXPORT
+
+#      CHECK:  - Type:            DATA
+# .data
+# CHECK-NEXT:    Segments:
+# CHECK-NEXT:      - SectionOffset:   7
+# CHECK-NEXT:        InitFlags:       0
+# CHECK-NEXT:        Offset:
+# CHECK-NEXT:          Opcode:          I32_CONST
+# CHECK-NEXT:          Value:           1024
+# CHECK-NEXT:        Content:         2A000000
+# .tdata
+# CHECK-NEXT:      - SectionOffset:   17
+# CHECK-NEXT:        InitFlags:       0
+# CHECK-NEXT:        Offset:
+# CHECK-NEXT:          Opcode:          I32_CONST
+# CHECK-NEXT:          Value:           1028
+# CHECK-NEXT:        Content:         2B000000

diff  --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index fb003558d1c9..5b12b48b0be6 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -541,21 +541,30 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
   return sym;
 }
 
-static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable,
-                                          int value) {
+static InputGlobal *createGlobal(StringRef name, bool isMutable) {
   llvm::wasm::WasmGlobal wasmGlobal;
   if (config->is64.getValueOr(false)) {
     wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
-    wasmGlobal.InitExpr.Value.Int64 = value;
     wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
+    wasmGlobal.InitExpr.Value.Int64 = 0;
   } else {
     wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
-    wasmGlobal.InitExpr.Value.Int32 = value;
     wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+    wasmGlobal.InitExpr.Value.Int32 = 0;
   }
   wasmGlobal.SymbolName = name;
-  return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
-                                    make<InputGlobal>(wasmGlobal, nullptr));
+  return make<InputGlobal>(wasmGlobal, nullptr);
+}
+
+static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) {
+  InputGlobal *g = createGlobal(name, isMutable);
+  return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN, g);
+}
+
+static GlobalSymbol *createOptionalGlobal(StringRef name, bool isMutable) {
+  InputGlobal *g = createGlobal(name, isMutable);
+  return symtab->addOptionalGlobalSymbols(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
+                                          g);
 }
 
 // Create ABI-defined synthetic symbols
@@ -602,7 +611,7 @@ static void createSyntheticSymbols() {
     WasmSym::tableBase->markLive();
   } else {
     // For non-PIC code
-    WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true, 0);
+    WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
     WasmSym::stackPointer->markLive();
   }
 
@@ -616,9 +625,9 @@ static void createSyntheticSymbols() {
     WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol(
         "__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN);
     assert(WasmSym::initMemoryFlag);
-    WasmSym::tlsBase = createGlobalVariable("__tls_base", true, 0);
-    WasmSym::tlsSize = createGlobalVariable("__tls_size", false, 0);
-    WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1);
+    WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
+    WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
+    WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
     WasmSym::initTLS = symtab->addSyntheticFunction(
         "__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
         make<SyntheticFunction>(
@@ -642,6 +651,18 @@ static void createOptionalSymbols() {
     WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
     WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
   }
+
+  // For non-shared memory programs we still need to define __tls_base since we
+  // allow object files built with TLS to be linked into single threaded
+  // programs, and such object files can contains refernced to this symbol.
+  //
+  // However, in this case __tls_base is immutable and points directly to the
+  // start of the `.tdata` static segment.
+  //
+  // __tls_size and __tls_align are not needed in this case since they are only
+  // needed for __wasm_init_tls (which we do not create in this case).
+  if (!config->sharedMemory)
+    WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
 }
 
 // Reconstructs command line arguments so that so that you can re-run

diff  --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index cb152d5fe8ef..ce119851dc32 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -206,7 +206,7 @@ DefinedFunction *SymbolTable::addSyntheticFunction(StringRef name,
                                         flags, nullptr, function);
 }
 
-// Adds an optional, linker generated, data symbols.  The symbol will only be
+// Adds an optional, linker generated, data symbol.  The symbol will only be
 // added if there is an undefine reference to it, or if it is explicitly
 // exported via the --export flag.  Otherwise we don't add the symbol and return
 // nullptr.
@@ -241,6 +241,18 @@ DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
                                       nullptr, global);
 }
 
+DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name,
+                                                     uint32_t flags,
+                                                     InputGlobal *global) {
+  LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbols: " << name << " -> " << global
+                    << "\n");
+  Symbol *s = find(name);
+  if (!s || s->isDefined())
+    return nullptr;
+  syntheticGlobals.emplace_back(global);
+  return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global);
+}
+
 static bool shouldReplace(const Symbol *existing, InputFile *newFile,
                           uint32_t newFlags) {
   // If existing symbol is undefined, replace it.

diff  --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index fe26f7057ae3..08978d338b63 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -83,6 +83,8 @@ class SymbolTable {
   DefinedFunction *addSyntheticFunction(StringRef name, uint32_t flags,
                                         InputFunction *function);
   DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
+  DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags,
+                                          InputGlobal *global);
 
   void handleSymbolVariants();
   void handleWeakUndefines();

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 975f173c500c..744c217965d4 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -204,6 +204,16 @@ void Writer::writeSections() {
   });
 }
 
+static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
+  if (config->is64.getValueOr(false)) {
+    assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I64_CONST);
+    g->global->global.InitExpr.Value.Int64 = memoryPtr;
+  } else {
+    assert(g->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
+    g->global->global.InitExpr.Value.Int32 = memoryPtr;
+  }
+}
+
 // Fix the memory layout of the output binary.  This assigns memory offsets
 // to each of the input data sections as well as the explicit stack region.
 // The default memory layout is as follows, from low to high.
@@ -267,18 +277,21 @@ void Writer::layoutMemory() {
     seg->startVA = memoryPtr;
     log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
                 memoryPtr, seg->size, seg->alignment));
-    memoryPtr += seg->size;
 
-    if (WasmSym::tlsSize && seg->name == ".tdata") {
-      auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
-      assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
-      tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
+    if (seg->name == ".tdata") {
+      if (config->sharedMemory) {
+        auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
+        setGlobalPtr(tlsSize, seg->size);
 
-      auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
-      assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
-      tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1}
-                                                      << seg->alignment;
+        auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
+        setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
+      } else {
+        auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
+        setGlobalPtr(tlsBase, memoryPtr);
+      }
     }
+
+    memoryPtr += seg->size;
   }
 
   // Make space for the memory initialization flag
@@ -768,7 +781,7 @@ void Writer::createOutputSegments() {
       if (s == nullptr) {
         LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
         s = make<OutputSegment>(name);
-        if (config->sharedMemory || name == ".tdata")
+        if (config->sharedMemory)
           s->initFlags = WASM_SEGMENT_IS_PASSIVE;
         // Exported memories are guaranteed to be zero-initialized, so no need
         // to emit data segments for bss sections.


        


More information about the llvm-commits mailing list