[WebAssembly] Compute and export TLS block alignment

Summary:
Add immutable WASM global `__tls_align` which stores the alignment
requirements of the TLS segment.

Add `__builtin_wasm_tls_align()` intrinsic to get this alignment in Clang.

The expected usage has now changed to:

    __wasm_init_tls(memalign(__builtin_wasm_tls_align(),
                             __builtin_wasm_tls_size()));

Reviewers: tlively, aheejin, sbc100, sunfish, alexcrichton

Reviewed By: tlively

Subscribers: dschuff, jgravelle-google, hiraditya, cfe-commits, llvm-commits

Tags: #clang, #llvm

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

llvm-svn: 366624
This commit is contained in:
Guanzhong Chen 2019-07-19 23:34:16 +00:00
parent 604f802fd3
commit 5204f7611f
13 changed files with 137 additions and 19 deletions

View File

@ -31,6 +31,7 @@ TARGET_BUILTIN(__builtin_wasm_data_drop, "vIUi", "", "bulk-memory")
// Thread-local storage
TARGET_BUILTIN(__builtin_wasm_tls_size, "z", "nc", "bulk-memory")
TARGET_BUILTIN(__builtin_wasm_tls_align, "z", "nc", "bulk-memory")
TARGET_BUILTIN(__builtin_wasm_tls_base, "v*", "nU", "bulk-memory")
// Floating point min/max

View File

@ -13924,6 +13924,11 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_tls_size, ResultType);
return Builder.CreateCall(Callee);
}
case WebAssembly::BI__builtin_wasm_tls_align: {
llvm::Type *ResultType = ConvertType(E->getType());
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_tls_align, ResultType);
return Builder.CreateCall(Callee);
}
case WebAssembly::BI__builtin_wasm_tls_base: {
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_tls_base);
return Builder.CreateCall(Callee);

View File

@ -44,6 +44,12 @@ __SIZE_TYPE__ tls_size() {
// WEBASSEMBLY64: call i64 @llvm.wasm.tls.size.i64()
}
__SIZE_TYPE__ tls_align() {
return __builtin_wasm_tls_align();
// WEBASSEMBLY32: call i32 @llvm.wasm.tls.align.i32()
// WEBASSEMBLY64: call i64 @llvm.wasm.tls.align.i64()
}
void *tls_base() {
return __builtin_wasm_tls_base();
// WEBASSEMBLY: call i8* @llvm.wasm.tls.base()

View File

@ -0,0 +1,51 @@
; RUN: llc -mattr=+bulk-memory -filetype=obj %s -o %t.o
target triple = "wasm32-unknown-unknown"
@no_tls = global i32 0, align 4
@tls1 = thread_local(localexec) global i32 1, align 4
@tls2 = thread_local(localexec) global i32 1, align 16
define i32* @tls1_addr() {
ret i32* @tls1
}
define i32* @tls2_addr() {
ret i32* @tls2
}
; RUN: wasm-ld -no-gc-sections --shared-memory --max-memory=131072 --no-entry -o %t.wasm %t.o
; RUN: obj2yaml %t.wasm | FileCheck %s
; CHECK: - Type: GLOBAL
; 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: 66608
; __tls_base
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Type: I32
; CHECK-NEXT: Mutable: true
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 0
; __tls_size
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Type: I32
; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 20
; __tls_align
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Type: I32
; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 16

View File

@ -14,6 +14,13 @@ define i32* @tls2_addr() {
ret i32* @tls2
}
define i32 @tls_align() {
%1 = call i32 @llvm.wasm.tls.align.i32()
ret i32 %1
}
declare i32 @llvm.wasm.tls.align.i32()
; RUN: wasm-ld -no-gc-sections --shared-memory --max-memory=131072 --no-entry -o %t.wasm %t.o
; RUN: obj2yaml %t.wasm | FileCheck %s
@ -28,12 +35,16 @@ define i32* @tls2_addr() {
; 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: true
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 0
; __tls_size
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Type: I32
; CHECK-NEXT: Mutable: false
@ -41,6 +52,14 @@ define i32* @tls2_addr() {
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 8
; __tls_align
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Type: I32
; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 4
; CHECK: - Type: CODE
; CHECK-NEXT: Functions:
@ -77,3 +96,11 @@ define i32* @tls2_addr() {
; i32.const 4
; i32.add
; end
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals: []
; CHECK-NEXT: Body: 2383808080000B
; Expected body of tls1_addr:
; global.get 3
; end

View File

@ -450,6 +450,16 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
return sym;
}
static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
wasmGlobal.InitExpr.Value.Int32 = 0;
wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
wasmGlobal.SymbolName = name;
return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
make<InputGlobal>(wasmGlobal, nullptr));
}
// Create ABI-defined synthetic symbols
static void createSyntheticSymbols() {
static WasmSignature nullSignature = {{}, {}};
@ -517,24 +527,9 @@ static void createSyntheticSymbols() {
}
if (config->sharedMemory && !config->shared) {
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::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>(i32ArgSignature, "__wasm_init_tls"));

View File

@ -35,6 +35,7 @@ DefinedData *WasmSym::heapBase;
GlobalSymbol *WasmSym::stackPointer;
GlobalSymbol *WasmSym::tlsBase;
GlobalSymbol *WasmSym::tlsSize;
GlobalSymbol *WasmSym::tlsAlign;
UndefinedGlobal *WasmSym::tableBase;
UndefinedGlobal *WasmSym::memoryBase;

View File

@ -435,6 +435,10 @@ struct WasmSym {
// Symbol whose value is the size of the TLS block.
static GlobalSymbol *tlsSize;
// __tls_size
// Symbol whose value is the alignment of the TLS block.
static GlobalSymbol *tlsAlign;
// __data_end
// Symbol marking the end of the data and bss.
static DefinedData *dataEnd;

View File

@ -247,6 +247,9 @@ void Writer::layoutMemory() {
if (WasmSym::tlsSize && seg->name == ".tdata") {
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
tlsAlign->global->global.InitExpr.Value.Int32 = 1U << seg->alignment;
}
}

View File

@ -133,6 +133,11 @@ def int_wasm_tls_size :
[],
[IntrNoMem, IntrSpeculatable]>;
def int_wasm_tls_align :
Intrinsic<[llvm_anyint_ty],
[],
[IntrNoMem, IntrSpeculatable]>;
def int_wasm_tls_base :
Intrinsic<[llvm_ptr_ty],
[],

View File

@ -224,6 +224,16 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
ReplaceNode(Node, TLSSize);
return;
}
case Intrinsic::wasm_tls_align: {
MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
MachineSDNode *TLSAlign = CurDAG->getMachineNode(
WebAssembly::GLOBAL_GET_I32, DL, PtrVT,
CurDAG->getTargetExternalSymbol("__tls_align", MVT::i32));
ReplaceNode(Node, TLSAlign);
return;
}
}
break;
}

View File

@ -79,7 +79,7 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
// Clang-provided symbols.
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) {
strcmp(Name, "__tls_size") == 0 || strcmp(Name, "__tls_align") == 0) {
bool Mutable =
strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);

View File

@ -75,6 +75,15 @@ define i32 @tls_size() {
ret i32 %1
}
; CHECK-LABEL: tls_align:
; CHECK-NEXT: .functype tls_align () -> (i32)
define i32 @tls_align() {
; CHECK-NEXT: global.get __tls_align
; CHECK-NEXT: return
%1 = call i32 @llvm.wasm.tls.align.i32()
ret i32 %1
}
; CHECK-LABEL: tls_base:
; CHECK-NEXT: .functype tls_base () -> (i32)
define i8* @tls_base() {
@ -104,4 +113,5 @@ define void @tls_base_write(i8** %output) {
@tls = internal thread_local global i32 0
declare i32 @llvm.wasm.tls.size.i32()
declare i32 @llvm.wasm.tls.align.i32()
declare i8* @llvm.wasm.tls.base()