; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM
; SHMEM: - Type: CODE
-; SHMEM: - Index: 6
+; SHMEM: - Index: 7
; SHMEM-NEXT: Locals: []
; SHMEM-NEXT: Body: 100310050B
; SHMEM-NEXT: - Index: 5
; SHMEM-NEXT: Name: __wasm_apply_global_relocs
; SHMEM-NEXT: - Index: 6
-; SHMEM-NEXT: Name: __wasm_start
+; SHMEM-NEXT: Name: __wasm_apply_global_tls_relocs
; SHMEM-NEXT: - Index: 7
-; SHMEM-NEXT: Name: foo
+; SHMEM-NEXT: Name: __wasm_start
; SHMEM-NEXT: - Index: 8
-; SHMEM-NEXT: Name: get_data_address
+; SHMEM-NEXT: Name: foo
; SHMEM-NEXT: - Index: 9
+; SHMEM-NEXT: Name: get_data_address
+; SHMEM-NEXT: - Index: 10
; SHMEM-NEXT: Name: _start
# SO1-NEXT: TableSize: 0
# SO1-NEXT: TableAlignment: 0
# SO1-NEXT: Needed: []
+# SO1-NEXT: ExportInfo: []
# SO1-NEXT: - Type: TYPE
# SO2: Sections:
# SO2-NEXT: TableAlignment: 0
# SO2-NEXT: Needed:
# SO2-NEXT: - shared-needed.s.tmp1.so
+# SO2-NEXT: ExportInfo: []
# SO2-NEXT: - Type: TYPE
# CHECK-NEXT: TableSize: 2
# CHECK-NEXT: TableAlignment: 0
# CHECK-NEXT: Needed: []
+# CHECK-NEXT: ExportInfo: []
# CHECK-NEXT: - Type: TYPE
# check for import of __table_base and __memory_base globals
# CHECK-NEXT: TableSize: 2
# CHECK-NEXT: TableAlignment: 0
# CHECK-NEXT: Needed: []
+# CHECK-NEXT: ExportInfo: []
# CHECK-NEXT: - Type: TYPE
# check for import of __table_base and __memory_base globals
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: wasm-ld -no-gc-sections --shared-memory --no-entry -o %t.wasm %t.o
-# RUN: not wasm-ld --shared-memory --no-entry --export=tls1 -o %t.wasm %t.o 2>&1 | FileCheck %s
-# With --export-all we ignore TLS symbols so we don't expect an error here
-# RUN: wasm-ld --shared-memory --no-entry --export-all -o %t.wasm %t.o
-
-# CHECK: error: TLS symbols cannot yet be exported: `tls1`
+# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o
+# RUN: obj2yaml %t.so | FileCheck %s
.section .tdata.tls1,"",@
.globl tls1
.int8 43
.int8 15
.ascii "mutable-globals"
+
+# CHECK: ExportInfo:
+# CHECK-NEXT: - Name: tls1
+# CHECK-NEXT: Flags: [ TLS ]
+# CHECK-NEXT: - Type: TYPE
+
+# CHECK: - Type: GLOBAL
+# CHECK-NEXT: Globals:
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: false
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 0
+
+# CHECK: - Type: EXPORT
+# CHECK-NEXT: Exports:
+# CHECK-NEXT: - Name: __wasm_call_ctors
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: tls1
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: Index: 2
+++ /dev/null
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: not wasm-ld -shared --experimental-pic --shared-memory -o %t.wasm %t.o 2>&1 | FileCheck %s
-
-# CHECK: error: {{.*}}.o: TLS symbol is undefined, but TLS symbols cannot yet be imported: `foo`
-
-.globl _start
-_start:
- .functype _start () -> ()
- i32.const foo@TLSREL
- drop
- end_function
-
-.section .custom_section.target_features,"",@
- .int8 3
- .int8 43
- .int8 7
- .ascii "atomics"
- .int8 43
- .int8 11
- .ascii "bulk-memory"
- .int8 43
- .int8 15
- .ascii "mutable-globals"
# Test that linking without shared memory causes __tls_base to be
-# internalized
+# internalized.
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
i32.add
end_function
+.globl get_tls1_got
+get_tls1_got:
+ .functype get_tls1_got () -> (i32)
+ global.get tls1@GOT@TLS
+ end_function
+
.section .data.no_tls,"",@
.globl no_tls
.p2align 2
.int32 42
.size no_tls, 4
-.section .tdata.tls1,"",@
+.section .tdata.tls1,"T",@
.globl tls1
.p2align 2
tls1:
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1024
+# GOT.data.internal.tls1
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: false
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1024
# CHECK-NEXT: - Type: EXPORT
# CHECK: - Type: DATA
}
if (config->isPic) {
- if (sym->isTLS() && sym->isUndefined()) {
- error(toString(file) +
- ": TLS symbol is undefined, but TLS symbols cannot yet be "
- "imported: `" +
- toString(*sym) + "`");
- }
switch (reloc.Type) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_SLEB64:
DefinedFunction *WasmSym::initMemory;
DefinedFunction *WasmSym::applyDataRelocs;
DefinedFunction *WasmSym::applyGlobalRelocs;
+DefinedFunction *WasmSym::applyGlobalTLSRelocs;
DefinedFunction *WasmSym::initTLS;
DefinedFunction *WasmSym::startFunction;
DefinedData *WasmSym::dsoHandle;
static DefinedFunction *applyDataRelocs;
// __wasm_apply_global_relocs
- // Function that applies relocations to data segment post-instantiation.
+ // Function that applies relocations to wasm globals post-instantiation.
// Unlike __wasm_apply_data_relocs this needs to run on every thread.
static DefinedFunction *applyGlobalRelocs;
+ // __wasm_apply_global_tls_relocs
+ // Like applyGlobalRelocs but for globals that hold TLS addresess. These
+ // must be delayed until __wasm_init_tls.
+ static DefinedFunction *applyGlobalTLSRelocs;
+
// __wasm_init_tls
// Function that allocates thread-local storage and initializes it.
static DefinedFunction *initTLS;
writeStr(sub.os, llvm::sys::path::filename(so->getName()), "so name");
sub.writeTo(os);
}
+
+ // Under certain circumstances we need to include extra information about the
+ // exports we are providing to the dynamic linker. Currently this is only the
+ // case for TLS symbols where the exported value is relative to __tls_base
+ // rather than __memory_base.
+ std::vector<const Symbol *> exportInfo;
+ for (const Symbol *sym : symtab->getSymbols()) {
+ if (sym->isExported() && sym->isLive() && sym->isTLS() &&
+ isa<DefinedData>(sym)) {
+ exportInfo.push_back(sym);
+ }
+ }
+
+ if (!exportInfo.empty()) {
+ SubSection sub(WASM_DYLINK_EXPORT_INFO);
+ writeUleb128(sub.os, exportInfo.size(), "num exports");
+
+ for (const Symbol *sym : exportInfo) {
+ LLVM_DEBUG(llvm::dbgs() << "export info: " << toString(*sym) << "\n");
+ StringRef name = sym->getName();
+ if (auto *f = dyn_cast<DefinedFunction>(sym)) {
+ if (Optional<StringRef> exportName = f->function->getExportName()) {
+ name = *exportName;
+ }
+ }
+ writeStr(sub.os, name, "sym name");
+ writeUleb128(sub.os, sym->flags, "sym flags");
+ }
+
+ sub.writeTo(os);
+ }
}
uint32_t TypeSection::registerType(const WasmSignature &sig) {
internalGotSymbols.push_back(sym);
}
-void GlobalSection::generateRelocationCode(raw_ostream &os) const {
+void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
bool is64 = config->is64.getValueOr(false);
unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
: WASM_OPCODE_I32_CONST;
: WASM_OPCODE_I32_ADD;
for (const Symbol *sym : internalGotSymbols) {
+ if (TLS != sym->isTLS())
+ continue;
+
if (auto *d = dyn_cast<DefinedData>(sym)) {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
- writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base");
+ if (sym->isTLS())
+ writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base");
+ else
+ writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
+ "__memory_base");
// Add the virtual address of the data symbol
writeU8(os, opcode_ptr_const, "CONST");
// transform a `global.get` to an `i32.const`.
void addInternalGOTEntry(Symbol *sym);
bool needsRelocations() { return internalGotSymbols.size(); }
- void generateRelocationCode(raw_ostream &os) const;
+ void generateRelocationCode(raw_ostream &os, bool TLS) const;
- std::vector<const DefinedData *> dataAddressGlobals;
+ std::vector<DefinedData *> dataAddressGlobals;
std::vector<InputGlobal *> inputGlobals;
std::vector<Symbol *> internalGotSymbols;
void createStartFunction();
void createApplyDataRelocationsFunction();
void createApplyGlobalRelocationsFunction();
+ void createApplyGlobalTLSRelocationsFunction();
void createCallCtorsFunction();
void createInitTLSFunction();
void createCommandExportWrappers();
} else if (auto *t = dyn_cast<DefinedTag>(sym)) {
export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()};
} else if (auto *d = dyn_cast<DefinedData>(sym)) {
- if (sym->isTLS()) {
- // We can't currenly export TLS data symbols.
- if (sym->isExportedExplicit())
- error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`");
- continue;
- }
out.globalSec->dataAddressGlobals.push_back(d);
export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
} else {
"__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
WasmSym::applyGlobalRelocs->markLive();
+ if (config->sharedMemory) {
+ WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction(
+ "__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+ make<SyntheticFunction>(nullSignature,
+ "__wasm_apply_global_tls_relocs"));
+ WasmSym::applyGlobalTLSRelocs->markLive();
+ }
}
}
{
raw_string_ostream os(bodyContent);
writeUleb128(os, 0, "num locals");
- out.globalSec->generateRelocationCode(os);
+ out.globalSec->generateRelocationCode(os, false);
writeU8(os, WASM_OPCODE_END, "END");
}
createFunction(WasmSym::applyGlobalRelocs, bodyContent);
}
+// Similar to createApplyGlobalRelocationsFunction but for
+// TLS symbols. This cannot be run during the start function
+// but must be delayed until __wasm_init_tls is called.
+void Writer::createApplyGlobalTLSRelocationsFunction() {
+ // First write the body's contents to a string.
+ std::string bodyContent;
+ {
+ raw_string_ostream os(bodyContent);
+ writeUleb128(os, 0, "num locals");
+ out.globalSec->generateRelocationCode(os, true);
+ writeU8(os, WASM_OPCODE_END, "END");
+ }
+
+ createFunction(WasmSym::applyGlobalTLSRelocs, bodyContent);
+}
+
// Create synthetic "__wasm_call_ctors" function based on ctor functions
// in input object.
void Writer::createCallCtorsFunction() {
writeUleb128(os, tlsSeg->index, "segment index immediate");
writeU8(os, 0, "memory index immediate");
}
+
+ if (WasmSym::applyGlobalTLSRelocs) {
+ writeU8(os, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(os, WasmSym::applyGlobalTLSRelocs->getFunctionIndex(),
+ "function index");
+ }
writeU8(os, WASM_OPCODE_END, "end function");
}
createApplyDataRelocationsFunction();
if (WasmSym::applyGlobalRelocs)
createApplyGlobalRelocationsFunction();
+ if (WasmSym::applyGlobalTLSRelocs)
+ createApplyGlobalTLSRelocationsFunction();
if (WasmSym::initMemory)
createInitMemoryFunction();
createStartFunction();
uint32_t Version;
};
+struct WasmDylinkExport {
+ StringRef Name;
+ uint32_t Flags;
+};
+
struct WasmDylinkInfo {
uint32_t MemorySize; // Memory size in bytes
uint32_t MemoryAlignment; // P2 alignment of memory
uint32_t TableSize; // Table size in elements
uint32_t TableAlignment; // P2 alignment of table
std::vector<StringRef> Needed; // Shared library dependencies
+ std::vector<WasmDylinkExport> ExportInfo; // Shared library dependencies
};
struct WasmProducerInfo {
enum : unsigned {
WASM_DYLINK_MEM_INFO = 0x1,
WASM_DYLINK_NEEDED = 0x2,
+ WASM_DYLINK_EXPORT_INFO = 0x3,
};
// Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO
VK_WASM_TLSREL, // Memory address relative to __tls_base
VK_WASM_MBREL, // Memory address relative to __memory_base
VK_WASM_TBREL, // Table index relative to __table_base
+ VK_WASM_GOT_TLS, // Wasm global index of TLS symbol.
VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo
VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi
yaml::BinaryRef Payload;
};
+struct DylinkExport {
+ StringRef Name;
+ SymbolFlags Flags;
+};
+
struct DylinkSection : CustomSection {
DylinkSection() : CustomSection("dylink.0") {}
uint32_t TableSize;
uint32_t TableAlignment;
std::vector<StringRef> Needed;
+ std::vector<DylinkExport> ExportInfo;
};
struct NameSection : CustomSection {
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ComdatEntry)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Comdat)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Tag)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExport)
namespace llvm {
namespace yaml {
static void mapping(IO &IO, WasmYAML::Tag &Tag);
};
+template <> struct MappingTraits<WasmYAML::DylinkExport> {
+ static void mapping(IO &IO, WasmYAML::DylinkExport &Export);
+};
+
} // end namespace yaml
} // end namespace llvm
case VK_WASM_MBREL: return "MBREL";
case VK_WASM_TLSREL: return "TLSREL";
case VK_WASM_TBREL: return "TBREL";
+ case VK_WASM_GOT_TLS: return "GOT@TLS";
case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo";
case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi";
case VK_AMDGPU_REL32_LO: return "rel32@lo";
.Case("tbrel", VK_WASM_TBREL)
.Case("mbrel", VK_WASM_MBREL)
.Case("tlsrel", VK_WASM_TLSREL)
+ .Case("got@tls", VK_WASM_GOT_TLS)
.Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO)
.Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI)
.Case("rel32@lo", VK_AMDGPU_REL32_LO)
case MCExpr::SymbolRef: {
const MCSymbolRefExpr &symRef = *cast<MCSymbolRefExpr>(expr);
- if (symRef.getKind() == MCSymbolRefExpr::VK_WASM_TLSREL) {
+ switch (symRef.getKind()) {
+ case MCSymbolRefExpr::VK_WASM_TLSREL:
+ case MCSymbolRefExpr::VK_WASM_GOT_TLS:
getAssembler().registerSymbol(symRef.getSymbol());
cast<MCSymbolWasm>(symRef.getSymbol()).setTLS();
+ break;
+ default:
+ break;
}
break;
}
SymA->setUsedInReloc();
}
- if (RefA->getKind() == MCSymbolRefExpr::VK_GOT)
+ switch (RefA->getKind()) {
+ case MCSymbolRefExpr::VK_GOT:
+ case MCSymbolRefExpr::VK_WASM_GOT_TLS:
SymA->setUsedInGOT();
+ break;
+ default:
+ break;
+ }
WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection);
LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n");
DylinkInfo.Needed.push_back(readString(Ctx));
}
break;
+ case wasm::WASM_DYLINK_EXPORT_INFO: {
+ uint32_t Count = readVaruint32(Ctx);
+ while (Count--) {
+ DylinkInfo.ExportInfo.push_back({readString(Ctx), readVaruint32(Ctx)});
+ }
+ break;
+ }
default:
return make_error<GenericBinaryError>("unknown dylink.0 sub-section",
object_error::parse_failed);
IO.mapRequired("TableSize", Section.TableSize);
IO.mapRequired("TableAlignment", Section.TableAlignment);
IO.mapRequired("Needed", Section.Needed);
+ IO.mapRequired("ExportInfo", Section.ExportInfo);
}
static void sectionMapping(IO &IO, WasmYAML::NameSection &Section) {
IO.mapRequired("SigIndex", Tag.SigIndex);
}
+void MappingTraits<WasmYAML::DylinkExport>::mapping(
+ IO &IO, WasmYAML::DylinkExport &Export) {
+ IO.mapRequired("Name", Export.Name);
+ IO.mapRequired("Flags", Export.Flags);
+}
+
void ScalarBitSetTraits<WasmYAML::LimitFlags>::bitset(
IO &IO, WasmYAML::LimitFlags &Value) {
#define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X)
break;
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
case wasm::WASM_SYMBOL_TYPE_DATA:
- if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) {
+ switch (SymRef->getKind()) {
+ case MCSymbolRefExpr::VK_GOT:
+ case MCSymbolRefExpr::VK_WASM_GOT_TLS:
Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32;
+ return false;
+ default:
break;
}
LLVM_FALLTHROUGH;
// platforms.
MO_GOT,
+ // Same as MO_GOT but the address stored in the global is a TLS address.
+ MO_GOT_TLS,
+
// On a symbol operand this indicates that the immediate is the symbol
// address relative the __memory_base wasm global.
// Only applicable to data symbols.
switch (Modifier) {
case MCSymbolRefExpr::VK_GOT:
+ case MCSymbolRefExpr::VK_WASM_GOT_TLS:
return wasm::R_WASM_GLOBAL_INDEX_LEB;
case MCSymbolRefExpr::VK_WASM_TBREL:
assert(SymA.isFunction());
: wasm::R_WASM_MEMORY_ADDR_REL_SLEB;
case MCSymbolRefExpr::VK_WASM_TYPEINDEX:
return wasm::R_WASM_TYPE_INDEX_LEB;
+ case MCSymbolRefExpr::VK_None:
+ break;
default:
+ report_fatal_error("unknown VariantKind");
break;
}
SelectionDAG &DAG) const {
SDLoc DL(Op);
const auto *GA = cast<GlobalAddressSDNode>(Op);
- MVT PtrVT = getPointerTy(DAG.getDataLayout());
MachineFunction &MF = DAG.getMachineFunction();
if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
false);
}
- auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
- : WebAssembly::GLOBAL_GET_I32;
- const char *BaseName = MF.createExternalSymbolName("__tls_base");
+ auto model = GV->getThreadLocalMode();
- SDValue BaseAddr(
- DAG.getMachineNode(GlobalGet, DL, PtrVT,
- DAG.getTargetExternalSymbol(BaseName, PtrVT)),
- 0);
+ // Unsupported TLS modes
+ assert(model != GlobalValue::NotThreadLocal);
+ assert(model != GlobalValue::InitialExecTLSModel);
- SDValue TLSOffset = DAG.getTargetGlobalAddress(
- GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
- SDValue SymOffset =
- DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
+ if (model == GlobalValue::LocalExecTLSModel ||
+ model == GlobalValue::LocalDynamicTLSModel ||
+ (model == GlobalValue::GeneralDynamicTLSModel &&
+ getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV))) {
+ // For DSO-local TLS variables we use offset from __tls_base
- return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
+ MVT PtrVT = getPointerTy(DAG.getDataLayout());
+ auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
+ : WebAssembly::GLOBAL_GET_I32;
+ const char *BaseName = MF.createExternalSymbolName("__tls_base");
+
+ SDValue BaseAddr(
+ DAG.getMachineNode(GlobalGet, DL, PtrVT,
+ DAG.getTargetExternalSymbol(BaseName, PtrVT)),
+ 0);
+
+ SDValue TLSOffset = DAG.getTargetGlobalAddress(
+ GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
+ SDValue SymOffset =
+ DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
+
+ return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
+ }
+
+ assert(model == GlobalValue::GeneralDynamicTLSModel);
+
+ EVT VT = Op.getValueType();
+ return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
+ DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
+ GA->getOffset(),
+ WebAssemblyII::MO_GOT_TLS));
}
SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
def : Pat<(i64 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
(CONST_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
+def : Pat<(i32 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
+ (GLOBAL_GET_I32 tglobaltlsaddr:$addr)>, Requires<[HasAddr32]>;
+def : Pat<(i64 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
+ (GLOBAL_GET_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
+
def : Pat<(i32 (WebAssemblyWrapper texternalsym:$addr)),
(GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC, HasAddr32]>;
def : Pat<(i64 (WebAssemblyWrapper texternalsym:$addr)),
switch (TargetFlags) {
case WebAssemblyII::MO_NO_FLAG:
break;
+ case WebAssemblyII::MO_GOT_TLS:
+ Kind = MCSymbolRefExpr::VK_WASM_GOT_TLS;
+ break;
case WebAssemblyII::MO_GOT:
Kind = MCSymbolRefExpr::VK_GOT;
break;
ret i32 ptrtoint(i32* @tls to i32)
}
+; CHECK-LABEL: address_of_tls_external:
+; CHECK-NEXT: .functype address_of_tls_external () -> (i32)
+define i32 @address_of_tls_external() {
+ ; TLS-DAG: global.get tls_external@GOT@TLS
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const tls_external
+ ; NO-TLS-NEXT: return
+ ret i32 ptrtoint(i32* @tls_external to i32)
+}
+
; CHECK-LABEL: ptr_to_tls:
; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
define i32* @ptr_to_tls() {
ret i32* @tls
}
+; CHECK-LABEL: ptr_to_tls_external:
+; CHECK-NEXT: .functype ptr_to_tls_external () -> (i32)
+define i32* @ptr_to_tls_external() {
+ ; TLS-DAG: global.get tls_external@GOT@TLS
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const tls_external
+ ; NO-TLS-NEXT: return
+ ret i32* @tls_external
+}
+
; CHECK-LABEL: tls_load:
; CHECK-NEXT: .functype tls_load () -> (i32)
define i32 @tls_load() {
ret i32 %tmp
}
+; CHECK-LABEL: tls_load_external:
+; CHECK-NEXT: .functype tls_load_external () -> (i32)
+define i32 @tls_load_external() {
+ ; TLS-DAG: global.get tls_external@GOT@TLS
+ ; TLS-NEXT: i32.load 0
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const 0
+ ; NO-TLS-NEXT: i32.load tls_external
+ ; NO-TLS-NEXT: return
+ %tmp = load i32, i32* @tls_external, align 4
+ ret i32 %tmp
+}
+
; CHECK-LABEL: tls_store:
; CHECK-NEXT: .functype tls_store (i32) -> ()
define void @tls_store(i32 %x) {
ret void
}
+; CHECK-LABEL: tls_store_external:
+; CHECK-NEXT: .functype tls_store_external (i32) -> ()
+define void @tls_store_external(i32 %x) {
+ ; TLS-DAG: global.get tls_external@GOT@TLS
+ ; TLS-NEXT: i32.store 0
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const 0
+ ; NO-TLS-NEXT: i32.store tls_external
+ ; NO-TLS-NEXT: return
+ store i32 %x, i32* @tls_external, align 4
+ ret void
+}
+
; CHECK-LABEL: tls_size:
; CHECK-NEXT: .functype tls_size () -> (i32)
define i32 @tls_size() {
; CHECK-NEXT: .int32 0
@tls = internal thread_local global i32 0
+@tls_external = external thread_local global i32, align 4
+
declare i32 @llvm.wasm.tls.size.i32()
declare i32 @llvm.wasm.tls.align.i32()
declare i8* @llvm.wasm.tls.base()
ret i32 ptrtoint(i32* @tls to i32)
}
+; CHECK-LABEL: address_of_tls_external:
+; CHECK-NEXT: .functype address_of_tls_external () -> (i32)
+define i32 @address_of_tls_external() {
+ ; TLS-DAG: global.get __tls_base
+ ; TLS-DAG: i32.const tls_external@TLSREL
+ ; TLS-NEXT: i32.add
+ ; TLS-NEXT: return
+
+ ; NO-TLS-NEXT: i32.const tls_external
+ ; NO-TLS-NEXT: return
+ ret i32 ptrtoint(i32* @tls_external to i32)
+}
+
; CHECK-LABEL: ptr_to_tls:
; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
define i32* @ptr_to_tls() {
; CHECK-NEXT: .int32 0
@tls = internal thread_local(localexec) global i32 0
+@tls_external = external thread_local(localexec) global i32, align 4
+
declare i32 @llvm.wasm.tls.size.i32()
tls_get_undefined:
.functype tls_get_undefined (i32) -> (i32)
- i32.const tls_undefined@TLSREL
+ global.get tls_undefined@GOT@TLS
end_function
.section .tls.foo,"T",@
# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_TLS_SLEB
# CHECK-OBJ-NEXT: Index: 2
# CHECK-OBJ-NEXT: Offset: 0xA
+# CHECK-OBJ-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
+# CHECK-OBJ-NEXT: Index: 4
+# CHECK-OBJ-NEXT: Offset: 0x19
# CHECK-OBJ: - Type: CUSTOM
# CHECK-OBJ-NEXT: Name: linking
DylinkSec->TableSize = Info.TableSize;
DylinkSec->TableAlignment = Info.TableAlignment;
DylinkSec->Needed = Info.Needed;
+ for (const auto &Exp : Info.ExportInfo)
+ DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags});
CustomSec = std::move(DylinkSec);
} else if (WasmSec.Name == "name") {
std::unique_ptr<WasmYAML::NameSection> NameSec =