Skip to content

Proposal: WebAssembly Support for LLGO #1031

Description

@cpunion

Progress

Background

LLGO already successfully compiles Go code to LLVM IR (.ll files). Unlike the official Go compiler, LLGO heavily relies on C libraries for implementation, making its dependency handling needs more similar to C/C++ projects targeting WebAssembly.

Problem Statement

Extending LLGO to support WebAssembly requires addressing how to compile LLVM IR (.ll files) to WebAssembly and handle the various C library dependencies.

Compilation Path Challenge

The existing path from .ll to WebAssembly requires:

  1. Compiling LLVM IR to WebAssembly object files
  2. Linking WebAssembly object files with required libraries
  3. Resolving external function calls

Library Dependencies

A minimal executable dependencies:

ldd ~/go/bin/test
    linux-vdso.so.1 (0x0000edf5b7cf6000)
    libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000edf5b7c10000)
    libatomic.so.1 => /lib/aarch64-linux-gnu/libatomic.so.1 (0x0000edf5b77d0000)
    libgc.so.1 => /lib/aarch64-linux-gnu/libgc.so.1 (0x0000edf5b7750000)
    libunwind.so.8 => /lib/aarch64-linux-gnu/libunwind.so.8 (0x0000edf5b7700000)
    libffi.so.8 => /lib/aarch64-linux-gnu/libffi.so.8 (0x0000edf5b76d0000)
    libuv.so.1 => /lib/aarch64-linux-gnu/libuv.so.1 (0x0000edf5b7670000)
    libssl.so.3 => /lib/aarch64-linux-gnu/libssl.so.3 (0x0000edf5b75a0000)
    libcrypto.so.3 => /lib/aarch64-linux-gnu/libcrypto.so.3 (0x0000edf5b7000000)
    libz.so.1 => /lib/aarch64-linux-gnu/libz.so.1 (0x0000edf5b7560000)
    libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000edf5b6e40000)
    /lib/ld-linux-aarch64.so.1 (0x0000edf5b7cb9000)
    liblzma.so.5 => /lib/aarch64-linux-gnu/liblzma.so.5 (0x0000edf5b7510000)

otool -L ~/go/bin/test
/Users/lijie/go/bin/test.test:
    /opt/homebrew/opt/bdw-gc/lib/libgc.1.dylib (compatibility version 1.0.0, current version 1.5.4)
    /opt/homebrew/opt/llvm@18/lib/libLLVM.dylib (compatibility version 1.0.0, current version 18.1.8)
    /opt/homebrew/opt/libuv/lib/libuv.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/lib/libffi.dylib (compatibility version 1.0.0, current version 39.0.0)
    /opt/homebrew/opt/openssl@3/lib/libssl.3.dylib (compatibility version 3.0.0, current version 3.0.0)
    /opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib (compatibility version 3.0.0, current version 3.0.0)
    /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)

LLGO runtime depends on several external libraries that need WebAssembly support:

  • libgc.so.1 / libgc.1.dylib (Boehm Garbage Collector)
  • libunwind.so.8 (stack unwinding)
  • libuv.so.1 / libuv.1.dylib (async I/O)
  • libatomic.so.1 (atomic operations)
  • libssl.so.3 / libssl.3.dylib (OpenSSL)
  • libcrypto.so.3 / libcrypto.3.dylib (OpenSSL crypto)
  • libz.so.1 (compression)
  • libLLVM.dylib (LLVM libraries)

Existing Solutions Comparison

WASI-SDK vs Emscripten

Library WASI-SDK Emscripten Notes
libm.so.6 Math library
libc.so.6 Standard C library
libatomic.so.1 Partial ✓ Atomic operations
libgc.so.1 Portable ✓ Garbage Collector
libunwind.so.8 Partial ✓ Stack unwinding
libffi.so.8 Portable ✓ Foreign Function Interface
libuv.so.1 Portable ✓ Async I/O
libssl.so.3 ✅ (ports) OpenSSL
libcrypto.so.3 ✅ (ports) OpenSSL crypto
libz.so.1 ✅ (ports) zlib compression
libLLVM.dylib LLVM library (Removed via #1032)

Implementation Approaches

  1. WASI-SDK Approach

    # Compile LLVM IR to object file
    llc -march=wasm32 -filetype=obj -o file.o file.ll
    
    # Link with WASI SDK
    wasm-ld --export-all -o output.wasm file.o
    • Handles standard C libraries
    • Most external dependencies require manual porting
    • Simpler, cleaner WebAssembly output
  2. Emscripten Approach

    # Compile LLVM IR to object file compatible with Emscripten
    llc -march=wasm32 -filetype=obj -o file.o file.ll
    
    # Link with Emscripten
    emcc file.o -s USE_ZLIB=1 -s USE_SSL=1 -o output.js
    • Better support for complex libraries
    • Built-in handling for many dependencies
    • More complex output with JavaScript dependencies

Platform-Specific libLLVM Runtime Dependency

The dependency on libLLVM.dylib is primarily a macOS-specific issue:

  1. Platform-specific nature:

    • On macOS: LLGO depends on libLLVM.dylib mainly for its libunwind functionality
    • On Linux: No direct runtime dependency on libLLVM exists
  2. WebAssembly adaptation approach:

    • For macOS-derived builds: Replace libunwind functionality with WebAssembly-compatible alternatives
    • Use platform-agnostic stack unwinding approach for WebAssembly target
    • Ensure any indirect LLVM runtime dependencies are identified and addressed

Third-party Library C Dependencies Challenge

Beyond LLGO's own dependencies, C dependencies in third-party libraries present a significant challenge:

  • Many popular Go third-party libraries wrap C libraries via CGO for core functionality
  • These C dependencies must also be compiled to WebAssembly
  • Some C libraries may use features that are challenging in WebAssembly environments (file system, networking, threading)

This means that even with WebAssembly support in LLGO itself, the complete compilation of user projects may be limited by whether the C dependencies used in third-party libraries can be successfully converted to WebAssembly format.

Compatibility with Source Mapping and ABI Optimization Proposal

This proposal is theoretically compatible with the "Implementing Source Mapping and ABI Optimization Using Custom LLVM Passes" proposal:

  • LLVM's WebAssembly backend supports DILocation and DWARF debug information
  • Custom LLVM passes for source mapping should work with WebAssembly target
  • Will need early validation to confirm DILocation preservation in WebAssembly output

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions