Merge branch 'main' into sy/experimental-threading

pull/640/head
Sy Brand 3 weeks ago
commit 1ace2ed4da

@ -7,7 +7,11 @@ runs:
- name: Setup `wasmtime` for tests
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
<<<<<<< HEAD
version: "dev"
=======
version: "44.0.0"
>>>>>>> main
- name: Install ccache, ninja (macOS)
run: brew install ccache ninja
if: runner.os == 'macOS'

@ -28,6 +28,21 @@ jobs:
- artifact: arm64-linux
os: ubuntu-22.04-arm
- artifact: riscv64-linux
os: ubuntu-24.04
rust_target: riscv64gc-unknown-linux-gnu
cross_cmake_args: >-
-DCMAKE_SYSTEM_NAME=Linux
-DCMAKE_SYSTEM_PROCESSOR=riscv64
-DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc
-DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++
-DWASI_SDK_LLDB=OFF
env:
WASI_SDK_CI_SKIP_SYSROOT: 1
WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >-
-DCMAKE_SYSTEM_NAME=Linux
-DCMAKE_SYSTEM_PROCESSOR=riscv64
- artifact: arm64-macos
os: macos-14
rust_target: aarch64-apple-darwin
@ -53,7 +68,7 @@ jobs:
env: ${{ matrix.env || fromJSON('{}') }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
@ -65,7 +80,7 @@ jobs:
# Bump the prefix number to evict all previous caches and enforce a clean
# build, in the unlikely case that some weird build error occur and ccache
# becomes a potential suspect.
- uses: actions/cache@v4
- uses: actions/cache@v5
id: cache-restore
with:
path: ${{ runner.tool_cache }}/ccache
@ -85,6 +100,9 @@ jobs:
rustup target add ${{ matrix.rust_target }}
cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}"
fi
if [ "${{ matrix.cross_cmake_args }}" != "" ]; then
cmake_args="$cmake_args ${{ matrix.cross_cmake_args }}"
fi
echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV
shell: bash
@ -125,7 +143,7 @@ jobs:
# Upload the `dist` folder from the build as the artifacts for this
# runner.
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: ${{ format( 'dist-{0}', matrix.artifact) }}
path: build/dist
@ -149,11 +167,48 @@ jobs:
# time instead of having to recreate everything each time a failure
# happens.
- if: always() && steps.cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
uses: actions/cache/save@v5
with:
path: ${{ runner.tool_cache }}/ccache
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
build-only-sysroot:
name: Build only sysroot - ${{ matrix.name }}
runs-on: ubuntu-24.04
strategy:
matrix:
include:
- name: default
- name: exceptions
defines: -DWASI_SDK_EXCEPTIONS=ON
- name: static
defines: -DWASI_SDK_BUILD_SHARED=OFF
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
- uses: ./.github/actions/install-deps
- run: cargo install wasm-component-ld@0.5.21
- name: Install LLVM 22
run: |
v=22
rel=$(lsb_release -cs)
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/keyrings/llvm.asc
echo "deb [signed-by=/etc/apt/keyrings/llvm.asc] http://apt.llvm.org/$rel/ llvm-toolchain-$rel-$v main" | sudo tee /etc/apt/sources.list.d/llvm-$v.list
sudo apt-get update -y && sudo apt-get install -y clang-$v lld-$v
- run: |
cmake -G Ninja -B build -S . \
-DCMAKE_C_COMPILER=/usr/lib/llvm-22/bin/clang \
-DCMAKE_SYSTEM_NAME=WASI \
-DWASI_SDK_INCLUDE_TESTS=ON \
-DWASI_SDK_CPU_CFLAGS="" \
-DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF \
-DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF \
${{ matrix.defines }}
- run: ninja -C build
- run: ctest --output-on-failure --parallel 10 --test-dir build/tests
# Once all of the above matrix entries have completed this job will run and
# assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot
# artifacts.
@ -162,16 +217,16 @@ jobs:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
# Download all artifacts from all platforms in `build`, merge them into
# final wasi-sdk-* artifacts, and then upload them.
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
- run: ./ci/merge-artifacts.sh
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: release-artifacts
path: dist
@ -222,12 +277,12 @@ jobs:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
- uses: ./.github/actions/install-deps
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: dist-x86_64-linux
path: dist-x86_64-linux

1
.gitignore vendored

@ -1,2 +1,3 @@
build
dist
experimental

@ -1,25 +1,14 @@
# Support for C++ Exceptions
The released artifacts for wasi-sdk at this time do not support C++ exceptions.
LLVM and Clang, however, have support for C++ exceptions in WebAssembly and this
is intended to serve as documentation of the current state of affairs of using
C++ exceptions. It should be noted though that the current status of C++
exceptions support is not intended to be the final state of support, and this is
all continuing to be iterated on over time.
From wasi-sdk-33 and onwards the artifacts produced by this repository support
compiling C++ code both with and without exceptions. The sysroot for wasm
targets contains two copies of the C++ standard library and headers -- one with
exceptions enabled and one with exceptions disabled. These are automatically
selected based on compilation flags. This means that wasi-sdk-produced binaries
can avoid using wasm exceptions entirely by disabling C++ exceptions, or C++
exceptions can be enabled in which case wasm exceptions will be used.
## Building wasi-sdk with exceptions
When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON`
to enable support for C++ exceptions. For example:
```shell script
$ cmake -G Ninja -B build/sysroot -S . \
-DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \
-DWASI_SDK_EXCEPTIONS=ON
```
The C++ standard library will be compiled with support for exceptions for the
desired targets and the resulting sysroot supports using exceptions.
Currently the default is for C++ exceptions to be disabled.
## Compiling code with C++ exceptions
@ -36,25 +25,47 @@ This can be specified for example with:
```shell script
$ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
$ export LDFLAGS="-lunwind"
$ export LDFLAGS="-fwasm-exceptions -lunwind"
```
## Limitations
Note that `-fwasm-exceptions` must be present when linking to select the
correct C++ standard library to link.
## Building wasi-sdk with exceptions
When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON`
to enable support for C++ exceptions. For example:
```shell script
$ cmake -G Ninja -B build/sysroot -S . \
-DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \
-DWASI_SDK_EXCEPTIONS=ON
```
Currently C++ exceptions support in wasi-sdk does not support shared libraries.
Fixing this will require resolving some miscellaneous build issues in this
repository itself.
The C++ standard library will be compiled with support for exceptions for the
desired targets and the resulting sysroot supports using exceptions. Note that
enabling C++ exceptions requires LLVM 22 or later.
C++ exceptions are disabled by default for local builds. With a future release
of LLVM 23 the dual-sysroot nature will be on-by-default.
## Limitations
## Future Plans
There are a few known limitations/bugs/todos around exceptions support in
wasi-sdk at this time:
There are a few tracking issues with historical discussion about C++ exceptions
support in wasi-sdk such as [#334](https://github.com/WebAssembly/wasi-sdk/issues/334)
and [#565](https://github.com/WebAssembly/wasi-sdk/issues/565). The major
remaining items are:
* Currently C++ exceptions support in wasi-sdk does not support shared
libraries. Fixing this will require resolving some miscellaneous build
issues in this repository itself as well as [resolving some upstream
issues](https://github.com/llvm/llvm-project/issues/188077).
* Currently `-fwasm-exceptions` is a required flag to enable C++ exceptions.
It's unclear whether `-fexceptions` should also be supported as a substitute.
* Currently LLVM defaults to using the legacy exception-handling proposal and
this will likely change in the future. Precompiled libraries for wasi-sdk are
all built with the standard exception-handling proposal.
* Currently `-lunwind` is required when linking, but this may become automatic
in the future.
* Currently exceptions and LTO have a known issue and may not work. See
[WebAssembly/wasi-sdk#629][#629] for more information.
* Figure out support for shared libraries.
* Determine how to ship a sysroot that supports both with-and-without
exceptions.
* Figure out how to avoid the need for extra compiler flags when using
exceptions.
* Figure out if a new wasm target is warranted.
[#629]: https://github.com/WebAssembly/wasi-sdk/issues/629

@ -90,6 +90,9 @@ in compiling WebAssembly code. Supported CMake flags are:
to enable/disable. The default is `-mcpu=lime1`.
* `-DWASI_SDK_LTO=ON` - whether to enable/disable builds of LTO-capable
libraries as part of the build.
* `-DWASI_SDK_BUILD_SHARED=ON` - whether to build shared libraries (`libc.so`,
`libc++.so`, etc.) for targets that support them. Set to `OFF` to produce a
static-only sysroot.
* `-DWASI_SDK_EXCEPTIONS=ON` - whether to enable/disable support for C++
exceptions, see [CppExceptions.md](./CppExceptions.md) for more information.
* `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and
@ -209,41 +212,30 @@ disabled in a configure step before building with WASI SDK.
## Notable Limitations
This repository does not yet support __C++ exceptions__. C++ code is supported
only with -fno-exceptions for now.
Work on support for [exception handling] is underway at the
language level which will support the features.
[exception handling]: https://github.com/WebAssembly/exception-handling/
See [C setjmp/longjmp support] about setjmp/longjmp support.
[C setjmp/longjmp support]: SetjmpLongjmp.md
This repository experimentally supports __threads__ with
`--target=wasm32-wasip1-threads`. It uses WebAssembly's [threads] primitives
(atomics, `wait`/`notify`, shared memory) and [wasi-threads] for spawning
threads. Note: this is experimental &mdash; do not expect long-term stability!
Note that the `pthread_*` family of functions, as well as C++ threading primitives
such as `<atomic>`, `<mutex>`, and `<thread>` are available on all targets.
Any attempt to spawn a thread will fail on `--target=wasm32-wasip1` or
`--target=wasm32-wasip2`, but other functionality, such as locks, still works.
This makes it easier to port C++ codebases, as only a fraction of code needs
to be modified to build for the single-threaded targets.
Defining a macro `_WASI_STRICT_PTHREAD` will make `pthread_create`,
`pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and `pthread_timedjoin_np`
fail with a compile time error when building for single-threaded targets.
* C++ exceptions are disabled by default and require extra configuration to get
working, see [CppExceptions.md].
* C `setjmp`/`longjmp` require some extra configuration to get working, see
[SetjmpLongjmp.md].
* Most targets do not support spawning a thread. Experimental support for
spawning threads is available with the `wasm32-wasip1-threads` target which
uses [wasi-threads]. Note that the `pthread_*` family of functions, as well as
C++ threading primitives such as `<atomic>`, `<mutex>`, and `<thread>` are
available on all targets. Defining a macro `_WASI_STRICT_PTHREAD` will make
`pthread_create`, `pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and
`pthread_timedjoin_np` fail with a compile time error when building for
single-threaded targets.
* Dynamic linking [is supported][dylink] but not as fully baked as static
linking. There might be obscure bugs in some situations related to dynamic
linking.
* The WASIp1 targets do not support networking, but WASIp2/WASIp3 support
networking.
* 64-bit linear memories (a "wasm64" target) are not supported at this time.
Supporting this will require resolving [WebAssembly/component-model#22] first
at which point it will be possible to add a `wasm64-wasip2` target. There are
no plans to add support for `wasm64-wasi{,-threads,p1,p1-threads}` at this
time.
[threads]: https://github.com/WebAssembly/threads
[wasi-threads]: https://github.com/WebAssembly/wasi-threads
This repository does not yet support __dynamic libraries__. While there are
[some efforts] to design a system for dynamic libraries in wasm, it is still in
development and not yet generally usable.
[some efforts]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
There is no support for __networking__. It is a goal of WASI to support
networking in the future though.
[dylink]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
[WebAssembly/component-model#22]: https://github.com/WebAssembly/component-model/issues/22

@ -37,6 +37,7 @@ cmake -G Ninja -B $build_dir/sysroot -S . \
-DCMAKE_C_COMPILER_WORKS=ON \
-DCMAKE_CXX_COMPILER_WORKS=ON \
-DWASI_SDK_INCLUDE_TESTS=ON \
-DWASI_SDK_EXCEPTIONS=DUAL \
"-DCMAKE_INSTALL_PREFIX=$build_dir/install"
ninja -C $build_dir/sysroot install dist -v

@ -15,8 +15,13 @@ fi
set -x
# Build the Docker imager
docker build --tag wasi-sdk-builder ci/docker
# Build the Docker image. Use an artifact-specific Dockerfile if one exists
# (e.g. ci/docker/Dockerfile.riscv64-linux), otherwise use the default.
dockerfile=ci/docker/Dockerfile
if [ -f "ci/docker/Dockerfile.$1" ]; then
dockerfile="ci/docker/Dockerfile.$1"
fi
docker build --tag wasi-sdk-builder --file "$dockerfile" ci/docker
# Perform the build in `/src`. The current directory is mounted read-write at
# this location as well. To ensure that container-created files are reasonable
@ -34,9 +39,11 @@ args="$args --volume $ccache_dir:/ccache:Z --env CCACHE_DIR=/ccache"
# Inherit some tools from the host into this container. This ensures that the
# decision made on CI of what versions to use is the canonical source of truth
# for theset ools
# for these tools.
args="$args --volume `rustc --print sysroot`:/rustc:ro"
args="$args --volume $(dirname $(which wasmtime)):/wasmtime:ro"
if [ "${WASI_SDK_CI_SKIP_SYSROOT:-}" != "1" ]; then
args="$args --volume $(dirname $(command -v wasmtime)):/wasmtime:ro"
fi
# Pass through some env vars that `build.sh` reads
args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS"

@ -12,7 +12,8 @@ RUN dnf install -y \
python3 \
git \
unzip \
cmake
cmake \
ncurses-devel
COPY ./install-ccache.sh .
RUN ./install-ccache.sh

@ -0,0 +1,28 @@
# Ubuntu 24.04 is used here (rather than AlmaLinux 8) because it has
# riscv64 cross-compilation packages in its repositories.
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
crossbuild-essential-riscv64 \
clang \
lld \
python3 \
git \
unzip \
cmake \
ninja-build \
ccache \
&& rm -rf /var/lib/apt/lists/*
# Cargo needs an explicit linker when cross-compiling for riscv64.
# The C/C++ cross-compiler is passed via CMAKE_C/CXX_COMPILER cmake flags
# rather than CC/CXX env vars so that LLVM's native tblgen sub-build can
# still find the host compiler (cmake cache vars are not inherited by
# subprocess cmake invocations, but env vars are).
ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
# Tell programs to cache in a location that both isn't a `--volume` mounted root
# and isn't `/root` in the container as that won't be writable during the build.
ENV XDG_CACHE_HOME=/tmp/cache

@ -34,8 +34,9 @@ make_deb() {
fi
case $build in
dist-x86_64-linux) deb_arch=amd64 ;;
dist-arm64-linux) deb_arch=arm64 ;;
dist-x86_64-linux) deb_arch=amd64 ;;
dist-arm64-linux) deb_arch=arm64 ;;
dist-riscv64-linux) deb_arch=riscv64 ;;
*)
echo "unknown build $build"
exit 1

@ -20,14 +20,34 @@ message(STATUS "Found executable for `ar`: ${CMAKE_AR}")
find_program(MAKE make REQUIRED)
set(EXCEPTIONS_DEFAULT "OFF")
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0.0)
set(EXCEPTIONS_DEFAULT "DUAL")
endif()
option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON)
option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF)
option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF)
option(WASI_SDK_LTO "Whether or not to build LTO assets" ON)
<<<<<<< HEAD
option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF)
option(WASI_SDK_COOP_THREADING "Whether or not to build with cooperative threading support" OFF)
=======
option(WASI_SDK_BUILD_SHARED "Whether or not to build shared libraries when supported" ON)
set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled")
>>>>>>> main
set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable")
if ((WASI_SDK_EXCEPTIONS STREQUAL "DUAL") OR (WASI_SDK_EXCEPTIONS STREQUAL "ON"))
if(CMAKE_C_COMPILER_VERSION VERSION_LESS 22.0.0)
message(FATAL_ERROR "enabling C++ exceptions requires Clang 22 or later")
endif()
elseif(WASI_SDK_EXCEPTIONS STREQUAL "OFF")
# No extra validation needed
else()
message(FATAL_ERROR "unknown WASI_SDK_EXCEPTIONS value ${WASI_SDK_EXCEPTIONS}, expected one of: OFF, ON, DUAL")
endif()
set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install)
set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot)
set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir)
@ -102,11 +122,27 @@ function(define_compiler_rt target)
add_dependencies(compiler-rt-build compiler-rt-build-${target})
endfunction()
<<<<<<< HEAD
define_compiler_rt(wasm32-wasip3)
=======
# The `compiler-rt` for `wasm32-wasip1` will be reused for `wasm32-wasip2` and
# `wasm32-wasi`. The version for `wasm32-wasip1-threads` will be reused for
# `wasm32-wasi-threads`. Different builds are needed for different codegen flags
# and such across the threaded/not target.
define_compiler_rt(wasm32-wasip1)
define_compiler_rt(wasm32-wasip1-threads)
>>>>>>> main
# If a p3 target is requested, also build compiler-rt for that target. WASIp3
# will eventually have a different ABI than wasm32-wasip2, so this separate
# build is needed.
if(WASI_SDK_TARGETS MATCHES p3)
define_compiler_rt(wasm32-wasip3)
endif()
# In addition to the default installation of `compiler-rt` itself also copy
# around some headers and make copies of the `wasi` directory as `wasip1` and
# `wasip2`
# `wasip2` and `wasip3`
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-resource-dir
OUTPUT_VARIABLE clang_resource_dir
@ -124,7 +160,6 @@ add_dependencies(compiler-rt-post-build compiler-rt-build)
add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build)
# =============================================================================
# wasi-libc build logic
# =============================================================================
@ -134,8 +169,13 @@ function(define_wasi_libc_sub target target_suffix lto)
get_property(directory_cflags DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS)
set(extra_cflags_list "${WASI_SDK_CPU_CFLAGS} ${CMAKE_C_FLAGS} ${directory_cflags}")
<<<<<<< HEAD
if(${target} MATCHES p2 OR ${target} MATCHES p3)
# Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets.
=======
if(${target} MATCHES "p[23]")
# Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets.
>>>>>>> main
# This makes `libc.a` more flexible and usable in dynamic linking situations.
list(APPEND extra_cflags_list -fPIC)
endif()
@ -157,9 +197,14 @@ function(define_wasi_libc_sub target target_suffix lto)
set(extra_cmake_args)
# Configure LTO in wasi libc if it's enabled. Be sure to disable shared
# libraries as well since that's not currently supported.
# libraries as well since that's not currently supported with LTO.
if (lto)
list(APPEND extra_cmake_args -DLTO=full -DBUILD_SHARED=OFF)
list(APPEND extra_cmake_args -DLTO=full)
endif()
# WASI_SDK_BUILD_SHARED=OFF allows producing a static-only sysroot; the LTO
# branch above already implies shared-off, so the two conditions are combined.
if (lto OR NOT WASI_SDK_BUILD_SHARED)
list(APPEND extra_cmake_args -DBUILD_SHARED=OFF)
endif()
ExternalProject_Add(wasi-libc-${target}${target_suffix}-build
@ -207,8 +252,13 @@ execute_process(
OUTPUT_VARIABLE llvm_version
OUTPUT_STRIP_TRAILING_WHITESPACE)
<<<<<<< HEAD
function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix)
if(${target} MATCHES threads OR ${target} MATCHES p3)
=======
function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions)
if(${target} MATCHES threads)
>>>>>>> main
set(pic OFF)
set(target_flags -pthread)
else()
@ -233,7 +283,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
--sysroot ${wasi_sysroot}
-resource-dir ${wasi_resource_dir})
if (WASI_SDK_EXCEPTIONS)
set(exnsuffix "")
if (exceptions)
# TODO: lots of builds fail with shared libraries and `-fPIC`. Looks like
# things are maybe changing in llvm/llvm-project#159143 but otherwise I'm at
# least not really sure what the state of shared libraries and exceptions
@ -242,6 +294,13 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
set(pic OFF)
set(runtimes "libunwind;${runtimes}")
list(APPEND extra_flags -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false)
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
set(exnsuffix "/eh")
endif()
else()
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
set(exnsuffix "/noeh")
endif()
endif()
# The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation
@ -250,6 +309,15 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
list(APPEND extra_flags -Wno-deprecated)
endif()
# `shared` is computed here, after the exceptions branch above may have forced
# pic OFF, so that LIBCXX_ENABLE_SHARED/LIBCXXABI_ENABLE_SHARED/LIBUNWIND_ENABLE_SHARED
# stay consistent with the final value of CMAKE_POSITION_INDEPENDENT_CODE.
if(WASI_SDK_BUILD_SHARED AND pic)
set(shared ON)
else()
set(shared OFF)
endif()
set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags})
list(JOIN extra_cflags_list " " extra_cflags)
set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags})
@ -261,7 +329,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
${default_cmake_args}
# Ensure headers are installed in a target-specific path instead of a
# target-generic path.
-DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}
-DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}${exnsuffix}
-DCMAKE_STAGING_PREFIX=${wasi_sysroot}
-DCMAKE_POSITION_INDEPENDENT_CODE=${pic}
-DLIBCXX_ENABLE_THREADS:BOOL=ON
@ -269,22 +337,22 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
-DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF
-DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF
-DLLVM_COMPILER_CHECKED=ON
-DLIBCXX_ENABLE_SHARED:BOOL=${pic}
-DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS}
-DLIBCXX_ENABLE_SHARED:BOOL=${shared}
-DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions}
-DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON
-DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF
-DLIBCXX_CXX_ABI=libcxxabi
-DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF
-DLIBCXX_ABI_VERSION=2
-DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS}
-DLIBCXXABI_ENABLE_SHARED:BOOL=${pic}
-DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions}
-DLIBCXXABI_ENABLE_SHARED:BOOL=${shared}
-DLIBCXXABI_SILENT_TERMINATE:BOOL=ON
-DLIBCXXABI_ENABLE_THREADS:BOOL=ON
-DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON
-DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF
-DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF
-DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${WASI_SDK_EXCEPTIONS}
-DLIBUNWIND_ENABLE_SHARED:BOOL=${pic}
-DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions}
-DLIBUNWIND_ENABLE_SHARED:BOOL=${shared}
-DLIBUNWIND_ENABLE_THREADS:BOOL=ON
-DLIBUNWIND_USE_COMPILER_RT:BOOL=ON
-DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF
@ -292,9 +360,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
-DCMAKE_C_FLAGS=${extra_cflags}
-DCMAKE_ASM_FLAGS=${extra_cflags}
-DCMAKE_CXX_FLAGS=${extra_cxxflags}
-DLIBCXX_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix}
-DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix}
-DLIBUNWIND_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix}
-DLIBCXX_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBCXXABI_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBUNWIND_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBCXX_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_BENCHMARKS=OFF
@ -309,18 +377,45 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
USES_TERMINAL_PATCH ON
PATCH_COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check"
COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch -R --check"
COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch -R --check"
)
add_dependencies(libcxx-${target} libcxx-${target}${target_suffix}-build)
endfunction()
function(define_libcxx target)
define_libcxx_sub(${target} "" "" "")
if(WASI_SDK_LTO)
function(define_libcxx_and_lto target target_suffix exceptions)
define_libcxx_sub(${target} "${target_suffix}" "" "" ${exceptions})
if (WASI_SDK_LTO)
# Note: clang knows this /llvm-lto/${llvm_version} convention.
# https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210
define_libcxx_sub(${target} "-lto" "-flto=full" "/llvm-lto/${llvm_version}")
define_libcxx_sub(${target} ${target_suffix}-lto "-flto=full" "/llvm-lto/${llvm_version}" ${exceptions})
endif()
endfunction()
function(define_libcxx target)
add_custom_target(libcxx-${target})
# For dual-mode exceptions-and-not there are two versions of libcxx which are
# compiled and placed into the sysroot. They're named slightly differently to
# have unique CMake rules.
#
# Otherwise there's only one build of libcxx and it's either got exceptions or
# it doesn't depending on configuration.
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
define_libcxx_and_lto(${target} "" OFF)
define_libcxx_and_lto(${target} "-exn" ON)
elseif(WASI_SDK_EXCEPTIONS STREQUAL "ON")
define_libcxx_and_lto(${target} "" ON)
else()
define_libcxx_and_lto(${target} "" OFF)
endif()
# As of this writing, `clang++` will ignore the target-specific include dirs
@ -328,8 +423,7 @@ function(define_libcxx target)
add_custom_target(libcxx-${target}-extra-dir
COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1
COMMENT "creating libcxx-specific header file folder")
add_custom_target(libcxx-${target}
DEPENDS libcxx-${target}-build $<$<BOOL:${WASI_SDK_LTO}>:libcxx-${target}-lto-build> libcxx-${target}-extra-dir)
add_dependencies(libcxx-${target} libcxx-${target}-extra-dir)
endfunction()
foreach(target IN LISTS WASI_SDK_TARGETS)

@ -5,6 +5,22 @@ set(LLVM_CMAKE_FLAGS "" CACHE STRING "Extra cmake flags to pass to LLVM's build"
set(RUST_TARGET "" CACHE STRING "Target to build Rust code for, if not the host")
set(WASI_SDK_ARTIFACT "" CACHE STRING "Name of the wasi-sdk artifact being produced")
option(WASI_SDK_LLDB "Include a build of LLDB" ON)
set(LIBEDIT_DEFAULT ON)
# I don't want to deal with running a `./configure` script on Windows, disable
# it by default.
if(WIN32)
set(LIBEDIT_DEFAULT OFF)
endif()
# I don't know how to resolve build failures when building libedit for x86_64
# from arm64 on macos, so disable it for now.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND LLVM_CMAKE_FLAGS MATCHES "x86_64")
set(LIBEDIT_DEFAULT OFF)
endif()
option(WASI_SDK_LIBEDIT "Whether or not to build libedit for LLDB" ${LIBEDIT_DEFAULT})
option(WASI_SDK_LIBXML2 "Whether or not to build libxml2 for LLDB" ON)
string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}")
set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install)
@ -40,6 +56,7 @@ set(tools
clang-format
clang-tidy
clang-apply-replacements
clang-scan-deps
lld
llvm-addr2line
llvm-mc
@ -57,7 +74,8 @@ set(tools
objdump
objcopy
c++filt
llvm-config)
llvm-config
libclang)
# By default link LLVM dynamically to all the various tools. This greatly
# reduces the binary size of all the tools through a shared library rather than
@ -72,6 +90,127 @@ if(NOT WIN32)
list(APPEND tools LLVM clang-cpp)
endif()
# Configure/add LLDB if requested.
#
# Note that LLDB depends on `libedit` which is more-or-less required to get a
# reasonable command-line experience, so this is built custom here to ensure
# that it's available for LLDB.
if(WASI_SDK_LLDB)
list(APPEND projects lldb)
list(APPEND tools lldb liblldb)
list(APPEND default_cmake_args
-DLLDB_INCLUDE_TESTS=OFF
-DLLDB_INCLUDE_UNITTESTS=OFF
-DLLDB_ENABLE_SWIG=OFF
-DLLDB_ENABLE_CURSES=OFF
-DLLDB_ENABLE_LZMA=OFF
-DLLDB_ENABLE_LUA=OFF
-DLLDB_ENABLE_PYTHON=OFF
-DLLDB_ENABLE_FBSDVMCORE=OFF
-DLLDB_ENABLE_LINUXPTY=OFF
)
set(extra_configure_commands)
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
set(extra_configure_commands
# By default it looks like `libedit` tries to link to `libncurses.so` and
# such on Linux. This is problematic as systems may not have that
# installed. Turns out though at least for AlmaLinux [1] they just edit
# makefile and pkg-config info and it works out. Who knew! I thought
# one of the millions of lines in `./configure` would take care of this
# but apparently we're still resorting to editing things raw...
#
# [1]: https://git.almalinux.org/rpms/libedit/src/commit/3f0893c4cd8e0cbb2f556d2fad48326c9c037a6c/SPECS/libedit.spec#L44-L48
COMMAND sed -i "s/lncurses/ltinfo/" src/Makefile
COMMAND sed -i "s/ -lncurses//" libedit.pc
)
endif()
if (WASI_SDK_LIBEDIT)
include(ProcessorCount)
ProcessorCount(nproc)
find_program(MAKE_EXECUTABLE make REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(libedit_ldflags -Wl,-install_name,@rpath/libedit.0.dylib)
endif()
ExternalProject_Add(libedit
URL https://thrysoee.dk/editline/libedit-20251016-3.1.tar.gz
URL_HASH SHA256=21362b00653bbfc1c71f71a7578da66b5b5203559d43134d2dd7719e313ce041
# Without this the build system tries to find and use `aclocal-1.18` where
# with this it doesn't so turn this on.
DOWNLOAD_EXTRACT_TIMESTAMP ON
CONFIGURE_COMMAND
<SOURCE_DIR>/configure
--prefix=${wasi_tmp_install}
--enable-pic
--disable-examples
--disable-static
--disable-silent-rules
CC=${CMAKE_C_COMPILER}
LDFLAGS=${libedit_ldflags}
${extra_configure_commands}
BUILD_COMMAND
${MAKE_EXECUTABLE} -j${nproc}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBEDIT=ON
-DLibEdit_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBEDIT=OFF)
add_custom_target(libedit)
endif()
set(libxml_cmake_args)
# Windows doesn't have iconv by default, so disable it for now.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
list(APPEND libxml_cmake_args -DLIBXML2_WITH_ICONV=OFF)
endif()
# Our AlmaLinux:8 container ends up using `lib64` instead of `lib` by default
# which doesn't match LLVM, so specifically use the same dir as LLVM.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
list(APPEND libxml_cmake_args -DCMAKE_INSTALL_LIBDIR=lib)
endif()
if (WASI_SDK_LIBXML2)
ExternalProject_Add(libxml2
URL https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.2.tar.xz
URL_HASH SHA256=c8b9bc81f8b590c33af8cc6c336dbff2f53409973588a351c95f1c621b13d09d
CMAKE_ARGS
${default_cmake_args}
-DLIBXML2_WITH_PROGRAMS=OFF
-DLIBXML2_WITH_DEBUG=OFF
-DLIBXML2_WITH_DOCS=OFF
-DLIBXML2_WITH_TESTS=OFF
${libxml_cmake_args}
${llvm_cmake_flags_list}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBXML2=ON
-DLibXml2_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBXML2=OFF)
add_custom_target(libxml2)
endif()
else()
add_custom_target(libedit)
add_custom_target(libxml2)
endif()
list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets)
list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets)
@ -87,12 +226,15 @@ ExternalProject_Add(llvm-build
-DLLVM_INCLUDE_BENCHMARKS=OFF
-DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_TARGETS_TO_BUILD=WebAssembly
-DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasi
-DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasip1
-DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE
-DLLVM_ENABLE_LIBXML2=OFF
# Pass `-s` to strip symbols by default and shrink the size of the
# distribution
-DCMAKE_EXE_LINKER_FLAGS=-s
# Looks to be required on macOS for, at build time, the dynamic linker to
# find `libedit.dylib` when that's enabled.
-DCMAKE_BUILD_RPATH=${wasi_tmp_install}/lib
${llvm_cmake_flags_list}
# See https://www.scivision.dev/cmake-externalproject-list-arguments/ for
# why this is in `CMAKE_CACHE_ARGS` instead of above
@ -106,9 +248,13 @@ ExternalProject_Add(llvm-build
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
PATCH_COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch -R --check"
)
add_custom_target(build ALL DEPENDS llvm-build)
ExternalProject_Add_StepDependencies(llvm-build configure libedit libxml2)
# Installation target for this outer project for installing the toolchain to the
# system.
@ -119,7 +265,7 @@ install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_ins
# Build logic for `wasm-component-ld` installed from Rust code.
set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld)
set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX})
set(wasm_component_ld_version 0.5.21)
set(wasm_component_ld_version 0.5.24)
if(RUST_TARGET)
set(rust_target_flag --target=${RUST_TARGET})
endif()

@ -1,22 +1,17 @@
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index f8e83e138eff..c5097d25b0c6 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
From 852c8a2ebc0fdb1e781591e3e6e08d3a539bcfc3 Mon Sep 17 00:00:00 2001
From: Yerzhan Zhamashev <yerzhan@novel.systems>
Date: Wed, 21 Jan 2026 16:50:41 +0200
Subject: [PATCH] libunwind: exclude __declspec from wasm build
---
libunwind/src/config.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index deb5a4d4d73d..23c9f012cbcf 100644
index f017403fa2234..6014a37e27212 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -66,7 +66,8 @@
@@ -75,7 +75,8 @@
#define _LIBUNWIND_EXPORT
#define _LIBUNWIND_HIDDEN
#else

@ -0,0 +1,105 @@
From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 15:49:55 -0700
Subject: [PATCH] [WebAssembly] Move __cpp_exception to libunwind
The `__cpp_exception` symbol is now defined in libunwind instead of
compiler-rt. This is moved for a few reasons, but the primary reason is
that compiler-rt is linked duplicate-ly into all shared objects meaning
that it's not suitable for define-once symbols such as
`__cpp_exception`. By moving the definition to the user of the symbol,
libunwind itself, that guarantees that the symbol should be defined
exactly once and only when appropriate. A secondary reason for this
movement is that it avoids the need to compile compiler-rt twice: once
with exception and once without, and instead the same build can be used
for both exceptions-and-not.
---
compiler-rt/lib/builtins/CMakeLists.txt | 1 -
.../lib/builtins/wasm/__cpp_exception.S | 26 -------------------
libunwind/src/Unwind-wasm.c | 15 +++++++++++
.../compiler-rt/lib/builtins/sources.gni | 1 -
4 files changed, 15 insertions(+), 28 deletions(-)
delete mode 100644 compiler-rt/lib/builtins/wasm/__cpp_exception.S
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 6c27f6d4d529e..f0570a9092f40 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -891,7 +891,6 @@ set(s390x_SOURCES
set(wasm_SOURCES
wasm/__c_longjmp.S
- wasm/__cpp_exception.S
${GENERIC_TF_SOURCES}
${GENERIC_SOURCES}
)
diff --git a/compiler-rt/lib/builtins/wasm/__cpp_exception.S b/compiler-rt/lib/builtins/wasm/__cpp_exception.S
deleted file mode 100644
index 0496e1dbf6158..0000000000000
--- a/compiler-rt/lib/builtins/wasm/__cpp_exception.S
+++ /dev/null
@@ -1,26 +0,0 @@
-//===-- __cpp_exception.S - Implement __cpp_exception ---------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements __cpp_exception which LLVM uses to implement exception
-// handling when Wasm EH is enabled.
-//
-//===----------------------------------------------------------------------===//
-
-#ifdef __wasm_exception_handling__
-
-#ifdef __wasm64__
-#define PTR i64
-#else
-#define PTR i32
-#endif
-
-.globl __cpp_exception
-.tagtype __cpp_exception PTR
-__cpp_exception:
-
-#endif // !__wasm_exception_handling__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 2f4498c3f3989..c0ca9b775d244 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -69,6 +69,21 @@ _Unwind_RaiseException(_Unwind_Exception *exception_object) {
__builtin_wasm_throw(0, exception_object);
}
+// Define the `__cpp_exception` symbol which `__builtin_wasm_throw` above will
+// reference. This is defined here in `libunwind` as the single canonical
+// definition for this API and it's required for users to ensure that there's
+// only one copy of `libunwind` within a wasm module to ensure this is only
+// defined once and exactly once.
+__asm__(".globl __cpp_exception\n"
+#if defined(__wasm32__)
+ ".tagtype __cpp_exception i32\n"
+#elif defined(__wasm64__)
+ ".tagtype __cpp_exception i64\n"
+#else
+#error "Unsupported Wasm architecture"
+#endif
+ "__cpp_exception:\n");
+
/// Called by __cxa_end_catch.
_LIBUNWIND_EXPORT void
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
index 2ac71aa8e8367..c9eeede16e3eb 100644
--- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
+++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
@@ -539,7 +539,6 @@ if (current_cpu == "ve") {
if (current_cpu == "wasm") {
builtins_sources += [
"wasm/__c_longjmp.S",
- "wasm/__cpp_exception.S",
]
}

@ -0,0 +1,152 @@
From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 16:14:36 -0700
Subject: [PATCH] [WebAssembly] Clang support for exception-based lookup paths
This commit is an attempt to make progress on WebAssembly/wasi-sdk#565
where with wasi-sdk I'd like to ship a single toolchain which is
capable of building binaries both with C++ exceptions and without. This
means that there can't be a single set of precompiled libraries that are
used because one set of libraries is wrong for the other mode. The
support added here is to use `-fwasm-exceptions` to automatically select
a lookup path in the sysroot. The intention is then that wasi-sdk will
ship both a "eh" set of C++ libraries as well as a "noeh" set of C++
libraries too. Clang will automatically select the correct one based on
compilation flags which means that the final distribution will be able
to build both binaries with exceptions and without.
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 51 ++++++++++++++-------
clang/test/Driver/wasm-toolchain.cpp | 35 ++++++++++++++
2 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..e532ef0743cc2 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -34,6 +34,15 @@ std::string WebAssembly::getMultiarchTriple(const Driver &D,
TargetTriple.getOSAndEnvironmentName()).str();
}
+/// Returns a directory name in which separate objects compile with/without
+/// exceptions may lie. This is used both for `#include` paths as well as lib
+/// paths.
+static std::string GetCXXExceptionsDir(const ArgList &DriverArgs) {
+ if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions))
+ return "eh";
+ return "noeh";
+}
+
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
@@ -230,12 +239,16 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}
-/// Given a base library directory, append path components to form the
-/// LTO directory.
-static std::string AppendLTOLibDir(const std::string &Dir) {
+/// Append `Dir` to `Paths`, but also include the LTO directories before that if
+/// LTO is eanbled.
+static void AppendLibDirAndLTODir(ToolChain::path_list &Paths, const Driver &D,
+ const std::string &Dir) {
+ if (D.isUsingLTO()) {
// The version allows the path to be keyed to the specific version of
// LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ Paths.push_back(Dir + "/llvm-lto/" LLVM_VERSION_STRING);
+ }
+ Paths.push_back(Dir);
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -256,14 +269,15 @@ WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
} else {
const std::string MultiarchTriple =
getMultiarchTriple(getDriver(), Triple, SysRoot);
- if (D.isUsingLTO()) {
- // For LTO, enable use of lto-enabled sysroot libraries too, if available.
- // Note that the directory is keyed to the LLVM revision, as LLVM's
- // bitcode format is not stable.
- auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
- getFilePaths().push_back(Dir);
- }
- getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
+ std::string TripleLibDir = SysRoot + "/lib/" + MultiarchTriple;
+ // Allow sysroots to segregate objects based on whether exceptions are
+ // enabled or not. This is intended to assist with distribution of pre-built
+ // sysroots that contain libraries that are capable of producing binaries
+ // entirely without exception-handling instructions but also with if
+ // exceptions are enabled, for example.
+ AppendLibDirAndLTODir(getFilePaths(), D,
+ TripleLibDir + "/" + GetCXXExceptionsDir(Args));
+ AppendLibDirAndLTODir(getFilePaths(), D, TripleLibDir);
}
if (getTriple().getOS() == llvm::Triple::WASI) {
@@ -580,13 +594,18 @@ void WebAssembly::addLibCxxIncludePaths(
if (Version.empty())
return;
- // First add the per-target include path if the OS is known.
+ // First add the per-target-per-exception-handling include path if the
+ // OS is known, then second add the per-target include path.
if (IsKnownOs) {
- std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
- addSystemInclude(DriverArgs, CC1Args, TargetDir);
+ std::string TargetDir = LibPath + "/" + MultiarchTriple;
+ std::string Suffix = "/c++/" + Version;
+ addSystemInclude(DriverArgs, CC1Args,
+ TargetDir + "/" + GetCXXExceptionsDir(DriverArgs) +
+ Suffix);
+ addSystemInclude(DriverArgs, CC1Args, TargetDir + Suffix);
}
- // Second add the generic one.
+ // Third add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
}
diff --git a/clang/test/Driver/wasm-toolchain.cpp b/clang/test/Driver/wasm-toolchain.cpp
index d7ff76cedfd10..30a2f9397e3f4 100644
--- a/clang/test/Driver/wasm-toolchain.cpp
+++ b/clang/test/Driver/wasm-toolchain.cpp
@@ -111,3 +111,38 @@
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[RESOURCE_DIR]]{{(/|\\\\)}}include"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-linux-muslwali"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// With a known OS "eh" and "noeh" directories are added to enable segregating
+// object built with/without exception-handling
+
+// RUN: %clangxx -### --target=wasm32-wasi --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_OFF %s
+// EH_OFF: "-cc1"
+// EH_OFF: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_OFF-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_ON %s
+// EH_ON: "-cc1"
+// EH_ON: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_ON-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+//
+// RUN: %clangxx -### --target=wasm32-wasi --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_OFF_LINK %s
+// EH_OFF_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/noeh" "-L/foo/lib/wasm32-wasi"
+//
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_ON_LINK %s
+// EH_ON_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/eh" "-L/foo/lib/wasm32-wasi"

@ -0,0 +1,48 @@
From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001
From: Catherine <whitequark@whitequark.org>
Date: Thu, 12 Mar 2026 08:19:49 +0000
Subject: [PATCH] [libc++abi] Revert gating of `__cxa_thread_atexit` on
Linux||Fuchsia
This was done in the commit 3c100d5d548d with the description
"Enable -Wmissing-prototypes" which seems incongruent to me.
Since then it's made its way into a release and broke the use of
`thread_local` variables with destructors on Wasm/WASI:
```cc
// repro.cc
struct c { ~c() {} };
thread_local c v;
int main() { (void)v; }
```
```console
$ ./wasi-sdk-31.0-x86_64-linux/bin/clang++ repro.cc
wasm-ld: error: /tmp/repro-dd1ad7.o: undefined symbol: __cxa_thread_atexit
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
```
---
libcxxabi/src/cxa_thread_atexit.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/libcxxabi/src/cxa_thread_atexit.cpp b/libcxxabi/src/cxa_thread_atexit.cpp
index 402a52c741012..1bdcb4ef192b4 100644
--- a/libcxxabi/src/cxa_thread_atexit.cpp
+++ b/libcxxabi/src/cxa_thread_atexit.cpp
@@ -106,7 +106,6 @@ namespace {
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
-#if defined(__linux__) || defined(__Fuchsia__)
extern "C" {
_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() {
@@ -141,6 +140,5 @@ extern "C" {
}
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
}
-} // extern "C"
-#endif // defined(__linux__) || defined(__Fuchsia__)
+ } // extern "C"
} // namespace __cxxabiv1

@ -1 +1 @@
Subproject commit fd5840e1ba9d23cbf610abe99f5ff59a4f1bec0e
Subproject commit 161b3195fc2558d2b1ba3eb9ffae3b2b47407623

@ -28,12 +28,26 @@ set(opt_flags -O0 -O2 "-O2 -flto")
add_custom_target(build-tests)
# Executes a single `test` specified.
# Registers `test` with CMake, compiling it with a number of flag combinations
# and for all enabled targets. This will register up to many tests with CTest.
#
# This will compile `test` for all the various targets and with various
# compiler options. If `runwasi` is non-empty then the test will be executed
# in that runner as well.
function(add_testcase runwasi test)
# This function takes CMake-style arguments, specified as:
#
# * `EMULATED_CLOCKS` - enables `-lwasi-emulated-process-clocks` when compiling.
# * `EMULATED_MMAN` - enables `-lwasi-emulated-mman` when compiling.
# * `EMULATED_SIGNAL` - enables `-lwasi-emulated-signal` when compiling.
# * `PRINTSCAN_LONG_DOUBLE` - enables `-lc-printscan-long-double` when compiling.
# * `FSDIR` - requires `${test}.dir` to exist and mounts it when running the test
# * `PASS_REGULAR_EXPRESSION` - same as the CTest property
# * `ENV` - env vars (the `--env` flag in Wasmtime) to pass to the test.
# * `COMPILE_ONLY` - does not actually execute this test, just compiles it.
function(add_testcase test)
set(options EMULATED_CLOCKS EMULATED_MMAN EMULATED_SIGNAL
PRINTSCAN_LONG_DOUBLE FSDIR COMPILE_ONLY)
set(oneValueArgs PASS_REGULAR_EXPRESSION)
set(multiValueArgs ENV)
cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${oneValueArgs}" "${multiValueArgs}")
foreach(target IN LISTS WASI_SDK_TARGETS)
foreach(compile_flags IN LISTS opt_flags)
# Mangle the options into something appropriate for a CMake rule name
@ -52,24 +66,27 @@ function(add_testcase runwasi test)
endif()
# Apply test-specific compile options and link flags.
if(test MATCHES "clocks.c$")
if(${arg_EMULATED_CLOCKS})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS)
target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks)
elseif(test MATCHES "mmap.c$")
endif()
if(${arg_EMULATED_MMAN})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN)
target_link_options(${target_name} PRIVATE -lwasi-emulated-mman)
elseif(test MATCHES "(sigabrt|signals).c$")
endif()
if(${arg_EMULATED_SIGNAL})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL)
target_link_options(${target_name} PRIVATE -lwasi-emulated-signal)
elseif(test MATCHES "printf-long-double-enabled.c$")
endif()
if(${arg_PRINTSCAN_LONG_DOUBLE})
target_link_options(${target_name} PRIVATE -lc-printscan-long-double)
endif()
# Apply language-specific options and dependencies.
if(test MATCHES "cc$")
if(WASI_SDK_EXCEPTIONS)
if(NOT (WASI_SDK_EXCEPTIONS STREQUAL "OFF"))
target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false)
target_link_options(${target_name} PRIVATE -lunwind)
target_link_options(${target_name} PRIVATE -fwasm-exceptions -lunwind)
else()
target_compile_options(${target_name} PRIVATE -fno-exceptions)
endif()
@ -93,27 +110,42 @@ function(add_testcase runwasi test)
target_link_options(${target_name} PRIVATE -Wno-deprecated)
endif()
if(runwasi)
set(runner ${runwasi})
if(${runner} MATCHES wasmtime)
if(target MATCHES threads)
set(runner "${runner} -Wshared-memory")
endif()
if(WASI_SDK_EXCEPTIONS)
set(runner "${runner} -Wexceptions")
endif()
if(target MATCHES p3)
set(runner "${runner} -Wcomponent-model-async -Wcomponent-model-threading -Sp3")
endif()
if(arg_COMPILE_ONLY)
continue()
endif()
set(runner ${WASI_SDK_RUNWASI})
set(args)
if(${runner} MATCHES wasmtime)
if(target MATCHES threads)
list(APPEND runner -Wshared-memory)
endif()
add_test(
NAME test-${target_name}
COMMAND
bash ../testcase.sh
${runner}
${test}
$<TARGET_FILE:${target_name}>
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
if(WASI_SDK_EXCEPTIONS)
list(APPEND runner -Wexceptions)
endif()
if(target MATCHES "wasip3")
list(APPEND runner -Wcomponent-model-async -Sp3)
endif()
endif()
foreach(env IN LISTS arg_ENV)
list(APPEND runner --env ${env})
endforeach()
if (${arg_FSDIR})
list(APPEND runner --dir ${CMAKE_CURRENT_SOURCE_DIR}/${test}.dir::${test}.dir)
list(APPEND args ${test}.dir)
endif()
add_test(
NAME test-${target_name}
COMMAND
${runner}
$<TARGET_FILE:${target_name}>
${args}
)
if (arg_PASS_REGULAR_EXPRESSION)
set_tests_properties(test-${target_name} PROPERTIES PASS_REGULAR_EXPRESSION ${arg_PASS_REGULAR_EXPRESSION})
endif()
endforeach()
endforeach()

@ -1,9 +1,3 @@
file(GLOB c_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c")
file(GLOB cxx_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc")
set(compile_tests ${c_compile_tests} ${cxx_compile_tests})
foreach(test IN LISTS compile_tests)
add_testcase("" ${test})
endforeach()
add_testcase(addresses.c COMPILE_ONLY)
add_testcase(printf-long-double.c COMPILE_ONLY)
add_testcase(test.cc COMPILE_ONLY)

@ -1,8 +1,69 @@
file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c")
file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc")
add_testcase(abort.c PASS_REGULAR_EXPRESSION "wasm trap.*unreachable")
add_testcase(argc_argv_main.c PASS_REGULAR_EXPRESSION "hello from argc argv main")
add_testcase(argc_argv_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ argc argv main")
add_testcase(assert-fail.c PASS_REGULAR_EXPRESSION "Assertion failed: false.*wasm trap:.*unreachable")
add_testcase(assert-pass.c)
add_testcase(clocks.c EMULATED_CLOCKS)
add_testcase(cpp_thread_local.cc)
add_testcase(ctors_dtors.c PASS_REGULAR_EXPRESSION "\
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello from_destructor101
"
)
add_testcase(ctors_dtors.cc PASS_REGULAR_EXPRESSION "\
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello StaticObject::StaticObject
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello StaticObject::~StaticObject
hello from_destructor101
")
add_testcase(empty.c)
add_testcase(env-absent.c PASS_REGULAR_EXPRESSION "HELLO = \\(null\\)")
add_testcase(env.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello")
add_testcase(environ.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello")
add_testcase(exceptions.cc)
add_testcase(getentropy.c)
add_testcase(iostream_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ main with cout")
add_testcase(main_errno.c PASS_REGULAR_EXPRESSION "initial errno is 0: Success")
add_testcase(mmap.c EMULATED_MMAN FSDIR PASS_REGULAR_EXPRESSION "\
Would you tell me, please, which way I ought to go from here\\?
set(general_tests ${c_general_tests} ${cxx_general_tests})
foreach(test IN LISTS general_tests)
add_testcase(${WASI_SDK_RUNWASI} ${test})
endforeach()
That depends a good deal on where you want to get to, said the Cat.
"
)
add_testcase(no_arg_main.c PASS_REGULAR_EXPRESSION "hello from no-arg main")
add_testcase(no_arg_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ no-arg main")
add_testcase(opendir.c FSDIR)
add_testcase(printf-long-double-enabled.c PRINTSCAN_LONG_DOUBLE PASS_REGULAR_EXPRESSION "the answer is 42.000000")
add_testcase(printf-no-float.c PASS_REGULAR_EXPRESSION "the answer is 42")
add_testcase(printf-no-long-double.c PASS_REGULAR_EXPRESSION "the answer is 42.000000")
add_testcase(sigabrt.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\
raising SIGABRT...
Program received fatal signal: Aborted
.*failed to run main module.*
")
add_testcase(signals.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\
psignal message for SIGINT: Interrupt
strsignal for SIGHUP: 'Hangup'
beginning handler test:
handler for signal Window changed
finished handler test
")
add_testcase(stat.c FSDIR)
add_testcase(void_main.c PASS_REGULAR_EXPRESSION "hello from void main")
add_testcase(void_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ void main")

@ -1,6 +1,7 @@
#include <stdlib.h>
void __wasm_set_tls_base(void *base);
int main(void) {
__wasm_set_tls_base(NULL);
abort();
return 0;
}

@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cat \
| sed -e 's/main module `.*abort\.c\.wasm`/main module `abort.c.---.wasm`/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| sed -E '/0x[[:xdigit:]]+/d'

@ -1 +0,0 @@
hello from C++ argc argv main!

@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cat \
| sed -e 's/main module `.*assert-fail\.c\.wasm`/main module `assert-fail.c.---.wasm`/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| sed -e 's/Assertion failed: false (.*assert-fail.c/Assertion failed: false (assert-fail.c/' \
| sed -E '/0x[[:xdigit:]]+/d'

@ -0,0 +1,3 @@
struct c { ~c() {} };
thread_local c v;
int main() { (void)v; }

@ -1,10 +0,0 @@
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello from_destructor101

@ -1,12 +0,0 @@
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello StaticObject::StaticObject
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello StaticObject::~StaticObject
hello from_destructor101

@ -1 +0,0 @@
hello from C++ main with cout!

@ -1 +0,0 @@
initial errno is 0: Success

@ -1,3 +0,0 @@
“Would you tell me, please, which way I ought to go from here?”
“That depends a good deal on where you want to get to,” said the Cat.

@ -1 +0,0 @@
hello from C++ no-arg main!

@ -1,8 +0,0 @@
#!/bin/bash
set -euo pipefail
cat \
| sed -e 's/main module `.*sigabrt\.c\.wasm`/main module `sigabrt.c.---.wasm`/' \
| sed -e 's/source location: @[[:xdigit:]]*$/source location: @----/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| head -n 6

@ -1,6 +0,0 @@
raising SIGABRT...
Program received fatal signal: Aborted
Error: failed to run main module `sigabrt.c.---.wasm`
Caused by:
0: failed to invoke `run` function

@ -1,6 +0,0 @@
raising SIGABRT...
Program received fatal signal: Aborted
Error: failed to run main module `sigabrt.c.---.wasm`
Caused by:
0: failed to invoke `run` function

@ -1 +0,0 @@
psignal message for SIGINT: Interrupt

@ -1,4 +0,0 @@
strsignal for SIGHUP: 'Hangup'
beginning handler test:
handler for signal Window changed
finished handler test

@ -1 +0,0 @@
hello from C++ void main!

@ -1,102 +0,0 @@
#!/bin/bash
set -ueo pipefail
# A simple testcase runner that runs a command, captures all its command-line
# outputs, and compares them against expected outputs.
# Command-line parsing; this script is meant to be run from a higher-level
# script, so don't do anything fancy.
runwasi="$1"
input="$2"
wasm="$3"
# Compile names for generated files.
stdout_observed="$wasm.stdout.observed"
stderr_observed="$wasm.stderr.observed"
exit_status_observed="$wasm.exit_status.observed"
# Double-check that a runwasi command was specified since otherwise this script
# was invoked with no arguments which isn't as intended.
if [ "$runwasi" == "" ]; then
exit 1
fi
# Determine the input file to write to stdin.
if [ -e "$input.stdin" ]; then
stdin="$input.stdin"
else
stdin="/dev/null"
fi
# Determine any environment variables to set.
if [ -e "$input.env" ]; then
env=$(sed -e 's/^/--env /' < "$input.env")
else
env=""
fi
# Determine a preopened directory to provide.
if [ -e "$input.dir" ]; then
dir="--dir $input.dir"
dirarg="$input.dir"
else
dir=""
dirarg=""
fi
# Run the test, capturing stdout, stderr, and the exit status.
exit_status=0
$runwasi $env $dir "$wasm" $dirarg \
< "$stdin" \
> "$stdout_observed" \
2> "$stderr_observed" \
|| exit_status=$?
echo $exit_status > "$exit_status_observed"
# On Windows Wasmtime will exit with error code 3 for aborts. On Unix Wasmtime
# will exit with status 134. Paper over this difference by pretending to be Unix
# on Windows and converting exit code 3 into 134 for the purposes of asserting
# test output.
if [ "$OSTYPE" = "msys" ] && [ "$exit_status" = "3" ]; then
echo 134 > "$exit_status_observed"
fi
# Determine the reference files to compare with.
if [ -e "$input.stdout.expected" ]; then
stdout_expected="$input.stdout.expected"
# Apply output filters.
if [ -e "$input.stdout.expected.filter" ]; then
cat "$stdout_observed" \
| "$input.stdout.expected.filter" \
> "${stdout_observed}.filtered"
stdout_observed="${stdout_observed}.filtered"
fi
else
stdout_expected="/dev/null"
fi
if [ -e "$input.stderr.expected" ]; then
stderr_expected="$input.stderr.expected"
# Apply output filters.
if [ -e "$input.stderr.expected.filter" ]; then
cat "$stderr_observed" \
| "./$input.stderr.expected.filter" \
> "${stderr_observed}.filtered"
stderr_observed="${stderr_observed}.filtered"
fi
else
stderr_expected="/dev/null"
fi
if [ -e "$input.exit_status.expected" ]; then
exit_status_expected="$input.exit_status.expected"
else
exit_status_expected=../exit_status_zero
fi
# If there are any differences, diff will return a non-zero exit status, and
# since this script uses "set -e", it will return a non-zero exit status too.
diff --ignore-space-change -u "$stderr_expected" "$stderr_observed"
diff --ignore-space-change -u "$stdout_expected" "$stdout_observed"
diff --ignore-space-change -u "$exit_status_expected" "$exit_status_observed"

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip1)
if(WIN32)
@ -31,7 +32,6 @@ set(CMAKE_ASM_COMPILER_TARGET ${triple})
# Don't look in the sysroot for executables to run during the build
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Only look in the sysroot (not in the host paths) for the rest
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip2)
if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip3)
if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
# (--export-memory is implicit unless --import-memory is given)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--import-memory")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-memory")
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
if(WIN32)
set(WASI_HOST_EXE_SUFFIX ".exe")
@ -20,7 +21,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasi)
if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

Loading…
Cancel
Save