[clang] [libunwind] [lld] [llvm] [wasm] Toolchain support for `wasm32-wali-linux-musl` target (PR #156087)
Arjun Ramesh via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 29 12:33:50 PDT 2025
https://github.com/arjunr2 created https://github.com/llvm/llvm-project/pull/156087
This PR adds minimal support to the `clang` frontend, `lld`, and `libcxx` for the [WALI](https://doc.rust-lang.org/rustc/platform-support/wasm32-wali-linux.html) target supported by the `rustc` toolchain
>From 758289d2c19997ada222c82d38afa5ef2debce84 Mon Sep 17 00:00:00 2001
From: Arjun Ramesh <arjunr2 at andrew.cmu.edu>
Date: Fri, 29 Aug 2025 13:28:05 -0400
Subject: [PATCH] Support for `wasm32-wali-linux-musl target` in `clang`,
`lld`, and `libcxx`
---
clang/lib/Basic/Targets.cpp | 7 +-
clang/lib/Basic/Targets/OSTargets.h | 17 +++++
clang/lib/Basic/Targets/WebAssembly.h | 18 +++--
clang/lib/Driver/Driver.cpp | 2 +
libunwind/src/assembly.h | 3 +
lld/wasm/Config.h | 10 +++
lld/wasm/Symbols.cpp | 4 +
lld/wasm/Symbols.h | 7 ++
lld/wasm/Writer.cpp | 99 ++++++++++++++++++++-----
llvm/include/llvm/BinaryFormat/Wasm.h | 2 +
llvm/include/llvm/TargetParser/Triple.h | 7 ++
llvm/lib/TargetParser/Triple.cpp | 3 +
12 files changed, 154 insertions(+), 25 deletions(-)
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);
}
More information about the llvm-commits
mailing list