[clang] [lld] [llvm] [WebAssembly] WASIP3 and component model threading support (PR #175800)
Sy Brand via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 18 05:27:59 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 01/32] 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 02/32] 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 03/32] 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 04/32] 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 05/32] 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 06/32] 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 +
>From e632709c827ad5e5bb6a6e87a7f2d852b14ecc3f Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 15:16:34 +0000
Subject: [PATCH 07/32] Fix TLS relocations for non WASIP3
---
lld/wasm/SyntheticSections.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 023c690c14354..27fe215b6143b 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -474,11 +474,12 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
if (auto *d = dyn_cast<DefinedData>(sym)) {
// Get __memory_base
- writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
if (sym->isTLS())
writeGetTLSBase(ctx, os);
- else
+ else {
+ writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "__memory_base");
+ }
// Add the virtual address of the data symbol
writePtrConst(os, d->getVA(), is64, "offset");
>From f44bf2984770351e7ff7100fb524aa1b1b9bf956 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 15:18:27 +0000
Subject: [PATCH 08/32] Add stack pointer ABI test
---
lld/test/wasm/stack-pointer-abi.s | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 lld/test/wasm/stack-pointer-abi.s
diff --git a/lld/test/wasm/stack-pointer-abi.s b/lld/test/wasm/stack-pointer-abi.s
new file mode 100644
index 0000000000000..11355a9c4586e
--- /dev/null
+++ b/lld/test/wasm/stack-pointer-abi.s
@@ -0,0 +1,13 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --component-model-thread-context -o %t-component-model.wasm %t.o
+# RUN: obj2yaml %t-component-model.wasm | FileCheck %s --check-prefix=WITH
+# RUN: wasm-ld -o %t-original.wasm %t.o
+# RUN: obj2yaml %t-original.wasm | FileCheck %s --check-prefix=WITHOUT
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
+
+# WITH: Name: __init_stack_pointer
+# WITHOUT: Name: __stack_pointer
\ No newline at end of file
>From 18a1ee3ae2db83e411bd8f8f842def0351be7a6c Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 15:43:16 +0000
Subject: [PATCH 09/32] Add tests for component model threading feature
---
...nent-model-threading-features-disallowed.s | 20 +++++++++++++++++++
.../wasm/component-model-threading-features.s | 20 +++++++++++++++++++
2 files changed, 40 insertions(+)
create mode 100644 lld/test/wasm/component-model-threading-features-disallowed.s
create mode 100644 lld/test/wasm/component-model-threading-features.s
diff --git a/lld/test/wasm/component-model-threading-features-disallowed.s b/lld/test/wasm/component-model-threading-features-disallowed.s
new file mode 100644
index 0000000000000..9644b7e7caedd
--- /dev/null
+++ b/lld/test/wasm/component-model-threading-features-disallowed.s
@@ -0,0 +1,20 @@
+# Test that objects with component-model-thread-context feature marked as DISALLOWED
+# cannot link with --component-model-thread-context flag
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-without.o %s
+# RUN: wasm-ld %t-without.o -o %t.wasm
+# RUN: not wasm-ld --component-model-thread-context %t-without.o -o %t2.wasm 2>&1 | FileCheck %s
+
+# CHECK: error: --component-model-thread-context is disallowed by {{.*}} because it was not compiled with the 'component-model-thread-context' feature.
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
+
+# Mark the feature as DISALLOWED (0x2d = '-' = WASM_FEATURE_PREFIX_DISALLOWED)
+.section .custom_section.target_features,"",@
+ .int8 1
+ .int8 45
+ .int8 30
+ .ascii "component-model-thread-context"
diff --git a/lld/test/wasm/component-model-threading-features.s b/lld/test/wasm/component-model-threading-features.s
new file mode 100644
index 0000000000000..dd617c6f8fec5
--- /dev/null
+++ b/lld/test/wasm/component-model-threading-features.s
@@ -0,0 +1,20 @@
+# Test that objects with component-model-thread-context feature marked as USED
+# can only link with --component-model-thread-context flag
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-with.o %s
+# RUN: wasm-ld --component-model-thread-context %t-with.o -o %t.wasm
+# RUN: not wasm-ld %t-with.o -o %t2.wasm 2>&1 | FileCheck %s
+
+# CHECK: error: component-model-thread-context feature used by {{.*}} but --component-model-thread-context not specified.
+
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
+
+# Mark the feature as USED (0x2b = '+' = WASM_FEATURE_PREFIX_USED)
+.section .custom_section.target_features,"",@
+ .int8 1
+ .int8 43
+ .int8 30
+ .ascii "component-model-thread-context"
>From 8980e38482f3d2f90e538a0316990151527c58f1 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 16:47:01 +0000
Subject: [PATCH 10/32] Add wasm-features test
---
clang/test/Driver/wasm-features.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang/test/Driver/wasm-features.c b/clang/test/Driver/wasm-features.c
index 89ced36eeffab..3a52150d2de27 100644
--- a/clang/test/Driver/wasm-features.c
+++ b/clang/test/Driver/wasm-features.c
@@ -112,3 +112,10 @@
// COMPACT-IMPORTS: "-target-feature" "+compact-imports"
// NO-COMPACT-IMPORTS: "-target-feature" "-compact-imports"
+
+// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcomponent-model-thread-context 2>&1 | FileCheck %s -check-prefix=COMPONENT-MODEL-THREAD-CONTEXT
+// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-component-model-thread-context 2>&1 | FileCheck %s -check-prefix=NO-COMPONENT-MODEL-THREAD-CONTEXT
+
+// COMPONENT-MODEL-THREAD-CONTEXT: "-target-feature" "+component-model-thread-context"
+// NO-COMPONENT-MODEL-THREAD-CONTEXT: "-target-feature" "-component-model-thread-context"
+
>From 08a3a164e68889291d218e67959c889fe7ca4084 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 16:58:11 +0000
Subject: [PATCH 11/32] wasm-toolchain tests
---
clang/test/Driver/wasm-toolchain.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/clang/test/Driver/wasm-toolchain.c b/clang/test/Driver/wasm-toolchain.c
index 29a94aeec77a9..8654021e9a959 100644
--- a/clang/test/Driver/wasm-toolchain.c
+++ b/clang/test/Driver/wasm-toolchain.c
@@ -303,3 +303,17 @@
// RUN: | FileCheck -check-prefix=LINK_WALI_BASIC %s
// LINK_WALI_BASIC: "-cc1" {{.*}} "-o" "[[temp:[^"]*]]"
// LINK_WALI_BASIC: wasm-ld{{.*}}" "-L/foo/lib/wasm32-linux-muslwali" "crt1.o" "[[temp]]" "-lc" "{{.*[/\\]}}libclang_rt.builtins.a" "-o" "a.out"
+
+// Test that `wasm32-wasip3` passes --component-model-thread-context to the linker by default.
+
+// RUN: %clang --target=wasm32-wasip3 %s -### 2>&1 | FileCheck -check-prefix=WASIP3_DEFAULT %s
+// WASIP3_DEFAULT: wasm-component-ld{{.*}}" {{.*}} "--component-model-thread-context"
+
+// Test that `wasm32-wasip3` does not pass --component-model-thread-context to the linker when
+// -mno-component-model-thread-context is used, and that it also passes -target-feature -component-model-thread-context
+// to disable the feature in clang-cc1.
+
+// RUN: %clang --target=wasm32-wasip3 %s -### -mno-component-model-thread-context 2>&1 | FileCheck -check-prefix=WASIP3_DISABLED %s
+
+// WASIP3_DISABLED-NOT: "--component-model-thread-context"
+// WASIP3_DISABLED: "-target-feature" "-component-model-thread-context"
\ No newline at end of file
>From 9092b93263908b886ff513a76febb59306907034 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:12:56 +0000
Subject: [PATCH 12/32] Formatting
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index eaa049237ce19..0d4cac5eeb6ae 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -31,13 +31,12 @@ std::string WebAssembly::getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,
StringRef SysRoot) const {
return (TargetTriple.getArchName() + "-" +
- TargetTriple.getOSAndEnvironmentName())
- .str();
+ 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) &&
@@ -249,9 +248,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,
@@ -524,8 +523,7 @@ 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");
}
@@ -654,6 +652,5 @@ 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");
}
>From 81ffb2b323bb1c757e88523d46b0289e8935e2b3 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:13:44 +0000
Subject: [PATCH 13/32] Formatting
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index 0d4cac5eeb6ae..bb70887c00ade 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -30,8 +30,8 @@ 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 {
>From a9e73101f8858721bd9781317f86607f2c6f8a54 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:14:41 +0000
Subject: [PATCH 14/32] Revert arch change
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index bb70887c00ade..e8214c80d5f6f 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -94,13 +94,10 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;
CmdArgs.push_back("-m");
- std::string arch;
if (ToolChain.getTriple().isArch64Bit())
- arch = "wasm64";
+ CmdArgs.push_back("wasm64");
else
- arch = "wasm32";
-
- CmdArgs.push_back(Args.MakeArgString(arch));
+ CmdArgs.push_back("wasm32");
if (Args.hasArg(options::OPT_s))
CmdArgs.push_back("--strip-all");
>From c369bb2ec1340fca42c9d1b05677c8c60125f617 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:15:21 +0000
Subject: [PATCH 15/32] Formatting
---
lld/wasm/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 24d1637f702a3..9c6e5ef555b5a 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -656,7 +656,7 @@ 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.
if (auto *arg = args.getLastArg(OPT_m)) {
StringRef s = arg->getValue();
if (s == "wasm32")
>From d3688381869e7616a7eb6918add87e259a479568 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:16:05 +0000
Subject: [PATCH 16/32] Revert formatting change
---
lld/wasm/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 9c6e5ef555b5a..c59f79b5a4b6e 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -892,7 +892,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");
>From 61f03e50ad420fc99ffe7cd453a9c5e35ea92e9c Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:17:33 +0000
Subject: [PATCH 17/32] Revert formatting change
---
.../MCTargetDesc/WebAssemblyMCTargetDesc.h | 242 +++++++++---------
1 file changed, 121 insertions(+), 121 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 5c6d07ba88c61..5dc0e3aa91622 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;
>From c9665c1f4ee21d51206dfa9fbaf24fe0bd94ba3c Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:20:06 +0000
Subject: [PATCH 18/32] Revert formatting changes
---
llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 5 ++---
.../Target/WebAssembly/WebAssemblyFrameLowering.cpp | 11 +++++++----
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 8019f1d37b3b1..d87fbfb64cfae 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -498,7 +498,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));
@@ -607,8 +607,7 @@ 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 5389318ad0e28..f54bdd0dedb35 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -188,7 +188,8 @@ 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;
@@ -212,13 +213,15 @@ 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;
@@ -321,7 +324,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);
>From 07e122d7e70c71349100efd7dbe1d9ec92c484a0 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:34:53 +0000
Subject: [PATCH 19/32] Revert formatting changes
---
lld/wasm/Driver.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index c59f79b5a4b6e..81cc99c4bb287 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1063,15 +1063,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
>From 651c362ae6bb6bc5aa6b325efa2a4eafac0adc33 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:35:25 +0000
Subject: [PATCH 20/32] Revert formatting changes
---
lld/wasm/Relocations.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index cb597fdeffcf3..046a04fa726ce 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())
>From 0175bd56857849abd25bf61d07c6128f2b21a446 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:35:46 +0000
Subject: [PATCH 21/32] Revert formatting changes
---
lld/wasm/Symbols.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 97a9871a06308..f2040441e6257 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,7 +223,9 @@ 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))
@@ -411,7 +413,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);
}
>From 9a93078408a3277e61a7faa1926787b2884ac285 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:36:44 +0000
Subject: [PATCH 22/32] Revert formatting changes
---
lld/wasm/SyntheticSections.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 27fe215b6143b..f71aeef7975f3 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -466,7 +466,8 @@ 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())
@@ -647,7 +648,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;
}
>From 85fab66cd4a511ab6b20792485b170876e51a767 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:38:10 +0000
Subject: [PATCH 23/32] Revert formatting changes
---
lld/wasm/Writer.cpp | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 431169d3fdd39..b5496f8c4a208 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -311,8 +311,7 @@ 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);
}
@@ -359,8 +358,7 @@ 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;
@@ -384,7 +382,6 @@ 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));
@@ -1196,7 +1193,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;
>From e577185ea0e9179b347f2b68f23abf85a4d79b7e Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:38:38 +0000
Subject: [PATCH 24/32] Revert formatting changes
---
lld/wasm/WriterUtils.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index f68964ac6f990..541863f800488 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -98,8 +98,7 @@ void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) {
encodeSLEB128(number, os);
}
-void writeBytes(raw_ostream &os, const char *bytes, size_t count,
- const Twine &msg) {
+void writeBytes(raw_ostream &os, const char *bytes, size_t count, const Twine &msg) {
debugWrite(os.tell(), msg + " [data[" + Twine(count) + "]]");
os.write(bytes, count);
}
>From 446d56ded155ba9c3d46c1db4268f3183743fbac Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Tue, 17 Feb 2026 17:40:25 +0000
Subject: [PATCH 25/32] Revert formatting changes
---
llvm/include/llvm/MC/MCSymbolWasm.h | 8 ++++++--
llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp | 6 +++---
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h
index 11cbce5b0ccd0..5c9f14e5e6d64 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -54,12 +54,16 @@ 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/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index f54bdd0dedb35..eb176161d1fdf 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -188,7 +188,7 @@ unsigned WebAssemblyFrameLowering::getFPReg(const MachineFunction &MF) {
: WebAssembly::FP32;
}
-unsigned
+unsigned
WebAssemblyFrameLowering::getOpcConst(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::CONST_I64
@@ -213,14 +213,14 @@ unsigned WebAssemblyFrameLowering::getOpcAnd(const MachineFunction &MF) {
: WebAssembly::AND_I32;
}
-unsigned
+unsigned
WebAssemblyFrameLowering::getOpcGlobGet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_GET_I64
: WebAssembly::GLOBAL_GET_I32;
}
-unsigned
+unsigned
WebAssemblyFrameLowering::getOpcGlobSet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_SET_I64
>From 3f10fe8cf65143e22b930d53700135c027758d75 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 10:30:52 +0000
Subject: [PATCH 26/32] Remove linker flag
---
clang/lib/Basic/Targets/WebAssembly.cpp | 4 ++
clang/lib/Basic/Targets/WebAssembly.h | 3 --
clang/lib/Driver/ToolChains/WebAssembly.cpp | 55 ++++++++++-----------
lld/wasm/Config.h | 16 ++++--
lld/wasm/Driver.cpp | 21 ++++----
lld/wasm/Relocations.cpp | 2 +-
lld/wasm/SymbolTable.cpp | 52 +++++++++++++++++++
lld/wasm/SymbolTable.h | 2 +
lld/wasm/SyntheticSections.cpp | 6 +--
lld/wasm/Writer.cpp | 22 +++------
lld/wasm/WriterUtils.cpp | 4 +-
11 files changed, 117 insertions(+), 70 deletions(-)
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 785bec699f925..bced84f317c76 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -215,6 +215,10 @@ bool WebAssemblyTargetInfo::initFeatureMap(
addBleedingEdgeFeatures();
}
+ if (getTriple().getOSName() == "wasip3") {
+ Features["component-model-thread-context"] = true;
+ }
+
return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
}
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 95d2f9ace8d56..524849f2eb767 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -109,9 +109,6 @@ 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 e8214c80d5f6f..dc66113de9af2 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -167,14 +167,6 @@ 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);
@@ -319,36 +311,39 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_use_init_array, true))
CC1Args.push_back("-fno-use-init-array");
- // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
+ // '-pthread' implies bulk-memory, and shared memory is also used,
+ // also implies atomics, mutable-globals, and sign-ext.
if (WantsPthread(getTriple(), DriverArgs)) {
- if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
- false))
- getDriver().Diag(diag::err_drv_argument_not_allowed_with)
- << "-pthread"
- << "-mno-atomics";
if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory,
options::OPT_mbulk_memory, false))
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< "-pthread"
<< "-mno-bulk-memory";
- if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
- options::OPT_mmutable_globals, false))
- getDriver().Diag(diag::err_drv_argument_not_allowed_with)
- << "-pthread"
- << "-mno-mutable-globals";
- if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext,
- false))
- getDriver().Diag(diag::err_drv_argument_not_allowed_with)
- << "-pthread"
- << "-mno-sign-ext";
- CC1Args.push_back("-target-feature");
- CC1Args.push_back("+atomics");
CC1Args.push_back("-target-feature");
CC1Args.push_back("+bulk-memory");
- CC1Args.push_back("-target-feature");
- CC1Args.push_back("+mutable-globals");
- CC1Args.push_back("-target-feature");
- CC1Args.push_back("+sign-ext");
+ if (WantsSharedMemory(getTriple(), DriverArgs)) {
+ if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
+ false))
+ getDriver().Diag(diag::err_drv_argument_not_allowed_with)
+ << "-pthread"
+ << "-mno-atomics";
+ if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
+ options::OPT_mmutable_globals, false))
+ getDriver().Diag(diag::err_drv_argument_not_allowed_with)
+ << "-pthread"
+ << "-mno-mutable-globals";
+ if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext,
+ false))
+ getDriver().Diag(diag::err_drv_argument_not_allowed_with)
+ << "-pthread"
+ << "-mno-sign-ext";
+ CC1Args.push_back("-target-feature");
+ CC1Args.push_back("+atomics");
+ CC1Args.push_back("-target-feature");
+ CC1Args.push_back("+mutable-globals");
+ CC1Args.push_back("-target-feature");
+ CC1Args.push_back("+sign-ext");
+ }
}
if (!DriverArgs.hasFlag(options::OPT_mmutable_globals,
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 0cda43f84e7ce..5514b1d6d7de2 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -51,12 +51,9 @@ 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 || componentModelThreadContext; }
-
bool allowMultipleDefinition;
bool bsymbolic;
bool checkFeatures;
- bool componentModelThreadContext;
bool compressRelocations;
bool demangle;
bool disableVerify;
@@ -138,6 +135,8 @@ struct Config {
llvm::SmallVector<uint8_t, 0> buildIdVector;
};
+enum class ThreadContextAbi { Undetermined, Globals, ComponentModelBuiltins };
+
// The Ctx object hold all other (non-configuration) global state.
struct Ctx {
Config arg;
@@ -283,8 +282,19 @@ struct Ctx {
0>
whyExtractRecords;
+ ThreadContextAbi threadContextAbi = ThreadContextAbi::Undetermined;
+
Ctx();
void reset();
+ bool componentModelThreadContext() const {
+ return threadContextAbi == ThreadContextAbi::ComponentModelBuiltins;
+ }
+ bool globalsThreadContext() const {
+ // Use the global thread context ABI by default, even if we can't determine
+ // the ABI from the object files passed.
+ return !componentModelThreadContext();
+ }
+ bool isMultithreaded() const { return componentModelThreadContext() || arg.sharedMemory; }
};
extern Ctx ctx;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 81cc99c4bb287..b1aacfe680451 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -705,9 +705,6 @@ 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);
}
@@ -831,8 +828,8 @@ static void checkOptions(opt::InputArgList &args) {
error("--table-base may not be used with -shared/-pie");
}
- if (ctx.arg.sharedMemory && ctx.arg.componentModelThreadContext) {
- error("--shared-memory is incompatible with component model thread context intrinsics");
+ if (ctx.arg.sharedMemory && ctx.componentModelThreadContext()) {
+ error("--shared-memory is currently incompatible with component model thread context intrinsics");
}
}
@@ -966,7 +963,7 @@ static void createSyntheticSymbols() {
bool is64 = ctx.arg.is64.value_or(false);
auto stack_pointer_name =
- ctx.arg.componentModelThreadContext ? "__init_stack_pointer" : "__stack_pointer";
+ ctx.componentModelThreadContext() ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
@@ -988,9 +985,9 @@ static void createSyntheticSymbols() {
ctx.sym.stackPointer->markLive();
}
- if (ctx.arg.isMultithreaded()) {
+ if (ctx.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- auto tls_base_name = ctx.arg.componentModelThreadContext ? "__init_tls_base" : "__tls_base";
+ auto tls_base_name = ctx.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,
@@ -1001,7 +998,7 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
- if (ctx.arg.componentModelThreadContext) {
+ if (ctx.componentModelThreadContext()) {
ctx.sym.tlsBase->markLive();
ctx.sym.tlsSize->markLive();
ctx.sym.tlsAlign->markLive();
@@ -1054,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.componentModelThreadContext)
+ if (!ctx.arg.sharedMemory && !ctx.componentModelThreadContext())
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1398,14 +1395,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ctx.arg.requiredExports.push_back(arg->getValue());
}
- createSyntheticSymbols();
-
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table.
for (InputFile *f : files)
symtab->addFile(f);
if (errorCount())
return;
+
+ createSyntheticSymbols();
// Handle the `--undefined <sym>` options.
for (auto *arg : args.filtered(OPT_undefined))
diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 046a04fa726ce..4a1523a371d15 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -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.isMultithreaded()) {
+ if (ctx.isMultithreaded()) {
if (!sym->isTLS()) {
error(toString(file) + ": relocation " +
relocTypeToString(reloc.Type) +
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 011e4341519cd..74e8f6440778a 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -69,6 +69,7 @@ void SymbolTable::addFile(InputFile *file, StringRef symName) {
auto *f = cast<ObjFile>(file);
f->parse(false);
ctx.objectFiles.push_back(f);
+ validateThreadContextAbi(f);
}
// This function is where all the optimizations of link-time
@@ -91,6 +92,57 @@ void SymbolTable::compileBitcodeFiles() {
auto *obj = cast<ObjFile>(file);
obj->parse(true);
ctx.objectFiles.push_back(obj);
+ validateThreadContextAbi(obj);
+ }
+}
+
+void SymbolTable::validateThreadContextAbi(const ObjFile *obj) {
+ // Check for the component-model-thread-context feature and validate that all
+ // files are consistent in whether they use it or disallow it.
+
+ // A complication is that a user may attempt to link together object files
+ // compiled with different versions of LLVM, where one does not specifiy
+ // -component-model-thread-context when using the global thread context ABI.
+ // They may also attempt to link object files with the global ABI compiled with
+ // older LLVM versions, but link them with a newer wasm-ld. To ensure the correct behavior
+ // in both of these cases, we treat the presence of an __stack_pointer global as an indication
+ // that the global thread context ABI is being used, even if the component-model-thread-context
+ // feature is not disallowed.
+
+ auto targetFeatures = obj->getWasmObj()->getTargetFeatures();
+ auto threadContextFeature = llvm::find_if(targetFeatures,
+ [](const auto &f) {
+ return f.Name == "component-model-thread-context";
+ });
+
+ bool usesComponentModelThreadContext = threadContextFeature != targetFeatures.end() &&
+ threadContextFeature->Prefix == WASM_FEATURE_PREFIX_USED;
+
+ if (threadContextFeature == targetFeatures.end()) {
+ // If the feature is not explicitly used or disallowed, check for the presence of __stack_pointer
+ // to determine if the global thread context ABI is being used.
+ auto sym = find("__stack_pointer");
+ if (!sym || !sym->isDefined()) {
+ // No __stack_pointer global, so this is probably an object file compiled from assembly or
+ // some other source that doesn't care about the thread context ABI. As such, we let it pass.
+ return;
+ }
+ }
+
+ if (usesComponentModelThreadContext) {
+ if (ctx.threadContextAbi == ThreadContextAbi::Undetermined) {
+ ctx.threadContextAbi = ThreadContextAbi::ComponentModelBuiltins;
+ } else if (ctx.threadContextAbi != ThreadContextAbi::ComponentModelBuiltins) {
+ error("thread context ABI mismatch: " + obj->getName() +
+ " uses component-model-thread-context but other files disallow it");
+ }
+ } else {
+ if (ctx.threadContextAbi == ThreadContextAbi::Undetermined) {
+ ctx.threadContextAbi = ThreadContextAbi::Globals;
+ } else if (ctx.threadContextAbi != ThreadContextAbi::Globals) {
+ error("thread context ABI mismatch: " + obj->getName() +
+ " disallows component-model-thread-context but other files use it");
+ }
}
}
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 5d09d8b685716..ef6119223c9c8 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -104,6 +104,8 @@ class SymbolTable {
void handleWeakUndefines();
DefinedFunction *createUndefinedStub(const WasmSignature &sig);
+ void validateThreadContextAbi(const ObjFile *obj);
+
private:
std::pair<Symbol *, bool> insert(StringRef name, const InputFile *file);
std::pair<Symbol *, bool> insertName(StringRef name);
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index f71aeef7975f3..d315b544fe9dd 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -523,7 +523,7 @@ void GlobalSection::writeBody() {
mutable_ = true;
// With multi-threading any TLS globals must be mutable since they get
// set during `__wasm_apply_global_tls_relocs`
- if (ctx.arg.isMultithreaded() && sym->isTLS())
+ if (ctx.isMultithreaded() && sym->isTLS())
mutable_ = true;
}
WasmGlobalType type{itype, mutable_};
@@ -564,7 +564,7 @@ void GlobalSection::writeBody() {
// `__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.isMultithreaded()), is64);
+ intConst(d->getVA(/*absolute=*/!ctx.isMultithreaded()), is64);
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
@@ -665,7 +665,7 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
- return numSegments && ctx.arg.isMultithreaded();
+ return numSegments && ctx.isMultithreaded();
}
void LinkingSection::writeBody() {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b5496f8c4a208..5b470f9c77020 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -647,17 +647,7 @@ void Writer::populateTargetFeatures() {
"' 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"] +
- " 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.");
-
- if (tlsUsed && !ctx.arg.componentModelThreadContext) {
+ if (tlsUsed && !ctx.componentModelThreadContext()) {
for (auto feature : {"atomics", "bulk-memory"})
if (!allowed.contains(feature))
error(StringRef("'") + feature +
@@ -1039,7 +1029,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.isMultithreaded())
+ if (ctx.isMultithreaded())
s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
if (!ctx.arg.relocatable && name.starts_with(".bss"))
s->isBss = true;
@@ -1173,14 +1163,14 @@ void Writer::createSyntheticInitFunctions() {
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
ctx.sym.initMemory->markLive();
- if (ctx.arg.isMultithreaded()) {
+ if (ctx.isMultithreaded()) {
// This global is assigned during __wasm_init_memory in the shared memory
// case.
ctx.sym.tlsBase->markLive();
}
}
- if (ctx.arg.isMultithreaded()) {
+ if (ctx.isMultithreaded()) {
if (out.globalSec->needsTLSRelocations()) {
ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
"__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
@@ -1431,7 +1421,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.isMultithreaded() && s->isTLS())
+ if (ctx.isMultithreaded() && s->isTLS())
continue;
// data.drop instruction
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
@@ -1640,7 +1630,7 @@ void Writer::createInitTLSFunction() {
if (tlsSeg) {
// 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) {
+ if (!ctx.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 541863f800488..f877149a77a94 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -272,7 +272,7 @@ void writeExport(raw_ostream &os, const WasmExport &export_) {
}
void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.arg.componentModelThreadContext) {
+ if (ctx.componentModelThreadContext()) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextGet1->getFunctionIndex(), "function index");
} else {
@@ -282,7 +282,7 @@ void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
}
void writeSetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.arg.componentModelThreadContext) {
+ if (ctx.componentModelThreadContext()) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(), "function index");
} else {
>From dbae3efdcb2c3124a236b287f164bbdce1459606 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 12:34:33 +0000
Subject: [PATCH 27/32] Determine thread context ABI automatically
---
.../disallow-component-model-thread-context.s | 6 ++
lld/test/wasm/Inputs/stack-pointer.s | 11 +++
.../use-component-model-thread-context.s | 6 ++
lld/test/wasm/archive-export.test | 6 +-
lld/test/wasm/comdats.ll | 6 +-
...nent-model-threading-features-disallowed.s | 20 ----
.../wasm/component-model-threading-features.s | 20 ----
lld/test/wasm/mutable-global-exports.s | 12 +--
lld/test/wasm/pie.s | 4 +
lld/test/wasm/shared-weak-symbols.s | 8 +-
lld/test/wasm/stack-pointer-abi.s | 16 +--
lld/test/wasm/thread-context-abi-mismatch.s | 28 ++++++
lld/test/wasm/visibility-hidden.ll | 6 +-
lld/test/wasm/weak-undefined-pic.s | 10 ++
lld/wasm/Config.h | 13 +--
lld/wasm/Driver.cpp | 99 ++++++++++++++++---
lld/wasm/SymbolTable.cpp | 52 ----------
lld/wasm/SymbolTable.h | 2 -
lld/wasm/Writer.cpp | 4 +-
lld/wasm/WriterUtils.cpp | 4 +-
20 files changed, 187 insertions(+), 146 deletions(-)
create mode 100644 lld/test/wasm/Inputs/disallow-component-model-thread-context.s
create mode 100644 lld/test/wasm/Inputs/stack-pointer.s
create mode 100644 lld/test/wasm/Inputs/use-component-model-thread-context.s
delete mode 100644 lld/test/wasm/component-model-threading-features-disallowed.s
delete mode 100644 lld/test/wasm/component-model-threading-features.s
create mode 100644 lld/test/wasm/thread-context-abi-mismatch.s
diff --git a/lld/test/wasm/Inputs/disallow-component-model-thread-context.s b/lld/test/wasm/Inputs/disallow-component-model-thread-context.s
new file mode 100644
index 0000000000000..1af0292d2ec4b
--- /dev/null
+++ b/lld/test/wasm/Inputs/disallow-component-model-thread-context.s
@@ -0,0 +1,6 @@
+# Mark the feature as DISALLOWED
+.section .custom_section.target_features,"",@
+ .int8 1
+ .int8 45
+ .int8 30
+ .ascii "component-model-thread-context"
\ No newline at end of file
diff --git a/lld/test/wasm/Inputs/stack-pointer.s b/lld/test/wasm/Inputs/stack-pointer.s
new file mode 100644
index 0000000000000..cfd76578a5e07
--- /dev/null
+++ b/lld/test/wasm/Inputs/stack-pointer.s
@@ -0,0 +1,11 @@
+.globaltype __stack_pointer, i32
+
+.globl _start
+_start:
+ .functype _start () -> (i32)
+ global.get __stack_pointer
+ i32.const 16
+ i32.sub
+ drop
+ i32.const 0
+ end_function
\ No newline at end of file
diff --git a/lld/test/wasm/Inputs/use-component-model-thread-context.s b/lld/test/wasm/Inputs/use-component-model-thread-context.s
new file mode 100644
index 0000000000000..1c9554edb7e55
--- /dev/null
+++ b/lld/test/wasm/Inputs/use-component-model-thread-context.s
@@ -0,0 +1,6 @@
+# Mark the feature as USED
+.section .custom_section.target_features,"",@
+ .int8 1
+ .int8 43
+ .int8 30
+ .ascii "component-model-thread-context"
\ No newline at end of file
diff --git a/lld/test/wasm/archive-export.test b/lld/test/wasm/archive-export.test
index c67e500e46dd2..1432214fb1dc2 100644
--- a/lld/test/wasm/archive-export.test
+++ b/lld/test/wasm/archive-export.test
@@ -14,9 +14,6 @@ CHECK: Exports:
CHECK-NEXT: - Name: memory
CHECK-NEXT: Kind: MEMORY
CHECK-NEXT: Index: 0
-CHECK-NEXT: - Name: __stack_pointer
-CHECK-NEXT: Kind: GLOBAL
-CHECK-NEXT: Index: 0
CHECK-NEXT: - Name: foo
CHECK-NEXT: Kind: FUNCTION
CHECK-NEXT: Index: 1
@@ -29,6 +26,9 @@ CHECK-NEXT: Index: 3
CHECK-NEXT: - Name: _start
CHECK-NEXT: Kind: FUNCTION
CHECK-NEXT: Index: 0
+CHECK-NEXT: - Name: __stack_pointer
+CHECK-NEXT: Kind: GLOBAL
+CHECK-NEXT: Index: 0
CHECK-NEXT: - Type: CODE
NOEXPORT: Exports:
diff --git a/lld/test/wasm/comdats.ll b/lld/test/wasm/comdats.ll
index 1662a983698ac..249ad279f8acc 100644
--- a/lld/test/wasm/comdats.ll
+++ b/lld/test/wasm/comdats.ll
@@ -35,9 +35,6 @@ entry:
; CHECK-NEXT: - Name: memory
; CHECK-NEXT: Kind: MEMORY
; CHECK-NEXT: Index: 0
-; CHECK-NEXT: - Name: __stack_pointer
-; CHECK-NEXT: Kind: GLOBAL
-; CHECK-NEXT: Index: 0
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
@@ -53,6 +50,9 @@ entry:
; CHECK-NEXT: - Name: callComdatFn2
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 5
+; CHECK-NEXT: - Name: __stack_pointer
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 0
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
diff --git a/lld/test/wasm/component-model-threading-features-disallowed.s b/lld/test/wasm/component-model-threading-features-disallowed.s
deleted file mode 100644
index 9644b7e7caedd..0000000000000
--- a/lld/test/wasm/component-model-threading-features-disallowed.s
+++ /dev/null
@@ -1,20 +0,0 @@
-# Test that objects with component-model-thread-context feature marked as DISALLOWED
-# cannot link with --component-model-thread-context flag
-
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-without.o %s
-# RUN: wasm-ld %t-without.o -o %t.wasm
-# RUN: not wasm-ld --component-model-thread-context %t-without.o -o %t2.wasm 2>&1 | FileCheck %s
-
-# CHECK: error: --component-model-thread-context is disallowed by {{.*}} because it was not compiled with the 'component-model-thread-context' feature.
-
-.globl _start
-_start:
- .functype _start () -> ()
- end_function
-
-# Mark the feature as DISALLOWED (0x2d = '-' = WASM_FEATURE_PREFIX_DISALLOWED)
-.section .custom_section.target_features,"",@
- .int8 1
- .int8 45
- .int8 30
- .ascii "component-model-thread-context"
diff --git a/lld/test/wasm/component-model-threading-features.s b/lld/test/wasm/component-model-threading-features.s
deleted file mode 100644
index dd617c6f8fec5..0000000000000
--- a/lld/test/wasm/component-model-threading-features.s
+++ /dev/null
@@ -1,20 +0,0 @@
-# Test that objects with component-model-thread-context feature marked as USED
-# can only link with --component-model-thread-context flag
-
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-with.o %s
-# RUN: wasm-ld --component-model-thread-context %t-with.o -o %t.wasm
-# RUN: not wasm-ld %t-with.o -o %t2.wasm 2>&1 | FileCheck %s
-
-# CHECK: error: component-model-thread-context feature used by {{.*}} but --component-model-thread-context not specified.
-
-.globl _start
-_start:
- .functype _start () -> ()
- end_function
-
-# Mark the feature as USED (0x2b = '+' = WASM_FEATURE_PREFIX_USED)
-.section .custom_section.target_features,"",@
- .int8 1
- .int8 43
- .int8 30
- .ascii "component-model-thread-context"
diff --git a/lld/test/wasm/mutable-global-exports.s b/lld/test/wasm/mutable-global-exports.s
index 3cf1ae746b30a..af2dd428d8d78 100644
--- a/lld/test/wasm/mutable-global-exports.s
+++ b/lld/test/wasm/mutable-global-exports.s
@@ -57,12 +57,12 @@ _start:
# CHECK-SP-NEXT: - Name: memory
# CHECK-SP-NEXT: Kind: MEMORY
# CHECK-SP-NEXT: Index: 0
-# CHECK-SP-NEXT: - Name: __stack_pointer
-# CHECK-SP-NEXT: Kind: GLOBAL
-# CHECK-SP-NEXT: Index: 0
# CHECK-SP-NEXT: - Name: _start
# CHECK-SP-NEXT: Kind: FUNCTION
# CHECK-SP-NEXT: Index: 0
+# CHECK-SP-NEXT: - Name: __stack_pointer
+# CHECK-SP-NEXT: Kind: GLOBAL
+# CHECK-SP-NEXT: Index: 0
# CHECK-SP-NEXT: - Type: CODE
# CHECK-ALL: - Type: EXPORT
@@ -73,9 +73,6 @@ _start:
# CHECK-ALL-NEXT: - Name: __wasm_call_ctors
# CHECK-ALL-NEXT: Kind: FUNCTION
# CHECK-ALL-NEXT: Index: 0
-# CHECK-ALL-NEXT: - Name: __stack_pointer
-# CHECK-ALL-NEXT: Kind: GLOBAL
-# CHECK-ALL-NEXT: Index: 0
# CHECK-ALL-NEXT: - Name: _start
# CHECK-ALL-NEXT: Kind: FUNCTION
# CHECK-ALL-NEXT: Index: 1
@@ -85,6 +82,9 @@ _start:
# CHECK-ALL-NEXT: - Name: bar_global
# CHECK-ALL-NEXT: Kind: GLOBAL
# CHECK-ALL-NEXT: Index: 5
+# CHECK-ALL-NEXT: - Name: __stack_pointer
+# CHECK-ALL-NEXT: Kind: GLOBAL
+# CHECK-ALL-NEXT: Index: 0
# CHECK-ALL-NEXT: - Name: __dso_handle
# CHECK-ALL-NEXT: Kind: GLOBAL
# CHECK-ALL-NEXT: Index: 6
diff --git a/lld/test/wasm/pie.s b/lld/test/wasm/pie.s
index 21eac79207318..41f405062fba9 100644
--- a/lld/test/wasm/pie.s
+++ b/lld/test/wasm/pie.s
@@ -98,6 +98,10 @@ _start:
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: true
# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: external_func
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: SigIndex: 1
+# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __memory_base
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
diff --git a/lld/test/wasm/shared-weak-symbols.s b/lld/test/wasm/shared-weak-symbols.s
index df049ce4600fe..62c46d8b9285f 100644
--- a/lld/test/wasm/shared-weak-symbols.s
+++ b/lld/test/wasm/shared-weak-symbols.s
@@ -42,6 +42,10 @@ call_weak:
# CHECK-NEXT: Memory:
# CHECK-NEXT: Minimum: 0x0
# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: weak_func
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: SigIndex: 0
+# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __memory_base
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
@@ -51,10 +55,6 @@ call_weak:
# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: GlobalType: I32
# CHECK-NEXT: GlobalMutable: false
-# CHECK-NEXT: - Module: env
-# CHECK-NEXT: Field: weak_func
-# CHECK-NEXT: Kind: FUNCTION
-# CHECK-NEXT: SigIndex: 0
# CHECK-NEXT: - Type: FUNCTION
# CHECK: - Type: EXPORT
diff --git a/lld/test/wasm/stack-pointer-abi.s b/lld/test/wasm/stack-pointer-abi.s
index 11355a9c4586e..fdffe30ce295a 100644
--- a/lld/test/wasm/stack-pointer-abi.s
+++ b/lld/test/wasm/stack-pointer-abi.s
@@ -1,13 +1,15 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-component-model.o %S/Inputs/use-component-model-thread-context.s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-global.o %S/Inputs/disallow-component-model-thread-context.s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: wasm-ld --component-model-thread-context -o %t-component-model.wasm %t.o
-# RUN: obj2yaml %t-component-model.wasm | FileCheck %s --check-prefix=WITH
-# RUN: wasm-ld -o %t-original.wasm %t.o
-# RUN: obj2yaml %t-original.wasm | FileCheck %s --check-prefix=WITHOUT
+# RUN: wasm-ld --component-model-thread-context -o %t-component-model.wasm %t-component-model.o %t.o
+# RUN: obj2yaml %t-component-model.wasm | FileCheck %s --check-prefix=COMPONENT-MODEL
+# RUN: wasm-ld -o %t-original.wasm %t-global.o %t.o
+# RUN: obj2yaml %t-original.wasm | FileCheck %s --check-prefix=GLOBAL
-.globl _start
+ .globl _start
_start:
.functype _start () -> ()
end_function
-# WITH: Name: __init_stack_pointer
-# WITHOUT: Name: __stack_pointer
\ No newline at end of file
+# COMPONENT-MODEL: Name: __init_stack_pointer
+# GLOBAL: Name: __stack_pointer
\ No newline at end of file
diff --git a/lld/test/wasm/thread-context-abi-mismatch.s b/lld/test/wasm/thread-context-abi-mismatch.s
new file mode 100644
index 0000000000000..3fe89a353134b
--- /dev/null
+++ b/lld/test/wasm/thread-context-abi-mismatch.s
@@ -0,0 +1,28 @@
+# Test that linking object files with mismatched thread context ABIs fails with an error.
+
+# Test that the presence of an import of __stack_pointer from the env module is treated
+# as an indication that the global thread context ABI is being used, even if the
+# component-model-thread-context feature is not disallowed.
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-with.o %s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-without.o %S/Inputs/stack-pointer.s
+# RUN: not wasm-ld %t-with.o %t-without.o -o %t.wasm 2>&1 | FileCheck %s
+
+# Test that explicitly disallowing the component-model-thread-context feature causes linking to fail
+# with an error when other files use the feature.
+
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t-disallow.o %S/Inputs/disallow-component-model-thread-context.s
+# RUN: not wasm-ld %t-with.o %t-disallow.o -o %t.wasm 2>&1 | FileCheck %s
+
+# CHECK: error: thread context ABI mismatch: {{.*}} disallows component-model-thread-context but other files use it
+.globl _start
+_start:
+ .functype _start () -> ()
+ end_function
+
+# Mark the feature as USED
+.section .custom_section.target_features,"",@
+ .int8 1
+ .int8 43
+ .int8 30
+ .ascii "component-model-thread-context"
diff --git a/lld/test/wasm/visibility-hidden.ll b/lld/test/wasm/visibility-hidden.ll
index 6ed7ba3afdc02..657748676849d 100644
--- a/lld/test/wasm/visibility-hidden.ll
+++ b/lld/test/wasm/visibility-hidden.ll
@@ -43,9 +43,6 @@ entry:
; CHECK-NEXT: - Name: memory
; CHECK-NEXT: Kind: MEMORY
; CHECK-NEXT: Index: 0
-; CHECK-NEXT: - Name: __stack_pointer
-; CHECK-NEXT: Kind: GLOBAL
-; CHECK-NEXT: Index: 0
; CHECK-NEXT: - Name: objectDefault
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 1
@@ -55,6 +52,9 @@ entry:
; CHECK-NEXT: - Name: archiveDefault
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 4
+; CHECK-NEXT: - Name: __stack_pointer
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 0
; CHECK-NEXT: - Type:
diff --git a/lld/test/wasm/weak-undefined-pic.s b/lld/test/wasm/weak-undefined-pic.s
index 1a3a1715b4bb9..4be482c72728d 100644
--- a/lld/test/wasm/weak-undefined-pic.s
+++ b/lld/test/wasm/weak-undefined-pic.s
@@ -81,6 +81,16 @@ _start:
# IMPORT: Field: foo
# IMPORT-NEXT: Kind: FUNCTION
# IMPORT-NEXT: SigIndex: 0
+# IMPORT-NEXT: - Module: env
+# IMPORT-NEXT: Field: __memory_base
+# IMPORT-NEXT: Kind: GLOBAL
+# IMPORT-NEXT: GlobalType: I32
+# IMPORT-NEXT: GlobalMutable: false
+# IMPORT-NEXT: - Module: env
+# IMPORT-NEXT: Field: __table_base
+# IMPORT-NEXT: Kind: GLOBAL
+# IMPORT-NEXT: GlobalType: I32
+# IMPORT-NEXT: GlobalMutable: false
# IMPORT-NEXT: - Module: GOT.func
# IMPORT-NEXT: Field: foo
# IMPORT-NEXT: Kind: GLOBAL
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 5514b1d6d7de2..2c4954e68bab4 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -282,19 +282,12 @@ struct Ctx {
0>
whyExtractRecords;
- ThreadContextAbi threadContextAbi = ThreadContextAbi::Undetermined;
+ // Whether to use component model thread context intrinsics for the stack pointer and TLS base.
+ bool componentModelThreadContext = false;
Ctx();
void reset();
- bool componentModelThreadContext() const {
- return threadContextAbi == ThreadContextAbi::ComponentModelBuiltins;
- }
- bool globalsThreadContext() const {
- // Use the global thread context ABI by default, even if we can't determine
- // the ABI from the object files passed.
- return !componentModelThreadContext();
- }
- bool isMultithreaded() const { return componentModelThreadContext() || arg.sharedMemory; }
+ bool isMultithreaded() const { return componentModelThreadContext || arg.sharedMemory; }
};
extern Ctx ctx;
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index b1aacfe680451..e1609b2cd6eb2 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -828,7 +828,7 @@ static void checkOptions(opt::InputArgList &args) {
error("--table-base may not be used with -shared/-pie");
}
- if (ctx.arg.sharedMemory && ctx.componentModelThreadContext()) {
+ if (ctx.arg.sharedMemory && ctx.componentModelThreadContext) {
error("--shared-memory is currently incompatible with component model thread context intrinsics");
}
}
@@ -942,12 +942,23 @@ static DefinedGlobal *createOptionalGlobal(StringRef name, bool isMutable) {
return symtab->addOptionalGlobalSymbol(name, g);
}
-// Create ABI-defined synthetic symbols
-static void createSyntheticSymbols() {
+// Create ABI-defined synthetic symbols that are needed early, before LTO.
+static void createEarlySyntheticSymbols() {
if (ctx.arg.relocatable)
return;
static WasmSignature nullSignature = {{}, {}};
+ ctx.sym.callCtors = symtab->addSyntheticFunction(
+ "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
+ make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));
+}
+
+// Create synthetic symbols that rely on information that is only available
+// after LTO, e.g. __stack_pointer, __tls_base.
+static void createPostLTOSymbols() {
+ if (ctx.arg.relocatable)
+ return;
+
static WasmSignature i32ArgSignature = {{}, {ValType::I32}};
static WasmSignature i64ArgSignature = {{}, {ValType::I64}};
static llvm::wasm::WasmGlobalType globalTypeI32 = {WASM_TYPE_I32, false};
@@ -956,14 +967,11 @@ static void createSyntheticSymbols() {
true};
static llvm::wasm::WasmGlobalType mutableGlobalTypeI64 = {WASM_TYPE_I64,
true};
- ctx.sym.callCtors = symtab->addSyntheticFunction(
- "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
- make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));
bool is64 = ctx.arg.is64.value_or(false);
auto stack_pointer_name =
- ctx.componentModelThreadContext() ? "__init_stack_pointer" : "__stack_pointer";
+ ctx.componentModelThreadContext ? "__init_stack_pointer" : "__stack_pointer";
if (ctx.isPic) {
ctx.sym.stackPointer =
createUndefinedGlobal(stack_pointer_name, ctx.arg.is64.value_or(false)
@@ -987,7 +995,7 @@ static void createSyntheticSymbols() {
if (ctx.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
- auto tls_base_name = ctx.componentModelThreadContext() ? "__init_tls_base" : "__tls_base";
+ auto tls_base_name = ctx.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,
@@ -998,7 +1006,7 @@ static void createSyntheticSymbols() {
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(is64 ? i64ArgSignature : i32ArgSignature,
"__wasm_init_tls"));
- if (ctx.componentModelThreadContext()) {
+ if (ctx.componentModelThreadContext) {
ctx.sym.tlsBase->markLive();
ctx.sym.tlsSize->markLive();
ctx.sym.tlsAlign->markLive();
@@ -1051,7 +1059,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.componentModelThreadContext())
+ if (!ctx.arg.sharedMemory && !ctx.componentModelThreadContext)
ctx.sym.tlsBase = createOptionalGlobal("__tls_base", false);
}
@@ -1306,6 +1314,67 @@ static void checkZOptions(opt::InputArgList &args) {
warn("unknown -z value: " + StringRef(arg->getValue()));
}
+// Determine the thread context ABI based on object file features.
+// This must be called after LTO, since LTO object files are needed.
+static void determineThreadContextAbi(ArrayRef<ObjFile *> files) {
+ // A complication is that a user may attempt to link together object files
+ // compiled with different versions of LLVM, where one does not specifiy
+ // -component-model-thread-context when using the global thread context ABI.
+ // They may also attempt to link object files with the global ABI compiled with
+ // older LLVM versions, but link them with a newer wasm-ld. To ensure the correct behavior
+ // in both of these cases, we treat the import of a __stack_pointer global from the env module
+ // as an indication that the global thread context ABI is being used.
+
+ enum class ThreadContextAbi {
+ Undetermined,
+ ComponentModelBuiltins,
+ Globals
+ };
+
+ ThreadContextAbi threadContextAbi = ThreadContextAbi::Undetermined;
+
+ for (ObjFile *obj : files) {
+ auto targetFeatures = obj->getWasmObj()->getTargetFeatures();
+ auto threadContextFeature = llvm::find_if(targetFeatures,
+ [](const auto &f) {
+ return f.Name == "component-model-thread-context";
+ });
+
+ bool usesComponentModelThreadContext = threadContextFeature != targetFeatures.end() &&
+ threadContextFeature->Prefix == WASM_FEATURE_PREFIX_USED;
+
+ if (threadContextFeature == targetFeatures.end()) {
+ // If the feature is not explicitly used or disallowed, check for the presence of __stack_pointer
+ // to determine if the global thread context ABI is being used.
+ auto sym = symtab->find("__stack_pointer");
+ if (!sym || sym->kind() != Symbol::UndefinedGlobalKind || sym->importModule != "env") {
+ // No __stack_pointer import, so this is probably an object file compiled from assembly or
+ // some other source that doesn't care about the thread context ABI. As such, we let it pass.
+ break;
+ }
+ }
+
+ if (usesComponentModelThreadContext) {
+ if (threadContextAbi == ThreadContextAbi::Undetermined) {
+ threadContextAbi = ThreadContextAbi::ComponentModelBuiltins;
+ } else if (threadContextAbi != ThreadContextAbi::ComponentModelBuiltins) {
+ error("thread context ABI mismatch: " + obj->getName() +
+ " uses component-model-thread-context but other files disallow it");
+ }
+ } else {
+ if (threadContextAbi == ThreadContextAbi::Undetermined) {
+ threadContextAbi = ThreadContextAbi::Globals;
+ } else if (threadContextAbi != ThreadContextAbi::Globals) {
+ error("thread context ABI mismatch: " + obj->getName() +
+ " disallows component-model-thread-context but other files use it");
+ }
+ }
+ }
+
+ // If the ABI is undetermined at this point, default to the globals ABI
+ ctx.componentModelThreadContext = (threadContextAbi == ThreadContextAbi::ComponentModelBuiltins);
+}
+
LinkerDriver::LinkerDriver(Ctx &ctx) : ctx(ctx) {}
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
@@ -1395,14 +1464,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ctx.arg.requiredExports.push_back(arg->getValue());
}
+ createEarlySyntheticSymbols();
+
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table.
for (InputFile *f : files)
symtab->addFile(f);
if (errorCount())
return;
-
- createSyntheticSymbols();
// Handle the `--undefined <sym>` options.
for (auto *arg : args.filtered(OPT_undefined))
@@ -1480,6 +1549,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (errorCount())
return;
+ // Now that LTO is complete and all object files are available, determine the
+ // thread context ABI and create symbols (__stack_pointer, __tls_base, etc.) based
+ // on the determined ABI.
+ determineThreadContextAbi(ctx.objectFiles);
+ createPostLTOSymbols();
+
// The LTO process can generate new undefined symbols, specifically libcall
// functions. Because those symbols might be declared in a stub library we
// need the process the stub libraries once again after LTO to handle all
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 74e8f6440778a..011e4341519cd 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -69,7 +69,6 @@ void SymbolTable::addFile(InputFile *file, StringRef symName) {
auto *f = cast<ObjFile>(file);
f->parse(false);
ctx.objectFiles.push_back(f);
- validateThreadContextAbi(f);
}
// This function is where all the optimizations of link-time
@@ -92,57 +91,6 @@ void SymbolTable::compileBitcodeFiles() {
auto *obj = cast<ObjFile>(file);
obj->parse(true);
ctx.objectFiles.push_back(obj);
- validateThreadContextAbi(obj);
- }
-}
-
-void SymbolTable::validateThreadContextAbi(const ObjFile *obj) {
- // Check for the component-model-thread-context feature and validate that all
- // files are consistent in whether they use it or disallow it.
-
- // A complication is that a user may attempt to link together object files
- // compiled with different versions of LLVM, where one does not specifiy
- // -component-model-thread-context when using the global thread context ABI.
- // They may also attempt to link object files with the global ABI compiled with
- // older LLVM versions, but link them with a newer wasm-ld. To ensure the correct behavior
- // in both of these cases, we treat the presence of an __stack_pointer global as an indication
- // that the global thread context ABI is being used, even if the component-model-thread-context
- // feature is not disallowed.
-
- auto targetFeatures = obj->getWasmObj()->getTargetFeatures();
- auto threadContextFeature = llvm::find_if(targetFeatures,
- [](const auto &f) {
- return f.Name == "component-model-thread-context";
- });
-
- bool usesComponentModelThreadContext = threadContextFeature != targetFeatures.end() &&
- threadContextFeature->Prefix == WASM_FEATURE_PREFIX_USED;
-
- if (threadContextFeature == targetFeatures.end()) {
- // If the feature is not explicitly used or disallowed, check for the presence of __stack_pointer
- // to determine if the global thread context ABI is being used.
- auto sym = find("__stack_pointer");
- if (!sym || !sym->isDefined()) {
- // No __stack_pointer global, so this is probably an object file compiled from assembly or
- // some other source that doesn't care about the thread context ABI. As such, we let it pass.
- return;
- }
- }
-
- if (usesComponentModelThreadContext) {
- if (ctx.threadContextAbi == ThreadContextAbi::Undetermined) {
- ctx.threadContextAbi = ThreadContextAbi::ComponentModelBuiltins;
- } else if (ctx.threadContextAbi != ThreadContextAbi::ComponentModelBuiltins) {
- error("thread context ABI mismatch: " + obj->getName() +
- " uses component-model-thread-context but other files disallow it");
- }
- } else {
- if (ctx.threadContextAbi == ThreadContextAbi::Undetermined) {
- ctx.threadContextAbi = ThreadContextAbi::Globals;
- } else if (ctx.threadContextAbi != ThreadContextAbi::Globals) {
- error("thread context ABI mismatch: " + obj->getName() +
- " disallows component-model-thread-context but other files use it");
- }
}
}
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index ef6119223c9c8..5d09d8b685716 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -104,8 +104,6 @@ class SymbolTable {
void handleWeakUndefines();
DefinedFunction *createUndefinedStub(const WasmSignature &sig);
- void validateThreadContextAbi(const ObjFile *obj);
-
private:
std::pair<Symbol *, bool> insert(StringRef name, const InputFile *file);
std::pair<Symbol *, bool> insertName(StringRef name);
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 5b470f9c77020..25330f45c82dc 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -647,7 +647,7 @@ void Writer::populateTargetFeatures() {
"' feature must be used in order to use shared memory");
}
- if (tlsUsed && !ctx.componentModelThreadContext()) {
+ if (tlsUsed && !ctx.componentModelThreadContext) {
for (auto feature : {"atomics", "bulk-memory"})
if (!allowed.contains(feature))
error(StringRef("'") + feature +
@@ -1630,7 +1630,7 @@ void Writer::createInitTLSFunction() {
if (tlsSeg) {
// 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.componentModelThreadContext()) {
+ if (!ctx.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 f877149a77a94..40c0883dc650e 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -272,7 +272,7 @@ void writeExport(raw_ostream &os, const WasmExport &export_) {
}
void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.componentModelThreadContext()) {
+ if (ctx.componentModelThreadContext) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextGet1->getFunctionIndex(), "function index");
} else {
@@ -282,7 +282,7 @@ void writeGetTLSBase(const Ctx &ctx, raw_ostream &os) {
}
void writeSetTLSBase(const Ctx &ctx, raw_ostream &os) {
- if (ctx.componentModelThreadContext()) {
+ if (ctx.componentModelThreadContext) {
writeU8(os, WASM_OPCODE_CALL, "call");
writeUleb128(os, ctx.sym.contextSet1->getFunctionIndex(), "function index");
} else {
>From e8babc79196f0e32d4650b139d209b72912ff193 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 13:14:23 +0000
Subject: [PATCH 28/32] Fix comment
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index dc66113de9af2..53a22f3a9f9c6 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -311,7 +311,7 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_use_init_array, true))
CC1Args.push_back("-fno-use-init-array");
- // '-pthread' implies bulk-memory, and shared memory is also used,
+ // '-pthread' implies bulk-memory, and, if shared memory is also used,
// also implies atomics, mutable-globals, and sign-ext.
if (WantsPthread(getTriple(), DriverArgs)) {
if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory,
@@ -321,6 +321,7 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
<< "-mno-bulk-memory";
CC1Args.push_back("-target-feature");
CC1Args.push_back("+bulk-memory");
+
if (WantsSharedMemory(getTriple(), DriverArgs)) {
if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
false))
>From 822ea98e4a7c8bef60170e4d3d1ffc089c462f18 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 13:14:34 +0000
Subject: [PATCH 29/32] Test updates
---
clang/test/Driver/wasm-toolchain.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/clang/test/Driver/wasm-toolchain.c b/clang/test/Driver/wasm-toolchain.c
index 8654021e9a959..4a22ea8a02417 100644
--- a/clang/test/Driver/wasm-toolchain.c
+++ b/clang/test/Driver/wasm-toolchain.c
@@ -304,16 +304,6 @@
// LINK_WALI_BASIC: "-cc1" {{.*}} "-o" "[[temp:[^"]*]]"
// LINK_WALI_BASIC: wasm-ld{{.*}}" "-L/foo/lib/wasm32-linux-muslwali" "crt1.o" "[[temp]]" "-lc" "{{.*[/\\]}}libclang_rt.builtins.a" "-o" "a.out"
-// Test that `wasm32-wasip3` passes --component-model-thread-context to the linker by default.
-
-// RUN: %clang --target=wasm32-wasip3 %s -### 2>&1 | FileCheck -check-prefix=WASIP3_DEFAULT %s
-// WASIP3_DEFAULT: wasm-component-ld{{.*}}" {{.*}} "--component-model-thread-context"
-
-// Test that `wasm32-wasip3` does not pass --component-model-thread-context to the linker when
-// -mno-component-model-thread-context is used, and that it also passes -target-feature -component-model-thread-context
-// to disable the feature in clang-cc1.
-
-// RUN: %clang --target=wasm32-wasip3 %s -### -mno-component-model-thread-context 2>&1 | FileCheck -check-prefix=WASIP3_DISABLED %s
-
-// WASIP3_DISABLED-NOT: "--component-model-thread-context"
+// `-target=wasm32-wasip2` sets -component-model-thread-context
+// RUN: %clang --target=wasm32-wasip2 %s -### 2>&1 | FileCheck -check-prefix=WASIP3_DISABLED %s
// WASIP3_DISABLED: "-target-feature" "-component-model-thread-context"
\ No newline at end of file
>From cb2fb8d14c2976c864d95a0ddc079c36efcdfc36 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 13:14:55 +0000
Subject: [PATCH 30/32] TLS fixes
---
lld/wasm/Driver.cpp | 32 +++++++++----------
lld/wasm/SyntheticSections.cpp | 11 +------
lld/wasm/Writer.cpp | 21 ++++--------
.../AsmParser/WebAssemblyAsmParser.cpp | 1 +
4 files changed, 25 insertions(+), 40 deletions(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 5973ca47dd80e..a9f9566b3efb4 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -827,10 +827,6 @@ 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.componentModelThreadContext) {
- error("--shared-memory is currently incompatible with component model thread context intrinsics");
- }
}
static const char *getReproduceOption(opt::InputArgList &args) {
@@ -996,7 +992,7 @@ static void createPostLTOSymbols() {
if (ctx.isMultithreaded()) {
// TLS symbols are all hidden/dso-local
auto tls_base_name = ctx.componentModelThreadContext ? "__init_tls_base" : "__tls_base";
- ctx.sym.tlsBase = createGlobalVariable(tls_base_name, true,
+ ctx.sym.tlsBase = createGlobalVariable(tls_base_name, !ctx.componentModelThreadContext,
WASM_SYMBOL_VISIBILITY_HIDDEN);
ctx.sym.tlsSize = createGlobalVariable("__tls_size", false,
WASM_SYMBOL_VISIBILITY_HIDDEN);
@@ -1010,6 +1006,7 @@ static void createPostLTOSymbols() {
ctx.sym.tlsBase->markLive();
ctx.sym.tlsSize->markLive();
ctx.sym.tlsAlign->markLive();
+ ctx.sym.initTLS->markLive();
static WasmSignature contextSet1Signature{{}, {ValType::I32}};
ctx.sym.contextSet1 = createUndefinedFunction(
"__wasm_component_model_builtin_context_set_1", "[context-set-1]",
@@ -1316,7 +1313,7 @@ static void checkZOptions(opt::InputArgList &args) {
// Determine the thread context ABI based on object file features.
// This must be called after LTO, since LTO object files are needed.
-static void determineThreadContextAbi(ArrayRef<ObjFile *> files) {
+static void determineThreadContextABI(ArrayRef<ObjFile *> files) {
// A complication is that a user may attempt to link together object files
// compiled with different versions of LLVM, where one does not specifiy
// -component-model-thread-context when using the global thread context ABI.
@@ -1325,13 +1322,13 @@ static void determineThreadContextAbi(ArrayRef<ObjFile *> files) {
// in both of these cases, we treat the import of a __stack_pointer global from the env module
// as an indication that the global thread context ABI is being used.
- enum class ThreadContextAbi {
+ enum class ThreadContextABI {
Undetermined,
ComponentModelBuiltins,
Globals
};
- ThreadContextAbi threadContextAbi = ThreadContextAbi::Undetermined;
+ ThreadContextABI threadContextABI = ThreadContextABI::Undetermined;
for (ObjFile *obj : files) {
auto targetFeatures = obj->getWasmObj()->getTargetFeatures();
@@ -1355,16 +1352,16 @@ static void determineThreadContextAbi(ArrayRef<ObjFile *> files) {
}
if (usesComponentModelThreadContext) {
- if (threadContextAbi == ThreadContextAbi::Undetermined) {
- threadContextAbi = ThreadContextAbi::ComponentModelBuiltins;
- } else if (threadContextAbi != ThreadContextAbi::ComponentModelBuiltins) {
+ if (threadContextABI == ThreadContextABI::Undetermined) {
+ threadContextABI = ThreadContextABI::ComponentModelBuiltins;
+ } else if (threadContextABI != ThreadContextABI::ComponentModelBuiltins) {
error("thread context ABI mismatch: " + obj->getName() +
" uses component-model-thread-context but other files disallow it");
}
} else {
- if (threadContextAbi == ThreadContextAbi::Undetermined) {
- threadContextAbi = ThreadContextAbi::Globals;
- } else if (threadContextAbi != ThreadContextAbi::Globals) {
+ if (threadContextABI == ThreadContextABI::Undetermined) {
+ threadContextABI = ThreadContextABI::Globals;
+ } else if (threadContextABI != ThreadContextABI::Globals) {
error("thread context ABI mismatch: " + obj->getName() +
" disallows component-model-thread-context but other files use it");
}
@@ -1372,7 +1369,10 @@ static void determineThreadContextAbi(ArrayRef<ObjFile *> files) {
}
// If the ABI is undetermined at this point, default to the globals ABI
- ctx.componentModelThreadContext = (threadContextAbi == ThreadContextAbi::ComponentModelBuiltins);
+ ctx.componentModelThreadContext = (threadContextABI == ThreadContextABI::ComponentModelBuiltins);
+ if (ctx.arg.sharedMemory && ctx.componentModelThreadContext) {
+ error("--shared-memory is currently incompatible with component model thread context intrinsics");
+ }
}
LinkerDriver::LinkerDriver(Ctx &ctx) : ctx(ctx) {}
@@ -1552,7 +1552,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Now that LTO is complete and all object files are available, determine the
// thread context ABI and create symbols (__stack_pointer, __tls_base, etc.) based
// on the determined ABI.
- determineThreadContextAbi(ctx.objectFiles);
+ determineThreadContextABI(ctx.objectFiles);
createPostLTOSymbols();
// The LTO process can generate new undefined symbols, specifically libcall
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index bd20a5d06216d..e00f769a21ce7 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -466,12 +466,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);
-<<<<<<< sy/wasip3
- 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;
->>>>>>> main
for (const Symbol *sym : internalGotSymbols) {
if (TLS != sym->isTLS())
@@ -669,9 +664,6 @@ void DataCountSection::writeBody() {
}
bool DataCountSection::isNeeded() const {
-<<<<<<< sy/wasip3
- return numSegments && ctx.isMultithreaded();
-=======
// The datacount section is only required under certain circumstance.
// Specifically, when the module includes bulk memory instructions that deal
// with passive data segments. i.e. memory.init/data.drop.
@@ -679,8 +671,7 @@ bool DataCountSection::isNeeded() const {
// instructions are not yet supported in input files. However, in the case
// of shared memory, lld itself will generate these instructions as part of
// `__wasm_init_memory`. See Writer::createInitMemoryFunction.
- return numSegments && ctx.arg.sharedMemory;
->>>>>>> main
+ return numSegments && ctx.isMultithreaded();
}
void LinkingSection::writeBody() {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 811aec740d446..23a9f3ddf8623 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -649,11 +649,13 @@ void Writer::populateTargetFeatures() {
"' feature must be used in order to use shared memory");
}
- if (tlsUsed && !ctx.componentModelThreadContext) {
- for (auto feature : {"atomics", "bulk-memory"})
- if (!allowed.contains(feature))
- error(StringRef("'") + feature +
- "' feature must be used in order to use thread-local storage");
+ if (tlsUsed) {
+ if (!allowed.contains("bulk-memory")) {
+ error("bulk-memory feature must be used in order to use thread-local storage");
+ }
+ if (!ctx.componentModelThreadContext && !allowed.contains("atomics")) {
+ error("atomics feature must be used in order to use thread-local storage");
+ }
}
// Validate that used features are allowed in output
@@ -1185,11 +1187,7 @@ void Writer::createSyntheticInitFunctions() {
auto hasTLSRelocs = [](const OutputSegment *segment) {
if (segment->isTLS())
-<<<<<<< sy/wasip3
- for (const auto* is : segment->inputSegments)
-=======
for (const auto *is : segment->inputSegments)
->>>>>>> main
if (is->getRelocations().size())
return true;
return false;
@@ -1644,13 +1642,8 @@ void Writer::createInitTLSFunction() {
writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "global index");
}
-<<<<<<< sy/wasip3
- // 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.
->>>>>>> main
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
writeUleb128(os, 0, "local index");
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 25704c9ef0ac4..7a9f675548a5c 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -430,6 +430,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
return Name;
}
error("Expected string or identifier, got: ", Lexer.getTok());
+ return StringRef();
}
bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
>From 6cf7c2a3e1830100f7c38973bc015c7c690b7feb Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 13:17:02 +0000
Subject: [PATCH 31/32] TLS test
---
lld/test/wasm/tls-component-model.s | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 lld/test/wasm/tls-component-model.s
diff --git a/lld/test/wasm/tls-component-model.s b/lld/test/wasm/tls-component-model.s
new file mode 100644
index 0000000000000..d347ec0c1f2dc
--- /dev/null
+++ b/lld/test/wasm/tls-component-model.s
@@ -0,0 +1,29 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t.wasm | FileCheck %s --check-prefix=DIS
+
+.globl _start
+_start:
+ .functype _start () -> (i32)
+ global.get tls_sym at GOT@TLS
+ end_function
+
+.section .tdata.tls_sym,"",@
+.globl tls_sym
+tls_sym:
+ .int32 1
+ .size tls_sym, 4
+
+.section .custom_section.target_features,"",@
+ .int8 2
+ .int8 43
+ .int8 30
+ .ascii "component-model-thread-context"
+ .int8 43
+ .int8 11
+ .ascii "bulk-memory"
+
+
+# CHECK: Name: __init_tls_base
+# DIS: __wasm_init_tls
\ No newline at end of file
>From fa7eea8ec4af4a799efb592ce4ff8c211fb6e138 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 18 Feb 2026 13:27:27 +0000
Subject: [PATCH 32/32] Continue rather than break when determiting thread
context ABI
---
lld/wasm/Driver.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index a9f9566b3efb4..f4924329e99c2 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -1347,7 +1347,7 @@ static void determineThreadContextABI(ArrayRef<ObjFile *> files) {
if (!sym || sym->kind() != Symbol::UndefinedGlobalKind || sym->importModule != "env") {
// No __stack_pointer import, so this is probably an object file compiled from assembly or
// some other source that doesn't care about the thread context ABI. As such, we let it pass.
- break;
+ continue;
}
}
More information about the cfe-commits
mailing list