[clang] [lld] [llvm] [lld][WebAssembly] WASIP3 and component model threading support (PR #175800)
Sy Brand via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 17 06:26:32 PST 2026
https://github.com/TartanLlama updated https://github.com/llvm/llvm-project/pull/175800
>From 48a90dd7e288ad6c3ce8038c5b2d143dcc1dac34 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Mon, 5 Jan 2026 14:43:56 +0000
Subject: [PATCH 1/6] Initial work
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 33 ++-
lld/wasm/Config.h | 10 +
lld/wasm/Driver.cpp | 66 +++--
lld/wasm/Symbols.cpp | 8 +-
lld/wasm/SyntheticSections.cpp | 11 +-
lld/wasm/Writer.cpp | 27 +-
lld/wasm/WriterUtils.cpp | 24 +-
lld/wasm/WriterUtils.h | 4 +
llvm/include/llvm/MC/MCSymbolWasm.h | 8 +-
.../AsmParser/WebAssemblyAsmParser.cpp | 22 +-
.../MCTargetDesc/WebAssemblyMCTargetDesc.h | 242 +++++++++---------
.../WebAssembly/WebAssemblyAsmPrinter.cpp | 31 ++-
.../WebAssembly/WebAssemblyFrameLowering.cpp | 53 ++--
.../WebAssembly/WebAssemblyFrameLowering.h | 6 +-
.../WebAssembly/WebAssemblyISelDAGToDAG.cpp | 6 +-
.../WebAssembly/WebAssemblyISelLowering.cpp | 21 +-
.../WebAssembly/WebAssemblyLateEHPrepare.cpp | 2 +-
.../WebAssembly/WebAssemblyUtilities.cpp | 18 ++
.../Target/WebAssembly/WebAssemblyUtilities.h | 9 +
19 files changed, 372 insertions(+), 229 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 15c6f19e87fee..d6ff6584258d2 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -30,13 +30,14 @@ using namespace llvm::opt;
std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
- return (TargetTriple.getArchName() + "-" +
- TargetTriple.getOSAndEnvironmentName()).str();
+ return (TargetTriple.getArchName() + "-" +
+ TargetTriple.getOSAndEnvironmentName())
+ .str();
}
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
- if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
+ if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
StringRef UseLinker = A->getValue();
if (!UseLinker.empty()) {
if (llvm::sys::path::is_absolute(UseLinker) &&
@@ -79,6 +80,10 @@ static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) {
return WantsPthread;
}
+static bool WantsSharedMemory(const llvm::Triple &Triple, const ArgList &Args) {
+ return WantsPthread(Triple, Args) && !TargetBuildsComponents(Triple);
+}
+
void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
@@ -90,10 +95,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
+ std::string arch;
if (ToolChain.getTriple().isArch64Bit())
- CmdArgs.push_back("wasm64");
+ arch = "wasm64";
else
- CmdArgs.push_back("wasm32");
+ arch = "wasm32";
+ if (ToolChain.getTriple().getOSName() == "wasip3")
+ arch += "-wasip3";
+ CmdArgs.push_back(Args.MakeArgString(arch));
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
@@ -160,7 +169,7 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (WantsPthread(ToolChain.getTriple(), Args))
+ if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
@@ -233,9 +242,9 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
/// Given a base library directory, append path components to form the
/// LTO directory.
static std::string AppendLTOLibDir(const std::string &Dir) {
- // The version allows the path to be keyed to the specific version of
- // LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ // The version allows the path to be keyed to the specific version of
+ // LLVM in used, as the bitcode format is not stable.
+ return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -502,7 +511,8 @@ void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
if (getTriple().getOS() != llvm::Triple::UnknownOS) {
const std::string MultiarchTriple =
getMultiarchTriple(D, getTriple(), D.SysRoot);
- addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
+ addSystemInclude(DriverArgs, CC1Args,
+ D.SysRoot + "/include/" + MultiarchTriple);
}
addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
}
@@ -631,5 +641,6 @@ void WebAssembly::addLibStdCXXIncludePaths(
// Second add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
// Third the backward one.
- addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
+ addSystemInclude(DriverArgs, CC1Args,
+ LibPath + "/c++/" + Version + "/backward");
}
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 9d903e0c799db..55dea0a78eadb 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -35,6 +35,7 @@ class Symbol;
class DefinedData;
class GlobalSymbol;
class DefinedFunction;
+class UndefinedFunction;
class UndefinedGlobal;
class TableSymbol;
@@ -70,6 +71,7 @@ struct Config {
bool importTable;
bool importUndefined;
std::optional<bool> is64;
+ bool isWasip3;
bool mergeDataSegments;
bool noinhibitExec;
bool pie;
@@ -248,6 +250,14 @@ struct Ctx {
// Used as an address space for function pointers, with each function that
// is used as a function pointer being allocated a slot.
TableSymbol *indirectFunctionTable;
+
+ // __wasm_component_model_builtin_context_set_1
+ // Function used to set TLS base in component model modules.
+ UndefinedFunction *contextSet1;
+
+ // __wasm_component_model_builtin_context_get_1
+ // Function used to get TLS base in component model modules.
+ UndefinedFunction *contextGet1;
};
WasmSym sym;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index fac166587cb9b..79ecc6c9f5858 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,15 +656,16 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared);
- // Parse wasm32/64.
+ // Parse wasm32/64 and maybe -wasip3.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
- if (s == "wasm32")
+ if (s.starts_with("wasm32"))
ctx.arg.is64 = false;
- else if (s == "wasm64")
+ else if (s.starts_with("wasm64"))
ctx.arg.is64 = true;
else
error("invalid target architecture: " + s);
+ ctx.arg.isWasip3 = s.ends_with("-wasip3");
}
// --threads= takes a positive integer and provides the default value for
@@ -827,6 +828,10 @@ static void checkOptions(opt::InputArgList &args) {
if (ctx.arg.tableBase)
error("--table-base may not be used with -shared/-pie");
}
+
+ if (ctx.arg.sharedMemory && ctx.arg.isWasip3) {
+ error("--shared-memory is incompatible with the wasip3 target");
+ }
}
static const char *getReproduceOption(opt::InputArgList &args) {
@@ -885,7 +890,7 @@ static void writeWhyExtract() {
// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker
static void demoteLazySymbols() {
for (Symbol *sym : symtab->symbols()) {
- if (auto* s = dyn_cast<LazySymbol>(sym)) {
+ if (auto *s = dyn_cast<LazySymbol>(sym)) {
if (s->signature) {
LLVM_DEBUG(llvm::dbgs()
<< "demoting lazy func: " << s->getName() << "\n");
@@ -906,6 +911,18 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
return sym;
}
+static UndefinedFunction *
+createUndefinedFunction(StringRef name, std::optional<StringRef> importName,
+ std::optional<StringRef> importModule,
+ WasmSignature *signature) {
+ auto *sym = cast<UndefinedFunction>(symtab->addUndefinedFunction(
+ name, importName, importModule, WASM_SYMBOL_UNDEFINED, nullptr, signature,
+ true));
+ ctx.arg.allowUndefinedSymbols.insert(sym->getName());
+ sym->isUsedInRegularObj = true;
+ return sym;
+}
+
static InputGlobal *createGlobal(StringRef name, bool isMutable) {
llvm::wasm::WasmGlobal wasmGlobal;
bool is64 = ctx.arg.is64.value_or(false);
@@ -946,11 +963,13 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
+ auto stack_pointer_name =
+ ctx.arg.isWasip3 ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
- createUndefinedGlobal("__stack_pointer", ctx.arg.is64.value_or(false)
- ? &mutableGlobalTypeI64
- : &mutableGlobalTypeI32);
+ createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
+ ? &mutableGlobalTypeI64
+ : &mutableGlobalTypeI32);
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
@@ -963,14 +982,15 @@ static void createSyntheticSymbols() {
ctx.sym.tableBase->markLive();
} else {
// For non-PIC code
- ctx.sym.stackPointer = createGlobalVariable("__stack_pointer", true);
+ ctx.sym.stackPointer = createGlobalVariable(stack_pointer_name, true);
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.sharedMemory || ctx.arg.isWasip3) {
// TLS symbols are all hidden/dso-local
- ctx.sym.tlsBase =
- createGlobalVariable("__tls_base", true, WASM_SYMBOL_VISIBILITY_HIDDEN);
+ auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
+ ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
+ WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsAlign = createGlobalVariable("__tls_align", false,
@@ -979,6 +999,16 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
+ static WasmSignature contextSet1Signature{{}, {ValType::I32}};
+ ctx.sym.contextSet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
+ "$root", &contextSet1Signature);
+ ctx.sym.contextSet1->markLive();
+ static WasmSignature contextGet1Signature{{ValType::I32}, {}};
+ ctx.sym.contextGet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
+ "$root", &contextGet1Signature);
+ ctx.sym.contextGet1->markLive();
}
}
@@ -1014,7 +1044,7 @@ static void createOptionalSymbols() {
//
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory)
+ if (!ctx.arg.sharedMemory && !ctx.arg.isWasip3)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1023,15 +1053,15 @@ static void processStubLibrariesPreLTO() {
for (auto &stub_file : ctx.stubFiles) {
LLVM_DEBUG(llvm::dbgs()
<< "processing stub file: " << stub_file->getName() << "\n");
- for (auto [name, deps]: stub_file->symbolDependencies) {
- auto* sym = symtab->find(name);
+ for (auto [name, deps] : stub_file->symbolDependencies) {
+ auto *sym = symtab->find(name);
// If the symbol is not present at all (yet), or if it is present but
// undefined, then mark the dependent symbols as used by a regular
// object so they will be preserved and exported by the LTO process.
if (!sym || sym->isUndefined()) {
for (const auto dep : deps) {
- auto* needed = symtab->find(dep);
- if (needed ) {
+ auto *needed = symtab->find(dep);
+ if (needed) {
needed->isUsedInRegularObj = true;
// Like with handleLibcall we have to extract any LTO archive
// members that might need to be exported due to stub library
@@ -1103,8 +1133,8 @@ static void processStubLibraries() {
// First look for any imported symbols that directly match
// the names of the stub imports
- for (auto [name, deps]: stub_file->symbolDependencies) {
- auto* sym = symtab->find(name);
+ for (auto [name, deps] : stub_file->symbolDependencies) {
+ auto *sym = symtab->find(name);
if (sym && sym->isUndefined()) {
depsAdded |= addStubSymbolDeps(stub_file, sym, deps);
} else {
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..97a9871a06308 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -95,7 +95,7 @@ WasmSymbolType Symbol::getWasmType() const {
}
const WasmSignature *Symbol::getSignature() const {
- if (auto* f = dyn_cast<FunctionSymbol>(this))
+ if (auto *f = dyn_cast<FunctionSymbol>(this))
return f->signature;
if (auto *t = dyn_cast<TagSymbol>(this))
return t->signature;
@@ -223,9 +223,7 @@ bool Symbol::isExportedExplicit() const {
return forceExport || flags & WASM_SYMBOL_EXPORTED;
}
-bool Symbol::isNoStrip() const {
- return flags & WASM_SYMBOL_NO_STRIP;
-}
+bool Symbol::isNoStrip() const { return flags & WASM_SYMBOL_NO_STRIP; }
uint32_t FunctionSymbol::getFunctionIndex() const {
if (const auto *u = dyn_cast<UndefinedFunction>(this))
@@ -413,7 +411,7 @@ void LazySymbol::setWeak() {
flags |= (flags & ~WASM_SYMBOL_BINDING_MASK) | WASM_SYMBOL_BINDING_WEAK;
}
-void printTraceSymbolUndefined(StringRef name, const InputFile* file) {
+void printTraceSymbolUndefined(StringRef name, const InputFile *file) {
message(toString(file) + ": reference to " + name);
}
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 399a5084e6595..0d3e060ac381e 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -434,8 +434,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
assert(!ctx.arg.extendedConst);
bool is64 = ctx.arg.is64.value_or(false);
- unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
- : WASM_OPCODE_I32_ADD;
+ unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
for (const Symbol *sym : internalGotSymbols) {
if (TLS != sym->isTLS())
@@ -445,7 +444,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
// Get __memory_base
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ writeGetTLSBase(ctx, os);
else
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");
@@ -614,7 +613,7 @@ void ElemSection::writeBody() {
uint32_t tableIndex = ctx.arg.tableBase;
for (const FunctionSymbol *sym : indirectFunctions) {
assert(sym->getTableIndex() == tableIndex);
- (void) tableIndex;
+ (void)tableIndex;
writeUleb128(os, sym->getFunctionIndex(), "function index");
++tableIndex;
}
@@ -631,7 +630,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && ctx.arg.sharedMemory;
+ return numSegments && (ctx.arg.sharedMemory || ctx.arg.isWasip3);
}
void LinkingSection::writeBody() {
@@ -960,4 +959,4 @@ void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) {
memcpy(hashPlaceholderPtr, buf.data(), hashSize);
}
-} // namespace wasm::lld
+} // namespace lld::wasm
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 9a5b56fc52e2f..12f72dc239a09 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -311,7 +311,8 @@ void Writer::writeBuildId() {
}
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
- LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
+ LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr
+ << "\n");
g->global->setPointerValue(memoryPtr);
}
@@ -358,7 +359,8 @@ void Writer::layoutMemory() {
placeStack();
if (ctx.arg.globalBase) {
if (ctx.arg.globalBase < memoryPtr) {
- error("--global-base cannot be less than stack size when --stack-first is used");
+ error("--global-base cannot be less than stack size when --stack-first "
+ "is used");
return;
}
memoryPtr = ctx.arg.globalBase;
@@ -382,6 +384,7 @@ void Writer::layoutMemory() {
for (OutputSegment *seg : segments) {
out.dylinkSec->memAlign = std::max(out.dylinkSec->memAlign, seg->alignment);
memoryPtr = alignTo(memoryPtr, 1ULL << seg->alignment);
+
seg->startVA = memoryPtr;
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name,
memoryPtr, seg->size, seg->alignment));
@@ -1175,7 +1178,7 @@ void Writer::createSyntheticInitFunctions() {
auto hasTLSRelocs = [](const OutputSegment *segment) {
if (segment->isTLS())
- for (const auto* is: segment->inputSegments)
+ for (const auto *is : segment->inputSegments)
if (is->getRelocations().size())
return true;
return false;
@@ -1350,8 +1353,7 @@ void Writer::createInitMemoryFunction() {
} else {
writePtrConst(os, s->startVA, is64, "destination address");
}
- writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET");
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ writeSetTLSBase(ctx, os);
if (ctx.isPic) {
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.tee");
writeUleb128(os, 1, "local 1");
@@ -1624,10 +1626,17 @@ void Writer::createInitTLSFunction() {
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
- writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set");
- writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "global index");
+ if (ctx.arg.isWasip3) {
+ writeU8(os, WASM_OPCODE_CALL, "call");
+ writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(),
+ "function index");
+ } else {
+ writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set");
+ writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "global index");
+ }
- // FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op.
+ // FIXME(wvo): this local needs to be I64 in wasm64, or we need an
+ // extend op.
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
@@ -1893,4 +1902,4 @@ void Writer::createHeader() {
void writeResult() { Writer().run(); }
-} // namespace wasm::lld
+} // namespace lld::wasm
diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index b78c354ffb97b..8e945f57ef39c 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include "WriterUtils.h"
+#include "Config.h"
+#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
@@ -97,7 +99,7 @@ void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) {
}
void writeBytes(raw_ostream &os, const char *bytes, size_t count,
- const Twine &msg) {
+ const Twine &msg) {
debugWrite(os.tell(), msg + " [data[" + Twine(count) + "]]");
os.write(bytes, count);
}
@@ -266,5 +268,25 @@ void writeExport(raw_ostream &os, const WasmExport &export_) {
}
}
+void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
+ if (ctx.arg.isWasip3) {
+ writeU8(os, WASM_OPCODE_CALL, "call");
+ writeUleb128(os, ctx.sym.contextGet1->getFunctionIndex(), "function index");
+ } else {
+ writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_SET");
+ writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ }
+}
+
+void writeSetTLSBase(const Ctx &ctx, raw_ostream &os) {
+ if (ctx.arg.isWasip3) {
+ writeU8(os, WASM_OPCODE_CALL, "call");
+ writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(), "function index");
+ } else {
+ writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET");
+ writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");
+ }
+}
+
} // namespace wasm
} // namespace lld
diff --git a/lld/wasm/WriterUtils.h b/lld/wasm/WriterUtils.h
index 2be79d1d86e97..404c5c33ed7b6 100644
--- a/lld/wasm/WriterUtils.h
+++ b/lld/wasm/WriterUtils.h
@@ -64,6 +64,10 @@ void writeImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
void writeExport(raw_ostream &os, const llvm::wasm::WasmExport &export_);
+struct Ctx;
+void writeGetTLSBase(const Ctx &ctx, raw_ostream &os);
+void writeSetTLSBase(const Ctx &ctx, raw_ostream &os);
+
} // namespace wasm
std::string toString(llvm::wasm::ValType type);
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h
index 5c9f14e5e6d64..11cbce5b0ccd0 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -54,16 +54,12 @@ class MCSymbolWasm : public MCSymbol {
void setType(wasm::WasmSymbolType type) { Type = type; }
- bool isExported() const {
- return getFlags() & wasm::WASM_SYMBOL_EXPORTED;
- }
+ bool isExported() const { return getFlags() & wasm::WASM_SYMBOL_EXPORTED; }
void setExported() const {
modifyFlags(wasm::WASM_SYMBOL_EXPORTED, wasm::WASM_SYMBOL_EXPORTED);
}
- bool isNoStrip() const {
- return getFlags() & wasm::WASM_SYMBOL_NO_STRIP;
- }
+ bool isNoStrip() const { return getFlags() & wasm::WASM_SYMBOL_NO_STRIP; }
void setNoStrip() const {
modifyFlags(wasm::WASM_SYMBOL_NO_STRIP, wasm::WASM_SYMBOL_NO_STRIP);
}
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 9175b2731dac0..25704c9ef0ac4 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -415,6 +415,23 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
return Name;
}
+ StringRef expectStringOrIdent() {
+ if (Lexer.is(AsmToken::String)) {
+ auto Str = Lexer.getTok().getString();
+ Parser.Lex();
+ Str.consume_front("\"");
+ Str.consume_back("\"");
+ return Str;
+ }
+ if (Lexer.is(AsmToken::Identifier)) {
+ auto Name = Lexer.getTok().getString();
+ Parser.Lex();
+ llvm::outs() << "Parsed ident: " << Name << "\n";
+ return Name;
+ }
+ error("Expected string or identifier, got: ", Lexer.getTok());
+ }
+
bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
while (Lexer.is(AsmToken::Identifier)) {
auto Type = WebAssembly::parseType(Lexer.getTok().getString());
@@ -1057,7 +1074,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
return ParseStatus::Failure;
if (expect(AsmToken::Comma, ","))
return ParseStatus::Failure;
- auto ImportModule = expectIdent();
+
+ StringRef ImportModule = expectStringOrIdent();
if (ImportModule.empty())
return ParseStatus::Failure;
auto *WasmSym =
@@ -1073,7 +1091,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
return ParseStatus::Failure;
if (expect(AsmToken::Comma, ","))
return ParseStatus::Failure;
- auto ImportName = expectIdent();
+ StringRef ImportName = expectStringOrIdent();
if (ImportName.empty())
return ParseStatus::Failure;
auto *WasmSym =
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 5dc0e3aa91622..5c6d07ba88c61 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -146,128 +146,128 @@ static const unsigned End = 0x0b;
/// Return the default p2align value for a load or store with the given opcode.
inline unsigned GetDefaultP2AlignAny(unsigned Opc) {
switch (Opc) {
-#define WASM_LOAD_STORE(NAME) \
- case WebAssembly::NAME##_A32: \
- case WebAssembly::NAME##_A64: \
- case WebAssembly::NAME##_A32_S: \
+#define WASM_LOAD_STORE(NAME) \
+ case WebAssembly::NAME##_A32: \
+ case WebAssembly::NAME##_A64: \
+ case WebAssembly::NAME##_A32_S: \
case WebAssembly::NAME##_A64_S:
- WASM_LOAD_STORE(LOAD8_S_I32)
- WASM_LOAD_STORE(LOAD8_U_I32)
- WASM_LOAD_STORE(LOAD8_S_I64)
- WASM_LOAD_STORE(LOAD8_U_I64)
- WASM_LOAD_STORE(ATOMIC_LOAD8_U_I32)
- WASM_LOAD_STORE(ATOMIC_LOAD8_U_I64)
- WASM_LOAD_STORE(STORE8_I32)
- WASM_LOAD_STORE(STORE8_I64)
- WASM_LOAD_STORE(ATOMIC_STORE8_I32)
- WASM_LOAD_STORE(ATOMIC_STORE8_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_ADD_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_ADD_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_SUB_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_SUB_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_AND_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_AND_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_OR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_OR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_XOR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_XOR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_XCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_XCHG_I64)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_CMPXCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW8_U_CMPXCHG_I64)
- WASM_LOAD_STORE(LOAD8_SPLAT)
- WASM_LOAD_STORE(LOAD_LANE_8)
- WASM_LOAD_STORE(STORE_LANE_I8x16)
- return 0;
- WASM_LOAD_STORE(LOAD16_S_I32)
- WASM_LOAD_STORE(LOAD16_U_I32)
- WASM_LOAD_STORE(LOAD16_S_I64)
- WASM_LOAD_STORE(LOAD16_U_I64)
- WASM_LOAD_STORE(ATOMIC_LOAD16_U_I32)
- WASM_LOAD_STORE(ATOMIC_LOAD16_U_I64)
- WASM_LOAD_STORE(STORE16_I32)
- WASM_LOAD_STORE(STORE16_I64)
- WASM_LOAD_STORE(ATOMIC_STORE16_I32)
- WASM_LOAD_STORE(ATOMIC_STORE16_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_ADD_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_ADD_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_SUB_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_SUB_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_AND_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_AND_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_OR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_OR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_XOR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_XOR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_XCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_XCHG_I64)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_CMPXCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW16_U_CMPXCHG_I64)
- WASM_LOAD_STORE(LOAD16_SPLAT)
- WASM_LOAD_STORE(LOAD_LANE_16)
- WASM_LOAD_STORE(STORE_LANE_I16x8)
- WASM_LOAD_STORE(LOAD_F16_F32)
- WASM_LOAD_STORE(STORE_F16_F32)
- return 1;
- WASM_LOAD_STORE(LOAD_I32)
- WASM_LOAD_STORE(LOAD_F32)
- WASM_LOAD_STORE(STORE_I32)
- WASM_LOAD_STORE(STORE_F32)
- WASM_LOAD_STORE(LOAD32_S_I64)
- WASM_LOAD_STORE(LOAD32_U_I64)
- WASM_LOAD_STORE(STORE32_I64)
- WASM_LOAD_STORE(ATOMIC_LOAD_I32)
- WASM_LOAD_STORE(ATOMIC_LOAD32_U_I64)
- WASM_LOAD_STORE(ATOMIC_STORE_I32)
- WASM_LOAD_STORE(ATOMIC_STORE32_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_ADD_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_ADD_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_SUB_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_SUB_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_AND_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_AND_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_OR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_OR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_XOR_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_XOR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_XCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_XCHG_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_CMPXCHG_I32)
- WASM_LOAD_STORE(ATOMIC_RMW32_U_CMPXCHG_I64)
- WASM_LOAD_STORE(MEMORY_ATOMIC_NOTIFY)
- WASM_LOAD_STORE(MEMORY_ATOMIC_WAIT32)
- WASM_LOAD_STORE(LOAD32_SPLAT)
- WASM_LOAD_STORE(LOAD_ZERO_32)
- WASM_LOAD_STORE(LOAD_LANE_32)
- WASM_LOAD_STORE(STORE_LANE_I32x4)
- return 2;
- WASM_LOAD_STORE(LOAD_I64)
- WASM_LOAD_STORE(LOAD_F64)
- WASM_LOAD_STORE(STORE_I64)
- WASM_LOAD_STORE(STORE_F64)
- WASM_LOAD_STORE(ATOMIC_LOAD_I64)
- WASM_LOAD_STORE(ATOMIC_STORE_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_ADD_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_SUB_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_AND_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_OR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_XOR_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_XCHG_I64)
- WASM_LOAD_STORE(ATOMIC_RMW_CMPXCHG_I64)
- WASM_LOAD_STORE(MEMORY_ATOMIC_WAIT64)
- WASM_LOAD_STORE(LOAD64_SPLAT)
- WASM_LOAD_STORE(LOAD_EXTEND_S_I16x8)
- WASM_LOAD_STORE(LOAD_EXTEND_U_I16x8)
- WASM_LOAD_STORE(LOAD_EXTEND_S_I32x4)
- WASM_LOAD_STORE(LOAD_EXTEND_U_I32x4)
- WASM_LOAD_STORE(LOAD_EXTEND_S_I64x2)
- WASM_LOAD_STORE(LOAD_EXTEND_U_I64x2)
- WASM_LOAD_STORE(LOAD_ZERO_64)
- WASM_LOAD_STORE(LOAD_LANE_64)
- WASM_LOAD_STORE(STORE_LANE_I64x2)
- return 3;
- WASM_LOAD_STORE(LOAD_V128)
- WASM_LOAD_STORE(STORE_V128)
+ WASM_LOAD_STORE(LOAD8_S_I32)
+ WASM_LOAD_STORE(LOAD8_U_I32)
+ WASM_LOAD_STORE(LOAD8_S_I64)
+ WASM_LOAD_STORE(LOAD8_U_I64)
+ WASM_LOAD_STORE(ATOMIC_LOAD8_U_I32)
+ WASM_LOAD_STORE(ATOMIC_LOAD8_U_I64)
+ WASM_LOAD_STORE(STORE8_I32)
+ WASM_LOAD_STORE(STORE8_I64)
+ WASM_LOAD_STORE(ATOMIC_STORE8_I32)
+ WASM_LOAD_STORE(ATOMIC_STORE8_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_ADD_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_ADD_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_SUB_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_SUB_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_AND_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_AND_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_OR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_OR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_XOR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_XOR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_XCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_XCHG_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_CMPXCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW8_U_CMPXCHG_I64)
+ WASM_LOAD_STORE(LOAD8_SPLAT)
+ WASM_LOAD_STORE(LOAD_LANE_8)
+ WASM_LOAD_STORE(STORE_LANE_I8x16)
+ return 0;
+ WASM_LOAD_STORE(LOAD16_S_I32)
+ WASM_LOAD_STORE(LOAD16_U_I32)
+ WASM_LOAD_STORE(LOAD16_S_I64)
+ WASM_LOAD_STORE(LOAD16_U_I64)
+ WASM_LOAD_STORE(ATOMIC_LOAD16_U_I32)
+ WASM_LOAD_STORE(ATOMIC_LOAD16_U_I64)
+ WASM_LOAD_STORE(STORE16_I32)
+ WASM_LOAD_STORE(STORE16_I64)
+ WASM_LOAD_STORE(ATOMIC_STORE16_I32)
+ WASM_LOAD_STORE(ATOMIC_STORE16_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_ADD_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_ADD_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_SUB_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_SUB_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_AND_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_AND_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_OR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_OR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_XOR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_XOR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_XCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_XCHG_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_CMPXCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW16_U_CMPXCHG_I64)
+ WASM_LOAD_STORE(LOAD16_SPLAT)
+ WASM_LOAD_STORE(LOAD_LANE_16)
+ WASM_LOAD_STORE(STORE_LANE_I16x8)
+ WASM_LOAD_STORE(LOAD_F16_F32)
+ WASM_LOAD_STORE(STORE_F16_F32)
+ return 1;
+ WASM_LOAD_STORE(LOAD_I32)
+ WASM_LOAD_STORE(LOAD_F32)
+ WASM_LOAD_STORE(STORE_I32)
+ WASM_LOAD_STORE(STORE_F32)
+ WASM_LOAD_STORE(LOAD32_S_I64)
+ WASM_LOAD_STORE(LOAD32_U_I64)
+ WASM_LOAD_STORE(STORE32_I64)
+ WASM_LOAD_STORE(ATOMIC_LOAD_I32)
+ WASM_LOAD_STORE(ATOMIC_LOAD32_U_I64)
+ WASM_LOAD_STORE(ATOMIC_STORE_I32)
+ WASM_LOAD_STORE(ATOMIC_STORE32_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_ADD_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_ADD_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_SUB_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_SUB_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_AND_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_AND_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_OR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_OR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_XOR_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_XOR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_XCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_XCHG_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_CMPXCHG_I32)
+ WASM_LOAD_STORE(ATOMIC_RMW32_U_CMPXCHG_I64)
+ WASM_LOAD_STORE(MEMORY_ATOMIC_NOTIFY)
+ WASM_LOAD_STORE(MEMORY_ATOMIC_WAIT32)
+ WASM_LOAD_STORE(LOAD32_SPLAT)
+ WASM_LOAD_STORE(LOAD_ZERO_32)
+ WASM_LOAD_STORE(LOAD_LANE_32)
+ WASM_LOAD_STORE(STORE_LANE_I32x4)
+ return 2;
+ WASM_LOAD_STORE(LOAD_I64)
+ WASM_LOAD_STORE(LOAD_F64)
+ WASM_LOAD_STORE(STORE_I64)
+ WASM_LOAD_STORE(STORE_F64)
+ WASM_LOAD_STORE(ATOMIC_LOAD_I64)
+ WASM_LOAD_STORE(ATOMIC_STORE_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_ADD_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_SUB_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_AND_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_OR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_XOR_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_XCHG_I64)
+ WASM_LOAD_STORE(ATOMIC_RMW_CMPXCHG_I64)
+ WASM_LOAD_STORE(MEMORY_ATOMIC_WAIT64)
+ WASM_LOAD_STORE(LOAD64_SPLAT)
+ WASM_LOAD_STORE(LOAD_EXTEND_S_I16x8)
+ WASM_LOAD_STORE(LOAD_EXTEND_U_I16x8)
+ WASM_LOAD_STORE(LOAD_EXTEND_S_I32x4)
+ WASM_LOAD_STORE(LOAD_EXTEND_U_I32x4)
+ WASM_LOAD_STORE(LOAD_EXTEND_S_I64x2)
+ WASM_LOAD_STORE(LOAD_EXTEND_U_I64x2)
+ WASM_LOAD_STORE(LOAD_ZERO_64)
+ WASM_LOAD_STORE(LOAD_LANE_64)
+ WASM_LOAD_STORE(STORE_LANE_I64x2)
+ return 3;
+ WASM_LOAD_STORE(LOAD_V128)
+ WASM_LOAD_STORE(STORE_V128)
return 4;
default:
return -1;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 526420bb2b294..ce17ee65c45eb 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -227,11 +227,11 @@ MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) {
// 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 (Name == "__stack_pointer" || Name == "__tls_base" ||
+ if (Name == "__stack_pointer" || Name == "__init_stack_pointer" ||
+ Name == "__tls_base" || Name == "__init_tls_base" ||
Name == "__memory_base" || Name == "__table_base" ||
Name == "__tls_size" || Name == "__tls_align") {
- bool Mutable =
- Name == "__stack_pointer" || Name == "__tls_base";
+ bool Mutable = Name == "__stack_pointer" || Name == "__tls_base";
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
WasmSym->setGlobalType(wasm::WasmGlobalType{
uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
@@ -259,6 +259,26 @@ MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) {
wasm::ValType AddrType =
Subtarget.hasAddr64() ? wasm::ValType::I64 : wasm::ValType::I32;
Params.push_back(AddrType);
+ } else if (Name == "__wasm_component_model_builtin_context_get_0") {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ WasmSym->setImportModule("$root");
+ WasmSym->setImportName("[context-get-0]");
+ Returns.push_back(wasm::ValType::I32);
+ } else if (Name == "__wasm_component_model_builtin_context_set_0") {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ WasmSym->setImportModule("$root");
+ WasmSym->setImportName("[context-set-0]");
+ Params.push_back(wasm::ValType::I32);
+ } else if (Name == "__wasm_component_model_builtin_context_get_1") {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ WasmSym->setImportModule("$root");
+ WasmSym->setImportName("[context-get-1]");
+ Returns.push_back(wasm::ValType::I32);
+ } else if (Name == "__wasm_component_model_builtin_context_set_1") {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ WasmSym->setImportModule("$root");
+ WasmSym->setImportName("[context-set-1]");
+ Params.push_back(wasm::ValType::I32);
} else { // Function symbols
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
WebAssembly::getLibcallSignature(Subtarget, Name, Returns, Params);
@@ -471,7 +491,7 @@ void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
OutStreamer->switchSection(Producers);
OutStreamer->emitULEB128IntValue(FieldCount);
for (auto &Producers : {std::make_pair("language", &Languages),
- std::make_pair("processed-by", &Tools)}) {
+ std::make_pair("processed-by", &Tools)}) {
if (Producers.second->empty())
continue;
OutStreamer->emitULEB128IntValue(strlen(Producers.first));
@@ -580,7 +600,8 @@ void WebAssemblyAsmPrinter::EmitFunctionAttributes(Module &M) {
// Emit a custom section for each unique attribute.
for (const auto &[Name, Symbols] : CustomSections) {
MCSectionWasm *CustomSection = OutContext.getWasmSection(
- ".custom_section.llvm.func_attr.annotate." + Name, SectionKind::getMetadata());
+ ".custom_section.llvm.func_attr.annotate." + Name,
+ SectionKind::getMetadata());
OutStreamer->pushSection();
OutStreamer->switchSection(CustomSection);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index 5a1779c2c80fb..67b9e75be482f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -188,8 +188,7 @@ unsigned WebAssemblyFrameLowering::getFPReg(const MachineFunction &MF) {
: WebAssembly::FP32;
}
-unsigned
-WebAssemblyFrameLowering::getOpcConst(const MachineFunction &MF) {
+unsigned WebAssemblyFrameLowering::getOpcConst(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::CONST_I64
: WebAssembly::CONST_I32;
@@ -213,31 +212,38 @@ unsigned WebAssemblyFrameLowering::getOpcAnd(const MachineFunction &MF) {
: WebAssembly::AND_I32;
}
-unsigned
-WebAssemblyFrameLowering::getOpcGlobGet(const MachineFunction &MF) {
+unsigned WebAssemblyFrameLowering::getOpcGlobGet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_GET_I64
: WebAssembly::GLOBAL_GET_I32;
}
-unsigned
-WebAssemblyFrameLowering::getOpcGlobSet(const MachineFunction &MF) {
+unsigned WebAssemblyFrameLowering::getOpcGlobSet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_SET_I64
: WebAssembly::GLOBAL_SET_I32;
}
-void WebAssemblyFrameLowering::writeSPToGlobal(
+void WebAssemblyFrameLowering::writeBackSP(
unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const {
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- const char *ES = "__stack_pointer";
- auto *SPSymbol = MF.createExternalSymbolName(ES);
+ if (MF.getSubtarget<WebAssemblySubtarget>().getTargetTriple().getOSName() ==
+ "wasip3") {
+ const char *ES = "__wasm_component_model_builtin_context_set_0";
+ auto *SPSymbol = MF.createExternalSymbolName(ES);
+ BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::CALL))
+ .addExternalSymbol(SPSymbol)
+ .addReg(SrcReg);
+ } else {
+ const char *ES = "__stack_pointer";
+ auto *SPSymbol = MF.createExternalSymbolName(ES);
- BuildMI(MBB, InsertStore, DL, TII->get(getOpcGlobSet(MF)))
- .addExternalSymbol(SPSymbol)
- .addReg(SrcReg);
+ BuildMI(MBB, InsertStore, DL, TII->get(getOpcGlobSet(MF)))
+ .addExternalSymbol(SPSymbol)
+ .addReg(SrcReg);
+ }
}
MachineBasicBlock::iterator
@@ -251,7 +257,7 @@ WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
needsSPWriteback(MF)) {
DebugLoc DL = I->getDebugLoc();
- writeSPToGlobal(getSPReg(MF), MF, MBB, I, DL);
+ writeBackSP(getSPReg(MF), MF, MBB, I, DL);
}
return MBB.erase(I);
}
@@ -283,10 +289,17 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
if (StackSize)
SPReg = MRI.createVirtualRegister(PtrRC);
- const char *ES = "__stack_pointer";
- auto *SPSymbol = MF.createExternalSymbolName(ES);
- BuildMI(MBB, InsertPt, DL, TII->get(getOpcGlobGet(MF)), SPReg)
- .addExternalSymbol(SPSymbol);
+ if (ST.getTargetTriple().getOSName() == "wasip3") {
+ const char *ES = "__wasm_component_model_builtin_context_get_0";
+ auto *SPSymbol = MF.createExternalSymbolName(ES);
+ BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CALL), SPReg)
+ .addExternalSymbol(SPSymbol);
+ } else {
+ const char *ES = "__stack_pointer";
+ auto *SPSymbol = MF.createExternalSymbolName(ES);
+ BuildMI(MBB, InsertPt, DL, TII->get(getOpcGlobGet(MF)), SPReg)
+ .addExternalSymbol(SPSymbol);
+ }
bool HasBP = hasBP(MF);
if (HasBP) {
@@ -309,7 +322,7 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
Register BitmaskReg = MRI.createVirtualRegister(PtrRC);
Align Alignment = MFI.getMaxAlign();
BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), BitmaskReg)
- .addImm((int64_t) ~(Alignment.value() - 1));
+ .addImm((int64_t)~(Alignment.value() - 1));
BuildMI(MBB, InsertPt, DL, TII->get(getOpcAnd(MF)), getSPReg(MF))
.addReg(getSPReg(MF))
.addReg(BitmaskReg);
@@ -322,7 +335,7 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
.addReg(getSPReg(MF));
}
if (StackSize && needsSPWriteback(MF)) {
- writeSPToGlobal(getSPReg(MF), MF, MBB, InsertPt, DL);
+ writeBackSP(getSPReg(MF), MF, MBB, InsertPt, DL);
}
}
@@ -364,7 +377,7 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
SPReg = SPFPReg;
}
- writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
+ writeBackSP(SPReg, MF, MBB, InsertPt, DL);
}
bool WebAssemblyFrameLowering::isSupportedStackID(
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
index 710d5173d64db..e567345e93773 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
@@ -23,7 +23,7 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
public:
/// Size of the red zone for the user stack (leaf functions can use this much
/// space below the stack pointer without writing it back to __stack_pointer
- /// global).
+ /// global/context.set).
// TODO: (ABI) Revisit and decide how large it should be.
static const size_t RedZoneSize = 128;
@@ -47,8 +47,8 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
bool needsPrologForEH(const MachineFunction &MF) const;
- /// Write SP back to __stack_pointer global.
- void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF,
+ /// Write SP back to __stack_pointer global or context.set.
+ void writeBackSP(unsigned SrcReg, MachineFunction &MF,
MachineBasicBlock &MBB,
MachineBasicBlock::iterator &InsertStore,
const DebugLoc &DL) const;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
index 2541b0433ab59..d083eefe9b29d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
@@ -307,10 +307,8 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());
switch (IntNo) {
case Intrinsic::wasm_tls_base: {
- MachineSDNode *TLSBase = CurDAG->getMachineNode(
- GlobalGetIns, DL, PtrVT, MVT::Other,
- CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),
- Node->getOperand(0));
+ MachineSDNode *TLSBase =
+ llvm::WebAssembly::getTLSBase(*CurDAG, DL, Subtarget);
ReplaceNode(Node, TLSBase);
return;
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index ad70d1b7e0a1e..295ee45abe8b5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2001,17 +2001,11 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
model == GlobalValue::LocalDynamicTLSModel ||
(model == GlobalValue::GeneralDynamicTLSModel &&
getTargetMachine().shouldAssumeDSOLocal(GV))) {
- // For DSO-local TLS variables we use offset from __tls_base
+ // For DSO-local TLS variables we use offset from __tls_base, or
+ // context.get(1) on wasip3.
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 BaseAddr(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
SDValue TLSOffset = DAG.getTargetGlobalAddress(
GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
@@ -2197,14 +2191,7 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
}
case Intrinsic::thread_pointer: {
- MVT PtrVT = getPointerTy(DAG.getDataLayout());
- auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
- : WebAssembly::GLOBAL_GET_I32;
- const char *TlsBase = MF.createExternalSymbolName("__tls_base");
- return SDValue(
- DAG.getMachineNode(GlobalGet, DL, PtrVT,
- DAG.getTargetExternalSymbol(TlsBase, PtrVT)),
- 0);
+ return SDValue(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
}
}
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
index 8ac32f939c5f2..24d5c19399bdb 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
@@ -405,7 +405,7 @@ bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
WebAssembly::isCatch(InsertPos->getOpcode()) &&
"catch/catch_all should be present in every EH pad at this point");
++InsertPos; // Skip the catch instruction
- FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB,
+ FrameLowering->writeBackSP(FrameLowering->getSPReg(MF), MF, MBB,
InsertPos, MBB.begin()->getDebugLoc());
}
return Changed;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 890486778e700..2996e8ca58829 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -194,3 +194,21 @@ bool WebAssembly::canLowerReturn(size_t ResultSize,
const WebAssemblySubtarget *Subtarget) {
return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
}
+
+MachineSDNode *WebAssembly::getTLSBase(SelectionDAG &DAG, const SDLoc &DL,
+ const WebAssemblySubtarget *Subtarget) {
+ MVT PtrVT = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32;
+ auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
+ : WebAssembly::GLOBAL_GET_I32;
+
+ if (Subtarget->getTargetTriple().getOSName() == "wasip3") {
+ return DAG.getMachineNode(
+ WebAssembly::CALL, DL, PtrVT, MVT::Other,
+ DAG.getTargetExternalSymbol(
+ "__wasm_component_model_builtin_context_get_1", PtrVT),
+ DAG.getEntryNode());
+ } else {
+ return DAG.getMachineNode(GlobalGetIns, DL, PtrVT,
+ DAG.getTargetExternalSymbol("__tls_base", PtrVT));
+ }
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
index 046b1b5db2a79..9dc94d53b46e8 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -27,6 +27,9 @@ class MCSymbolWasm;
class TargetRegisterClass;
class WebAssemblyFunctionInfo;
class WebAssemblySubtarget;
+class MachineSDNode;
+class SDLoc;
+class SelectionDAG;
namespace WebAssembly {
@@ -73,6 +76,12 @@ bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget);
/// memory.
bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget);
+// Get the TLS base value for the current target
+// On wasip3: calls __wasm_component_model_builtin_context_get_1
+// Otherwise: global.get __tls_base
+MachineSDNode *getTLSBase(SelectionDAG &DAG, const SDLoc &DL,
+ const WebAssemblySubtarget *Subtarget);
+
} // end namespace WebAssembly
} // end namespace llvm
>From 540d785b5489e8b48bde3de39e736b2ab2865ca3 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Fri, 9 Jan 2026 12:24:41 +0000
Subject: [PATCH 2/6] Get wasip3 into working state
---
lld/wasm/Config.h | 2 ++
lld/wasm/Driver.cpp | 27 ++++++++-------
lld/wasm/Relocations.cpp | 4 +--
lld/wasm/SyntheticSections.cpp | 13 ++++----
lld/wasm/Writer.cpp | 10 +++---
.../WebAssembly/WebAssemblyTargetMachine.cpp | 33 ++++++++++++-------
6 files changed, 54 insertions(+), 35 deletions(-)
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 55dea0a78eadb..e26ec0e399893 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -50,6 +50,8 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid };
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Config {
+ bool isMultithreaded() const { return sharedMemory || isWasip3; }
+
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 79ecc6c9f5858..4c12e8ac9f8e7 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -986,7 +986,7 @@ static void createSyntheticSymbols() {
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.sharedMemory || ctx.arg.isWasip3) {
+ if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
@@ -999,16 +999,21 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
- static WasmSignature contextSet1Signature{{}, {ValType::I32}};
- ctx.sym.contextSet1 = createUndefinedFunction(
- "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
- "$root", &contextSet1Signature);
- ctx.sym.contextSet1->markLive();
- static WasmSignature contextGet1Signature{{ValType::I32}, {}};
- ctx.sym.contextGet1 = createUndefinedFunction(
- "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
- "$root", &contextGet1Signature);
- ctx.sym.contextGet1->markLive();
+ if (ctx.arg.isWasip3) {
+ ctx.sym.tlsBase->markLive();
+ ctx.sym.tlsSize->markLive();
+ ctx.sym.tlsAlign->markLive();
+ static WasmSignature contextSet1Signature{{}, {ValType::I32}};
+ ctx.sym.contextSet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_set_1", "[context-set-1]",
+ "$root", &contextSet1Signature);
+ ctx.sym.contextSet1->markLive();
+ static WasmSignature contextGet1Signature{{ValType::I32}, {}};
+ ctx.sym.contextGet1 = createUndefinedFunction(
+ "__wasm_component_model_builtin_context_get_1", "[context-get-1]",
+ "$root", &contextGet1Signature);
+ ctx.sym.contextGet1->markLive();
+ }
}
}
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 52888ad25034e..46bbc4806961c 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -33,7 +33,7 @@ static bool requiresGOTAccess(const Symbol *sym) {
return true;
}
-static bool allowUndefined(const Symbol* sym) {
+static bool allowUndefined(const Symbol *sym) {
// Symbols that are explicitly imported are always allowed to be undefined at
// link time.
if (sym->isImported())
@@ -125,7 +125,7 @@ void scanRelocations(InputChunk *chunk) {
// In single-threaded builds TLS is lowered away and TLS data can be
// merged with normal data and allowing TLS relocation in non-TLS
// segments.
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 0d3e060ac381e..1a4fe26145e81 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -487,9 +487,9 @@ void GlobalSection::writeBody() {
// the correct runtime value during `__wasm_apply_global_relocs`.
if (!ctx.arg.extendedConst && ctx.isPic && !sym->isTLS())
mutable_ = true;
- // With multi-theadeding any TLS globals must be mutable since they get
+ // With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.sharedMemory && sym->isTLS())
+ if (ctx.arg.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -526,10 +526,11 @@ void GlobalSection::writeBody() {
} else {
WasmInitExpr initExpr;
if (auto *d = dyn_cast<DefinedData>(sym))
- // In the sharedMemory case TLS globals are set during
- // `__wasm_apply_global_tls_relocs`, but in the non-shared case
+ // In the multi-threaded case, TLS globals are set during
+ // `__wasm_apply_global_tls_relocs`, but in the non-multi-threaded case
// we know the absolute value at link time.
- initExpr = intConst(d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64);
+ initExpr =
+ intConst(d->getVA(/*absolute=*/!ctx.arg.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -630,7 +631,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && (ctx.arg.sharedMemory || ctx.arg.isWasip3);
+ return numSegments && ctx.arg.isMultithreaded();
}
void LinkingSection::writeBody() {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 12f72dc239a09..b75cda6384b70 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1024,7 +1024,7 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
OutputSegment *Writer::createOutputSegment(StringRef name) {
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
OutputSegment *s = make<OutputSegment>(name);
- if (ctx.arg.sharedMemory)
+ if (ctx.arg.isMultithreaded())
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1158,14 +1158,14 @@ void Writer::createSyntheticInitFunctions() {
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
ctx.sym.initMemory->markLive();
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
// This global is assigned during __wasm_init_memory in the shared memory
// case.
ctx.sym.tlsBase->markLive();
}
}
- if (ctx.arg.sharedMemory) {
+ if (ctx.arg.isMultithreaded()) {
if (out.globalSec->needsTLSRelocations()) {
ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
"__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
@@ -1416,7 +1416,7 @@ void Writer::createInitMemoryFunction() {
if (needsPassiveInitialization(s) && !s->isBss) {
// The TLS region should not be dropped since its is needed
// during the initialization of each thread (__wasm_init_tls).
- if (ctx.arg.sharedMemory && s->isTLS())
+ if (ctx.arg.isMultithreaded() && s->isTLS())
continue;
// data.drop instruction
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
@@ -1626,6 +1626,8 @@ void Writer::createInitTLSFunction() {
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
+ // On WASIP3, we call `context.set 1` to set the TLS base,
+ // otherwise, we set the `__tls_base` global.
if (ctx.arg.isWasip3) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(),
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 621640c12f695..6c11958e8cf3b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -260,7 +260,8 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
// Take the union of all features used in the module and use it for each
// function individually, since having multiple feature sets in one module
// currently does not make sense for WebAssembly. If atomics are not enabled,
- // also strip atomic operations and thread local storage.
+ // also strip atomic operations and thread local storage, unless the target
+ // is WASIP3, which can use TLS without atomics due to cooperative threading.
static char ID;
WebAssemblyTargetMachine *WasmTM;
@@ -279,17 +280,25 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
bool StrippedAtomics = false;
bool StrippedTLS = false;
- if (!Features[WebAssembly::FeatureAtomics]) {
- StrippedAtomics = stripAtomics(M);
- StrippedTLS = stripThreadLocals(M);
- } else if (!Features[WebAssembly::FeatureBulkMemory]) {
- StrippedTLS |= stripThreadLocals(M);
- }
+ if (WasmTM->getTargetTriple().getOSName() == "wasip3") {
+ // WASIP3 allows TLS without atomics, so don't strip TLS even if
+ // atomics are disabled.
+ if (!Features[WebAssembly::FeatureAtomics]) {
+ StrippedAtomics = stripAtomics(M);
+ }
+ } else {
+ if (!Features[WebAssembly::FeatureAtomics]) {
+ StrippedAtomics = stripAtomics(M);
+ StrippedTLS = stripThreadLocals(M);
+ } else if (!Features[WebAssembly::FeatureBulkMemory]) {
+ StrippedTLS |= stripThreadLocals(M);
+ }
- if (StrippedAtomics && !StrippedTLS)
- stripThreadLocals(M);
- else if (StrippedTLS && !StrippedAtomics)
- stripAtomics(M);
+ if (StrippedAtomics && !StrippedTLS)
+ stripThreadLocals(M);
+ else if (StrippedTLS && !StrippedAtomics)
+ stripAtomics(M);
+ }
recordFeatures(M, Features, StrippedAtomics || StrippedTLS);
@@ -404,7 +413,7 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
// Code compiled without atomics or bulk-memory may have had its atomics or
// thread-local data lowered to nonatomic operations or non-thread-local
// data. In that case, we mark the pseudo-feature "shared-mem" as disallowed
- // to tell the linker that it would be unsafe to allow this code ot be used
+ // to tell the linker that it would be unsafe to allow this code to be used
// in a module with shared memory.
if (Stripped) {
M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
>From 8f222c968466b30c21fa7de858c2f4da8048b584 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 08:03:38 +0000
Subject: [PATCH 3/6] Don't disable threads on WASIP3 if no atomics, and don't
set the TLS from __tls_init
---
clang/lib/Basic/Targets/WebAssembly.cpp | 3 ++-
lld/wasm/Writer.cpp | 14 +++++---------
2 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 5bbb7af4c2ca1..160d39996d902 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -398,7 +398,8 @@ void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
// Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
// or __STDCPP_THREADS__ if we will eventually end up stripping atomics
// because they are unsupported.
- if (!HasAtomics || !HasBulkMemory) {
+ if (getTriple().getOSName() != "wasip3" &&
+ (!HasAtomics || !HasBulkMemory)) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
Opts.ThreadsafeStatics = false;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b75cda6384b70..67dc7c8acd343 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1623,16 +1623,12 @@ void Writer::createInitTLSFunction() {
writeUleb128(os, 0, "num locals");
if (tlsSeg) {
- writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
- writeUleb128(os, 0, "local index");
+ // On WASIP3, we don't set the TLS base inside the thread context;
+ // this should be done as part of the thread startup stub.
+ if (!ctx.arg.isWasip3) {
+ writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
+ writeUleb128(os, 0, "local index");
- // On WASIP3, we call `context.set 1` to set the TLS base,
- // otherwise, we set the `__tls_base` global.
- if (ctx.arg.isWasip3) {
- writeU8(os, WASM_OPCODE_CALL, "call");
- writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(),
- "function index");
- } else {
writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set");
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "global index");
}
>From 6d2b4645c91b5dcf9cddd984b48d721860d86b98 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 12:26:43 +0000
Subject: [PATCH 4/6] Factor out WASIP3 into options and features
---
clang/include/clang/Options/Options.td | 2 ++
clang/lib/Basic/Targets/WebAssembly.cpp | 16 ++++++++++----
clang/lib/Basic/Targets/WebAssembly.h | 4 ++++
clang/lib/Driver/ToolChains/WebAssembly.cpp | 11 ++++++++--
lld/wasm/Config.h | 4 ++--
lld/wasm/Driver.cpp | 22 ++++++++++---------
lld/wasm/Options.td | 3 +++
lld/wasm/Writer.cpp | 6 ++---
lld/wasm/WriterUtils.cpp | 4 ++--
llvm/lib/Target/WebAssembly/WebAssembly.td | 4 ++++
.../WebAssembly/WebAssemblyFrameLowering.cpp | 5 ++---
.../WebAssembly/WebAssemblyISelLowering.cpp | 2 +-
.../WebAssembly/WebAssemblyInstrInfo.td | 5 +++++
.../WebAssembly/WebAssemblySubtarget.cpp | 5 +++++
.../Target/WebAssembly/WebAssemblySubtarget.h | 2 ++
.../WebAssembly/WebAssemblyTargetMachine.cpp | 9 ++++----
.../WebAssembly/WebAssemblyUtilities.cpp | 2 +-
.../Target/WebAssembly/WebAssemblyUtilities.h | 2 +-
18 files changed, 75 insertions(+), 33 deletions(-)
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index cda11fdc94230..04972a2cd4b9c 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5438,6 +5438,8 @@ def mbulk_memory_opt : Flag<["-"], "mbulk-memory-opt">, Group<m_wasm_Features_Gr
def mno_bulk_memory_opt : Flag<["-"], "mno-bulk-memory-opt">, Group<m_wasm_Features_Group>;
def mcall_indirect_overlong : Flag<["-"], "mcall-indirect-overlong">, Group<m_wasm_Features_Group>;
def mno_call_indirect_overlong : Flag<["-"], "mno-call-indirect-overlong">, Group<m_wasm_Features_Group>;
+def mcomponent_model_thread_context : Flag<["-"], "mcomponent-model-thread-context">, Group<m_wasm_Features_Group>;
+def mno_component_model_thread_context : Flag<["-"], "mno-component-model-thread-context">, Group<m_wasm_Features_Group>;
def mexception_handing : Flag<["-"], "mexception-handling">, Group<m_wasm_Features_Group>;
def mno_exception_handing : Flag<["-"], "mno-exception-handling">, Group<m_wasm_Features_Group>;
def mextended_const : Flag<["-"], "mextended-const">, Group<m_wasm_Features_Group>;
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 160d39996d902..1d557bec6157e 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -362,6 +362,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasWideArithmetic = false;
continue;
}
+ if (Feature == "+component-model-thread-context") {
+ HasComponentModelThreadContext = true;
+ continue;
+ }
+ if (Feature == "-component-model-thread-context") {
+ HasComponentModelThreadContext = false;
+ continue;
+ }
Diags.Report(diag::err_opt_not_valid_with_opt)
<< Feature << "-target-feature";
@@ -395,10 +403,10 @@ WebAssemblyTargetInfo::getTargetBuiltins() const {
void WebAssemblyTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts,
const TargetInfo *Aux) {
TargetInfo::adjust(Diags, Opts, Aux);
- // Turn off POSIXThreads and ThreadModel so that we don't predefine _REENTRANT
- // or __STDCPP_THREADS__ if we will eventually end up stripping atomics
- // because they are unsupported.
- if (getTriple().getOSName() != "wasip3" &&
+ // If not using component model threading intrinsics, turn off POSIXThreads
+ // and ThreadModel so that we don't predefine _REENTRANT or __STDCPP_THREADS__
+ // if we will eventually end up stripping atomics because they are unsupported.
+ if (!HasComponentModelThreadContext &&
(!HasAtomics || !HasBulkMemory)) {
Opts.POSIXThreads = false;
Opts.setThreadModel(LangOptions::ThreadModelKind::Single);
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..5bd8fae7c8311 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -61,6 +61,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
bool HasBulkMemory = false;
bool HasBulkMemoryOpt = false;
bool HasCallIndirectOverlong = false;
+ bool HasComponentModelThreadContext = false;
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
bool HasFP16 = false;
@@ -105,6 +106,9 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
PtrDiffType = SignedLong;
IntPtrType = SignedLong;
}
+ if (T.getOSName() == "wasip3") {
+ HasComponentModelThreadContext = true;
+ }
}
StringRef getABI() const override;
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index d6ff6584258d2..436794445379d 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -100,8 +100,7 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
arch = "wasm64";
else
arch = "wasm32";
- if (ToolChain.getTriple().getOSName() == "wasip3")
- arch += "-wasip3";
+
CmdArgs.push_back(Args.MakeArgString(arch));
if (Args.hasArg(options::OPT_s))
@@ -172,6 +171,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (WantsSharedMemory(ToolChain.getTriple(), Args))
CmdArgs.push_back("--shared-memory");
+ // Enable component model thread context by default for wasip3
+ bool DefaultComponentModelThreadContext =
+ ToolChain.getTriple().getOSName() == "wasip3";
+ if (Args.hasFlag(options::OPT_mcomponent_model_thread_context,
+ options::OPT_mno_component_model_thread_context,
+ DefaultComponentModelThreadContext))
+ CmdArgs.push_back("--component-model-thread-context");
+
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
if (ToolChain.ShouldLinkCXXStdlib(Args))
ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index e26ec0e399893..8f7566803231d 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -50,11 +50,12 @@ enum class BuildIdKind { None, Fast, Sha1, Hexstring, Uuid };
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Config {
- bool isMultithreaded() const { return sharedMemory || isWasip3; }
+ bool isMultithreaded() const { return sharedMemory || componentModelThreadContext; }
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
+ bool componentModelThreadContext;
bool compressRelocations;
bool demangle;
bool disableVerify;
@@ -73,7 +74,6 @@ struct Config {
bool importTable;
bool importUndefined;
std::optional<bool> is64;
- bool isWasip3;
bool mergeDataSegments;
bool noinhibitExec;
bool pie;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 4c12e8ac9f8e7..ed2287d649844 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,16 +656,15 @@ static void readConfigs(opt::InputArgList &args) {
ctx.arg.exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, ctx.arg.shared);
- // Parse wasm32/64 and maybe -wasip3.
+ // Parse wasm32/64.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
- if (s.starts_with("wasm32"))
+ if (s == "wasm32")
ctx.arg.is64 = false;
- else if (s.starts_with("wasm64"))
+ else if (s == "wasm64")
ctx.arg.is64 = true;
else
error("invalid target architecture: " + s);
- ctx.arg.isWasip3 = s.ends_with("-wasip3");
}
// --threads= takes a positive integer and provides the default value for
@@ -706,6 +705,9 @@ static void readConfigs(opt::InputArgList &args) {
if (args.hasArg(OPT_print_map))
ctx.arg.mapFile = "-";
+ if (args.hasArg(OPT_component_model_thread_context))
+ ctx.arg.componentModelThreadContext = true;
+
std::tie(ctx.arg.buildId, ctx.arg.buildIdVector) = getBuildId(args);
}
@@ -829,8 +831,8 @@ static void checkOptions(opt::InputArgList &args) {
error("--table-base may not be used with -shared/-pie");
}
- if (ctx.arg.sharedMemory && ctx.arg.isWasip3) {
- error("--shared-memory is incompatible with the wasip3 target");
+ if (ctx.arg.sharedMemory && ctx.arg.componentModelThreadContext) {
+ error("--shared-memory is incompatible with component model thread context intrinsics");
}
}
@@ -964,7 +966,7 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
auto stack_pointer_name =
- ctx.arg.isWasip3 ? "__init_stack_pointer" : "__stack_pointer";
+ ctx.arg.componentModelThreadContext ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
@@ -988,7 +990,7 @@ static void createSyntheticSymbols() {
if (ctx.arg.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- auto tls_base_name = ctx.arg.isWasip3 ? "__init_tls_base" : "__tls_base";
+ auto tls_base_name = ctx.arg.componentModelThreadContext ? "__init_tls_base" : "__tls_base";
ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
@@ -999,7 +1001,7 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
- if (ctx.arg.isWasip3) {
+ if (ctx.arg.componentModelThreadContext) {
ctx.sym.tlsBase->markLive();
ctx.sym.tlsSize->markLive();
ctx.sym.tlsAlign->markLive();
@@ -1049,7 +1051,7 @@ static void createOptionalSymbols() {
//
// __tls_size and __tls_align are not needed in this case since they are only
// needed for __wasm_init_tls (which we do not create in this case).
- if (!ctx.arg.sharedMemory && !ctx.arg.isWasip3)
+ if (!ctx.arg.sharedMemory && !ctx.arg.componentModelThreadContext)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 33ecf03176d36..a9def1bc47c9a 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -190,6 +190,9 @@ def allow_undefined_file: J<"allow-undefined-file=">,
def allow_undefined_file_s: Separate<["-"], "allow-undefined-file">,
Alias<allow_undefined_file>;
+def component_model_thread_context: FF<"component-model-thread-context">,
+ HelpText<"Use component model thread context intrinsics instead of global variables for the stack pointer and thread local storage">;
+
defm export: Eq<"export", "Force a symbol to be exported">;
defm export_if_defined: Eq<"export-if-defined",
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 67dc7c8acd343..ab6998a6e3bc2 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1623,9 +1623,9 @@ void Writer::createInitTLSFunction() {
writeUleb128(os, 0, "num locals");
if (tlsSeg) {
- // On WASIP3, we don't set the TLS base inside the thread context;
- // this should be done as part of the thread startup stub.
- if (!ctx.arg.isWasip3) {
+ // When using component model thread context intrinsics, we don't set the TLS base
+ //inside __init_tls; this should be done as part of the thread startup stub.
+ if (!ctx.arg.componentModelThreadContext) {
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index 8e945f57ef39c..4cb2a9018cca8 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -269,7 +269,7 @@ void writeExport(raw_ostream &os, const WasmExport &export_) {
}
void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.arg.isWasip3) {
+ if (ctx.arg.componentModelThreadContext) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextGet1->getFunctionIndex(), "function index");
} else {
@@ -279,7 +279,7 @@ void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
}
void writeSetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.arg.isWasip3) {
+ if (ctx.arg.componentModelThreadContext) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(), "function index");
} else {
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td
index 089be5f1dc70e..ff6a78027037d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.td
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.td
@@ -37,6 +37,10 @@ def FeatureCallIndirectOverlong :
SubtargetFeature<"call-indirect-overlong", "HasCallIndirectOverlong", "true",
"Enable overlong encoding for call_indirect immediates">;
+def FeatureComponentModelThreadContext :
+ SubtargetFeature<"component-model-thread-context", "HasComponentModelThreadContext", "false",
+ "Enable component model thread context intrinsics">;
+
def FeatureExceptionHandling :
SubtargetFeature<"exception-handling", "HasExceptionHandling", "true",
"Enable Wasm exception handling">;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index 67b9e75be482f..5389318ad0e28 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -229,8 +229,7 @@ void WebAssemblyFrameLowering::writeBackSP(
MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const {
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- if (MF.getSubtarget<WebAssemblySubtarget>().getTargetTriple().getOSName() ==
- "wasip3") {
+ if (MF.getSubtarget<WebAssemblySubtarget>().hasComponentModelThreadContext()) {
const char *ES = "__wasm_component_model_builtin_context_set_0";
auto *SPSymbol = MF.createExternalSymbolName(ES);
BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::CALL))
@@ -289,7 +288,7 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
if (StackSize)
SPReg = MRI.createVirtualRegister(PtrRC);
- if (ST.getTargetTriple().getOSName() == "wasip3") {
+ if (ST.hasComponentModelThreadContext()) {
const char *ES = "__wasm_component_model_builtin_context_get_0";
auto *SPSymbol = MF.createExternalSymbolName(ES);
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CALL), SPReg)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 295ee45abe8b5..dcbd39dc71e66 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2002,7 +2002,7 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
(model == GlobalValue::GeneralDynamicTLSModel &&
getTargetMachine().shouldAssumeDSOLocal(GV))) {
// For DSO-local TLS variables we use offset from __tls_base, or
- // context.get(1) on wasip3.
+ // context.get(1) if using component model threading intrinsics.
MVT PtrVT = getPointerTy(DAG.getDataLayout());
SDValue BaseAddr(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 13d048a98d6ea..d4fd93cb71233 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -38,6 +38,11 @@ def HasCallIndirectOverlong :
Predicate<"Subtarget->hasCallIndirectOverlong()">,
AssemblerPredicate<(all_of FeatureCallIndirectOverlong), "call-indirect-overlong">;
+def HasComponentModelThreadContext :
+ Predicate<"Subtarget->hasComponentModelThreadContext()">,
+ AssemblerPredicate<(all_of FeatureComponentModelThreadContext),
+ "component-model-thread-context">;
+
def HasExceptionHandling :
Predicate<"Subtarget->hasExceptionHandling()">,
AssemblerPredicate<(all_of FeatureExceptionHandling), "exception-handling">;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index a3ce40f0297ec..68d474631f23f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -33,6 +33,11 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
if (CPU.empty())
CPU = "generic";
+ // WASIP3 implies using the component model thread context intrinsics by default.
+ if (TargetTriple.getOSName() == "wasip3") {
+ HasComponentModelThreadContext = true;
+ }
+
ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);
FeatureBitset Bits = getFeatureBits();
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
index 2f88bbba05d00..5c086b8a4fe31 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -43,6 +43,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool HasBulkMemory = false;
bool HasBulkMemoryOpt = false;
bool HasCallIndirectOverlong = false;
+ bool HasComponentModelThreadContext = false;
bool HasExceptionHandling = false;
bool HasExtendedConst = false;
bool HasFP16 = false;
@@ -100,6 +101,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool hasBulkMemory() const { return HasBulkMemory; }
bool hasBulkMemoryOpt() const { return HasBulkMemoryOpt; }
bool hasCallIndirectOverlong() const { return HasCallIndirectOverlong; }
+ bool hasComponentModelThreadContext() const { return HasComponentModelThreadContext; }
bool hasExceptionHandling() const { return HasExceptionHandling; }
bool hasExtendedConst() const { return HasExtendedConst; }
bool hasFP16() const { return HasFP16; }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 6c11958e8cf3b..bd34f4149fc5e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -261,7 +261,8 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
// function individually, since having multiple feature sets in one module
// currently does not make sense for WebAssembly. If atomics are not enabled,
// also strip atomic operations and thread local storage, unless the target
- // is WASIP3, which can use TLS without atomics due to cooperative threading.
+ // is using component model threading intrinsics which allow thread local storage
+ // without atomics, in which case only strip atomics.
static char ID;
WebAssemblyTargetMachine *WasmTM;
@@ -280,9 +281,9 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
bool StrippedAtomics = false;
bool StrippedTLS = false;
- if (WasmTM->getTargetTriple().getOSName() == "wasip3") {
- // WASIP3 allows TLS without atomics, so don't strip TLS even if
- // atomics are disabled.
+ if (Features[WebAssembly::FeatureComponentModelThreadContext]) {
+ // Using component model threading intrinsics allows TLS without
+ // atomics, so don't strip TLS even if atomics are disabled.
if (!Features[WebAssembly::FeatureAtomics]) {
StrippedAtomics = stripAtomics(M);
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 2996e8ca58829..1c8aeae8bc1cb 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -201,7 +201,7 @@ MachineSDNode *WebAssembly::getTLSBase(SelectionDAG &DAG, const SDLoc &DL,
auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
: WebAssembly::GLOBAL_GET_I32;
- if (Subtarget->getTargetTriple().getOSName() == "wasip3") {
+ if (Subtarget->hasComponentModelThreadContext()) {
return DAG.getMachineNode(
WebAssembly::CALL, DL, PtrVT, MVT::Other,
DAG.getTargetExternalSymbol(
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
index 9dc94d53b46e8..5b4e9cff19a55 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -77,7 +77,7 @@ bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget);
bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget);
// Get the TLS base value for the current target
-// On wasip3: calls __wasm_component_model_builtin_context_get_1
+// If using component model threading intrinsics: calls __wasm_component_model_builtin_context_get_1
// Otherwise: global.get __tls_base
MachineSDNode *getTLSBase(SelectionDAG &DAG, const SDLoc &DL,
const WebAssemblySubtarget *Subtarget);
>From 4a18333fe63c7a16d6b74633e334981cbbda5ade Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 14:16:17 +0000
Subject: [PATCH 5/6] Tighten up features
---
clang/lib/Basic/Targets/WebAssembly.cpp | 3 +++
lld/wasm/Writer.cpp | 10 ++++++++++
llvm/lib/Target/WebAssembly/WebAssembly.td | 2 +-
llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp | 9 ++++++---
.../Target/WebAssembly/WebAssemblyTargetMachine.cpp | 8 ++++++++
5 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 0d7e99b11576e..785bec699f925 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -56,6 +56,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
.Case("bulk-memory", HasBulkMemory)
.Case("bulk-memory-opt", HasBulkMemoryOpt)
.Case("call-indirect-overlong", HasCallIndirectOverlong)
+ .Case("component-model-thread-context", HasComponentModelThreadContext)
.Case("compact-imports", HasCompactImports)
.Case("exception-handling", HasExceptionHandling)
.Case("extended-const", HasExtendedConst)
@@ -120,6 +121,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_tail_call__");
if (HasWideArithmetic)
Builder.defineMacro("__wasm_wide_arithmetic__");
+ if (HasComponentModelThreadContext)
+ Builder.defineMacro("__wasm_component_model_thread_context__");
// Note that not all wasm features appear here. For example,
// HasCompatctImports
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index eaf5481a5dcd6..d18999f907561 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -644,6 +644,16 @@ void Writer::populateTargetFeatures() {
" because it was not compiled with 'atomics' or 'bulk-memory' "
"features.");
+ if (ctx.arg.componentModelThreadContext && disallowed.contains("component-model-thread-context"))
+ error("--component-model-thread-context is disallowed by " +
+ disallowed["component-model-thread-context"] +
+ " because it was not compiled with the 'component-model-thread-context' feature.");
+
+ if (!ctx.arg.componentModelThreadContext && used.contains("component-model-thread-context"))
+ error("component-model-thread-context feature used by " +
+ used["component-model-thread-context"] +
+ " but --component-model-thread-context not specified.");
+
for (auto feature : {"atomics", "bulk-memory"})
if (!allowed.contains(feature))
error(StringRef("'") + feature +
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td
index 0575f471b5412..5c1076aff985b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.td
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.td
@@ -38,7 +38,7 @@ def FeatureCallIndirectOverlong :
"Enable overlong encoding for call_indirect immediates">;
def FeatureComponentModelThreadContext :
- SubtargetFeature<"component-model-thread-context", "HasComponentModelThreadContext", "false",
+ SubtargetFeature<"component-model-thread-context", "HasComponentModelThreadContext", "true",
"Enable component model thread context intrinsics">;
def FeatureExceptionHandling :
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index e027221f454ce..fa99249840dd9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -38,13 +38,16 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
if (CPU.empty())
CPU = "generic";
+ ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);
+
// WASIP3 implies using the component model thread context intrinsics by default.
- if (TargetTriple.getOSName() == "wasip3") {
+ if (!FS.contains("component-model-thread-context") &&
+ !HasComponentModelThreadContext &&
+ TargetTriple.getOSName() == "wasip3") {
+ ToggleFeature(WebAssembly::FeatureComponentModelThreadContext);
HasComponentModelThreadContext = true;
}
- ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);
-
FeatureBitset Bits = getFeatureBits();
// bulk-memory implies bulk-memory-opt
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 8ed00ec2cf064..b484468d95a1e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -429,6 +429,14 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
wasm::WASM_FEATURE_PREFIX_DISALLOWED);
}
+
+ // Mark component-model-thread-context as disallowed when not in use to
+ // prevent linking object files with incompatible threading ABIs.
+ if (!Features[WebAssembly::FeatureComponentModelThreadContext]) {
+ M.addModuleFlag(Module::ModFlagBehavior::Error,
+ "wasm-feature-component-model-thread-context",
+ wasm::WASM_FEATURE_PREFIX_DISALLOWED);
+ }
}
};
char CoalesceFeaturesAndStripAtomics::ID = 0;
>From 927daeb7e990692208adea16de038b271e659f60 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 14:26:15 +0000
Subject: [PATCH 6/6] Make feature detection work properly
---
lld/wasm/Writer.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index d18999f907561..431169d3fdd39 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -644,6 +644,12 @@ void Writer::populateTargetFeatures() {
" because it was not compiled with 'atomics' or 'bulk-memory' "
"features.");
+ for (auto feature : {"atomics", "bulk-memory"})
+ if (!allowed.contains(feature))
+ error(StringRef("'") + feature +
+ "' feature must be used in order to use shared memory");
+ }
+
if (ctx.arg.componentModelThreadContext && disallowed.contains("component-model-thread-context"))
error("--component-model-thread-context is disallowed by " +
disallowed["component-model-thread-context"] +
@@ -654,13 +660,7 @@ void Writer::populateTargetFeatures() {
used["component-model-thread-context"] +
" but --component-model-thread-context not specified.");
- for (auto feature : {"atomics", "bulk-memory"})
- if (!allowed.contains(feature))
- error(StringRef("'") + feature +
- "' feature must be used in order to use shared memory");
- }
-
- if (tlsUsed) {
+ if (tlsUsed && !ctx.arg.componentModelThreadContext) {
for (auto feature : {"atomics", "bulk-memory"})
if (!allowed.contains(feature))
error(StringRef("'") + feature +
More information about the cfe-commits
mailing list