https://github.com/hmelder created 
https://github.com/llvm/llvm-project/pull/169043

This pull request adds initial support for compiling Objective-C to 
WebAssembly. I tested my changes with 
[libobjc2](https://github.com/gnustep/libobjc2) and the 
[swift-corelibs-blocksruntime](https://github.com/swiftlang/swift-corelibs-blocksruntime/pull/7).

There are two outstanding issues, which I cannot fix as deeper knowledge of the 
subsystems is required:
1. Symbols marked as explicitly hidden in code generation are exported
2. Clang crashes in SelectionDAG when compiling an Objective-C try/catch block 
with `-fwasm-exceptions`

### First Issue

Emscripten is processing the generated `.wasm` file in `emscripten.py` and 
checks if all exported symbols are valid javascript identifiers 
([tools/js_manipulation.py#L104](https://github.com/emscripten-core/emscripten/blob/e43e97f16fd0ed82bb0a1f25d6e1b28b2c8c08c3/tools/js_manipulation.py#L104)).
 However, hidden symbols such as `.objc_init` are intentionally an invalid C 
identifier.

The core of the problem is that symbols with the `WASM_SYMBOL_NO_STRIP` 
attribute are exported when targeting Emscripten 
(https://reviews.llvm.org/D62542). This attribute is added to the symbol during 
relocation in  `WasmObjectWriter::recordRelocation`. So we are accidentally 
exporting a lot of hidden symbols and not only ones generated by ObjC CG...

I'm currently hacking around this by not exporting no-strip symbols. This is 
the default behaviour for Wasm.

### Second Issue

Here is a minimal example that triggers the crash. 

```objc
#include<stdio.h>

int main(void) {
        int ret = 0;
        @try {
        }
        @catch (id a)
        {
                ret = 1;
                 puts("abc");
        }

        return ret;
}
```
The following assertion is triggered:

```
clang: 
/home/vm/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp:124:
 void llvm::WebAssemblyExceptionInfo::recalculate(MachineFunction &, 
MachineDominatorTree &, const MachineDominanceFrontier &): Assertion `EHInfo' 
failed.
```
Here is the crash report 
[main-c3884.zip](https://github.com/user-attachments/files/23677308/main-c3884.zip).

You can use `emcc` with a modified LLVM build by exporting `EM_LLVM_ROOT` 
before sourcing `emsdk/emsdk_env.sh`:
```sh
emcc -fobjc-runtime=gnustep-2.2 -fwasm-exceptions -c main.m
```

or just invoke clang directly:

```sh
/home/vm/llvm-build-wasm/bin/clang -target wasm32-unknown-emscripten -mllvm 
-combiner-global-alias-analysis=false -mllvm -wasm-enable-sjlj -mllvm 
-wasm-use-legacy-eh=false -mllvm -disable-lsr 
--sysroot=/home/vm/emsdk/upstream/emscripten/cache/sysroot -DEMSCRIPTEN 
-fobjc-runtime=gnustep-2.2 -fwasm-exceptions -c main.m
```

## Building libobjc2 and the BlocksRuntime

### Building the BlocksRuntime
```sh
cmake 
-DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
   -DCMAKE_INSTALL_PREFIX=/home/vm/demo-install -DCMAKE_BUILD_TYPE=Debug -B 
build -G Ninja
```

### Building libobjc2
```sh
cmake 
-DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
   -DCMAKE_INSTALL_PREFIX=/home/vm/demo-install 
-DBlocksRuntime_LIBRARIES=/home/vm/demo-install/lib/libBlocksRuntime.a 
-DBlocksRuntime_INCLUDE_DIR=/home/vm/demo-install/include/BlocksRuntime 
-DEMBEDDED_BLOCKS_RUNTIME=OFF -DTESTS=OFF  -B build  -DCMAKE_BUILD_TYPE=Debug  
-G Ninja
```

>From a07542b0e2274e36a9276ce034697ebcd0d0fee8 Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Fri, 21 Nov 2025 10:42:47 +0000
Subject: [PATCH 1/4] [clang] Whitelist wasm when targeting GNUstep 2.x

---
 clang/lib/Driver/ToolChains/Clang.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 30d3e5293a31b..6cbec5e17ae1a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8001,7 +8001,8 @@ ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args,
     if ((runtime.getKind() == ObjCRuntime::GNUstep) &&
         (runtime.getVersion() >= VersionTuple(2, 0)))
       if (!getToolChain().getTriple().isOSBinFormatELF() &&
-          !getToolChain().getTriple().isOSBinFormatCOFF()) {
+          !getToolChain().getTriple().isOSBinFormatCOFF() &&
+          !getToolChain().getTriple().isOSBinFormatWasm()) {
         getToolChain().getDriver().Diag(
             diag::err_drv_gnustep_objc_runtime_incompatible_binary)
           << runtime.getVersion().getMajor();

>From 90a6ea80ab587a670040c6ec7c574ae29419fb02 Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Fri, 21 Nov 2025 10:50:35 +0000
Subject: [PATCH 2/4] [CodeGen][ObjC] Mangle public symbols for wasm

Emscripten requires that exported symbols. See
https://github.com/emscripten-core/emscripten/pull/23563.
---
 clang/lib/CodeGen/CGObjCGNU.cpp | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index 06643d4bdc211..d86aed0d977fa 100644
--- a/clang/lib/CodeGen/CGObjCGNU.cpp
+++ b/clang/lib/CodeGen/CGObjCGNU.cpp
@@ -179,8 +179,15 @@ class CGObjCGNU : public CGObjCRuntime {
       (R.getVersion() >= VersionTuple(major, minor));
   }
 
-  std::string ManglePublicSymbol(StringRef Name) {
-    return (StringRef(CGM.getTriple().isOSBinFormatCOFF() ? "$_" : "._") + 
Name).str();
+  const std::string ManglePublicSymbol(StringRef Name) {
+    auto triple = CGM.getTriple();
+
+    // Exported symbols in Emscripten must be a valid Javascript identifier.
+    if (triple.isOSBinFormatCOFF() || triple.isOSBinFormatWasm()) {
+      return (StringRef("$_") + Name).str();
+    } else {
+      return (StringRef("._") + Name).str();
+    }
   }
 
   std::string SymbolForProtocol(Twine Name) {

>From c1b0453c01e3a1a4f41b413eb6b1c6dfd61dc828 Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Fri, 21 Nov 2025 13:04:33 +0000
Subject: [PATCH 3/4] [CodeGen][ObjC] Fix class_registerAlias_np signature

---
 clang/lib/CodeGen/CGObjCGNU.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index d86aed0d977fa..3b9f9f306829d 100644
--- a/clang/lib/CodeGen/CGObjCGNU.cpp
+++ b/clang/lib/CodeGen/CGObjCGNU.cpp
@@ -4113,8 +4113,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() {
   if (!ClassAliases.empty()) {
     llvm::Type *ArgTypes[2] = {PtrTy, PtrToInt8Ty};
     llvm::FunctionType *RegisterAliasTy =
-      llvm::FunctionType::get(Builder.getVoidTy(),
-                              ArgTypes, false);
+        llvm::FunctionType::get(BoolTy, ArgTypes, false);
     llvm::Function *RegisterAlias = llvm::Function::Create(
       RegisterAliasTy,
       llvm::GlobalValue::ExternalWeakLinkage, "class_registerAlias_np",

>From 0233bd22d901de804558cd40435a7c6c0fa770c7 Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Fri, 21 Nov 2025 13:07:10 +0000
Subject: [PATCH 4/4] [Wasm] HACK: Do not export hidden symbols

---
 llvm/lib/MC/WasmObjectWriter.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 15590b31fd07f..d882146e21b8a 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -1794,9 +1794,6 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler 
&Asm,
       Flags |= wasm::WASM_SYMBOL_UNDEFINED;
     if (WS.isNoStrip()) {
       Flags |= wasm::WASM_SYMBOL_NO_STRIP;
-      if (isEmscripten()) {
-        Flags |= wasm::WASM_SYMBOL_EXPORTED;
-      }
     }
     if (WS.hasImportName())
       Flags |= wasm::WASM_SYMBOL_EXPLICIT_NAME;

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

Reply via email to