quantum created this revision.
quantum added reviewers: tlively, aheejin, kripken, sbc100.
Herald added subscribers: llvm-commits, cfe-commits, jfb, sunfish, hiraditya, 
jgravelle-google, dschuff.
Herald added projects: clang, LLVM.
quantum planned changes to this revision.
quantum added a comment.

Need to add tests for the compiler intrinsic `__builtin_wasm_tls_size`.


Thread local variables are placed inside a `.tdata` segment. Their symbols are
offsets from the start of the segment. The address of a thread local variable
is computed as `__tls_base` + the offset from the start of the segment.

`.tdata` segment is a passive segment and `memory.init` is used once per thread
to initialize the thread local storage.

`__tls_base` is a wasm global. Since each thread has its own wasm instance,
it is effectively thread local. Currently, `__tls_base` must be initialized
at thread startup, and so cannot be used with dynamic libraries.

`__tls_base` is to be initialized with a new linker-synthesized function,
`__wasm_init_tls`, which takes as an argument a block of memory to use as the
storage for thread locals. It then initializes the block of memory and sets
`__tls_base`.

To help allocating memory for thread-local storage, a new compiler intrinsic
is introduced: `__builtin_wasm_tls_size()`. This instrinsic function returns
the size of the thread-local storage for the current function.

The expected usage is to run something like the following upon thread startup:

  __wasm_init_tls(calloc(__builtin_wasm_tls_size(), 1));


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D64537

Files:
  clang/include/clang/Basic/BuiltinsWebAssembly.def
  clang/lib/CodeGen/CGBuiltin.cpp
  lld/test/wasm/data-layout.ll
  lld/test/wasm/gc-sections.ll
  lld/test/wasm/tls.ll
  lld/wasm/Driver.cpp
  lld/wasm/Symbols.cpp
  lld/wasm/Symbols.h
  lld/wasm/Writer.cpp
  llvm/include/llvm/BinaryFormat/Wasm.h
  llvm/include/llvm/IR/IntrinsicsWebAssembly.td
  llvm/include/llvm/MC/MCSectionWasm.h
  llvm/lib/MC/WasmObjectWriter.cpp
  llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
  llvm/lib/Target/WebAssembly/WebAssemblyISD.def
  llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
  llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
  llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
  llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
  llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
  llvm/test/CodeGen/WebAssembly/target-features-tls.ll
  llvm/test/CodeGen/WebAssembly/tls.ll

Index: llvm/test/CodeGen/WebAssembly/tls.ll
===================================================================
--- llvm/test/CodeGen/WebAssembly/tls.ll
+++ llvm/test/CodeGen/WebAssembly/tls.ll
@@ -1,17 +1,77 @@
-; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck --check-prefix=SINGLE %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals | FileCheck --check-prefixes=CHECK,O2 %s
+; RUN: llc -O0 < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals | FileCheck --check-prefixes=CHECK,O0 %s
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
-; SINGLE-LABEL: address_of_tls:
+; O0-LABEL: address_of_tls:
+; O2-LABEL: address_of_tls:
 define i32 @address_of_tls() {
-  ; SINGLE: i32.const $push0=, tls
-  ; SINGLE-NEXT: return $pop0
+  ; O0: i32.const tls
+  ; O0-NEXT: global.get __tls_base
+  ; O0-NEXT: i32.add
+  ; O0-NEXT: return
+
+  ; O2: global.get __tls_base
+  ; O2-NEXT: i32.const tls
+  ; O2-NEXT: i32.add
+  ; O2-NEXT: return
   ret i32 ptrtoint(i32* @tls to i32)
 }
 
-; SINGLE: .type	tls,@object
-; SINGLE-NEXT: .section	.bss.tls,"",@
-; SINGLE-NEXT: .p2align 2
-; SINGLE-NEXT: tls:
-; SINGLE-NEXT: .int32 0
+; O0-LABEL: ptr_to_tls
+; O2-LABEL: ptr_to_tls
+define i32* @ptr_to_tls() {
+  ; O0: i32.const tls
+  ; O0-NEXT: global.get __tls_base
+  ; O0-NEXT: i32.add
+  ; O0-NEXT: return
+
+  ; O2: global.get __tls_base
+  ; O2-NEXT: i32.const tls
+  ; O2-NEXT: i32.add
+  ; O2-NEXT: return
+  ret i32* @tls
+}
+
+; O0-LABEL: tls_load
+; O2-LABEL: tls_load
+define i32 @tls_load() {
+  ; O0: i32.const tls
+  ; O0-NEXT: global.get __tls_base
+  ; O0-NEXT: i32.add
+  ; O0-NEXT: i32.load 0
+  ; O0-NEXT: return
+
+  ; O2: global.get __tls_base
+  ; O2-NEXT: i32.const tls
+  ; O2-NEXT: i32.add
+  ; O2-NEXT: i32.load 0
+  ; O2-NEXT: return
+  %tmp = load i32, i32* @tls, align 4
+  ret i32 %tmp
+}
+
+; O0-LABEL: tls_store
+; O2-LABEL: tls_store
+define void @tls_store(i32 %x) {
+  ; O0: i32.const tls
+  ; O0-NEXT: global.get __tls_base
+  ; O0-NEXT: i32.add
+  ; O0-NEXT: i32.store 0
+  ; O0-NEXT: return
+
+  ; O2: global.get __tls_base
+  ; O2-NEXT: i32.const tls
+  ; O2-NEXT: i32.add
+  ; O2-NEXT: i32.store 0
+  ; O2-NEXT: return
+  store i32 %x, i32* @tls, align 4
+  ret void
+}
+
+; CHECK: .type tls,@object
+; CHECK-NEXT: .section .tbss.tls,"",@
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: tls:
+; CHECK-NEXT: .int32 0
 @tls = internal thread_local global i32 0
Index: llvm/test/CodeGen/WebAssembly/target-features-tls.ll
===================================================================
--- llvm/test/CodeGen/WebAssembly/target-features-tls.ll
+++ llvm/test/CodeGen/WebAssembly/target-features-tls.ll
@@ -1,5 +1,5 @@
-; RUN: llc < %s -mattr=-atomics | FileCheck %s --check-prefixes CHECK,NO-ATOMICS
-; RUN: llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS
+; RUN: llc < %s -mattr=-atomics | FileCheck %s --check-prefixes NO-ATOMICS
+; RUN: llc < %s -mattr=+atomics | FileCheck %s --check-prefixes ATOMICS
 
 ; Test that the target features section contains -atomics or +atomics
 ; for modules that have thread local storage in their source.
@@ -9,16 +9,13 @@
 
 @foo = internal thread_local global i32 0
 
-; CHECK-LABEL: .custom_section.target_features,"",@
 
 ; -atomics
-; NO-ATOMICS-NEXT: .int8 1
-; NO-ATOMICS-NEXT: .int8 45
-; NO-ATOMICS-NEXT: .int8 7
-; NO-ATOMICS-NEXT: .ascii "atomics"
-; NO-ATOMICS-NEXT: .bss.foo,"",@
+; NO-ATOMICS-NOT: .custom_section.target_features,"",@
+; NO-ATOMICS: .tbss.foo,"",@
 
 ; +atomics
+; ATOMICS-LABEL: .custom_section.target_features,"",@
 ; ATOMICS-NEXT: .int8 1
 ; ATOMICS-NEXT: .int8 43
 ; ATOMICS-NEXT: .int8 7
Index: llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -189,7 +189,6 @@
     bool Stripped = false;
     if (!Features[WebAssembly::FeatureAtomics]) {
       Stripped |= stripAtomics(M);
-      Stripped |= stripThreadLocals(M);
     }
 
     recordFeatures(M, Features, Stripped);
@@ -252,18 +251,6 @@
     return true;
   }
 
-  bool stripThreadLocals(Module &M) {
-    bool Stripped = false;
-    for (auto &GV : M.globals()) {
-      if (GV.getThreadLocalMode() !=
-          GlobalValue::ThreadLocalMode::NotThreadLocal) {
-        Stripped = true;
-        GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
-      }
-    }
-    return Stripped;
-  }
-
   void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) {
     for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
       std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
Index: llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -77,9 +77,11 @@
   // functions. It's OK to hardcode knowledge of specific symbols here; this
   // method is precisely there for fetching the signatures of known
   // Clang-provided symbols.
-  if (strcmp(Name, "__stack_pointer") == 0 ||
-      strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0) {
-    bool Mutable = strcmp(Name, "__stack_pointer") == 0;
+  if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
+      strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
+      strcmp(Name, "__tls_size") == 0) {
+    bool Mutable =
+        strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
     WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
     WasmSym->setGlobalType(wasm::WasmGlobalType{
         uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
Index: llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -80,6 +80,10 @@
 def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
                                                      SDTCisPtrTy<0>]>;
 def SDT_WebAssemblyThrow      : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyGlobalGetI32 : SDTypeProfile<1, 1, [SDTCisVT<0, i32>,
+                                                       SDTCisVT<1, i32>]>;
+def SDT_WebAssemblyConstI32   : SDTypeProfile<1, 1, [SDTCisVT<0, i32>,
+                                                       SDTCisVT<1, i32>]>;
 
 //===----------------------------------------------------------------------===//
 // WebAssembly-specific DAG Nodes.
@@ -113,6 +117,11 @@
                                      SDT_WebAssemblyWrapperPIC>;
 def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
                               [SDNPHasChain, SDNPVariadic]>;
+def WebAssemblyglobalgeti32 : SDNode<"WebAssemblyISD::GLOBAL_GET_I32",
+                                    SDT_WebAssemblyGlobalGetI32,
+                                    [SDNPMayLoad]>;
+def WebAssemblyconsti32 : SDNode<"WebAssemblyISD::CONST_I32",
+                                    SDT_WebAssemblyConstI32>;
 
 //===----------------------------------------------------------------------===//
 // WebAssembly-specific Operands.
@@ -333,6 +342,12 @@
 def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>;
 def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>;
 
+def : Pat<(i32 (WebAssemblyglobalgeti32 texternalsym:$idx)),
+          (GLOBAL_GET_I32 texternalsym:$idx)>;
+
+def : Pat<(i32 (WebAssemblyconsti32 tglobaltlsaddr:$idx)),
+          (CONST_I32 tglobaltlsaddr:$idx)>;
+
 //===----------------------------------------------------------------------===//
 // Additional sets of instructions.
 //===----------------------------------------------------------------------===//
Index: llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -95,6 +95,7 @@
   SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
Index: llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -69,6 +69,7 @@
   computeRegisterProperties(Subtarget->getRegisterInfo());
 
   setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
+  setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom);
   setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
   setOperationAction(ISD::JumpTable, MVTPtr, Custom);
   setOperationAction(ISD::BlockAddress, MVTPtr, Custom);
@@ -937,6 +938,8 @@
     return LowerFrameIndex(Op, DAG);
   case ISD::GlobalAddress:
     return LowerGlobalAddress(Op, DAG);
+  case ISD::GlobalTLSAddress:
+    return LowerGlobalTLSAddress(Op, DAG);
   case ISD::ExternalSymbol:
     return LowerExternalSymbol(Op, DAG);
   case ISD::JumpTable:
@@ -1087,6 +1090,27 @@
                                                 GA->getOffset(), OperandFlags));
 }
 
+SDValue
+WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
+                                                 SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  EVT VT = Op.getValueType();
+  const auto *GA = cast<GlobalAddressSDNode>(Op);
+
+  assert(!Subtarget->hasAddr64() && "Should define and use GLOBAL_GET_I64");
+  assert(!isPositionIndependent() && "PIC code need to allocate TLS if "
+                                     "__tls_base is nullptr");
+  SDValue TLSBase =
+      DAG.getNode(WebAssemblyISD::GLOBAL_GET_I32, DL, MVT::i32,
+                  DAG.getTargetExternalSymbol("__tls_base", MVT::i32));
+
+  SDValue TLSOffset = DAG.getNode(
+      WebAssemblyISD::CONST_I32, DL, VT,
+      DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), 0));
+
+  return DAG.getNode(ISD::ADD, DL, VT, TLSBase, TLSOffset);
+}
+
 SDValue
 WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op,
                                                SelectionDAG &DAG) const {
@@ -1200,6 +1224,11 @@
                            Op.getOperand(3)  // thrown value
                        });
   }
+
+  case Intrinsic::wasm_tls_size: {
+    return DAG.getNode(WebAssemblyISD::GLOBAL_GET_I32, DL, MVT::i32,
+                       DAG.getTargetExternalSymbol("__tls_size", MVT::i32));
+  }
   }
 }
 
Index: llvm/lib/Target/WebAssembly/WebAssemblyISD.def
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISD.def
+++ llvm/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -32,5 +32,7 @@
 HANDLE_NODETYPE(THROW)
 HANDLE_NODETYPE(MEMORY_COPY)
 HANDLE_NODETYPE(MEMORY_FILL)
+HANDLE_NODETYPE(GLOBAL_GET_I32)
+HANDLE_NODETYPE(CONST_I32)
 
 // add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here...
Index: llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -233,6 +233,8 @@
       return false;
     if (Addr.getGlobalValue())
       return false;
+    if (GV->isThreadLocal())
+      return false;
     Addr.setGlobalValue(GV);
     return true;
   }
@@ -614,6 +616,8 @@
   if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) {
     if (TLI.isPositionIndependent())
       return 0;
+    if (GV->isThreadLocal())
+      return 0;
     unsigned ResultReg =
         createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
                                                : &WebAssembly::I32RegClass);
Index: llvm/lib/MC/WasmObjectWriter.cpp
===================================================================
--- llvm/lib/MC/WasmObjectWriter.cpp
+++ llvm/lib/MC/WasmObjectWriter.cpp
@@ -1263,7 +1263,9 @@
       WasmDataSegment &Segment = DataSegments.back();
       Segment.Name = SectionName;
       Segment.InitFlags =
-          Section.getPassive() ? (uint32_t)wasm::WASM_SEGMENT_IS_PASSIVE : 0;
+          Section.getKind().isThreadLocal() || Section.getPassive()
+              ? (uint32_t)wasm::WASM_SEGMENT_IS_PASSIVE
+              : 0;
       Segment.Offset = DataSize;
       Segment.Section = &Section;
       addData(Segment.Data, Section);
Index: llvm/include/llvm/MC/MCSectionWasm.h
===================================================================
--- llvm/include/llvm/MC/MCSectionWasm.h
+++ llvm/include/llvm/MC/MCSectionWasm.h
@@ -66,7 +66,8 @@
   bool isVirtualSection() const override;
 
   bool isWasmData() const {
-    return Kind.isGlobalWriteableData() || Kind.isReadOnly();
+    return Kind.isGlobalWriteableData() || Kind.isReadOnly() ||
+           Kind.isThreadLocal();
   }
 
   bool isUnique() const { return UniqueID != ~0U; }
Index: llvm/include/llvm/IR/IntrinsicsWebAssembly.td
===================================================================
--- llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -124,4 +124,13 @@
             [llvm_i32_ty],
             [IntrNoDuplicate, IntrHasSideEffects, ImmArg<0>]>;
 
+//===----------------------------------------------------------------------===//
+// Thread-local storage intrinsics
+//===----------------------------------------------------------------------===//
+
+def int_wasm_tls_size :
+  Intrinsic<[llvm_i32_ty],
+            [],
+            [IntrNoMem, IntrSpeculatable]>;
+
 } // TargetPrefix = "wasm"
Index: llvm/include/llvm/BinaryFormat/Wasm.h
===================================================================
--- llvm/include/llvm/BinaryFormat/Wasm.h
+++ llvm/include/llvm/BinaryFormat/Wasm.h
@@ -242,7 +242,9 @@
 enum : unsigned {
   WASM_OPCODE_END = 0x0b,
   WASM_OPCODE_CALL = 0x10,
+  WASM_OPCODE_LOCAL_GET = 0x20,
   WASM_OPCODE_GLOBAL_GET = 0x23,
+  WASM_OPCODE_GLOBAL_SET = 0x24,
   WASM_OPCODE_I32_STORE = 0x36,
   WASM_OPCODE_I32_CONST = 0x41,
   WASM_OPCODE_I64_CONST = 0x42,
Index: lld/wasm/Writer.cpp
===================================================================
--- lld/wasm/Writer.cpp
+++ lld/wasm/Writer.cpp
@@ -57,6 +57,7 @@
   void createInitMemoryFunction();
   void createApplyRelocationsFunction();
   void createCallCtorsFunction();
+  void createInitTLSFunction();
 
   void assignIndexes();
   void populateSymtab();
@@ -242,6 +243,11 @@
     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);
+      TLSSize->Global->Global.InitExpr.Value.Int32 = Seg->Size;
+    }
   }
 
   // TODO: Add .bss space here.
@@ -353,6 +359,7 @@
   StringMap<std::string> Used;
   StringMap<std::string> Required;
   StringMap<std::string> Disallowed;
+  bool TLSUsed = false;
 
   // Only infer used features if user did not specify features
   bool InferFeatures = !Config->Features.hasValue();
@@ -385,6 +392,14 @@
               std::to_string(Feature.Prefix));
       }
     }
+
+    for (InputSegment *Segment : File->Segments) {
+      if (!Segment->Live)
+        continue;
+      StringRef Name = Segment->getName();
+      if (Name.startswith(".tdata.") || Name.startswith(".tbss."))
+        TLSUsed = true;
+    }
   }
 
   if (InferFeatures)
@@ -411,6 +426,10 @@
     error("'bulk-memory' feature must be used in order to emit passive "
           "segments");
 
+  if (!Used.count("bulk-memory") && TLSUsed)
+    error("'bulk-memory' feature must be used in order to use thread-local "
+          "storage");
+
   // Validate that used features are allowed in output
   if (!InferFeatures) {
     for (auto &Feature : Used.keys()) {
@@ -493,7 +512,7 @@
       // See: https://github.com/WebAssembly/mutable-global
       if (G->getGlobalType()->Mutable) {
         // Only the __stack_pointer should ever be create as mutable.
-        assert(G == WasmSym::StackPointer);
+        assert(G == WasmSym::StackPointer || G == WasmSym::TLSBase);
         continue;
       }
       Export = {Name, WASM_EXTERNAL_GLOBAL, G->getGlobalIndex()};
@@ -612,6 +631,11 @@
     return ".bss";
   if (Name.startswith(".rodata."))
     return ".rodata";
+  if (Name.startswith(".tdata."))
+    return ".tdata";
+  // Merge .tbss into .tdata so that they share the same offsets.
+  if (Name.startswith(".tbss."))
+    return ".tdata";
   return Name;
 }
 
@@ -625,7 +649,7 @@
       if (S == nullptr) {
         LLVM_DEBUG(dbgs() << "new segment: " << Name << "\n");
         S = make<OutputSegment>(Name, Segments.size());
-        if (Config->PassiveSegments)
+        if (Config->PassiveSegments || Name == ".tdata")
           S->InitFlags = WASM_SEGMENT_IS_PASSIVE;
         Segments.push_back(S);
       }
@@ -655,7 +679,7 @@
 
     // initialize passive data segments
     for (const OutputSegment *S : Segments) {
-      if (S->InitFlags & WASM_SEGMENT_IS_PASSIVE) {
+      if (S->InitFlags & WASM_SEGMENT_IS_PASSIVE && S->Name != ".tdata") {
         // destination address
         writeU8(OS, WASM_OPCODE_I32_CONST, "i32.const");
         writeUleb128(OS, S->StartVA, "destination address");
@@ -737,6 +761,54 @@
   CreateFunction(WasmSym::CallCtors, BodyContent);
 }
 
+void Writer::createInitTLSFunction() {
+  if (!WasmSym::InitTLS->isLive())
+    return;
+
+  std::string BodyContent;
+  {
+    raw_string_ostream OS(BodyContent);
+
+    OutputSegment *TLSSeg = nullptr;
+    for (auto *Seg : Segments) {
+      if (Seg->Name == ".tdata")
+        TLSSeg = Seg;
+      break;
+    }
+
+    if (TLSSeg) {
+      writeUleb128(OS, 0, "num locals");
+
+      writeU8(OS, WASM_OPCODE_LOCAL_GET, "local.get");
+      writeUleb128(OS, 0, "local index");
+
+      writeU8(OS, WASM_OPCODE_GLOBAL_SET, "global.set");
+      writeUleb128(OS, WasmSym::TLSBase->getGlobalIndex(), "global index");
+
+      writeU8(OS, WASM_OPCODE_LOCAL_GET, "local.get");
+      writeUleb128(OS, 0, "local index");
+
+      writeU8(OS, WASM_OPCODE_I32_CONST, "i32.const");
+      writeUleb128(OS, 0, "segment offset");
+
+      writeU8(OS, WASM_OPCODE_I32_CONST, "i32.const");
+      writeUleb128(OS, TLSSeg->Size, "memory region size");
+
+      writeU8(OS, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
+      writeUleb128(OS, WASM_OPCODE_MEMORY_INIT, "MEMORY.INIT");
+      writeUleb128(OS, TLSSeg->Index, "segment index immediate");
+      writeU8(OS, 0, "memory index immediate");
+
+      writeU8(OS, WASM_OPCODE_END, "end function");
+    } else {
+      writeUleb128(OS, 0, "num locals");
+      writeU8(OS, WASM_OPCODE_END, "end function");
+    }
+  }
+
+  CreateFunction(WasmSym::InitTLS, BodyContent);
+}
+
 // Populate InitFunctions vector with init functions from all input objects.
 // This is then used either when creating the output linking section or to
 // synthesize the "__wasm_call_ctors" function.
@@ -831,11 +903,13 @@
     // Make sure we have resolved all symbols.
     if (!Config->AllowUndefined)
       Symtab->reportRemainingUndefines();
-
-    if (errorCount())
-      return;
   }
 
+  createInitTLSFunction();
+
+  if (errorCount())
+    return;
+
   log("-- calculateTypes");
   calculateTypes();
   log("-- calculateExports");
Index: lld/wasm/Symbols.h
===================================================================
--- lld/wasm/Symbols.h
+++ lld/wasm/Symbols.h
@@ -426,6 +426,15 @@
   // linear memory.
   static GlobalSymbol *StackPointer;
 
+  // __tls_base
+  // Global that holds the address of the base of the current thread's
+  // TLS block.
+  static GlobalSymbol *TLSBase;
+
+  // __tls_size
+  // Symbol whose value is the size of the TLS block.
+  static GlobalSymbol *TLSSize;
+
   // __data_end
   // Symbol marking the end of the data and bss.
   static DefinedData *DataEnd;
@@ -448,6 +457,10 @@
   // Function that applies relocations to data segment post-instantiation.
   static DefinedFunction *ApplyRelocs;
 
+  // __wasm_init_tls
+  // Function that allocates thread-local storage and initializes it.
+  static DefinedFunction *InitTLS;
+
   // __dso_handle
   // Symbol used in calls to __cxa_atexit to determine current DLL
   static DefinedData *DsoHandle;
Index: lld/wasm/Symbols.cpp
===================================================================
--- lld/wasm/Symbols.cpp
+++ lld/wasm/Symbols.cpp
@@ -27,11 +27,14 @@
 DefinedFunction *WasmSym::CallCtors;
 DefinedFunction *WasmSym::InitMemory;
 DefinedFunction *WasmSym::ApplyRelocs;
+DefinedFunction *WasmSym::InitTLS;
 DefinedData *WasmSym::DsoHandle;
 DefinedData *WasmSym::DataEnd;
 DefinedData *WasmSym::GlobalBase;
 DefinedData *WasmSym::HeapBase;
 GlobalSymbol *WasmSym::StackPointer;
+GlobalSymbol *WasmSym::TLSBase;
+GlobalSymbol *WasmSym::TLSSize;
 UndefinedGlobal *WasmSym::TableBase;
 UndefinedGlobal *WasmSym::MemoryBase;
 
@@ -200,8 +203,11 @@
 
 uint32_t DefinedData::getVirtualAddress() const {
   LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
-  if (Segment)
+  if (Segment) {
+    if (Segment->OutputSeg->Name == ".tdata")
+      return Segment->OutputSegmentOffset + Offset;
     return Segment->OutputSeg->StartVA + Segment->OutputSegmentOffset + Offset;
+  }
   return Offset;
 }
 
Index: lld/wasm/Driver.cpp
===================================================================
--- lld/wasm/Driver.cpp
+++ lld/wasm/Driver.cpp
@@ -454,6 +454,7 @@
 // Create ABI-defined synthetic symbols
 static void createSyntheticSymbols() {
   static WasmSignature NullSignature = {{}, {}};
+  static WasmSignature I32ArgSignature = {{}, {ValType::I32}};
   static llvm::wasm::WasmGlobalType GlobalTypeI32 = {WASM_TYPE_I32, false};
   static llvm::wasm::WasmGlobalType MutableGlobalTypeI32 = {WASM_TYPE_I32,
                                                             true};
@@ -519,6 +520,28 @@
     WasmSym::TableBase->markLive();
   }
 
+  llvm::wasm::WasmGlobal TLSBaseGlobal;
+  TLSBaseGlobal.Type = {WASM_TYPE_I32, true};
+  TLSBaseGlobal.InitExpr.Value.Int32 = 0;
+  TLSBaseGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+  TLSBaseGlobal.SymbolName = "__tls_base";
+  WasmSym::TLSBase =
+      Symtab->addSyntheticGlobal("__tls_base", WASM_SYMBOL_VISIBILITY_HIDDEN,
+                                 make<InputGlobal>(TLSBaseGlobal, nullptr));
+
+  llvm::wasm::WasmGlobal TLSSizeGlobal;
+  TLSSizeGlobal.Type = {WASM_TYPE_I32, false};
+  TLSSizeGlobal.InitExpr.Value.Int32 = 0;
+  TLSSizeGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+  TLSSizeGlobal.SymbolName = "__tls_size";
+  WasmSym::TLSSize =
+      Symtab->addSyntheticGlobal("__tls_size", WASM_SYMBOL_VISIBILITY_HIDDEN,
+                                 make<InputGlobal>(TLSSizeGlobal, nullptr));
+
+  WasmSym::InitTLS = Symtab->addSyntheticFunction(
+      "__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
+      make<SyntheticFunction>(I32ArgSignature, "__wasm_init_tls"));
+
   WasmSym::DsoHandle = Symtab->addSyntheticDataSymbol(
       "__dso_handle", WASM_SYMBOL_VISIBILITY_HIDDEN);
 }
Index: lld/test/wasm/tls.ll
===================================================================
--- /dev/null
+++ lld/test/wasm/tls.ll
@@ -0,0 +1,42 @@
+; RUN: llc -mattr=+bulk-memory -filetype=obj %s -o %t.o
+
+target triple = "wasm32-unknown-unknown"
+
+@tls1 = thread_local global i32 1, align 4
+@tls2 = thread_local global i32 1, align 4
+
+define i32* @tls1_addr() {
+  ret i32* @tls1
+}
+
+define i32* @tls2_addr() {
+  ret i32* @tls2
+}
+
+; RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; CHECK:      - Type:            CODE
+; CHECK-NEXT:   Functions:
+; CHECK-NEXT:     - Index:           0
+; CHECK-NEXT:       Locals:          []
+; CHECK-NEXT:       Body:            0B
+; CHECK-NEXT:     - Index:           1
+; CHECK-NEXT:       Locals:          []
+; CHECK-NEXT:       Body:            2381808080004180808080006A0B
+
+; Expected body of tls1_addr:
+;   global.get 1
+;   i32.const 0
+;   i32.add
+;   end
+
+; CHECK-NEXT:     - Index:           2
+; CHECK-NEXT:       Locals:          []
+; CHECK-NEXT:       Body:            2381808080004184808080006A0B
+
+; Expected body of tls1_addr:
+;   global.get 1
+;   i32.const 4
+;   i32.add
+;   end
Index: lld/test/wasm/gc-sections.ll
===================================================================
--- lld/test/wasm/gc-sections.ll
+++ lld/test/wasm/gc-sections.ll
@@ -112,12 +112,18 @@
 ; NO-GC-NEXT:           Opcode:          I32_CONST
 ; NO-GC-NEXT:           Value:           66576
 ; NO-GC-NEXT:       - Index:       1
+; NO-GC-NEXT:         Type:        I32
+; NO-GC-NEXT:         Mutable:     true
+; NO-GC-NEXT:         InitExpr:
+; NO-GC-NEXT:           Opcode:          I32_CONST
+; NO-GC-NEXT:           Value:           0
+; NO-GC-NEXT:       - Index:       2
 ; NO-GC-NEXT:         Type:        I64
 ; NO-GC-NEXT:         Mutable:     true
 ; NO-GC-NEXT:         InitExpr:
 ; NO-GC-NEXT:           Opcode:          I64_CONST
 ; NO-GC-NEXT:           Value:           123
-; NO-GC-NEXT:       - Index:       2
+; NO-GC-NEXT:       - Index:       3
 ; NO-GC-NEXT:         Type:        I64
 ; NO-GC-NEXT:         Mutable:     true
 ; NO-GC-NEXT:         InitExpr:
Index: lld/test/wasm/data-layout.ll
===================================================================
--- lld/test/wasm/data-layout.ll
+++ lld/test/wasm/data-layout.ll
@@ -29,11 +29,17 @@
 ; CHECK-NEXT:           Value:           66608
 ; CHECK-NEXT:       - Index:           1
 ; CHECK-NEXT:         Type:            I32
+; CHECK-NEXT:         Mutable:         true
+; CHECK-NEXT:         InitExpr:
+; CHECK-NEXT:           Opcode:          I32_CONST
+; CHECK-NEXT:           Value:           0
+; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
 ; CHECK-NEXT:           Opcode:          I32_CONST
 ; CHECK-NEXT:           Value:           1071
-; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:       - Index:           3
 ; CHECK-NEXT:         Type:            I32
 ; CHECK-NEXT:         Mutable:         false
 ; CHECK-NEXT:         InitExpr:
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -13896,6 +13896,10 @@
     Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_data_drop);
     return Builder.CreateCall(Callee, {Arg});
   }
+  case WebAssembly::BI__builtin_wasm_tls_size: {
+    Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_tls_size);
+    return Builder.CreateCall(Callee, {});
+  }
   case WebAssembly::BI__builtin_wasm_throw: {
     Value *Tag = EmitScalarExpr(E->getArg(0));
     Value *Obj = EmitScalarExpr(E->getArg(1));
Index: clang/include/clang/Basic/BuiltinsWebAssembly.def
===================================================================
--- clang/include/clang/Basic/BuiltinsWebAssembly.def
+++ clang/include/clang/Basic/BuiltinsWebAssembly.def
@@ -29,6 +29,9 @@
 TARGET_BUILTIN(__builtin_wasm_memory_init, "vIUiIUiv*UiUi", "", "bulk-memory")
 TARGET_BUILTIN(__builtin_wasm_data_drop, "vIUi", "", "bulk-memory")
 
+// Thread-local storage
+TARGET_BUILTIN(__builtin_wasm_tls_size, "z", "nc", "bulk-memory")
+
 // Floating point min/max
 BUILTIN(__builtin_wasm_min_f32, "fff", "nc")
 BUILTIN(__builtin_wasm_max_f32, "fff", "nc")
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to