diff --git a/lld/test/wasm/tls.s b/lld/test/wasm/tls.s --- a/lld/test/wasm/tls.s +++ b/lld/test/wasm/tls.s @@ -40,6 +40,8 @@ .int32 0 .size no_tls, 4 +// Older versions of LLVM did not use the "T" flag so we need to support +// infering TLS from the name alone. .section .tdata.tls1,"",@ .globl tls1 .p2align 2 @@ -47,14 +49,14 @@ .int32 1 .size tls1, 4 -.section .tdata.tls2,"",@ +.section sec_tls1,"T",@ .globl tls2 .p2align 2 tls2: .int32 1 .size tls2, 4 -.section .tbss.tls3,"",@ +.section sec_tls2,"T",@ .globl tls3 .p2align 2 tls3: diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -137,8 +137,11 @@ uint64_t getVA(uint64_t offset = 0) const; - bool isTLS() { - return getName().startswith(".tdata") || getName().startswith(".tbss"); + bool isTLS() const { + // Older object files don't include WASM_SEG_FLAG_TLS and instead + // relied on the naming convention. + return flags & llvm::wasm::WASM_SEG_FLAG_TLS || + getName().startswith(".tdata") || getName().startswith(".tbss"); } const OutputSegment *outputSeg = nullptr; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -461,9 +461,7 @@ // Find TLS data segments auto isTLS = [](InputSegment *segment) { - StringRef name = segment->getName(); - return segment->live && - (name.startswith(".tdata") || name.startswith(".tbss")); + return segment->live && segment->isTLS(); }; tlsUsed = tlsUsed || std::any_of(file->segments.begin(), file->segments.end(), isTLS); @@ -818,12 +816,12 @@ out.tableSec->assignIndexes(); } -static StringRef getOutputDataSegmentName(StringRef name) { - // We only support one thread-local segment, so we must merge the segments - // despite --no-merge-data-segments. - // We also need to merge .tbss into .tdata so they share the same offsets. - if (name.startswith(".tdata") || name.startswith(".tbss")) +static StringRef getOutputDataSegmentName(const InputSegment &seg) { + // We always merge .tbss and .tdata into a single TLS segment so all TLS + // symbols are be relative to single __tls_base. + if (seg.isTLS()) return ".tdata"; + StringRef name = seg.getName(); if (!config->mergeDataSegments) return name; if (name.startswith(".text.")) @@ -857,7 +855,7 @@ for (InputSegment *segment : file->segments) { if (!segment->live) continue; - StringRef name = getOutputDataSegmentName(segment->getName()); + StringRef name = getOutputDataSegmentName(*segment); OutputSegment *s = nullptr; // When running in relocatable mode we can't merge segments that are part // of comdat groups since the ultimate linker needs to be able exclude or diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -359,6 +359,7 @@ enum WasmSegmentFlag : unsigned { WASM_SEG_FLAG_STRINGS = 0x1, + WASM_SEG_FLAG_TLS = 0x2, }; // Kinds of event attributes. diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2009,11 +2009,14 @@ static unsigned getWasmSectionFlags(SectionKind K) { unsigned Flags = 0; - // TODO(sbc): Add suport for K.isMergeableConst() + if (K.isThreadLocal()) + Flags |= wasm::WASM_SEG_FLAG_TLS; if (K.isMergeableCString()) Flags |= wasm::WASM_SEG_FLAG_STRINGS; + // TODO(sbc): Add suport for K.isMergeableConst() + return Flags; } diff --git a/llvm/lib/MC/MCParser/WasmAsmParser.cpp b/llvm/lib/MC/MCParser/WasmAsmParser.cpp --- a/llvm/lib/MC/MCParser/WasmAsmParser.cpp +++ b/llvm/lib/MC/MCParser/WasmAsmParser.cpp @@ -100,6 +100,9 @@ case 'G': Group = true; break; + case 'T': + flags |= wasm::WASM_SEG_FLAG_TLS; + break; case 'S': flags |= wasm::WASM_SEG_FLAG_STRINGS; break; diff --git a/llvm/lib/MC/MCSectionWasm.cpp b/llvm/lib/MC/MCSectionWasm.cpp --- a/llvm/lib/MC/MCSectionWasm.cpp +++ b/llvm/lib/MC/MCSectionWasm.cpp @@ -69,6 +69,8 @@ OS << 'G'; if (SegmentFlags & wasm::WASM_SEG_FLAG_STRINGS) OS << 'S'; + if (SegmentFlags & wasm::WASM_SEG_FLAG_TLS) + OS << 'T'; OS << '"'; diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -544,6 +544,7 @@ IO &IO, WasmYAML::SegmentFlags &Value) { #define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_SEG_FLAG_##X) BCase(STRINGS); + BCase(TLS); #undef BCase } diff --git a/llvm/test/MC/WebAssembly/tls.s b/llvm/test/MC/WebAssembly/tls.s --- a/llvm/test/MC/WebAssembly/tls.s +++ b/llvm/test/MC/WebAssembly/tls.s @@ -5,15 +5,26 @@ tls_store: .functype tls_store (i32) -> () # CHECK: global.get __tls_base - # CHECK-NEXT: i32.const tls@TLSREL + # CHECK-NEXT: i32.const tls1@TLSREL # CHECK-NEXT: i32.add # CHECK-NEXT: i32.store 0 global.get __tls_base - i32.const tls@TLSREL + i32.const tls1@TLSREL i32.add i32.store 0 end_function +.section .tls.foo,"T",@ +# CHECK: .tls.foo,"T",@ +tls1: + .int32 42 + .size tls1, 4 + +.section custom_tls,"T",@ +# CHECK: custom_tls,"T",@ +tls2: + .int32 43 + .size tls2, 4 # CHECK-OBJ: - Type: CODE # CHECK-OBJ-NEXT: Relocations: @@ -39,5 +50,22 @@ # CHECK-OBJ-NEXT: Flags: [ UNDEFINED ] # CHECK-OBJ-NEXT: - Index: 2 # CHECK-OBJ-NEXT: Kind: DATA -# CHECK-OBJ-NEXT: Name: tls -# CHECK-OBJ-NEXT: Flags: [ UNDEFINED ] +# CHECK-OBJ-NEXT: Name: tls1 +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Segment: 0 +# CHECK-OBJ-NEXT: Size: 4 +# CHECK-OBJ-NEXT: - Index: 3 +# CHECK-OBJ-NEXT: Kind: DATA +# CHECK-OBJ-NEXT: Name: tls2 +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Segment: 1 +# CHECK-OBJ-NEXT: Size: 4 +# CHECK-OBJ-NEXT: SegmentInfo: +# CHECK-OBJ-NEXT: - Index: 0 +# CHECK-OBJ-NEXT: Name: .tls.foo +# CHECK-OBJ-NEXT: Alignment: 0 +# CHECK-OBJ-NEXT: Flags: [ TLS ] +# CHECK-OBJ-NEXT: - Index: 1 +# CHECK-OBJ-NEXT: Name: custom_tls +# CHECK-OBJ-NEXT: Alignment: 0 +# CHECK-OBJ-NEXT: Flags: [ TLS ]