[llvm] r366272 - [WebAssembly] Implement thread-local storage (local-exec model)
Guanzhong Chen via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 16 15:00:46 PDT 2019
Author: quantum
Date: Tue Jul 16 15:00:45 2019
New Revision: 366272
URL: http://llvm.org/viewvc/llvm-project?rev=366272&view=rev
Log:
[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
Modified:
llvm/trunk/include/llvm/BinaryFormat/Wasm.h
llvm/trunk/include/llvm/IR/IntrinsicsWebAssembly.td
llvm/trunk/include/llvm/MC/MCSectionWasm.h
llvm/trunk/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
llvm/trunk/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
llvm/trunk/test/CodeGen/WebAssembly/target-features-tls.ll
llvm/trunk/test/CodeGen/WebAssembly/tls.ll
Modified: llvm/trunk/include/llvm/BinaryFormat/Wasm.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/BinaryFormat/Wasm.h?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/include/llvm/BinaryFormat/Wasm.h (original)
+++ llvm/trunk/include/llvm/BinaryFormat/Wasm.h Tue Jul 16 15:00:45 2019
@@ -242,7 +242,9 @@ enum : unsigned {
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,
Modified: llvm/trunk/include/llvm/IR/IntrinsicsWebAssembly.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/IntrinsicsWebAssembly.td?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/IntrinsicsWebAssembly.td (original)
+++ llvm/trunk/include/llvm/IR/IntrinsicsWebAssembly.td Tue Jul 16 15:00:45 2019
@@ -124,4 +124,13 @@ def int_wasm_data_drop :
[llvm_i32_ty],
[IntrNoDuplicate, IntrHasSideEffects, ImmArg<0>]>;
+//===----------------------------------------------------------------------===//
+// Thread-local storage intrinsics
+//===----------------------------------------------------------------------===//
+
+def int_wasm_tls_size :
+ Intrinsic<[llvm_anyint_ty],
+ [],
+ [IntrNoMem, IntrSpeculatable]>;
+
} // TargetPrefix = "wasm"
Modified: llvm/trunk/include/llvm/MC/MCSectionWasm.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/MC/MCSectionWasm.h?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/include/llvm/MC/MCSectionWasm.h (original)
+++ llvm/trunk/include/llvm/MC/MCSectionWasm.h Tue Jul 16 15:00:45 2019
@@ -66,7 +66,8 @@ public:
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; }
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyFastISel.cpp?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyFastISel.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyFastISel.cpp Tue Jul 16 15:00:45 2019
@@ -233,6 +233,8 @@ bool WebAssemblyFastISel::computeAddress
return false;
if (Addr.getGlobalValue())
return false;
+ if (GV->isThreadLocal())
+ return false;
Addr.setGlobalValue(GV);
return true;
}
@@ -614,6 +616,8 @@ unsigned WebAssemblyFastISel::fastMateri
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);
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp Tue Jul 16 15:00:45 2019
@@ -15,6 +15,7 @@
#include "WebAssembly.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h" // To access function attributes.
#include "llvm/Support/Debug.h"
#include "llvm/Support/KnownBits.h"
@@ -171,6 +172,54 @@ void WebAssemblyDAGToDAGISel::Select(SDN
}
}
+ case ISD::GlobalTLSAddress: {
+ const auto *GA = cast<GlobalAddressSDNode>(Node);
+
+ if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
+ report_fatal_error("cannot use thread-local storage without bulk memory",
+ false);
+
+ if (GA->getGlobal()->getThreadLocalMode() !=
+ GlobalValue::LocalExecTLSModel) {
+ report_fatal_error("only -ftls-model=local-exec is supported for now",
+ false);
+ }
+
+ MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
+ assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
+
+ SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT);
+ SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress(
+ GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0);
+
+ MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32,
+ DL, MVT::i32, TLSBaseSym);
+ MachineSDNode *TLSOffset = CurDAG->getMachineNode(
+ WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym);
+ MachineSDNode *TLSAddress =
+ CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32,
+ SDValue(TLSBase, 0), SDValue(TLSOffset, 0));
+ ReplaceNode(Node, TLSAddress);
+ return;
+ }
+
+ case ISD::INTRINSIC_WO_CHAIN: {
+ unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue();
+ switch (IntNo) {
+ case Intrinsic::wasm_tls_size: {
+ MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
+ assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
+
+ MachineSDNode *TLSSize = CurDAG->getMachineNode(
+ WebAssembly::GLOBAL_GET_I32, DL, PtrVT,
+ CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32));
+ ReplaceNode(Node, TLSSize);
+ return;
+ }
+ }
+ break;
+ }
+
default:
break;
}
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp Tue Jul 16 15:00:45 2019
@@ -77,9 +77,11 @@ MCSymbol *WebAssemblyMCInstLower::GetExt
// 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
Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp Tue Jul 16 15:00:45 2019
@@ -186,13 +186,21 @@ public:
for (auto &F : M)
replaceFeatures(F, FeatureStr);
- bool Stripped = false;
- if (!Features[WebAssembly::FeatureAtomics]) {
- Stripped |= stripAtomics(M);
- Stripped |= stripThreadLocals(M);
- }
+ bool StrippedAtomics = false;
+ bool StrippedTLS = false;
+
+ if (!Features[WebAssembly::FeatureAtomics])
+ StrippedAtomics = stripAtomics(M);
+
+ if (!Features[WebAssembly::FeatureBulkMemory])
+ StrippedTLS = stripThreadLocals(M);
+
+ if (StrippedAtomics && !StrippedTLS)
+ stripThreadLocals(M);
+ else if (StrippedTLS && !StrippedAtomics)
+ stripAtomics(M);
- recordFeatures(M, Features, Stripped);
+ recordFeatures(M, Features, StrippedAtomics || StrippedTLS);
// Conservatively assume we have made some change
return true;
@@ -271,7 +279,8 @@ private:
// "atomics" is special: code compiled without atomics may have had its
// atomics lowered to nonatomic operations. In that case, atomics is
// disallowed to prevent unsafe linking with atomics-enabled objects.
- assert(!Features[WebAssembly::FeatureAtomics]);
+ assert(!Features[WebAssembly::FeatureAtomics] ||
+ !Features[WebAssembly::FeatureBulkMemory]);
M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
wasm::WASM_FEATURE_PREFIX_DISALLOWED);
} else if (Features[KV.Value]) {
Modified: llvm/trunk/test/CodeGen/WebAssembly/target-features-tls.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/target-features-tls.ll?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/target-features-tls.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/target-features-tls.ll Tue Jul 16 15:00:45 2019
@@ -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=-bulk-memory | FileCheck %s --check-prefixes NO-BULK-MEM
+; RUN: llc < %s -mattr=+bulk-memory | FileCheck %s --check-prefixes BULK-MEM
; Test that the target features section contains -atomics or +atomics
; for modules that have thread local storage in their source.
@@ -9,18 +9,18 @@ target triple = "wasm32-unknown-unknown"
@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,"",@
-
-; +atomics
-; ATOMICS-NEXT: .int8 1
-; ATOMICS-NEXT: .int8 43
-; ATOMICS-NEXT: .int8 7
-; ATOMICS-NEXT: .ascii "atomics"
-; ATOMICS-NEXT: .tbss.foo,"",@
+; -bulk-memory
+; NO-BULK-MEM-LABEL: .custom_section.target_features,"",@
+; NO-BULK-MEM-NEXT: .int8 1
+; NO-BULK-MEM-NEXT: .int8 45
+; NO-BULK-MEM-NEXT: .int8 7
+; NO-BULK-MEM-NEXT: .ascii "atomics"
+; NO-BULK-MEM-NEXT: .bss.foo,"",@
+
+; +bulk-memory
+; BULK-MEM-LABEL: .custom_section.target_features,"",@
+; BULK-MEM-NEXT: .int8 1
+; BULK-MEM-NEXT: .int8 43
+; BULK-MEM-NEXT: .int8 11
+; BULK-MEM-NEXT: .ascii "bulk-memory"
+; BULK-MEM-NEXT: .tbss.foo,"",@
Modified: llvm/trunk/test/CodeGen/WebAssembly/tls.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/tls.ll?rev=366272&r1=366271&r2=366272&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/tls.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/tls.ll Tue Jul 16 15:00:45 2019
@@ -1,17 +1,82 @@
-; 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 -mattr=+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS
+; 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
+; 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
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
-; SINGLE-LABEL: address_of_tls:
+; CHECK-LABEL: address_of_tls:
+; CHECK-NEXT: .functype address_of_tls () -> (i32)
define i32 @address_of_tls() {
- ; SINGLE: i32.const $push0=, tls
- ; SINGLE-NEXT: return $pop0
+ ; TLS-DAG: global.get __tls_base
+ ; TLS-DAG: i32.const tls
+ ; TLS-NEXT: i32.add
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const tls
+ ; NO-TLS-NEXT: return
ret i32 ptrtoint(i32* @tls to i32)
}
-; SINGLE: .type tls, at object
-; SINGLE-NEXT: .section .bss.tls,"",@
-; SINGLE-NEXT: .p2align 2
-; SINGLE-NEXT: tls:
-; SINGLE-NEXT: .int32 0
- at tls = internal thread_local global i32 0
+; CHECK-LABEL: ptr_to_tls:
+; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
+define i32* @ptr_to_tls() {
+ ; TLS-DAG: global.get __tls_base
+ ; TLS-DAG: i32.const tls
+ ; TLS-NEXT: i32.add
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const tls
+ ; NO-TLS-NEXT: return
+ ret i32* @tls
+}
+
+; CHECK-LABEL: tls_load:
+; CHECK-NEXT: .functype tls_load () -> (i32)
+define i32 @tls_load() {
+ ; TLS-DAG: global.get __tls_base
+ ; TLS-DAG: i32.const tls
+ ; TLS-NEXT: i32.add
+ ; TLS-NEXT: i32.load 0
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const 0
+ ; NO-TLS-NEXT: i32.load tls
+ ; NO-TLS-NEXT: return
+ %tmp = load i32, i32* @tls, align 4
+ ret i32 %tmp
+}
+
+; CHECK-LABEL: tls_store:
+; CHECK-NEXT: .functype tls_store (i32) -> ()
+define void @tls_store(i32 %x) {
+ ; TLS-DAG: global.get __tls_base
+ ; TLS-DAG: i32.const tls
+ ; TLS-NEXT: i32.add
+ ; TLS-NEXT: i32.store 0
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const 0
+ ; NO-TLS-NEXT: i32.store tls
+ ; NO-TLS-NEXT: return
+ store i32 %x, i32* @tls, align 4
+ ret void
+}
+
+; CHECK-LABEL: tls_size:
+; CHECK-NEXT: .functype tls_size () -> (i32)
+define i32 @tls_size() {
+; CHECK-NEXT: global.get __tls_size
+; CHECK-NEXT: return
+ %1 = call i32 @llvm.wasm.tls.size.i32()
+ ret i32 %1
+}
+
+; CHECK: .type tls, at object
+; TLS-NEXT: .section .tbss.tls,"",@
+; NO-TLS-NEXT: .section .bss.tls,"",@
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: tls:
+; CHECK-NEXT: .int32 0
+ at tls = internal thread_local(localexec) global i32 0
+
+declare i32 @llvm.wasm.tls.size.i32()
More information about the llvm-commits
mailing list