llvm.org GIT mirror llvm / b261aae
[WebAssembly] Implement thread-local storage (local-exec model) Summary: 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`. As `__wasm_init_tls` will handle the memory initialization, the memory does not have to be zeroed. 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(malloc(__builtin_wasm_tls_size())); Reviewers: tlively, aheejin, kripken, sbc100 Subscribers: dschuff, jgravelle-google, hiraditya, sunfish, jfb, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D64537 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@366272 91177308-0d34-0410-b5e6-96231b3b80d8 Guanzhong Chen 3 months ago
9 changed file(s) with 179 addition(s) and 38 deletion(s). Raw diff Collapse all Expand all
241241 enum : unsigned {
242242 WASM_OPCODE_END = 0x0b,
243243 WASM_OPCODE_CALL = 0x10,
244 WASM_OPCODE_LOCAL_GET = 0x20,
244245 WASM_OPCODE_GLOBAL_GET = 0x23,
246 WASM_OPCODE_GLOBAL_SET = 0x24,
245247 WASM_OPCODE_I32_STORE = 0x36,
246248 WASM_OPCODE_I32_CONST = 0x41,
247249 WASM_OPCODE_I64_CONST = 0x42,
123123 [llvm_i32_ty],
124124 [IntrNoDuplicate, IntrHasSideEffects, ImmArg<0>]>;
125125
126 //===----------------------------------------------------------------------===//
127 // Thread-local storage intrinsics
128 //===----------------------------------------------------------------------===//
129
130 def int_wasm_tls_size :
131 Intrinsic<[llvm_anyint_ty],
132 [],
133 [IntrNoMem, IntrSpeculatable]>;
134
126135 } // TargetPrefix = "wasm"
6565 bool isVirtualSection() const override;
6666
6767 bool isWasmData() const {
68 return Kind.isGlobalWriteableData() || Kind.isReadOnly();
68 return Kind.isGlobalWriteableData() || Kind.isReadOnly() ||
69 Kind.isThreadLocal();
6970 }
7071
7172 bool isUnique() const { return UniqueID != ~0U; }
231231 if (TLI.isPositionIndependent())
232232 return false;
233233 if (Addr.getGlobalValue())
234 return false;
235 if (GV->isThreadLocal())
234236 return false;
235237 Addr.setGlobalValue(GV);
236238 return true;
613615 if (const GlobalValue *GV = dyn_cast(C)) {
614616 if (TLI.isPositionIndependent())
615617 return 0;
618 if (GV->isThreadLocal())
619 return 0;
616620 unsigned ResultReg =
617621 createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
618622 : &WebAssembly::I32RegClass);
1414 #include "WebAssembly.h"
1515 #include "WebAssemblyTargetMachine.h"
1616 #include "llvm/CodeGen/SelectionDAGISel.h"
17 #include "llvm/IR/DiagnosticInfo.h"
1718 #include "llvm/IR/Function.h" // To access function attributes.
1819 #include "llvm/Support/Debug.h"
1920 #include "llvm/Support/KnownBits.h"
170171 }
171172 }
172173
174 case ISD::GlobalTLSAddress: {
175 const auto *GA = cast(Node);
176
177 if (!MF.getSubtarget().hasBulkMemory())
178 report_fatal_error("cannot use thread-local storage without bulk memory",
179 false);
180
181 if (GA->getGlobal()->getThreadLocalMode() !=
182 GlobalValue::LocalExecTLSModel) {
183 report_fatal_error("only -ftls-model=local-exec is supported for now",
184 false);
185 }
186
187 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
188 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
189
190 SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT);
191 SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress(
192 GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0);
193
194 MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32,
195 DL, MVT::i32, TLSBaseSym);
196 MachineSDNode *TLSOffset = CurDAG->getMachineNode(
197 WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym);
198 MachineSDNode *TLSAddress =
199 CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32,
200 SDValue(TLSBase, 0), SDValue(TLSOffset, 0));
201 ReplaceNode(Node, TLSAddress);
202 return;
203 }
204
205 case ISD::INTRINSIC_WO_CHAIN: {
206 unsigned IntNo = cast(Node->getOperand(0))->getZExtValue();
207 switch (IntNo) {
208 case Intrinsic::wasm_tls_size: {
209 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
210 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
211
212 MachineSDNode *TLSSize = CurDAG->getMachineNode(
213 WebAssembly::GLOBAL_GET_I32, DL, PtrVT,
214 CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32));
215 ReplaceNode(Node, TLSSize);
216 return;
217 }
218 }
219 break;
220 }
221
173222 default:
174223 break;
175224 }
7676 // functions. It's OK to hardcode knowledge of specific symbols here; this
7777 // method is precisely there for fetching the signatures of known
7878 // Clang-provided symbols.
79 if (strcmp(Name, "__stack_pointer") == 0 ||
80 strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0) {
81 bool Mutable = strcmp(Name, "__stack_pointer") == 0;
79 if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
80 strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
81 strcmp(Name, "__tls_size") == 0) {
82 bool Mutable =
83 strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
8284 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
8385 WasmSym->setGlobalType(wasm::WasmGlobalType{
8486 uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
185185 for (auto &F : M)
186186 replaceFeatures(F, FeatureStr);
187187
188 bool Stripped = false;
189 if (!Features[WebAssembly::FeatureAtomics]) {
190 Stripped |= stripAtomics(M);
191 Stripped |= stripThreadLocals(M);
192 }
193
194 recordFeatures(M, Features, Stripped);
188 bool StrippedAtomics = false;
189 bool StrippedTLS = false;
190
191 if (!Features[WebAssembly::FeatureAtomics])
192 StrippedAtomics = stripAtomics(M);
193
194 if (!Features[WebAssembly::FeatureBulkMemory])
195 StrippedTLS = stripThreadLocals(M);
196
197 if (StrippedAtomics && !StrippedTLS)
198 stripThreadLocals(M);
199 else if (StrippedTLS && !StrippedAtomics)
200 stripAtomics(M);
201
202 recordFeatures(M, Features, StrippedAtomics || StrippedTLS);
195203
196204 // Conservatively assume we have made some change
197205 return true;
270278 // "atomics" is special: code compiled without atomics may have had its
271279 // atomics lowered to nonatomic operations. In that case, atomics is
272280 // disallowed to prevent unsafe linking with atomics-enabled objects.
273 assert(!Features[WebAssembly::FeatureAtomics]);
281 assert(!Features[WebAssembly::FeatureAtomics] ||
282 !Features[WebAssembly::FeatureBulkMemory]);
274283 M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
275284 wasm::WASM_FEATURE_PREFIX_DISALLOWED);
276285 } else if (Features[KV.Value]) {
None ; RUN: llc < %s -mattr=-atomics | FileCheck %s --check-prefixes CHECK,NO-ATOMICS
1 ; RUN: llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS
0 ; RUN: llc < %s -mattr=-bulk-memory | FileCheck %s --check-prefixes NO-BULK-MEM
1 ; RUN: llc < %s -mattr=+bulk-memory | FileCheck %s --check-prefixes BULK-MEM
22
33 ; Test that the target features section contains -atomics or +atomics
44 ; for modules that have thread local storage in their source.
88
99 @foo = internal thread_local global i32 0
1010
11 ; CHECK-LABEL: .custom_section.target_features,"",@
11 ; -bulk-memory
12 ; NO-BULK-MEM-LABEL: .custom_section.target_features,"",@
13 ; NO-BULK-MEM-NEXT: .int8 1
14 ; NO-BULK-MEM-NEXT: .int8 45
15 ; NO-BULK-MEM-NEXT: .int8 7
16 ; NO-BULK-MEM-NEXT: .ascii "atomics"
17 ; NO-BULK-MEM-NEXT: .bss.foo,"",@
1218
13 ; -atomics
14 ; NO-ATOMICS-NEXT: .int8 1
15 ; NO-ATOMICS-NEXT: .int8 45
16 ; NO-ATOMICS-NEXT: .int8 7
17 ; NO-ATOMICS-NEXT: .ascii "atomics"
18 ; NO-ATOMICS-NEXT: .bss.foo,"",@
19
20 ; +atomics
21 ; ATOMICS-NEXT: .int8 1
22 ; ATOMICS-NEXT: .int8 43
23 ; ATOMICS-NEXT: .int8 7
24 ; ATOMICS-NEXT: .ascii "atomics"
25 ; ATOMICS-NEXT: .tbss.foo,"",@
19 ; +bulk-memory
20 ; BULK-MEM-LABEL: .custom_section.target_features,"",@
21 ; BULK-MEM-NEXT: .int8 1
22 ; BULK-MEM-NEXT: .int8 43
23 ; BULK-MEM-NEXT: .int8 11
24 ; BULK-MEM-NEXT: .ascii "bulk-memory"
25 ; BULK-MEM-NEXT: .tbss.foo,"",@
None ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck --check-prefix=SINGLE %s
0 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -mattr=+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -mattr=+bulk-memory -fast-isel | FileCheck %s --check-prefixes=CHECK,TLS
2 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -mattr=-bulk-memory | FileCheck %s --check-prefixes=CHECK,NO-TLS
13 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
24 target triple = "wasm32-unknown-unknown"
35
4 ; SINGLE-LABEL: address_of_tls:
6 ; CHECK-LABEL: address_of_tls:
7 ; CHECK-NEXT: .functype address_of_tls () -> (i32)
58 define i32 @address_of_tls() {
6 ; SINGLE: i32.const $push0=, tls
7 ; SINGLE-NEXT: return $pop0
9 ; TLS-DAG: global.get __tls_base
10 ; TLS-DAG: i32.const tls
11 ; TLS-NEXT: i32.add
12 ; TLS-NEXT: return
13
14 ; NO-TLS-NEXT: i32.const tls
15 ; NO-TLS-NEXT: return
816 ret i32 ptrtoint(i32* @tls to i32)
917 }
1018
11 ; SINGLE: .type tls,@object
12 ; SINGLE-NEXT: .section .bss.tls,"",@
13 ; SINGLE-NEXT: .p2align 2
14 ; SINGLE-NEXT: tls:
15 ; SINGLE-NEXT: .int32 0
16 @tls = internal thread_local global i32 0
19 ; CHECK-LABEL: ptr_to_tls:
20 ; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
21 define i32* @ptr_to_tls() {
22 ; TLS-DAG: global.get __tls_base
23 ; TLS-DAG: i32.const tls
24 ; TLS-NEXT: i32.add
25 ; TLS-NEXT: return
26
27 ; NO-TLS-NEXT: i32.const tls
28 ; NO-TLS-NEXT: return
29 ret i32* @tls
30 }
31
32 ; CHECK-LABEL: tls_load:
33 ; CHECK-NEXT: .functype tls_load () -> (i32)
34 define i32 @tls_load() {
35 ; TLS-DAG: global.get __tls_base
36 ; TLS-DAG: i32.const tls
37 ; TLS-NEXT: i32.add
38 ; TLS-NEXT: i32.load 0
39 ; TLS-NEXT: return
40
41 ; NO-TLS-NEXT: i32.const 0
42 ; NO-TLS-NEXT: i32.load tls
43 ; NO-TLS-NEXT: return
44 %tmp = load i32, i32* @tls, align 4
45 ret i32 %tmp
46 }
47
48 ; CHECK-LABEL: tls_store:
49 ; CHECK-NEXT: .functype tls_store (i32) -> ()
50 define void @tls_store(i32 %x) {
51 ; TLS-DAG: global.get __tls_base
52 ; TLS-DAG: i32.const tls
53 ; TLS-NEXT: i32.add
54 ; TLS-NEXT: i32.store 0
55 ; TLS-NEXT: return
56
57 ; NO-TLS-NEXT: i32.const 0
58 ; NO-TLS-NEXT: i32.store tls
59 ; NO-TLS-NEXT: return
60 store i32 %x, i32* @tls, align 4
61 ret void
62 }
63
64 ; CHECK-LABEL: tls_size:
65 ; CHECK-NEXT: .functype tls_size () -> (i32)
66 define i32 @tls_size() {
67 ; CHECK-NEXT: global.get __tls_size
68 ; CHECK-NEXT: return
69 %1 = call i32 @llvm.wasm.tls.size.i32()
70 ret i32 %1
71 }
72
73 ; CHECK: .type tls,@object
74 ; TLS-NEXT: .section .tbss.tls,"",@
75 ; NO-TLS-NEXT: .section .bss.tls,"",@
76 ; CHECK-NEXT: .p2align 2
77 ; CHECK-NEXT: tls:
78 ; CHECK-NEXT: .int32 0
79 @tls = internal thread_local(localexec) global i32 0
80
81 declare i32 @llvm.wasm.tls.size.i32()