https://github.com/TartanLlama created 
https://github.com/llvm/llvm-project/pull/175800

(Currently in draft, as this will evolve alongside other toolchain component 
updates)

The [WebAssembly Component 
Model](https://component-model.bytecodealliance.org/) has added support for 
[cooperative 
multithreading](https://github.com/WebAssembly/component-model/pull/557). This 
has been implemented in the [Wasmtime 
engine](https://github.com/bytecodealliance/wasmtime/pull/11751) and is part of 
the wider project of [WASI preview 
3](https://wasi.dev/roadmap#upcoming-wasi-03-releases), which is currently 
tracked [here](https://github.com/orgs/bytecodealliance/projects/16).

These changes will require updating the way that `__stack_pointer` and 
`__tls_base` work purely for a new `wasm32-wasip3` target; other targets will 
not be touched. Specifically, rather than using a Wasm global for tracking the 
stack pointer and TLS base, the new 
[`context.get/set`](https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#-canon-contextget)
 component model builtin functions will be used (the intention being that 
runtimes will need to aggressively optimize these calls into single 
load/stores). For justification on this choice rather than switching out the 
global at context-switch boundaries, see [this 
comment](https://github.com/WebAssembly/wasi-libc/issues/691#issuecomment-3716405618)
 and [this 
comment](https://github.com/WebAssembly/wasi-libc/issues/691#issuecomment-3716916730).

>From 48a90dd7e288ad6c3ce8038c5b2d143dcc1dac34 Mon Sep 17 00:00:00 2001
From: Sy Brand <[email protected]>
Date: Mon, 5 Jan 2026 14:43:56 +0000
Subject: [PATCH 1/2] 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 <[email protected]>
Date: Fri, 9 Jan 2026 12:24:41 +0000
Subject: [PATCH 2/2] 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",

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to