[WebAssembly] Allow import and export of TLS symbols between DSOs

We previously had a limitation that TLS variables could not
be exported (and therefore could also not be imported).  This
change removed that limitation.

Differential Revision: https://reviews.llvm.org/D108877
This commit is contained in:
Sam Clegg
2021-08-26 15:29:32 -04:00
parent 5041a485b9
commit ef8c9135ef
31 changed files with 307 additions and 70 deletions

View File

@@ -91,7 +91,7 @@ declare void @external_func()
; 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
@@ -109,11 +109,13 @@ declare void @external_func()
; 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

View File

@@ -29,6 +29,7 @@ data:
# SO1-NEXT: TableSize: 0
# SO1-NEXT: TableAlignment: 0
# SO1-NEXT: Needed: []
# SO1-NEXT: ExportInfo: []
# SO1-NEXT: - Type: TYPE
# SO2: Sections:
@@ -40,4 +41,5 @@ data:
# SO2-NEXT: TableAlignment: 0
# SO2-NEXT: Needed:
# SO2-NEXT: - shared-needed.s.tmp1.so
# SO2-NEXT: ExportInfo: []
# SO2-NEXT: - Type: TYPE

View File

@@ -133,6 +133,7 @@ get_local_func_address:
# 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

View File

@@ -134,6 +134,7 @@ get_local_func_address:
# 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

View File

@@ -1,10 +1,6 @@
# 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
@@ -24,3 +20,26 @@ 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

View File

@@ -1,23 +0,0 @@
# 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"

View File

@@ -1,5 +1,5 @@
# 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
@@ -13,6 +13,12 @@ get_tls1:
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
@@ -20,7 +26,7 @@ no_tls:
.int32 42
.size no_tls, 4
.section .tdata.tls1,"",@
.section .tdata.tls1,"T",@
.globl tls1
.p2align 2
tls1:
@@ -58,6 +64,13 @@ 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

View File

@@ -139,12 +139,6 @@ void scanRelocations(InputChunk *chunk) {
}
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:

View File

@@ -74,6 +74,7 @@ DefinedFunction *WasmSym::callDtors;
DefinedFunction *WasmSym::initMemory;
DefinedFunction *WasmSym::applyDataRelocs;
DefinedFunction *WasmSym::applyGlobalRelocs;
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
DefinedFunction *WasmSym::initTLS;
DefinedFunction *WasmSym::startFunction;
DefinedData *WasmSym::dsoHandle;

View File

@@ -552,10 +552,15 @@ struct WasmSym {
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;

View File

@@ -73,6 +73,37 @@ void DylinkSection::writeBody() {
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) {
@@ -345,7 +376,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
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;
@@ -353,10 +384,17 @@ void GlobalSection::generateRelocationCode(raw_ostream &os) 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");

View File

@@ -287,9 +287,9 @@ public:
// 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;

View File

@@ -63,6 +63,7 @@ private:
void createStartFunction();
void createApplyDataRelocationsFunction();
void createApplyGlobalRelocationsFunction();
void createApplyGlobalTLSRelocationsFunction();
void createCallCtorsFunction();
void createInitTLSFunction();
void createCommandExportWrappers();
@@ -639,12 +640,6 @@ void Writer::calculateExports() {
} 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 {
@@ -991,6 +986,13 @@ void Writer::createSyntheticInitFunctions() {
"__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();
}
}
}
@@ -1235,13 +1237,29 @@ void Writer::createApplyGlobalRelocationsFunction() {
{
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() {
@@ -1352,6 +1370,12 @@ void Writer::createInitTLSFunction() {
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");
}
@@ -1486,6 +1510,8 @@ void Writer::run() {
createApplyDataRelocationsFunction();
if (WasmSym::applyGlobalRelocs)
createApplyGlobalRelocationsFunction();
if (WasmSym::applyGlobalTLSRelocs)
createApplyGlobalTLSRelocationsFunction();
if (WasmSym::initMemory)
createInitMemoryFunction();
createStartFunction();

View File

@@ -36,12 +36,18 @@ struct WasmObjectHeader {
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 {
@@ -345,6 +351,7 @@ enum : unsigned {
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

View File

@@ -328,6 +328,7 @@ public:
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

View File

@@ -199,6 +199,11 @@ struct CustomSection : Section {
yaml::BinaryRef Payload;
};
struct DylinkExport {
StringRef Name;
SymbolFlags Flags;
};
struct DylinkSection : CustomSection {
DylinkSection() : CustomSection("dylink.0") {}
@@ -212,6 +217,7 @@ struct DylinkSection : CustomSection {
uint32_t TableSize;
uint32_t TableAlignment;
std::vector<StringRef> Needed;
std::vector<DylinkExport> ExportInfo;
};
struct NameSection : CustomSection {
@@ -426,6 +432,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction)
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 {
@@ -574,6 +581,10 @@ template <> struct MappingTraits<WasmYAML::Tag> {
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

View File

@@ -358,6 +358,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
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";
@@ -499,6 +500,7 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
.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)

View File

@@ -232,9 +232,14 @@ void MCWasmStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) {
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;
}

View File

@@ -565,8 +565,14 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
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");

View File

@@ -384,6 +384,13 @@ Error WasmObjectFile::parseDylink0Section(ReadContext &Ctx) {
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);

View File

@@ -55,6 +55,7 @@ static void sectionMapping(IO &IO, WasmYAML::DylinkSection &Section) {
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) {
@@ -531,6 +532,12 @@ void MappingTraits<WasmYAML::Tag>::mapping(IO &IO, WasmYAML::Tag &Tag) {
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)

View File

@@ -155,8 +155,12 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
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;

View File

@@ -95,6 +95,9 @@ enum TOF {
// 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.

View File

@@ -74,6 +74,7 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
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());
@@ -88,7 +89,10 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
: 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;
}

View File

@@ -1539,7 +1539,6 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
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())
@@ -1561,21 +1560,43 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
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,

View File

@@ -398,6 +398,11 @@ def : Pat<(i32 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
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)),

View File

@@ -102,6 +102,9 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
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;

View File

@@ -20,6 +20,17 @@ define i32 @address_of_tls() {
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() {
@@ -33,6 +44,17 @@ 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() {
@@ -49,6 +71,20 @@ 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) {
@@ -65,6 +101,20 @@ 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() {
@@ -111,6 +161,8 @@ define void @tls_base_write(i8** %output) {
; 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()

View File

@@ -16,6 +16,19 @@ define i32 @address_of_tls() {
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() {
@@ -78,4 +91,6 @@ define i32 @tls_size() {
; 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()

View File

@@ -20,7 +20,7 @@ tls_store:
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",@
@@ -43,6 +43,9 @@ tls2:
# 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

View File

@@ -60,6 +60,8 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
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 =