Skip to content

Conversation

arjunr2
Copy link

@arjunr2 arjunr2 commented Aug 29, 2025

This PR adds minimal support to the clang frontend, lld, and libcxx for the WALI target supported by the rustc toolchain

@arjunr2 arjunr2 requested a review from a team as a code owner August 29, 2025 19:33
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category lld libunwind backend:WebAssembly clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" lld:wasm llvm:binary-utilities labels Aug 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-libunwind
@llvm/pr-subscribers-lld

@llvm/pr-subscribers-clang

Author: Arjun Ramesh (arjunr2)

Changes

This PR adds minimal support to the clang frontend, lld, and libcxx for the WALI target supported by the rustc toolchain


Full diff: https://github.com/llvm/llvm-project/pull/156087.diff

12 Files Affected:

  • (modified) clang/lib/Basic/Targets.cpp (+6-1)
  • (modified) clang/lib/Basic/Targets/OSTargets.h (+17)
  • (modified) clang/lib/Basic/Targets/WebAssembly.h (+13-5)
  • (modified) clang/lib/Driver/Driver.cpp (+2)
  • (modified) libunwind/src/assembly.h (+3)
  • (modified) lld/wasm/Config.h (+10)
  • (modified) lld/wasm/Symbols.cpp (+4)
  • (modified) lld/wasm/Symbols.h (+7)
  • (modified) lld/wasm/Writer.cpp (+80-19)
  • (modified) llvm/include/llvm/BinaryFormat/Wasm.h (+2)
  • (modified) llvm/include/llvm/TargetParser/Triple.h (+7)
  • (modified) llvm/lib/TargetParser/Triple.cpp (+3)
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index e3f9760ac7ce3..11222bc836775 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -687,7 +687,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
   }
   case llvm::Triple::wasm32:
     if (Triple.getSubArch() != llvm::Triple::NoSubArch ||
-        Triple.getVendor() != llvm::Triple::UnknownVendor ||
+        (!Triple.isWALI() &&
+         Triple.getVendor() != llvm::Triple::UnknownVendor) ||
         !Triple.isOSBinFormatWasm())
       return nullptr;
     switch (os) {
@@ -697,6 +698,10 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
       case llvm::Triple::Emscripten:
       return std::make_unique<EmscriptenTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
+      // WALI OS target
+      case llvm::Triple::Linux:
+        return std::make_unique<WALITargetInfo<WebAssembly32TargetInfo>>(Triple,
+                                                                         Opts);
       case llvm::Triple::UnknownOS:
       return std::make_unique<WebAssemblyOSTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index a733f6e97b3a4..2199bfcfbd7ab 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -936,6 +936,23 @@ class LLVM_LIBRARY_VISIBILITY WASITargetInfo
   using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
 };
 
+// WALI target
+template <typename Target>
+class LLVM_LIBRARY_VISIBILITY WALITargetInfo
+    : public WebAssemblyOSTargetInfo<Target> {
+  void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+                    MacroBuilder &Builder) const final {
+    WebAssemblyOSTargetInfo<Target>::getOSDefines(Opts, Triple, Builder);
+    // Linux defines; list based off of gcc output
+    DefineStd(Builder, "unix", Opts);
+    DefineStd(Builder, "linux", Opts);
+    Builder.defineMacro("__wali__");
+  }
+
+public:
+  using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
+};
+
 // Emscripten target
 template <typename Target>
 class LLVM_LIBRARY_VISIBILITY EmscriptenTargetInfo
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index eba74229dfc14..81fd40a62d3a3 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -88,12 +88,20 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
     LongDoubleWidth = LongDoubleAlign = 128;
     LongDoubleFormat = &llvm::APFloat::IEEEquad();
     MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
-    // size_t being unsigned long for both wasm32 and wasm64 makes mangled names
-    // more consistent between the two.
-    SizeType = UnsignedLong;
-    PtrDiffType = SignedLong;
-    IntPtrType = SignedLong;
     HasUnalignedAccess = true;
+    if (T.isWALI()) {
+      // WALI ABI requires 64-bit longs on both wasm32 and wasm64
+      LongAlign = LongWidth = 64;
+      SizeType = UnsignedInt;
+      PtrDiffType = SignedInt;
+      IntPtrType = SignedInt;
+    } else {
+      // size_t being unsigned long for both wasm32 and wasm64 makes mangled
+      // names more consistent between the two.
+      SizeType = UnsignedLong;
+      PtrDiffType = SignedLong;
+      IntPtrType = SignedLong;
+    }
   }
 
   StringRef getABI() const override;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f110dbab3e5a5..e99b2263c81c6 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -6833,6 +6833,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
         TC = std::make_unique<toolchains::VEToolChain>(*this, Target, Args);
       else if (Target.isOHOSFamily())
         TC = std::make_unique<toolchains::OHOS>(*this, Target, Args);
+      else if (Target.isWALI())
+        TC = std::make_unique<toolchains::WebAssembly>(*this, Target, Args);
       else
         TC = std::make_unique<toolchains::Linux>(*this, Target, Args);
       break;
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index f8e83e138eff5..c5097d25b0c63 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -249,6 +249,9 @@ aliasname:                                                                     \
 #define WEAK_ALIAS(name, aliasname)
 #define NO_EXEC_STACK_DIRECTIVE
 
+#elif defined(__wasm__)
+#define NO_EXEC_STACK_DIRECTIVE
+
 // clang-format on
 #else
 
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 9d903e0c799db..cbc71db083edc 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -199,6 +199,16 @@ struct Ctx {
     // Function that initializes passive data segments during instantiation.
     DefinedFunction *initMemory;
 
+    // __wasm_memory_grow
+    // Function to perform memory.grow. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memoryGrow;
+
+    // __wasm_memory_size
+    // Function to perform memory.size. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memorySize;
+
     // __wasm_call_ctors
     // Function that directly calls all ctors in priority order.
     DefinedFunction *callCtors;
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..2c521745e3414 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -285,6 +285,10 @@ uint32_t DefinedFunction::getExportedFunctionIndex() const {
   return function->getFunctionIndex();
 }
 
+void DefinedFunction::setExportNoWrap(bool v) { exportNoWrap = v; }
+
+bool DefinedFunction::getExportNoWrap() const { return exportNoWrap; }
+
 uint64_t DefinedData::getVA(bool absolute) const {
   LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
   // TLS symbols (by default) are relative to the start of the TLS output
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 55ee21939ce07..fb8188a4e749f 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -229,6 +229,10 @@ class DefinedFunction : public FunctionSymbol {
     return s->kind() == DefinedFunctionKind;
   }
 
+  // Get/set the exportNoWrap
+  void setExportNoWrap(bool v);
+  bool getExportNoWrap() const;
+
   // Get the function index to be used when exporting.  This only applies to
   // defined functions and can be differ from the regular function index for
   // weakly defined functions (that are imported and used via one index but
@@ -236,6 +240,9 @@ class DefinedFunction : public FunctionSymbol {
   uint32_t getExportedFunctionIndex() const;
 
   InputFunction *function;
+
+protected:
+  bool exportNoWrap = false;
 };
 
 class UndefinedFunction : public FunctionSymbol {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b704677d36c93..0628e37850915 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -60,6 +60,8 @@ class Writer {
 
   void createSyntheticInitFunctions();
   void createInitMemoryFunction();
+  void createMemoryGrowFunction();
+  void createMemorySizeFunction();
   void createStartFunction();
   void createApplyDataRelocationsFunction();
   void createApplyGlobalRelocationsFunction();
@@ -888,31 +890,33 @@ void Writer::createCommandExportWrappers() {
         toWrap.push_back(f);
 
   for (auto *f : toWrap) {
-    auto funcNameStr = (f->getName() + ".command_export").str();
-    commandExportWrapperNames.push_back(funcNameStr);
-    const std::string &funcName = commandExportWrapperNames.back();
+    if (!(f->getExportNoWrap())) {
+      auto funcNameStr = (f->getName() + ".command_export").str();
+      commandExportWrapperNames.push_back(funcNameStr);
+      const std::string &funcName = commandExportWrapperNames.back();
 
-    auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
-    if (f->function->getExportName())
-      func->setExportName(f->function->getExportName()->str());
-    else
-      func->setExportName(f->getName().str());
+      auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
+      if (f->function->getExportName())
+        func->setExportName(f->function->getExportName()->str());
+      else
+        func->setExportName(f->getName().str());
 
-    DefinedFunction *def =
-        symtab->addSyntheticFunction(funcName, f->flags, func);
-    def->markLive();
+      DefinedFunction *def =
+          symtab->addSyntheticFunction(funcName, f->flags, func);
+      def->markLive();
 
-    def->flags |= WASM_SYMBOL_EXPORTED;
-    def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
-    def->forceExport = f->forceExport;
+      def->flags |= WASM_SYMBOL_EXPORTED;
+      def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
+      def->forceExport = f->forceExport;
 
-    f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
-    f->flags &= ~WASM_SYMBOL_EXPORTED;
-    f->forceExport = false;
+      f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
+      f->flags &= ~WASM_SYMBOL_EXPORTED;
+      f->forceExport = false;
 
-    out.functionSec->addFunction(func);
+      out.functionSec->addFunction(func);
 
-    createCommandExportWrapper(f->getFunctionIndex(), def);
+      createCommandExportWrapper(f->getFunctionIndex(), def);
+    }
   }
 }
 
@@ -1136,6 +1140,8 @@ void Writer::createSyntheticInitFunctions() {
     return;
 
   static WasmSignature nullSignature = {{}, {}};
+  static WasmSignature memoryGrowSignature = {{ValType::I32}, {ValType::I32}};
+  static WasmSignature memorySizeSignature = {{ValType::I32}, {}};
 
   createApplyDataRelocationsFunction();
 
@@ -1156,6 +1162,25 @@ void Writer::createSyntheticInitFunctions() {
     }
   }
 
+  // Memory grow/size export hooks
+  auto memoryGrowFunc =
+      make<SyntheticFunction>(memoryGrowSignature, "__wasm_memory_grow");
+  memoryGrowFunc->setExportName("__wasm_memory_grow");
+  ctx.sym.memoryGrow = symtab->addSyntheticFunction(
+      "__wasm_memory_grow",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memoryGrowFunc);
+  ctx.sym.memoryGrow->markLive();
+  ctx.sym.memoryGrow->setExportNoWrap(true);
+
+  auto memorySizeFunc =
+      make<SyntheticFunction>(memorySizeSignature, "__wasm_memory_size");
+  memorySizeFunc->setExportName("__wasm_memory_size");
+  ctx.sym.memorySize = symtab->addSyntheticFunction(
+      "__wasm_memory_size",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memorySizeFunc);
+  ctx.sym.memorySize->markLive();
+  ctx.sym.memorySize->setExportNoWrap(true);
+
   if (ctx.arg.sharedMemory) {
     if (out.globalSec->needsTLSRelocations()) {
       ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
@@ -1200,6 +1225,36 @@ void Writer::createSyntheticInitFunctions() {
   }
 }
 
+void Writer::createMemoryGrowFunction() {
+  LLVM_DEBUG(dbgs() << "createMemoryGrowFunction\n");
+  assert(ctx.sym.memoryGrow);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
+    writeUleb128(os, 0, "local 0");
+    writeU8(os, WASM_OPCODE_MEMORY_GROW, "memory grow");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memoryGrow, bodyContent);
+}
+
+void Writer::createMemorySizeFunction() {
+  LLVM_DEBUG(dbgs() << "createMemorySizeFunction\n");
+  assert(ctx.sym.memorySize);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_MEMORY_SIZE, "memory size");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memorySize, bodyContent);
+}
+
 void Writer::createInitMemoryFunction() {
   LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
   assert(ctx.sym.initMemory);
@@ -1788,6 +1843,12 @@ void Writer::run() {
     if (ctx.sym.initMemory) {
       createInitMemoryFunction();
     }
+    if (ctx.sym.memoryGrow) {
+      createMemoryGrowFunction();
+    }
+    if (ctx.sym.memorySize) {
+      createMemorySizeFunction();
+    }
     createStartFunction();
 
     createCallCtorsFunction();
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index cf90a43d0d7e8..e489026635934 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -135,6 +135,8 @@ enum : unsigned {
   WASM_OPCODE_BR_TABLE = 0x0e,
   WASM_OPCODE_RETURN = 0x0f,
   WASM_OPCODE_DROP = 0x1a,
+  WASM_OPCODE_MEMORY_SIZE = 0x3f,
+  WASM_OPCODE_MEMORY_GROW = 0x40,
   WASM_OPCODE_MISC_PREFIX = 0xfc,
   WASM_OPCODE_MEMORY_INIT = 0x08,
   WASM_OPCODE_MEMORY_FILL = 0x0b,
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index ede9797ac7488..447a94a48af67 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -199,6 +199,7 @@ class Triple {
     SUSE,
     OpenEmbedded,
     Intel,
+    WALI,
     Meta,
     LastVendorType = Meta
   };
@@ -795,6 +796,12 @@ class Triple {
     return getObjectFormat() == Triple::DXContainer;
   }
 
+  /// Tests whether the target uses WALI Wasm
+  bool isWALI() const {
+    return getArch() == Triple::wasm32 && getVendor() == Triple::WALI &&
+           getOS() == Triple::Linux;
+  }
+
   /// Tests whether the target is the PS4 platform.
   bool isPS4() const {
     return getArch() == Triple::x86_64 &&
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 6acb0bc49ecfe..5fc0086fc8d76 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -277,6 +277,8 @@ StringRef Triple::getVendorTypeName(VendorType Kind) {
   case PC: return "pc";
   case SCEI: return "scei";
   case SUSE: return "suse";
+  case WALI:
+    return "wali";
   case Meta:
     return "meta";
   }
@@ -681,6 +683,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) {
       .Case("suse", Triple::SUSE)
       .Case("oe", Triple::OpenEmbedded)
       .Case("intel", Triple::Intel)
+      .Case("wali", Triple::WALI)
       .Case("meta", Triple::Meta)
       .Default(Triple::UnknownVendor);
 }

@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-lld-wasm

Author: Arjun Ramesh (arjunr2)

Changes

This PR adds minimal support to the clang frontend, lld, and libcxx for the WALI target supported by the rustc toolchain


Full diff: https://github.com/llvm/llvm-project/pull/156087.diff

12 Files Affected:

  • (modified) clang/lib/Basic/Targets.cpp (+6-1)
  • (modified) clang/lib/Basic/Targets/OSTargets.h (+17)
  • (modified) clang/lib/Basic/Targets/WebAssembly.h (+13-5)
  • (modified) clang/lib/Driver/Driver.cpp (+2)
  • (modified) libunwind/src/assembly.h (+3)
  • (modified) lld/wasm/Config.h (+10)
  • (modified) lld/wasm/Symbols.cpp (+4)
  • (modified) lld/wasm/Symbols.h (+7)
  • (modified) lld/wasm/Writer.cpp (+80-19)
  • (modified) llvm/include/llvm/BinaryFormat/Wasm.h (+2)
  • (modified) llvm/include/llvm/TargetParser/Triple.h (+7)
  • (modified) llvm/lib/TargetParser/Triple.cpp (+3)
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index e3f9760ac7ce3..11222bc836775 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -687,7 +687,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
   }
   case llvm::Triple::wasm32:
     if (Triple.getSubArch() != llvm::Triple::NoSubArch ||
-        Triple.getVendor() != llvm::Triple::UnknownVendor ||
+        (!Triple.isWALI() &&
+         Triple.getVendor() != llvm::Triple::UnknownVendor) ||
         !Triple.isOSBinFormatWasm())
       return nullptr;
     switch (os) {
@@ -697,6 +698,10 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
       case llvm::Triple::Emscripten:
       return std::make_unique<EmscriptenTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
+      // WALI OS target
+      case llvm::Triple::Linux:
+        return std::make_unique<WALITargetInfo<WebAssembly32TargetInfo>>(Triple,
+                                                                         Opts);
       case llvm::Triple::UnknownOS:
       return std::make_unique<WebAssemblyOSTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index a733f6e97b3a4..2199bfcfbd7ab 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -936,6 +936,23 @@ class LLVM_LIBRARY_VISIBILITY WASITargetInfo
   using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
 };
 
+// WALI target
+template <typename Target>
+class LLVM_LIBRARY_VISIBILITY WALITargetInfo
+    : public WebAssemblyOSTargetInfo<Target> {
+  void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+                    MacroBuilder &Builder) const final {
+    WebAssemblyOSTargetInfo<Target>::getOSDefines(Opts, Triple, Builder);
+    // Linux defines; list based off of gcc output
+    DefineStd(Builder, "unix", Opts);
+    DefineStd(Builder, "linux", Opts);
+    Builder.defineMacro("__wali__");
+  }
+
+public:
+  using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
+};
+
 // Emscripten target
 template <typename Target>
 class LLVM_LIBRARY_VISIBILITY EmscriptenTargetInfo
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index eba74229dfc14..81fd40a62d3a3 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -88,12 +88,20 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
     LongDoubleWidth = LongDoubleAlign = 128;
     LongDoubleFormat = &llvm::APFloat::IEEEquad();
     MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
-    // size_t being unsigned long for both wasm32 and wasm64 makes mangled names
-    // more consistent between the two.
-    SizeType = UnsignedLong;
-    PtrDiffType = SignedLong;
-    IntPtrType = SignedLong;
     HasUnalignedAccess = true;
+    if (T.isWALI()) {
+      // WALI ABI requires 64-bit longs on both wasm32 and wasm64
+      LongAlign = LongWidth = 64;
+      SizeType = UnsignedInt;
+      PtrDiffType = SignedInt;
+      IntPtrType = SignedInt;
+    } else {
+      // size_t being unsigned long for both wasm32 and wasm64 makes mangled
+      // names more consistent between the two.
+      SizeType = UnsignedLong;
+      PtrDiffType = SignedLong;
+      IntPtrType = SignedLong;
+    }
   }
 
   StringRef getABI() const override;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f110dbab3e5a5..e99b2263c81c6 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -6833,6 +6833,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
         TC = std::make_unique<toolchains::VEToolChain>(*this, Target, Args);
       else if (Target.isOHOSFamily())
         TC = std::make_unique<toolchains::OHOS>(*this, Target, Args);
+      else if (Target.isWALI())
+        TC = std::make_unique<toolchains::WebAssembly>(*this, Target, Args);
       else
         TC = std::make_unique<toolchains::Linux>(*this, Target, Args);
       break;
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index f8e83e138eff5..c5097d25b0c63 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -249,6 +249,9 @@ aliasname:                                                                     \
 #define WEAK_ALIAS(name, aliasname)
 #define NO_EXEC_STACK_DIRECTIVE
 
+#elif defined(__wasm__)
+#define NO_EXEC_STACK_DIRECTIVE
+
 // clang-format on
 #else
 
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 9d903e0c799db..cbc71db083edc 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -199,6 +199,16 @@ struct Ctx {
     // Function that initializes passive data segments during instantiation.
     DefinedFunction *initMemory;
 
+    // __wasm_memory_grow
+    // Function to perform memory.grow. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memoryGrow;
+
+    // __wasm_memory_size
+    // Function to perform memory.size. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memorySize;
+
     // __wasm_call_ctors
     // Function that directly calls all ctors in priority order.
     DefinedFunction *callCtors;
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..2c521745e3414 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -285,6 +285,10 @@ uint32_t DefinedFunction::getExportedFunctionIndex() const {
   return function->getFunctionIndex();
 }
 
+void DefinedFunction::setExportNoWrap(bool v) { exportNoWrap = v; }
+
+bool DefinedFunction::getExportNoWrap() const { return exportNoWrap; }
+
 uint64_t DefinedData::getVA(bool absolute) const {
   LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
   // TLS symbols (by default) are relative to the start of the TLS output
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 55ee21939ce07..fb8188a4e749f 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -229,6 +229,10 @@ class DefinedFunction : public FunctionSymbol {
     return s->kind() == DefinedFunctionKind;
   }
 
+  // Get/set the exportNoWrap
+  void setExportNoWrap(bool v);
+  bool getExportNoWrap() const;
+
   // Get the function index to be used when exporting.  This only applies to
   // defined functions and can be differ from the regular function index for
   // weakly defined functions (that are imported and used via one index but
@@ -236,6 +240,9 @@ class DefinedFunction : public FunctionSymbol {
   uint32_t getExportedFunctionIndex() const;
 
   InputFunction *function;
+
+protected:
+  bool exportNoWrap = false;
 };
 
 class UndefinedFunction : public FunctionSymbol {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b704677d36c93..0628e37850915 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -60,6 +60,8 @@ class Writer {
 
   void createSyntheticInitFunctions();
   void createInitMemoryFunction();
+  void createMemoryGrowFunction();
+  void createMemorySizeFunction();
   void createStartFunction();
   void createApplyDataRelocationsFunction();
   void createApplyGlobalRelocationsFunction();
@@ -888,31 +890,33 @@ void Writer::createCommandExportWrappers() {
         toWrap.push_back(f);
 
   for (auto *f : toWrap) {
-    auto funcNameStr = (f->getName() + ".command_export").str();
-    commandExportWrapperNames.push_back(funcNameStr);
-    const std::string &funcName = commandExportWrapperNames.back();
+    if (!(f->getExportNoWrap())) {
+      auto funcNameStr = (f->getName() + ".command_export").str();
+      commandExportWrapperNames.push_back(funcNameStr);
+      const std::string &funcName = commandExportWrapperNames.back();
 
-    auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
-    if (f->function->getExportName())
-      func->setExportName(f->function->getExportName()->str());
-    else
-      func->setExportName(f->getName().str());
+      auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
+      if (f->function->getExportName())
+        func->setExportName(f->function->getExportName()->str());
+      else
+        func->setExportName(f->getName().str());
 
-    DefinedFunction *def =
-        symtab->addSyntheticFunction(funcName, f->flags, func);
-    def->markLive();
+      DefinedFunction *def =
+          symtab->addSyntheticFunction(funcName, f->flags, func);
+      def->markLive();
 
-    def->flags |= WASM_SYMBOL_EXPORTED;
-    def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
-    def->forceExport = f->forceExport;
+      def->flags |= WASM_SYMBOL_EXPORTED;
+      def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
+      def->forceExport = f->forceExport;
 
-    f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
-    f->flags &= ~WASM_SYMBOL_EXPORTED;
-    f->forceExport = false;
+      f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
+      f->flags &= ~WASM_SYMBOL_EXPORTED;
+      f->forceExport = false;
 
-    out.functionSec->addFunction(func);
+      out.functionSec->addFunction(func);
 
-    createCommandExportWrapper(f->getFunctionIndex(), def);
+      createCommandExportWrapper(f->getFunctionIndex(), def);
+    }
   }
 }
 
@@ -1136,6 +1140,8 @@ void Writer::createSyntheticInitFunctions() {
     return;
 
   static WasmSignature nullSignature = {{}, {}};
+  static WasmSignature memoryGrowSignature = {{ValType::I32}, {ValType::I32}};
+  static WasmSignature memorySizeSignature = {{ValType::I32}, {}};
 
   createApplyDataRelocationsFunction();
 
@@ -1156,6 +1162,25 @@ void Writer::createSyntheticInitFunctions() {
     }
   }
 
+  // Memory grow/size export hooks
+  auto memoryGrowFunc =
+      make<SyntheticFunction>(memoryGrowSignature, "__wasm_memory_grow");
+  memoryGrowFunc->setExportName("__wasm_memory_grow");
+  ctx.sym.memoryGrow = symtab->addSyntheticFunction(
+      "__wasm_memory_grow",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memoryGrowFunc);
+  ctx.sym.memoryGrow->markLive();
+  ctx.sym.memoryGrow->setExportNoWrap(true);
+
+  auto memorySizeFunc =
+      make<SyntheticFunction>(memorySizeSignature, "__wasm_memory_size");
+  memorySizeFunc->setExportName("__wasm_memory_size");
+  ctx.sym.memorySize = symtab->addSyntheticFunction(
+      "__wasm_memory_size",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memorySizeFunc);
+  ctx.sym.memorySize->markLive();
+  ctx.sym.memorySize->setExportNoWrap(true);
+
   if (ctx.arg.sharedMemory) {
     if (out.globalSec->needsTLSRelocations()) {
       ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
@@ -1200,6 +1225,36 @@ void Writer::createSyntheticInitFunctions() {
   }
 }
 
+void Writer::createMemoryGrowFunction() {
+  LLVM_DEBUG(dbgs() << "createMemoryGrowFunction\n");
+  assert(ctx.sym.memoryGrow);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
+    writeUleb128(os, 0, "local 0");
+    writeU8(os, WASM_OPCODE_MEMORY_GROW, "memory grow");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memoryGrow, bodyContent);
+}
+
+void Writer::createMemorySizeFunction() {
+  LLVM_DEBUG(dbgs() << "createMemorySizeFunction\n");
+  assert(ctx.sym.memorySize);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_MEMORY_SIZE, "memory size");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memorySize, bodyContent);
+}
+
 void Writer::createInitMemoryFunction() {
   LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
   assert(ctx.sym.initMemory);
@@ -1788,6 +1843,12 @@ void Writer::run() {
     if (ctx.sym.initMemory) {
       createInitMemoryFunction();
     }
+    if (ctx.sym.memoryGrow) {
+      createMemoryGrowFunction();
+    }
+    if (ctx.sym.memorySize) {
+      createMemorySizeFunction();
+    }
     createStartFunction();
 
     createCallCtorsFunction();
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index cf90a43d0d7e8..e489026635934 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -135,6 +135,8 @@ enum : unsigned {
   WASM_OPCODE_BR_TABLE = 0x0e,
   WASM_OPCODE_RETURN = 0x0f,
   WASM_OPCODE_DROP = 0x1a,
+  WASM_OPCODE_MEMORY_SIZE = 0x3f,
+  WASM_OPCODE_MEMORY_GROW = 0x40,
   WASM_OPCODE_MISC_PREFIX = 0xfc,
   WASM_OPCODE_MEMORY_INIT = 0x08,
   WASM_OPCODE_MEMORY_FILL = 0x0b,
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index ede9797ac7488..447a94a48af67 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -199,6 +199,7 @@ class Triple {
     SUSE,
     OpenEmbedded,
     Intel,
+    WALI,
     Meta,
     LastVendorType = Meta
   };
@@ -795,6 +796,12 @@ class Triple {
     return getObjectFormat() == Triple::DXContainer;
   }
 
+  /// Tests whether the target uses WALI Wasm
+  bool isWALI() const {
+    return getArch() == Triple::wasm32 && getVendor() == Triple::WALI &&
+           getOS() == Triple::Linux;
+  }
+
   /// Tests whether the target is the PS4 platform.
   bool isPS4() const {
     return getArch() == Triple::x86_64 &&
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 6acb0bc49ecfe..5fc0086fc8d76 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -277,6 +277,8 @@ StringRef Triple::getVendorTypeName(VendorType Kind) {
   case PC: return "pc";
   case SCEI: return "scei";
   case SUSE: return "suse";
+  case WALI:
+    return "wali";
   case Meta:
     return "meta";
   }
@@ -681,6 +683,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) {
       .Case("suse", Triple::SUSE)
       .Case("oe", Triple::OpenEmbedded)
       .Case("intel", Triple::Intel)
+      .Case("wali", Triple::WALI)
       .Case("meta", Triple::Meta)
       .Default(Triple::UnknownVendor);
 }

@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Arjun Ramesh (arjunr2)

Changes

This PR adds minimal support to the clang frontend, lld, and libcxx for the WALI target supported by the rustc toolchain


Full diff: https://github.com/llvm/llvm-project/pull/156087.diff

12 Files Affected:

  • (modified) clang/lib/Basic/Targets.cpp (+6-1)
  • (modified) clang/lib/Basic/Targets/OSTargets.h (+17)
  • (modified) clang/lib/Basic/Targets/WebAssembly.h (+13-5)
  • (modified) clang/lib/Driver/Driver.cpp (+2)
  • (modified) libunwind/src/assembly.h (+3)
  • (modified) lld/wasm/Config.h (+10)
  • (modified) lld/wasm/Symbols.cpp (+4)
  • (modified) lld/wasm/Symbols.h (+7)
  • (modified) lld/wasm/Writer.cpp (+80-19)
  • (modified) llvm/include/llvm/BinaryFormat/Wasm.h (+2)
  • (modified) llvm/include/llvm/TargetParser/Triple.h (+7)
  • (modified) llvm/lib/TargetParser/Triple.cpp (+3)
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index e3f9760ac7ce3..11222bc836775 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -687,7 +687,8 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
   }
   case llvm::Triple::wasm32:
     if (Triple.getSubArch() != llvm::Triple::NoSubArch ||
-        Triple.getVendor() != llvm::Triple::UnknownVendor ||
+        (!Triple.isWALI() &&
+         Triple.getVendor() != llvm::Triple::UnknownVendor) ||
         !Triple.isOSBinFormatWasm())
       return nullptr;
     switch (os) {
@@ -697,6 +698,10 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
       case llvm::Triple::Emscripten:
       return std::make_unique<EmscriptenTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
+      // WALI OS target
+      case llvm::Triple::Linux:
+        return std::make_unique<WALITargetInfo<WebAssembly32TargetInfo>>(Triple,
+                                                                         Opts);
       case llvm::Triple::UnknownOS:
       return std::make_unique<WebAssemblyOSTargetInfo<WebAssembly32TargetInfo>>(
           Triple, Opts);
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index a733f6e97b3a4..2199bfcfbd7ab 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -936,6 +936,23 @@ class LLVM_LIBRARY_VISIBILITY WASITargetInfo
   using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
 };
 
+// WALI target
+template <typename Target>
+class LLVM_LIBRARY_VISIBILITY WALITargetInfo
+    : public WebAssemblyOSTargetInfo<Target> {
+  void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+                    MacroBuilder &Builder) const final {
+    WebAssemblyOSTargetInfo<Target>::getOSDefines(Opts, Triple, Builder);
+    // Linux defines; list based off of gcc output
+    DefineStd(Builder, "unix", Opts);
+    DefineStd(Builder, "linux", Opts);
+    Builder.defineMacro("__wali__");
+  }
+
+public:
+  using WebAssemblyOSTargetInfo<Target>::WebAssemblyOSTargetInfo;
+};
+
 // Emscripten target
 template <typename Target>
 class LLVM_LIBRARY_VISIBILITY EmscriptenTargetInfo
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index eba74229dfc14..81fd40a62d3a3 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -88,12 +88,20 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
     LongDoubleWidth = LongDoubleAlign = 128;
     LongDoubleFormat = &llvm::APFloat::IEEEquad();
     MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
-    // size_t being unsigned long for both wasm32 and wasm64 makes mangled names
-    // more consistent between the two.
-    SizeType = UnsignedLong;
-    PtrDiffType = SignedLong;
-    IntPtrType = SignedLong;
     HasUnalignedAccess = true;
+    if (T.isWALI()) {
+      // WALI ABI requires 64-bit longs on both wasm32 and wasm64
+      LongAlign = LongWidth = 64;
+      SizeType = UnsignedInt;
+      PtrDiffType = SignedInt;
+      IntPtrType = SignedInt;
+    } else {
+      // size_t being unsigned long for both wasm32 and wasm64 makes mangled
+      // names more consistent between the two.
+      SizeType = UnsignedLong;
+      PtrDiffType = SignedLong;
+      IntPtrType = SignedLong;
+    }
   }
 
   StringRef getABI() const override;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f110dbab3e5a5..e99b2263c81c6 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -6833,6 +6833,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
         TC = std::make_unique<toolchains::VEToolChain>(*this, Target, Args);
       else if (Target.isOHOSFamily())
         TC = std::make_unique<toolchains::OHOS>(*this, Target, Args);
+      else if (Target.isWALI())
+        TC = std::make_unique<toolchains::WebAssembly>(*this, Target, Args);
       else
         TC = std::make_unique<toolchains::Linux>(*this, Target, Args);
       break;
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index f8e83e138eff5..c5097d25b0c63 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -249,6 +249,9 @@ aliasname:                                                                     \
 #define WEAK_ALIAS(name, aliasname)
 #define NO_EXEC_STACK_DIRECTIVE
 
+#elif defined(__wasm__)
+#define NO_EXEC_STACK_DIRECTIVE
+
 // clang-format on
 #else
 
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 9d903e0c799db..cbc71db083edc 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -199,6 +199,16 @@ struct Ctx {
     // Function that initializes passive data segments during instantiation.
     DefinedFunction *initMemory;
 
+    // __wasm_memory_grow
+    // Function to perform memory.grow. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memoryGrow;
+
+    // __wasm_memory_size
+    // Function to perform memory.size. Serves as a hook to
+    // relieve engine APIs from performing this internally
+    DefinedFunction *memorySize;
+
     // __wasm_call_ctors
     // Function that directly calls all ctors in priority order.
     DefinedFunction *callCtors;
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index f2040441e6257..2c521745e3414 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -285,6 +285,10 @@ uint32_t DefinedFunction::getExportedFunctionIndex() const {
   return function->getFunctionIndex();
 }
 
+void DefinedFunction::setExportNoWrap(bool v) { exportNoWrap = v; }
+
+bool DefinedFunction::getExportNoWrap() const { return exportNoWrap; }
+
 uint64_t DefinedData::getVA(bool absolute) const {
   LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
   // TLS symbols (by default) are relative to the start of the TLS output
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 55ee21939ce07..fb8188a4e749f 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -229,6 +229,10 @@ class DefinedFunction : public FunctionSymbol {
     return s->kind() == DefinedFunctionKind;
   }
 
+  // Get/set the exportNoWrap
+  void setExportNoWrap(bool v);
+  bool getExportNoWrap() const;
+
   // Get the function index to be used when exporting.  This only applies to
   // defined functions and can be differ from the regular function index for
   // weakly defined functions (that are imported and used via one index but
@@ -236,6 +240,9 @@ class DefinedFunction : public FunctionSymbol {
   uint32_t getExportedFunctionIndex() const;
 
   InputFunction *function;
+
+protected:
+  bool exportNoWrap = false;
 };
 
 class UndefinedFunction : public FunctionSymbol {
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index b704677d36c93..0628e37850915 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -60,6 +60,8 @@ class Writer {
 
   void createSyntheticInitFunctions();
   void createInitMemoryFunction();
+  void createMemoryGrowFunction();
+  void createMemorySizeFunction();
   void createStartFunction();
   void createApplyDataRelocationsFunction();
   void createApplyGlobalRelocationsFunction();
@@ -888,31 +890,33 @@ void Writer::createCommandExportWrappers() {
         toWrap.push_back(f);
 
   for (auto *f : toWrap) {
-    auto funcNameStr = (f->getName() + ".command_export").str();
-    commandExportWrapperNames.push_back(funcNameStr);
-    const std::string &funcName = commandExportWrapperNames.back();
+    if (!(f->getExportNoWrap())) {
+      auto funcNameStr = (f->getName() + ".command_export").str();
+      commandExportWrapperNames.push_back(funcNameStr);
+      const std::string &funcName = commandExportWrapperNames.back();
 
-    auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
-    if (f->function->getExportName())
-      func->setExportName(f->function->getExportName()->str());
-    else
-      func->setExportName(f->getName().str());
+      auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
+      if (f->function->getExportName())
+        func->setExportName(f->function->getExportName()->str());
+      else
+        func->setExportName(f->getName().str());
 
-    DefinedFunction *def =
-        symtab->addSyntheticFunction(funcName, f->flags, func);
-    def->markLive();
+      DefinedFunction *def =
+          symtab->addSyntheticFunction(funcName, f->flags, func);
+      def->markLive();
 
-    def->flags |= WASM_SYMBOL_EXPORTED;
-    def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
-    def->forceExport = f->forceExport;
+      def->flags |= WASM_SYMBOL_EXPORTED;
+      def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
+      def->forceExport = f->forceExport;
 
-    f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
-    f->flags &= ~WASM_SYMBOL_EXPORTED;
-    f->forceExport = false;
+      f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
+      f->flags &= ~WASM_SYMBOL_EXPORTED;
+      f->forceExport = false;
 
-    out.functionSec->addFunction(func);
+      out.functionSec->addFunction(func);
 
-    createCommandExportWrapper(f->getFunctionIndex(), def);
+      createCommandExportWrapper(f->getFunctionIndex(), def);
+    }
   }
 }
 
@@ -1136,6 +1140,8 @@ void Writer::createSyntheticInitFunctions() {
     return;
 
   static WasmSignature nullSignature = {{}, {}};
+  static WasmSignature memoryGrowSignature = {{ValType::I32}, {ValType::I32}};
+  static WasmSignature memorySizeSignature = {{ValType::I32}, {}};
 
   createApplyDataRelocationsFunction();
 
@@ -1156,6 +1162,25 @@ void Writer::createSyntheticInitFunctions() {
     }
   }
 
+  // Memory grow/size export hooks
+  auto memoryGrowFunc =
+      make<SyntheticFunction>(memoryGrowSignature, "__wasm_memory_grow");
+  memoryGrowFunc->setExportName("__wasm_memory_grow");
+  ctx.sym.memoryGrow = symtab->addSyntheticFunction(
+      "__wasm_memory_grow",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memoryGrowFunc);
+  ctx.sym.memoryGrow->markLive();
+  ctx.sym.memoryGrow->setExportNoWrap(true);
+
+  auto memorySizeFunc =
+      make<SyntheticFunction>(memorySizeSignature, "__wasm_memory_size");
+  memorySizeFunc->setExportName("__wasm_memory_size");
+  ctx.sym.memorySize = symtab->addSyntheticFunction(
+      "__wasm_memory_size",
+      WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED, memorySizeFunc);
+  ctx.sym.memorySize->markLive();
+  ctx.sym.memorySize->setExportNoWrap(true);
+
   if (ctx.arg.sharedMemory) {
     if (out.globalSec->needsTLSRelocations()) {
       ctx.sym.applyGlobalTLSRelocs = symtab->addSyntheticFunction(
@@ -1200,6 +1225,36 @@ void Writer::createSyntheticInitFunctions() {
   }
 }
 
+void Writer::createMemoryGrowFunction() {
+  LLVM_DEBUG(dbgs() << "createMemoryGrowFunction\n");
+  assert(ctx.sym.memoryGrow);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
+    writeUleb128(os, 0, "local 0");
+    writeU8(os, WASM_OPCODE_MEMORY_GROW, "memory grow");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memoryGrow, bodyContent);
+}
+
+void Writer::createMemorySizeFunction() {
+  LLVM_DEBUG(dbgs() << "createMemorySizeFunction\n");
+  assert(ctx.sym.memorySize);
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    writeU8(os, WASM_OPCODE_MEMORY_SIZE, "memory size");
+    writeUleb128(os, 0, "reserved memory byte");
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+  createFunction(ctx.sym.memorySize, bodyContent);
+}
+
 void Writer::createInitMemoryFunction() {
   LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
   assert(ctx.sym.initMemory);
@@ -1788,6 +1843,12 @@ void Writer::run() {
     if (ctx.sym.initMemory) {
       createInitMemoryFunction();
     }
+    if (ctx.sym.memoryGrow) {
+      createMemoryGrowFunction();
+    }
+    if (ctx.sym.memorySize) {
+      createMemorySizeFunction();
+    }
     createStartFunction();
 
     createCallCtorsFunction();
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index cf90a43d0d7e8..e489026635934 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -135,6 +135,8 @@ enum : unsigned {
   WASM_OPCODE_BR_TABLE = 0x0e,
   WASM_OPCODE_RETURN = 0x0f,
   WASM_OPCODE_DROP = 0x1a,
+  WASM_OPCODE_MEMORY_SIZE = 0x3f,
+  WASM_OPCODE_MEMORY_GROW = 0x40,
   WASM_OPCODE_MISC_PREFIX = 0xfc,
   WASM_OPCODE_MEMORY_INIT = 0x08,
   WASM_OPCODE_MEMORY_FILL = 0x0b,
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index ede9797ac7488..447a94a48af67 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -199,6 +199,7 @@ class Triple {
     SUSE,
     OpenEmbedded,
     Intel,
+    WALI,
     Meta,
     LastVendorType = Meta
   };
@@ -795,6 +796,12 @@ class Triple {
     return getObjectFormat() == Triple::DXContainer;
   }
 
+  /// Tests whether the target uses WALI Wasm
+  bool isWALI() const {
+    return getArch() == Triple::wasm32 && getVendor() == Triple::WALI &&
+           getOS() == Triple::Linux;
+  }
+
   /// Tests whether the target is the PS4 platform.
   bool isPS4() const {
     return getArch() == Triple::x86_64 &&
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 6acb0bc49ecfe..5fc0086fc8d76 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -277,6 +277,8 @@ StringRef Triple::getVendorTypeName(VendorType Kind) {
   case PC: return "pc";
   case SCEI: return "scei";
   case SUSE: return "suse";
+  case WALI:
+    return "wali";
   case Meta:
     return "meta";
   }
@@ -681,6 +683,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) {
       .Case("suse", Triple::SUSE)
       .Case("oe", Triple::OpenEmbedded)
       .Case("intel", Triple::Intel)
+      .Case("wali", Triple::WALI)
       .Case("meta", Triple::Meta)
       .Default(Triple::UnknownVendor);
 }

Copy link
Member

@dschuff dschuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see more documentation/explanation for the changes here, and what these changes are actually targeting (e.g. the WALI system itself... the only link here is to the rust documentation)

@dschuff
Copy link
Member

dschuff commented Aug 29, 2025

also /cc @sbc100 since this is largely linker changes.

@dschuff
Copy link
Member

dschuff commented Aug 29, 2025

Oh, I missed that the rust doc mentions the ABI difference... but now I have more questions :D
"This is required to mantain minimum source code changes for 64-bit host platforms currently supported. "
What does this mean? I would assume the minimum changes would be required for porting from ILP32 linux platforms, not LP64 platforms?

@arjunr2
Copy link
Author

arjunr2 commented Aug 30, 2025

Removing changes from the linker to a separate RT leaves pretty much primarily front-end changes.

WALI, as per the research paper and Rust, (currently) virtualizes the 64-bit Linux syscall interfaces across many ISAs. It requires 64-bit long to adhere to the Host-ABIs (i.e. x86-64, arm64, and riscv64). In the long term, there may exist another interface specifically for 32-bit Linux platforms or a compatibility layer over this to accomplish 32-bit support.

Copy link
Member

@carlocab carlocab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs tests added.

@arjunr2
Copy link
Author

arjunr2 commented Aug 30, 2025

@carlocab Where should i be adding tests? And is it primarily for parsing of the target?

Also it's unclear to me from the CI logs why the libcxx build is failing. Says it received a user-cancelled signal -- was it manually aborted?

@arjunr2
Copy link
Author

arjunr2 commented Sep 2, 2025

Added a few tests to clang for appropriate parsing

@arjunr2
Copy link
Author

arjunr2 commented Sep 4, 2025

Ping

Copy link
Member

@dschuff dschuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if @llvm/reviewers-libunwind wants to comment but LGTM

@dschuff
Copy link
Member

dschuff commented Sep 4, 2025

Oh, it looks like the buildkite failure you got is a real failure, because it wants a newline at the end of the assembly files. Please fix that.

SUSE,
OpenEmbedded,
Intel,
WALI,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is WALI a vendor?

Copy link
Author

@arjunr2 arjunr2 Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed during the Rust target addition, and we decided to go with WALi as a vendor for now: https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Support.20for.20a.20new.20.60wasm32-linux-musl.60.20Tie.E2.80.A6.20compiler-team.23797

In reality, long term, we'd just want something like wasm32-linux-musl, but sounds like that would require a standardized Linux interface for Wasm to be accepted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for consistency it makes sense to have it described as a vendor here as well. It should be a simple to update the target later if necessary.

Copy link
Member

@aheejin aheejin Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would WALI be more similar to the environment type, given that we don't actually have a corporate vendor named WALI?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you're right; were you thinking something like MuslWALI? That'll imply that if variants beyond musl need to be used, we'll need a new enum variant for each.

I'm fine with it either way; I primarily chose this to maintain consistency with Rust. In the future, the goal would be to remove it regardless.

Copy link
Author

@arjunr2 arjunr2 Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just referring back to the Zulip thread mentioned, sounds like they would like to see grounds that this is a canonical ABI for Linux/Wasm interactions.

the way I formulated my question was kind of deliberately silly, as I was fishing for "does this reflect a consensus on what the Linux ABI should look like on wasm, formally blessed by such powers that have authority to define Linux?

"because Linus Torvalds says so" is pretty likely to get everyone to fall in line, but in practice something like a cabal of distros would be equally authoritative. even if only because there's few things that actually have cross-distro agreement.

I am unsure what this means concretely since I have yet to bring this up as a canonical ABI for Linux. But this seems to dwell outside the context of LLVM maintainers here, so I'm happy to comply with whatever makes sense from your perspective. If there really is no constraint, I would opt for just dropping wali in vendor/environment altogether

Copy link
Member

@aheejin aheejin Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It being a vendor seems weird because the current list of vendors basically only includes companies.

think to minimize cfg changes and have quick support to the ecosystem, target_os would have to be linux and target_env would have to be musl

What is cfg? This sounds like making this as a host environment will make something inconvenient. Is that the case?

If there really is no constraint, I would opt for just dropping wali in vendor/environment altogether

Do all changes in this PR, including the specifications of the type sizes, apply to musl in general?

@dschuff WDYT?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aheejin cfg is how feature gating based on targets is performed at Rust. This is fully independent of the LLVM target backend, so we can make the host environment whatever we want here.

The PR changes are specifically the WALI ABI. Musl is just the only currently supported environment backend

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, triples are well known to be not really sufficient for capturing everything you'd want to know about a compilation, all the aspects of the architecture, set of syscalls available, and ABI; and there's a messy history of vendors with either the same or different ABIs. Since this is adding a new OS and ABI to the set of triples, if an RFC is the right way to go, then we should do that. If the consensus is that we should do something other than just add a vendor, is it too late to modify rustc to agree with LLVM and clang?

Copy link
Author

@arjunr2 arjunr2 Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're really not adding a new OS to the set of triples here since we're reusing Linux, just the new ABI for Wasm.

It's not too late to modify the rustc, but notably, that will not really be practical since right now, having the rustc target with arch = wasm32, os = linux, and env = musl allows minimal patching across crates by reusing existing cfg environments. Hence the decision to opt for just the vendor field in Rust -- changing the environment to something else will be a huge pain

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this change is less about creating a new environment for WebAssembly and more about adding a new platform to TripleParser, the clang driver, and libunwind. Therefore, I think an RFC proposal is necessary. The discussion at https://discourse.llvm.org/t/rfc-new-proposed-managarm-support-for-llvm-and-clang-87845/85884 is a good example to follow.

@arjunr2
Copy link
Author

arjunr2 commented Sep 5, 2025

Created an RFC detailing the discussion

@arjunr2
Copy link
Author

arjunr2 commented Sep 15, 2025

Ping

@JonChesterfield
Copy link
Collaborator

Wasm + musl running on Linux is a good thing. Does this run well enough that we can self host on it? I.e. build clang to the target.

I'm not clear why this is especially associated with rust - looks frontend language independent at first sight.

@arjunr2
Copy link
Author

arjunr2 commented Sep 16, 2025

@JonChesterfield we might have to wrangle with some build system logistics, but I think we should be able to self host clang. Have not tried yet. I've ported several other compilers over to it already.

It's fully frontend language independent. I just use Rust as an example of a already supported language port with a large user base.

@arjunr2
Copy link
Author

arjunr2 commented Sep 24, 2025

Can someone help me identify why libc++ is failing on macos in particular? This PR doesn't change any part of libcxx and only reorganizes the #defines for libunwind?

@dschuff
Copy link
Member

dschuff commented Sep 24, 2025

Is it also failing on the main branch at the version you're synced to? It might not actually be relevant to your PR.

@arjunr2
Copy link
Author

arjunr2 commented Oct 9, 2025

Created a new PR #162581 to reflect the changes as per the RFC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:WebAssembly clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category libunwind lld:wasm lld llvm:binary-utilities
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants