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 <arju...@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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits