From 80bccc912f70ca38d3c994b4b22b2025357af565 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 14:46:03 +0200 Subject: [PATCH 01/25] Add some primitive Emscripten support, based on WASI --- driver/main.cpp | 6 ++-- driver/tool.cpp | 2 ++ runtime/CMakeLists.txt | 4 +++ runtime/druntime/src/core/sys/posix/time.d | 41 ++++++++++++++++++++++ tests/codegen/emscripten.d | 5 +-- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/driver/main.cpp b/driver/main.cpp index 89475c50bd2..55954d834ab 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -960,10 +960,10 @@ void registerPredefinedTargetVersions() { #endif case llvm::Triple::Emscripten: VersionCondition::addPredefinedGlobalIdent("Emscripten"); - // Emscripten uses musl and libc++, so mimic a musl Linux platform: - VersionCondition::addPredefinedGlobalIdent("linux"); + VersionCondition::addPredefinedGlobalIdent("WASI"); + VersionCondition::addPredefinedGlobalIdent("WASIp1"); VersionCondition::addPredefinedGlobalIdent("Posix"); - VersionCondition::addPredefinedGlobalIdent("CRuntime_Musl"); + VersionCondition::addPredefinedGlobalIdent("CRuntime_WASI"); VersionCondition::addPredefinedGlobalIdent("CppRuntime_LLVM"); VersionCondition::addPredefinedGlobalIdent("CppRuntime_Clang"); // legacy break; diff --git a/driver/tool.cpp b/driver/tool.cpp index 5a5666ccc9c..158e2480df0 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -96,6 +96,8 @@ llvm::SmallVector findCCFallback() { choices = {{"wasm32-wasi-clang"}}; break; } + } else if (triple.isOSEmscripten()) { + choices = { { "emcc" } }; } else { choices = { { opts::mTargetTriple + "-gcc" }, diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 63efdb03b4a..9e65a6b1478 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -104,6 +104,10 @@ if("${TARGET_SYSTEM}" MATCHES "WASI|Emscripten") append("-Wl,-z,stack-size=1048576 -Wl,--stack-first" LD_FLAGS) + if("${TARGET_SYSTEM}" MATCHES "Emscripten") + append("-sALLOW_MEMORY_GROWTH" LD_FLAGS) + endif() + list(APPEND D_FLAGS --wasm-enable-eh) if(LLVM_VERSION_MAJOR GREATER_EQUAL 20) list(APPEND D_FLAGS --wasm-use-legacy-eh=false) diff --git a/runtime/druntime/src/core/sys/posix/time.d b/runtime/druntime/src/core/sys/posix/time.d index c90ac45bcab..aad88d35ece 100644 --- a/runtime/druntime/src/core/sys/posix/time.d +++ b/runtime/druntime/src/core/sys/posix/time.d @@ -164,6 +164,10 @@ else version (Solaris) { enum CLOCK_MONOTONIC = 4; } +else version (Emscripten) +{ + enum CLOCK_MONOTONIC = 1; +} else version (CRuntime_WASI) { struct __clockid; @@ -539,6 +543,43 @@ else version (CRuntime_Musl) int timer_settime(timer_t, int, const scope itimerspec*, itimerspec*); int timer_getoverrun(timer_t); } +else version (Emscripten) +{ + alias clockid_t = int; + alias timer_t = void*; + + struct itimerspec + { + timespec it_interval; + timespec it_value; + } + + enum TIMER_ABSTIME = 1; + + enum CLOCK_REALTIME = 0; + enum CLOCK_PROCESS_CPUTIME_ID = 2; + enum CLOCK_THREAD_CPUTIME_ID = 3; + enum CLOCK_REALTIME_COARSE = 5; + enum CLOCK_BOOTTIME = 7; + enum CLOCK_REALTIME_ALARM = 8; + enum CLOCK_BOOTTIME_ALARM = 9; + enum CLOCK_SGI_CYCLE = 10; + enum CLOCK_TAI = 11; + + int nanosleep(const scope timespec*, timespec*); + + int clock_getres(clockid_t, timespec*); + int clock_gettime(clockid_t, timespec*); + int clock_settime(clockid_t, const scope timespec*); + int clock_nanosleep(clockid_t, int, const scope timespec*, timespec*); + int clock_getcpuclockid(pid_t, clockid_t *); + + //int timer_create(clockid_t, sigevent*, timer_t*); + int timer_delete(timer_t); + int timer_gettime(timer_t, itimerspec*); + int timer_settime(timer_t, int, const scope itimerspec*, itimerspec*); + int timer_getoverrun(timer_t); +} else version (CRuntime_WASI) { alias clockid_t = const __clockid*; diff --git a/tests/codegen/emscripten.d b/tests/codegen/emscripten.d index 74505912e59..cf72136bc3f 100644 --- a/tests/codegen/emscripten.d +++ b/tests/codegen/emscripten.d @@ -7,9 +7,10 @@ // test predefined versions: version (Emscripten) {} else static assert(0); -version (linux) {} else static assert(0); +version (WASI) {} else static assert(0); +version (WASIp1) {} else static assert(0); version (Posix) {} else static assert(0); -version (CRuntime_Musl) {} else static assert(0); +version (CRuntime_WASI) {} else static assert(0); version (CppRuntime_LLVM) {} else static assert(0); From e5da7df8b9251712e5de57403bb144bc784da71c Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 15:00:32 +0200 Subject: [PATCH 02/25] CI: Add Emscripten job --- .../actions/1a-setup-emscripten/action.yml | 18 +++++++++ .github/actions/5a-emscripten/action.yml | 37 +++++++++++++++++++ .github/workflows/main.yml | 20 +++++++++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 .github/actions/1a-setup-emscripten/action.yml create mode 100644 .github/actions/5a-emscripten/action.yml diff --git a/.github/actions/1a-setup-emscripten/action.yml b/.github/actions/1a-setup-emscripten/action.yml new file mode 100644 index 00000000000..4d7a76d5360 --- /dev/null +++ b/.github/actions/1a-setup-emscripten/action.yml @@ -0,0 +1,18 @@ +name: "Emscripten: Install additional prerequisites" +inputs: + emsdk_version: + required: false + default: '6.0.6' +runs: + using: composite + steps: + - name: "Install emsdk" # into ../emsdk + shell: bash + run: | + set -eux + cd .. + + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install '${{ inputs.emsdk_version }}' + ./emsdk activate '${{ inputs.emsdk_version }}' diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml new file mode 100644 index 00000000000..c8f011f46b5 --- /dev/null +++ b/.github/actions/5a-emscripten/action.yml @@ -0,0 +1,37 @@ +name: "Emscripten: Cross-compile druntime incl. test runners, install libs + .conf, run druntime unittests" +inputs: + arch: + required: true +runs: + using: composite + steps: + - name: Cross-compile druntime incl. test runners & install libs + .conf + shell: bash + run: | + set -eux + cd .. + + os='emscripten' + arch='${{ inputs.arch }}' + triple="$arch-unknown-$os" + + bootstrap-ldc/bin/ldc-build-runtime \ + --ninja \ + --dFlags="-mtriple=$triple" \ + --ldcSrcDir="$PWD/ldc" \ + --installWithSuffix="-$os-$arch" \ + --testrunners \ + CMAKE_INSTALL_PREFIX="$PWD/install" \ + RT_CONF_TRIPLE_REGEX="$arch-.*-$os" \ + CMAKE_TOOLCHAIN_FILE="$PWD/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" \ + PHOBOS2_DIR="" \ + BUILD_LTO_LIBS=ON + + - name: Run druntime unittests + shell: bash + run: | + set -eux + cd ../ldc-build-runtime.tmp + + ../emsdk/node/*/bin/node ./druntime-test-runner-debug.js + ../emsdk/node/*/bin/node ./druntime-test-runner.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dd998f011fc..c6e1dbe05f5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -387,6 +387,10 @@ jobs: matrix: include: - job_name: WASI wasm32 + os: wasi + arch: wasm32 + - job_name: Emscripten wasm32 + os: emscripten arch: wasm32 name: ${{ matrix.job_name }} runs-on: ubuntu-22.04-arm @@ -404,28 +408,40 @@ jobs: clang_major: ${{ env.CLANG_MAJOR }} arch: aarch64 - name: 'WASI: Install additional prerequisites' + if: matrix.os == 'wasi' uses: ./.github/actions/1a-setup-wasi + - name: 'Emscripten: Install additional prerequisites' + if: matrix.os == 'emscripten' + uses: ./.github/actions/1a-setup-emscripten - name: Build bootstrap LDC uses: ./.github/actions/2-build-bootstrap - name: 'WASIp1: Cross-compile druntime & phobos incl. test runners & run druntime & phobos unittests' + if: matrix.os == 'wasi' uses: ./.github/actions/5a-wasi with: os: wasip1 arch: ${{ matrix.arch }} - name: 'WASIp2: Cross-compile druntime & phobos incl. test runners & run druntime & phobos unittests' + if: matrix.os == 'wasi' uses: ./.github/actions/5a-wasi with: os: wasip2 arch: ${{ matrix.arch }} + - name: 'Emscripten: Cross-compile druntime incl. test runners & run druntime unittests' + if: matrix.os == 'emscripten' + uses: ./.github/actions/5a-emscripten + with: + arch: ${{ matrix.arch }} - name: Make installed .conf files portable uses: ./.github/actions/5b-make-portable - name: Create addon package & upload artifact uses: ./.github/actions/7-package with: arch: ${{ matrix.arch }} - os: wasi + os: ${{ matrix.os }} is_addon: true - - name: Smoke-test add-on package + - name: 'WASI: Smoke-test add-on package' + if: matrix.os == 'wasi' run: | set -eux # unpack addon archive into ../bootstrap-ldc From 1f50a082aca11f9c3b96338f3d5faa20017be04f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 15:55:10 +0200 Subject: [PATCH 03/25] [try building Phobos too] --- .github/actions/5a-emscripten/action.yml | 5 ++--- .github/workflows/main.yml | 2 +- runtime/phobos | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index c8f011f46b5..0b8b691c95e 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -1,11 +1,11 @@ -name: "Emscripten: Cross-compile druntime incl. test runners, install libs + .conf, run druntime unittests" +name: "Emscripten: Cross-compile druntime & phobos incl. test runners, install libs + .conf, run druntime unittests" inputs: arch: required: true runs: using: composite steps: - - name: Cross-compile druntime incl. test runners & install libs + .conf + - name: Cross-compile druntime & phobos incl. test runners & install libs + .conf shell: bash run: | set -eux @@ -24,7 +24,6 @@ runs: CMAKE_INSTALL_PREFIX="$PWD/install" \ RT_CONF_TRIPLE_REGEX="$arch-.*-$os" \ CMAKE_TOOLCHAIN_FILE="$PWD/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" \ - PHOBOS2_DIR="" \ BUILD_LTO_LIBS=ON - name: Run druntime unittests diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c6e1dbe05f5..3a0ffa0e99d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -427,7 +427,7 @@ jobs: with: os: wasip2 arch: ${{ matrix.arch }} - - name: 'Emscripten: Cross-compile druntime incl. test runners & run druntime unittests' + - name: 'Emscripten: Cross-compile druntime & phobos incl. test runners & run druntime unittests' if: matrix.os == 'emscripten' uses: ./.github/actions/5a-emscripten with: diff --git a/runtime/phobos b/runtime/phobos index 8017f195c44..7b79b4a1c9e 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 8017f195c44ce6dd7f883489b7b12f33dac66c45 +Subproject commit 7b79b4a1c9e6ad2164f016c7789d39bc915f6fba From 9ed081277599da169908181fbc98f7d609a69b36 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 16:38:12 +0200 Subject: [PATCH 04/25] [ignore unittest failures, and try running the Phobos debug testrunner too] --- .github/actions/5a-emscripten/action.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 0b8b691c95e..5fa0d649d75 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -32,5 +32,13 @@ runs: set -eux cd ../ldc-build-runtime.tmp - ../emsdk/node/*/bin/node ./druntime-test-runner-debug.js - ../emsdk/node/*/bin/node ./druntime-test-runner.js + ../emsdk/node/*/bin/node ./druntime-test-runner-debug.js || true + ../emsdk/node/*/bin/node ./druntime-test-runner.js || true + + - name: Run phobos unittests + shell: bash + run: | + set -eux + cd ../ldc-build-runtime.tmp + + ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true From a4e9abd70f3d9327657b3f9c58d93e366da67293 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 17:28:00 +0200 Subject: [PATCH 05/25] [fix linking with emcc] --- driver/linker-gcc.cpp | 23 ++++++++++++----------- driver/linker.cpp | 5 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 11320083e51..98c8b4e7465 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -635,6 +635,8 @@ void ArgsBuilder::addLinker() { ////////////////////////////////////////////////////////////////////////////// void ArgsBuilder::addUserSwitches() { + const bool isEmcc = global.params.targetTriple->isOSEmscripten(); + // additional linker and cc switches (preserve order across both lists) for (unsigned ilink = 0, icc = 0;;) { unsigned linkpos = ilink < opts::linkerSwitches.size() @@ -654,7 +656,14 @@ void ArgsBuilder::addUserSwitches() { if (!(str.starts_with("-l") || str.starts_with("-L") || str.starts_with("-Wl,") || str.starts_with("-shared") || str.starts_with("-static"))) { - args.push_back("-Xlinker"); + // avoid `-Xlinker

` for Emscripten's emcc, seems buggy; use + // `-Wl,

` instead + if (isEmcc) { + args.push_back("-Wl," + p); + continue; + } else { + args.push_back("-Xlinker"); + } } args.push_back(p); } else if (ccpos < linkpos) { @@ -807,16 +816,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, #endif // build command-line for gcc-compatible linker driver - // exception: invoke (ld-compatible) linker directly for WebAssembly targets - std::string tool; - std::unique_ptr argsBuilder; - if (global.params.targetTriple->isOSBinFormatWasm() && !global.params.targetTriple->isOSWASI()) { - argsBuilder = std::make_unique(); - tool = getProgram("wasm-ld", &opts::linker); - } else { - argsBuilder = std::make_unique(); - tool = getCC(argsBuilder->args); - } + auto argsBuilder = std::make_unique(); + std::string tool = getCC(argsBuilder->args); // build arguments argsBuilder->build(outputPath, defaultLibNames); diff --git a/driver/linker.cpp b/driver/linker.cpp index 6f5cccd28ec..6774adcddec 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -103,9 +103,8 @@ static std::string getOutputPath() { extension = target.dll_ext.ptr; } else if (triple.isOSWindows()) { extension = "exe"; - } else if (triple.getArch() == llvm::Triple::wasm32 || - triple.getArch() == llvm::Triple::wasm64) { - extension = "wasm"; + } else if (triple.isWasm()) { + extension = triple.isOSEmscripten() ? "js" : "wasm"; } if (global.params.exefile.length) { From f75de5eb9144d39c056d94a6b6c1e59d337460d0 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 17:36:18 +0200 Subject: [PATCH 06/25] [enable addon-package smoke test for Emscripten too] --- .github/workflows/main.yml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3a0ffa0e99d..e214b33616a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -440,8 +440,7 @@ jobs: arch: ${{ matrix.arch }} os: ${{ matrix.os }} is_addon: true - - name: 'WASI: Smoke-test add-on package' - if: matrix.os == 'wasi' + - name: 'Smoke-test add-on package' run: | set -eux # unpack addon archive into ../bootstrap-ldc @@ -449,17 +448,29 @@ jobs: tar xf ${{ github.workspace }}/artifacts/*-addon-*.tar.xz # generate hello.d echo 'void main() { import std.stdio; writefln!"Hello world, %d bits"(size_t.sizeof * 8); }' > hello.d - # put WASI-SDK clang onto PATH - export PATH="$PWD/../wasi-sdk/bin:$PATH" - # build & run hello-world - for os in wasip1 wasip2; do - # test LTO too + + if [[ '${{ matrix.os }}' == wasi ]]; then + # put WASI-SDK clang onto PATH + export PATH="$PWD/../wasi-sdk/bin:$PATH" + # build & run hello-world + for os in wasip1 wasip2; do + # test LTO too + for args in "" "-O -flto=full -defaultlib=druntime-ldc-lto,phobos2-ldc-lto"; do + bin/ldc2 -mtriple=${{ matrix.arch }}-unknown-$os $args hello.d + ls -l hello.wasm + ../wasmtime/wasmtime -W exceptions ./hello.wasm + done + done + elif [[ '${{ matrix.os }}' == emscripten ]]; then + # put emcc onto PATH + export PATH="$PWD/../emsdk/upstream/emscripten:$PATH" + # build & run hello-world for args in "" "-O -flto=full -defaultlib=druntime-ldc-lto,phobos2-ldc-lto"; do - bin/ldc2 -mtriple=${{ matrix.arch }}-unknown-$os $args hello.d - ls -l hello.wasm - ../wasmtime/wasmtime -W exceptions ./hello.wasm + bin/ldc2 -mtriple=${{ matrix.arch }}-unknown-emscripten $args hello.d + ls -l hello.{js,wasm} + ../emsdk/node/*/bin/node ./hello.js done - done + fi merge-macos: From a825d0cd29b9e4d71119bcdc71d4aaac03821cb3 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 18:03:20 +0200 Subject: [PATCH 07/25] [add importc_compare test] --- .github/actions/5a-emscripten/action.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 5fa0d649d75..08c7c3133da 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -42,3 +42,25 @@ runs: cd ../ldc-build-runtime.tmp ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true + + - name: Run `importc_compare` + shell: bash + run: | + set -eux + cd .. + + os='emscripten' + arch='${{ inputs.arch }}' + triple="$arch-unknown-$os" + + bootstrap-ldc/bin/ldc2 \ + -defaultlib=druntime-ldc -L-Linstall/lib-$os-$arch \ + -gcc="$PWD/emsdk/upstream/emscripten/emcc" \ + --mtriple=$triple \ + --d-version=WASI_EMULATED_SIGNAL \ + --d-version=WASI_EMULATED_PROCESS_CLOCKS \ + --d-version=WASI_EMULATED_MMAN \ + -I$PWD/ldc/runtime/druntime/test/importc_compare/src \ + $PWD/ldc/runtime/druntime/test/importc_compare/src/importc_compare.d + + emsdk/node/*/bin/node ./importc_compare.js || true From 834c6d1c89d3ab1939395dec7a7233a670ddb0b8 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Mon, 10 Aug 2026 19:54:20 +0200 Subject: [PATCH 08/25] [druntime: Fix some issues reported by importc_compare] --- runtime/druntime/src/core/stdc/fenv.d | 9 +++++ runtime/druntime/src/core/sys/posix/dirent.d | 11 ++++++ .../druntime/src/core/sys/posix/netinet/in_.d | 11 ++++-- runtime/druntime/src/core/sys/posix/signal.d | 25 +++++++++++++ .../druntime/src/core/sys/posix/stdc/time.d | 19 +++++++++- .../src/core/sys/posix/sys/resource.d | 29 +++++++++++++++ .../druntime/src/core/sys/posix/sys/socket.d | 36 ++++++++++++++----- runtime/druntime/src/core/sys/posix/sys/un.d | 8 +++++ 8 files changed, 136 insertions(+), 12 deletions(-) diff --git a/runtime/druntime/src/core/stdc/fenv.d b/runtime/druntime/src/core/stdc/fenv.d index 4e4b0317aa6..9708fc62c86 100644 --- a/runtime/druntime/src/core/stdc/fenv.d +++ b/runtime/druntime/src/core/stdc/fenv.d @@ -450,6 +450,15 @@ else version (CRuntime_Musl) static assert(false, "Architecture not supported."); } } +else version (Emscripten) +{ + struct fenv_t + { + uint __cw; + } + + alias fexcept_t = ushort; +} else version (CRuntime_WASI) { import core.stdc.config : c_ulong; diff --git a/runtime/druntime/src/core/sys/posix/dirent.d b/runtime/druntime/src/core/sys/posix/dirent.d index 868e8fba246..20cd54c434b 100644 --- a/runtime/druntime/src/core/sys/posix/dirent.d +++ b/runtime/druntime/src/core/sys/posix/dirent.d @@ -158,6 +158,17 @@ else version (Solaris) char[1] d_name = 0; } } +else version (Emscripten) +{ + struct dirent + { + ino_t d_ino; + off_t d_off; + ushort d_reclen; + ubyte d_type; + char[256] d_name = 0; + } +} else version (CRuntime_WASI) { struct dirent diff --git a/runtime/druntime/src/core/sys/posix/netinet/in_.d b/runtime/druntime/src/core/sys/posix/netinet/in_.d index 276bddd8181..374d762c4b3 100644 --- a/runtime/druntime/src/core/sys/posix/netinet/in_.d +++ b/runtime/druntime/src/core/sys/posix/netinet/in_.d @@ -1631,8 +1631,15 @@ else version (CRuntime_WASI) ubyte[16] s6_addr; } struct sockaddr_in6 { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t sin6_family; + version (Emscripten) // no alignment override + { + sa_family_t sin6_family; + } + else + { + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t sin6_family; + } in_port_t sin6_port; uint sin6_flowinfo; diff --git a/runtime/druntime/src/core/sys/posix/signal.d b/runtime/druntime/src/core/sys/posix/signal.d index 76ba404e5c7..9ebf39f982f 100644 --- a/runtime/druntime/src/core/sys/posix/signal.d +++ b/runtime/druntime/src/core/sys/posix/signal.d @@ -1484,6 +1484,31 @@ else version (Solaris) enum SI_ASYNCIO = -4; enum SI_MESGQ = -5; } +else version (Emscripten) +{ + struct sigset_t + { + c_ulong[2] __bits; + + } + + enum SIG_BLOCK = 0; + enum SIG_UNBLOCK = 1; + enum SIG_SETMASK = 2; + + enum + { + SI_ASYNCNL = -60, + SI_TKILL = -6, + SI_SIGIO, + SI_ASYNCIO, + SI_MESGQ, + SI_TIMER, + SI_QUEUE, + SI_USER, + SI_KERNEL = 0x80 + } +} else version (CRuntime_WASI) { alias sigset_t = ubyte; diff --git a/runtime/druntime/src/core/sys/posix/stdc/time.d b/runtime/druntime/src/core/sys/posix/stdc/time.d index 3278751f591..8adad1b2594 100644 --- a/runtime/druntime/src/core/sys/posix/stdc/time.d +++ b/runtime/druntime/src/core/sys/posix/stdc/time.d @@ -34,7 +34,24 @@ nothrow: @nogc: /// -version (CRuntime_WASI) +version (Emscripten) +{ + struct tm + { + int tm_sec; /// seconds after the minute [0-60] + int tm_min; /// minutes after the hour [0-59] + int tm_hour; /// hours since midnight [0-23] + int tm_mday; /// day of the month [1-31] + int tm_mon; /// months since January [0-11] + int tm_year; /// years since 1900 + int tm_wday; /// days since Sunday [0-6] + int tm_yday; /// days since January 1 [0-365] + int tm_isdst; /// Daylight Savings Time flag + c_long tm_gmtoff; /// offset from CUT in seconds + char* tm_zone; /// timezone abbreviation + } +} +else version (CRuntime_WASI) { struct tm { diff --git a/runtime/druntime/src/core/sys/posix/sys/resource.d b/runtime/druntime/src/core/sys/posix/sys/resource.d index 62d3e05d9f9..b147fced256 100644 --- a/runtime/druntime/src/core/sys/posix/sys/resource.d +++ b/runtime/druntime/src/core/sys/posix/sys/resource.d @@ -496,6 +496,35 @@ else version (Solaris) RLIMIT_AS = 6, } } +else version (Emscripten) +{ + struct rusage + { + timeval ru_utime; + timeval ru_stime; + c_long ru_maxrss; + c_long ru_ixrss; + c_long ru_idrss; + c_long ru_isrss; + c_long ru_minflt; + c_long ru_majflt; + c_long ru_nswap; + c_long ru_inblock; + c_long ru_oublock; + c_long ru_msgsnd; + c_long ru_msgrcv; + c_long ru_nsignals; + c_long ru_nvcsw; + c_long ru_nivcsw; + c_long[16] __reserved; + } + + enum + { + RUSAGE_SELF = 0, + RUSAGE_CHILDREN = -1, + } +} else version (CRuntime_WASI) { struct rusage diff --git a/runtime/druntime/src/core/sys/posix/sys/socket.d b/runtime/druntime/src/core/sys/posix/sys/socket.d index a0e1b97d40e..3cee828529b 100644 --- a/runtime/druntime/src/core/sys/posix/sys/socket.d +++ b/runtime/druntime/src/core/sys/posix/sys/socket.d @@ -1603,19 +1603,37 @@ else version (CRuntime_WASI) alias socklen_t = uint; alias sa_family_t = ushort; - struct sockaddr + version (Emscripten) { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t sa_family; + struct sockaddr + { + sa_family_t sa_family; + char[14] sa_data = 0; + } - byte[0] sa_data; + struct sockaddr_storage + { + sa_family_t ss_family; + char[128 - c_ulong.sizeof - sa_family_t.sizeof] __ss_padding = 0; + c_ulong __ss_align; + } } - - struct sockaddr_storage + else { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t ss_family; - byte[32] __ss_data; + struct sockaddr + { + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t sa_family; + + byte[0] sa_data; + } + + struct sockaddr_storage + { + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t ss_family; + byte[32] __ss_data; + } } struct msghdr diff --git a/runtime/druntime/src/core/sys/posix/sys/un.d b/runtime/druntime/src/core/sys/posix/sys/un.d index 62a124b974e..efcb02cf73e 100644 --- a/runtime/druntime/src/core/sys/posix/sys/un.d +++ b/runtime/druntime/src/core/sys/posix/sys/un.d @@ -104,6 +104,14 @@ else version (Solaris) byte[108] sun_path; } } +else version (Emscripten) +{ + struct sockaddr_un + { + sa_family_t sun_family; + byte[108] sun_path; + } +} else version (CRuntime_WASI) { struct sockaddr_un From 918f918274fc13dbcfc58924b9875d9bd25f1842 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 12 Aug 2026 20:15:39 +0200 Subject: [PATCH 09/25] [fix core.sys.posix.sys.types and core.stdc.limits] Based on the C+ headers. --- runtime/druntime/src/core/stdc/limits.d | 13 +++ .../druntime/src/core/sys/posix/sys/types.d | 83 +++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/runtime/druntime/src/core/stdc/limits.d b/runtime/druntime/src/core/stdc/limits.d index 20f1c0c0f6b..641515399c9 100644 --- a/runtime/druntime/src/core/stdc/limits.d +++ b/runtime/druntime/src/core/stdc/limits.d @@ -181,6 +181,19 @@ else version (Windows) /// enum PIPE_BUF = 5120; } +else version (Emscripten) +{ + /// + enum MAX_CANON = 255; + /// + enum MAX_INPUT = 255; + /// + enum NAME_MAX = 14; + /// + enum PATH_MAX = 256; + /// + enum PIPE_BUF = 512; +} else version (CRuntime_WASI) { /// diff --git a/runtime/druntime/src/core/sys/posix/sys/types.d b/runtime/druntime/src/core/sys/posix/sys/types.d index 10defc4f61a..bd313f11c0b 100644 --- a/runtime/druntime/src/core/sys/posix/sys/types.d +++ b/runtime/druntime/src/core/sys/posix/sys/types.d @@ -328,6 +328,27 @@ else version (Solaris) alias time_t = c_long; alias uid_t = uint; } +else version (Emscripten) +{ + alias blkcnt_t = int; + alias ino_t = ulong; + alias off_t = long; + + alias blksize_t = int; + + alias dev_t = uint; + alias gid_t = uint; + alias mode_t = uint; + + alias nlink_t = size_t; + + alias pid_t = int; + //size_t (defined in core.stdc.stddef) + alias ssize_t = c_long; + alias uid_t = uint; + + alias time_t = long; +} else version (CRuntime_WASI) { alias blkcnt_t = long; @@ -460,6 +481,17 @@ else version (Solaris) alias zoneid_t = id_t; alias ctid_t = id_t; } +else version (Emscripten) +{ + alias fsblkcnt_t = ulong; + alias fsfilcnt_t = ulong; + + alias clock_t = int; + alias id_t = uint; + alias key_t = int; + alias suseconds_t = int; + alias useconds_t = uint; +} else version (CRuntime_WASI) { alias fsblkcnt_t = ulong; @@ -776,6 +808,57 @@ version (CRuntime_Glibc) alias pthread_t = c_ulong; } +else version (Emscripten) +{ + struct pthread_attr_t + { + union + { + int[10] __i; + uint[10] __s; + } + const(char)* _a_transferredcanvases; + } + + union pthread_cond_t + { + int[12] __i; + void*[12] __p; + } + + union pthread_mutex_t + { + int[6] __i; + void*[6] __p; + } + + union pthread_rwlock_t + { + int[8] __i; + void*[8] __p; + } + + struct pthread_rwlockattr_t + { + uint[2] __attr; + } + + alias pthread_key_t = uint; + + struct pthread_condattr_t + { + uint __attr; + } + + struct pthread_mutexattr_t + { + uint __attr; + } + + alias pthread_once_t = int; + + alias pthread_t = c_ulong; +} else version (CRuntime_Musl) { version (D_LP64) From 8771d6e8b88945ec9773fcec52bc2a2812960f3a Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 12 Aug 2026 21:22:21 +0200 Subject: [PATCH 10/25] [fix core.sys.posix.sys.stat] --- .../druntime/src/core/sys/posix/sys/stat.d | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/runtime/druntime/src/core/sys/posix/sys/stat.d b/runtime/druntime/src/core/sys/posix/sys/stat.d index 5586ca90a37..9836d3d113f 100644 --- a/runtime/druntime/src/core/sys/posix/sys/stat.d +++ b/runtime/druntime/src/core/sys/posix/sys/stat.d @@ -1540,6 +1540,36 @@ else version (Solaris) enum S_ISGID = 0x400; enum S_ISVTX = 0x200; } +else version (Emscripten) +{ + struct stat_t + { + dev_t st_dev; /* inode's device */ + mode_t st_mode; /* inode protection mode */ + nlink_t st_nlink; /* number of hard links */ + uid_t st_uid; /* user ID of the file's owner */ + gid_t st_gid; /* group ID of the file's group */ + dev_t st_rdev; /* device type */ + off_t st_size; /* file size, in bytes */ + blksize_t st_blksize; /* optimal blocksize for I/O */ + blkcnt_t st_blocks; /* blocks allocated for file */ + timespec st_atim; /* time of last access */ + timespec st_mtim; /* time of last data modification */ + timespec st_ctim; /* time of last file status change */ + ino_t st_ino; /* inode's number */ + + extern(D) @safe @property inout pure nothrow + { + ref inout(time_t) st_atime() return { return st_atim.tv_sec; } + ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } + ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } + } + } + + enum S_ISUID = 0x800; // octal 04000 + enum S_ISGID = 0x400; // octal 02000 + enum S_ISVTX = 0x200; // octal 01000 +} else version (CRuntime_WASI) { struct stat_t @@ -2302,6 +2332,11 @@ else version (Solaris) enum UTIME_NOW = -1; enum UTIME_OMIT = -2; } +else version (Emscripten) +{ + enum UTIME_NOW = 0x3fffffff; + enum UTIME_OMIT = 0x3ffffffe; +} else version (CRuntime_WASI) { enum UTIME_NOW = -1; @@ -2413,6 +2448,17 @@ else version (Solaris) enum S_IFDOOR = 0xD000; enum S_IFPORT = 0xE000; } +else version (Emscripten) +{ + enum S_IFMT = 0xF000; // octal 0170000 + enum S_IFBLK = 0x6000; // octal 0060000 + enum S_IFCHR = 0x2000; // octal 0020000 + enum S_IFIFO = 0x1000; // octal 0010000 + enum S_IFREG = 0x8000; // octal 0100000 + enum S_IFDIR = 0x4000; // octal 0040000 + enum S_IFLNK = 0xA000; // octal 0120000 + enum S_IFSOCK = 0xC000; // octal 0140000 +} else version (CRuntime_WASI) { enum S_IFMT = S_IFBLK | S_IFCHR | S_IFDIR | S_IFIFO | S_IFLNK | From e4616bfbe1c0d362f597308a62afc85fa2d7bc83 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Wed, 12 Aug 2026 21:23:57 +0200 Subject: [PATCH 11/25] [fix core.sys.posix.sys.socket] --- .../druntime/src/core/sys/posix/sys/socket.d | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/runtime/druntime/src/core/sys/posix/sys/socket.d b/runtime/druntime/src/core/sys/posix/sys/socket.d index 3cee828529b..a673f363baf 100644 --- a/runtime/druntime/src/core/sys/posix/sys/socket.d +++ b/runtime/druntime/src/core/sys/posix/sys/socket.d @@ -1598,6 +1598,114 @@ else version (Solaris) SHUT_RDWR } } +else version (Emscripten) +{ + alias socklen_t = uint; + alias sa_family_t = ushort; + + struct sockaddr + { + sa_family_t sa_family; + char[14] sa_data = 0; + } + + struct sockaddr_storage + { + sa_family_t ss_family; + char[128 - c_ulong.sizeof - sa_family_t.sizeof] __ss_padding = 0; + c_ulong __ss_align; + } + + struct msghdr + { + void* msg_name; + socklen_t msg_namelen; + iovec* msg_iov; + int msg_iovlen; + void* msg_control; + socklen_t msg_controllen; + int msg_flags; + } + + struct linger + { + int l_onoff; + int l_linger; + } + + enum + { + SOCK_DGRAM = 2, + SOCK_STREAM = 1, + } + + enum + { + SOL_SOCKET = 1 + } + + enum + { + SO_REUSEADDR = 2, + SO_ERROR = 4, + SO_SNDBUF = 7, + SO_RCVBUF = 8, + SO_KEEPALIVE = 9, + SO_ACCEPTCONN = 30, + SO_PROTOCOL = 38, + SO_DOMAIN = 39, + + SO_TYPE = 3 + } + + version (D_LP64) + { + enum + { + SO_RCVTIMEO = 20, + SO_SNDTIMEO = 21, + } + } + else + { + enum + { + SO_RCVTIMEO = 66, + SO_SNDTIMEO = 67, + } + } + + enum + { + SOMAXCONN = 128 + } + + enum : uint + { + MSG_DONTWAIT = 0x0040, + MSG_NOSIGNAL = 0x4000, + MSG_PEEK = 0x0002, + MSG_WAITALL = 0x0100, + MSG_TRUNC = 0x0020 + } + + enum + { + PF_UNSPEC = 0, + PF_INET = 2, + + AF_UNSPEC = PF_UNSPEC, + AF_INET = PF_INET, + AF_UNIX = 1, + } + + enum + { + SHUT_RD, + SHUT_WR, + SHUT_RDWR + } +} else version (CRuntime_WASI) { alias socklen_t = uint; @@ -2060,6 +2168,14 @@ else version (Solaris) AF_INET6 = 26, } } +else version (Emscripten) +{ + enum + { + PF_INET6 = 10, + AF_INET6 = PF_INET6, + } +} else version (CRuntime_WASI) { enum @@ -2129,6 +2245,13 @@ else version (Solaris) SOCK_RAW = 4, } } +else version (Emscripten) +{ + enum + { + SOCK_RAW = 3 + } +} else version (CRuntime_WASI) { } From f342c2ca0823ff826858dd078e94d6bdbb1abf14 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 13 Aug 2026 15:12:08 +0200 Subject: [PATCH 12/25] Emscripten: Switch from predefined CRuntime_WASI to CRuntime_Musl --- .github/actions/5a-emscripten/action.yml | 8 +- .github/workflows/main.yml | 2 +- driver/main.cpp | 2 +- runtime/druntime/src/core/demangle.d | 1 + runtime/druntime/src/core/stdc/config.d | 4 +- runtime/druntime/src/core/stdc/errno.d | 2 +- runtime/druntime/src/core/stdc/fenv.d | 29 ++++--- runtime/druntime/src/core/stdc/wchar_.d | 12 +++ runtime/druntime/src/core/sys/posix/aio.d | 2 +- runtime/druntime/src/core/sys/posix/fcntl.d | 68 +++++++++++++++++ .../druntime/src/core/sys/posix/netinet/in_.d | 11 +-- runtime/druntime/src/core/sys/posix/poll.d | 16 ++++ runtime/druntime/src/core/sys/posix/sched.d | 2 +- runtime/druntime/src/core/sys/posix/signal.d | 76 +++++++++++++++++++ runtime/druntime/src/core/sys/posix/spawn.d | 2 +- .../druntime/src/core/sys/posix/stdc/time.d | 19 +---- .../druntime/src/core/sys/posix/sys/ioctl.d | 5 ++ runtime/druntime/src/core/sys/posix/sys/ipc.d | 2 +- .../druntime/src/core/sys/posix/sys/mman.d | 33 ++++++++ .../src/core/sys/posix/sys/resource.d | 2 + .../druntime/src/core/sys/posix/sys/select.d | 4 +- runtime/druntime/src/core/sys/posix/sys/shm.d | 2 +- .../druntime/src/core/sys/posix/sys/socket.d | 36 +++------ .../druntime/src/core/sys/posix/sys/wait.d | 2 +- runtime/druntime/src/core/sys/posix/time.d | 45 ++--------- runtime/druntime/src/core/thread/osthread.d | 2 +- runtime/druntime/src/rt/sections.d | 4 +- runtime/druntime/src/rt/sections_elf_shared.d | 3 +- .../importc_compare/src/importc_compare.d | 4 + runtime/phobos | 2 +- tests/codegen/emscripten.d | 2 +- 31 files changed, 281 insertions(+), 123 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 08c7c3133da..418f76bdc16 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -1,4 +1,4 @@ -name: "Emscripten: Cross-compile druntime & phobos incl. test runners, install libs + .conf, run druntime unittests" +name: "Emscripten: Cross-compile druntime & phobos incl. test runners, install libs + .conf, run druntime & phobos unittests" inputs: arch: required: true @@ -42,6 +42,7 @@ runs: cd ../ldc-build-runtime.tmp ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true + ../emsdk/node/*/bin/node ./phobos2-test-runner.js || true - name: Run `importc_compare` shell: bash @@ -57,10 +58,7 @@ runs: -defaultlib=druntime-ldc -L-Linstall/lib-$os-$arch \ -gcc="$PWD/emsdk/upstream/emscripten/emcc" \ --mtriple=$triple \ - --d-version=WASI_EMULATED_SIGNAL \ - --d-version=WASI_EMULATED_PROCESS_CLOCKS \ - --d-version=WASI_EMULATED_MMAN \ -I$PWD/ldc/runtime/druntime/test/importc_compare/src \ $PWD/ldc/runtime/druntime/test/importc_compare/src/importc_compare.d - emsdk/node/*/bin/node ./importc_compare.js || true + emsdk/node/*/bin/node ./importc_compare.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e214b33616a..6781de0f1b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -427,7 +427,7 @@ jobs: with: os: wasip2 arch: ${{ matrix.arch }} - - name: 'Emscripten: Cross-compile druntime & phobos incl. test runners & run druntime unittests' + - name: 'Emscripten: Cross-compile druntime & phobos incl. test runners & run druntime & phobos unittests' if: matrix.os == 'emscripten' uses: ./.github/actions/5a-emscripten with: diff --git a/driver/main.cpp b/driver/main.cpp index 55954d834ab..e536ddc5947 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -963,7 +963,7 @@ void registerPredefinedTargetVersions() { VersionCondition::addPredefinedGlobalIdent("WASI"); VersionCondition::addPredefinedGlobalIdent("WASIp1"); VersionCondition::addPredefinedGlobalIdent("Posix"); - VersionCondition::addPredefinedGlobalIdent("CRuntime_WASI"); + VersionCondition::addPredefinedGlobalIdent("CRuntime_Musl"); VersionCondition::addPredefinedGlobalIdent("CppRuntime_LLVM"); VersionCondition::addPredefinedGlobalIdent("CppRuntime_Clang"); // legacy break; diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index bcd7620cd77..6953d6394c2 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -3003,6 +3003,7 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted version (OpenBSD) import core.sys.openbsd.dlfcn : RTLD_DEFAULT; version (Darwin) import core.sys.darwin.dlfcn : RTLD_DEFAULT; version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT; + version (Emscripten) import core.sys.posix.dlfcn : RTLD_DEFAULT; version (CRuntime_WASI) import core.sys.posix.dlfcn : RTLD_DEFAULT; if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle")) diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index 61b6b66eed1..b4d61f66b97 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -638,7 +638,9 @@ version (unittest) // https://musl.libc.org/time64.html version (CRuntime_Musl) { - version (CRuntime_Musl_Pre_Time64) + version (Emscripten) + enum muslRedirTime64 = false; + else version (CRuntime_Musl_Pre_Time64) enum muslRedirTime64 = false; else { diff --git a/runtime/druntime/src/core/stdc/errno.d b/runtime/druntime/src/core/stdc/errno.d index 8a956a71687..73fbc5dc577 100644 --- a/runtime/druntime/src/core/stdc/errno.d +++ b/runtime/druntime/src/core/stdc/errno.d @@ -2328,7 +2328,7 @@ else version (Haiku) enum B_NO_TRANSLATOR = (B_TRANSLATION_ERROR_BASE + 1); enum B_ILLEGAL_DATA = (B_TRANSLATION_ERROR_BASE + 2); } -else version (CRuntime_WASI) +else version (WASI) { enum E2BIG = 1; enum EACCES = 2; diff --git a/runtime/druntime/src/core/stdc/fenv.d b/runtime/druntime/src/core/stdc/fenv.d index 9708fc62c86..68c13bc053c 100644 --- a/runtime/druntime/src/core/stdc/fenv.d +++ b/runtime/druntime/src/core/stdc/fenv.d @@ -445,19 +445,19 @@ else version (CRuntime_Musl) alias fenv_t = uint; alias fexcept_t = uint; } - else + else version (Emscripten) { - static assert(false, "Architecture not supported."); + struct fenv_t + { + uint __cw; + } + + alias fexcept_t = ushort; } -} -else version (Emscripten) -{ - struct fenv_t + else { - uint __cw; + static assert(false, "Architecture not supported."); } - - alias fexcept_t = ushort; } else version (CRuntime_WASI) { @@ -647,6 +647,17 @@ else version (Solaris) static assert(0, "Unimplemented architecture"); } } +else version (Emscripten) +{ + enum + { + FE_ALL_EXCEPT = 0, /// + FE_TONEAREST = 0, /// + FE_DOWNWARD = 0x400, /// + FE_UPWARD = 0x800, /// + FE_TOWARDZERO = 0xC00, /// + } +} else version (CRuntime_WASI) { enum diff --git a/runtime/druntime/src/core/stdc/wchar_.d b/runtime/druntime/src/core/stdc/wchar_.d index 81af1304152..6c9fc47a073 100644 --- a/runtime/druntime/src/core/stdc/wchar_.d +++ b/runtime/druntime/src/core/stdc/wchar_.d @@ -146,6 +146,18 @@ else version (Windows) /// alias mbstate_t = __mbstate_t; } +else version (Emscripten) +{ + /// + struct __mbstate_t + { + uint __opaque1; + uint __opaque2; + } + + /// + alias mbstate_t = __mbstate_t; +} else version (CRuntime_WASI) { /// diff --git a/runtime/druntime/src/core/sys/posix/aio.d b/runtime/druntime/src/core/sys/posix/aio.d index 5a9be3c26f2..37ce24bd624 100644 --- a/runtime/druntime/src/core/sys/posix/aio.d +++ b/runtime/druntime/src/core/sys/posix/aio.d @@ -8,7 +8,7 @@ */ module core.sys.posix.aio; -version (CRuntime_WASI) {} +version (WASI) {} else: import core.stdc.config; diff --git a/runtime/druntime/src/core/sys/posix/fcntl.d b/runtime/druntime/src/core/sys/posix/fcntl.d index e110c86b3e3..b773cb115df 100644 --- a/runtime/druntime/src/core/sys/posix/fcntl.d +++ b/runtime/druntime/src/core/sys/posix/fcntl.d @@ -870,6 +870,74 @@ else version (Solaris) enum AT_REMOVEDIR = 0x1; enum AT_EACCESS = 0x4; } +else version (Emscripten) +{ + enum F_GETFD = 1; + enum F_SETFD = 2; + enum F_GETFL = 3; + enum F_SETFL = 4; + enum F_DUPFD = 0; + enum F_DUPFD_CLOEXEC = 1030; + version (D_LP64) + { + enum F_GETLK = 5; + enum F_SETLK = 6; + enum F_SETLKW = 7; + } + else + { + enum F_GETLK = 12; + enum F_SETLK = 13; + enum F_SETLKW = 14; + } + + enum F_RDLCK = 0; + enum F_WRLCK = 1; + enum F_UNLCK = 2; + + enum FD_CLOEXEC = 1; + + enum O_APPEND = 0x400; // octal 02000 + enum O_DSYNC = 0x1000; // octal 010000 + enum O_NONBLOCK = 0x800; // octal 04000 + enum O_RSYNC = 0x101000; // octal 04010000 + enum O_SYNC = 0x101000; // octal 04010000 + enum O_CREAT = 0x40; // octal 0100 + enum O_DIRECTORY = 0x10000; // octal 0200000 + enum O_EXCL = 0x80; // octal 0200 + enum O_TRUNC = 0x200; // octal 01000 + + enum O_NOFOLLOW = 0x20000; // octal 0400000 + enum O_EXEC = 0x200000; // octal 010000000 + enum O_RDONLY = 0; + enum O_SEARCH = 0x200000; // octal 010000000 + enum O_WRONLY = 1; + + enum O_CLOEXEC = 0x80000; // octal 02000000 + + enum O_TTY_INIT = 0; + + enum O_NOCTTY = 0x100; // octal 0400 + + enum O_RDWR = 2; + enum O_ACCMODE = 3 | O_SEARCH; + + struct flock + { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; + } + + enum AT_EACCESS = 0x200; + enum AT_SYMLINK_NOFOLLOW = 0x100; + enum AT_SYMLINK_FOLLOW = 0x400; + enum AT_REMOVEDIR = 0x200; + + enum AT_FDCWD = -100; +} else version (CRuntime_WASI) { enum F_GETFD = 1; diff --git a/runtime/druntime/src/core/sys/posix/netinet/in_.d b/runtime/druntime/src/core/sys/posix/netinet/in_.d index 374d762c4b3..276bddd8181 100644 --- a/runtime/druntime/src/core/sys/posix/netinet/in_.d +++ b/runtime/druntime/src/core/sys/posix/netinet/in_.d @@ -1631,15 +1631,8 @@ else version (CRuntime_WASI) ubyte[16] s6_addr; } struct sockaddr_in6 { - version (Emscripten) // no alignment override - { - sa_family_t sin6_family; - } - else - { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t sin6_family; - } + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t sin6_family; in_port_t sin6_port; uint sin6_flowinfo; diff --git a/runtime/druntime/src/core/sys/posix/poll.d b/runtime/druntime/src/core/sys/posix/poll.d index bf7594ab548..5f3d315430f 100644 --- a/runtime/druntime/src/core/sys/posix/poll.d +++ b/runtime/druntime/src/core/sys/posix/poll.d @@ -351,6 +351,22 @@ else version (Solaris) POLLNVAL = 0x0020, } } +else version (Emscripten) +{ + enum + { + POLLIN = 0x0001, + POLLPRI = 0x0002, + POLLOUT = 0x0004, + POLLRDNORM = 0x0040, + POLLRDBAND = 0x0080, + POLLWRNORM = 0x0100, + POLLWRBAND = 0x0200, + POLLERR = 0x0008, + POLLHUP = 0x0010, + POLLNVAL = 0x0020, + } +} else version (CRuntime_WASI) { enum diff --git a/runtime/druntime/src/core/sys/posix/sched.d b/runtime/druntime/src/core/sys/posix/sched.d index b5f5a3b496c..cc89f54fd8f 100644 --- a/runtime/druntime/src/core/sys/posix/sched.d +++ b/runtime/druntime/src/core/sys/posix/sched.d @@ -169,7 +169,7 @@ else version (Solaris) enum SCHED_FX = 6; enum _SCHED_NEXT = 7; } -else version (CRuntime_WASI) +else version (WASI) { struct sched_param { diff --git a/runtime/druntime/src/core/sys/posix/signal.d b/runtime/druntime/src/core/sys/posix/signal.d index 9ebf39f982f..9eed4fa8415 100644 --- a/runtime/druntime/src/core/sys/posix/signal.d +++ b/runtime/druntime/src/core/sys/posix/signal.d @@ -596,6 +596,35 @@ else version (Solaris) enum SIGUSR2 = 17; enum SIGURG = 21; } +else version (Emscripten) +{ + enum SIGHUP = 1; + //SIGINT (defined in core.stdc.signal) + enum SIGQUIT = 3; + //SIGILL (defined in core.stdc.signal) + //SIGABRT (defined in core.stdc.signal) + enum SIGIOT = SIGABRT; + enum SIGBUS = 7; + //SIGFPE (defined in core.stdc.signal) + enum SIGKILL = 9; + enum SIGUSR1 = 10; + //SIGSEGV (defined in core.stdc.signal) + enum SIGUSR2 = 12; + enum SIGPIPE = 13; + enum SIGALRM = 14; + //SIGTERM (defined in core.stdc.signal) + enum SIGSTKFLT = 16; + enum SIGCHLD = 17; + enum SIGCONT = 18; + enum SIGSTOP = 19; + enum SIGTSTP = 20; + enum SIGTTIN = 21; + enum SIGTTOU = 22; + enum SIGURG = 23; + enum SIGWINCH = 28; + enum SIGIO = 29; + enum SIGPWR = 30; +} else version (CRuntime_WASI) { enum SIGHUP = 1; @@ -912,6 +941,20 @@ else version (Darwin) int sa_flags; } } +else version (Emscripten) +{ + struct sigaction_t + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + sigset_t sa_mask; + int sa_flags; + void function() sa_restorer; + } +} else version (CRuntime_WASI) { } @@ -1496,6 +1539,8 @@ else version (Emscripten) enum SIG_UNBLOCK = 1; enum SIG_SETMASK = 2; + struct siginfo_t; // FIXME + enum { SI_ASYNCNL = -60, @@ -2501,6 +2546,17 @@ else version (Solaris) POLL_HUP, } } +else version (Emscripten) +{ + enum SIGPOLL = 29; + enum SIGPROF = 27; + enum SIGSYS = 31; + enum SIGTRAP = 5; + enum SIGVTALRM = 26; + enum SIGXCPU = 24; + enum SIGXFSZ = 25; + enum SIGUNUSED = SIGSYS; +} else version (CRuntime_WASI) { enum SIGPOLL = 29; @@ -3140,6 +3196,26 @@ else version (Solaris) int __sigev_pad2; } } +else version (Emscripten) +{ + struct sigevent + { + sigval sigev_value; + int sigev_signo; + int sigev_notify; + + union + { + byte[64 - 2 * int.sizeof - sigval.sizeof] __pad; + pid_t sigev_notify_thread_id; + struct + { + void function(sigval) sigev_notify_function; + pthread_attr_t* sigev_notify_attributes; + } + } + } +} else version (CRuntime_WASI) { } diff --git a/runtime/druntime/src/core/sys/posix/spawn.d b/runtime/druntime/src/core/sys/posix/spawn.d index a22957c0f1f..ddd9d765efc 100644 --- a/runtime/druntime/src/core/sys/posix/spawn.d +++ b/runtime/druntime/src/core/sys/posix/spawn.d @@ -9,7 +9,7 @@ */ module core.sys.posix.spawn; -version (CRuntime_WASI) {} +version (WASI) {} else: /* diff --git a/runtime/druntime/src/core/sys/posix/stdc/time.d b/runtime/druntime/src/core/sys/posix/stdc/time.d index 8adad1b2594..3278751f591 100644 --- a/runtime/druntime/src/core/sys/posix/stdc/time.d +++ b/runtime/druntime/src/core/sys/posix/stdc/time.d @@ -34,24 +34,7 @@ nothrow: @nogc: /// -version (Emscripten) -{ - struct tm - { - int tm_sec; /// seconds after the minute [0-60] - int tm_min; /// minutes after the hour [0-59] - int tm_hour; /// hours since midnight [0-23] - int tm_mday; /// day of the month [1-31] - int tm_mon; /// months since January [0-11] - int tm_year; /// years since 1900 - int tm_wday; /// days since Sunday [0-6] - int tm_yday; /// days since January 1 [0-365] - int tm_isdst; /// Daylight Savings Time flag - c_long tm_gmtoff; /// offset from CUT in seconds - char* tm_zone; /// timezone abbreviation - } -} -else version (CRuntime_WASI) +version (CRuntime_WASI) { struct tm { diff --git a/runtime/druntime/src/core/sys/posix/sys/ioctl.d b/runtime/druntime/src/core/sys/posix/sys/ioctl.d index a6b458390c7..0407df42f5e 100644 --- a/runtime/druntime/src/core/sys/posix/sys/ioctl.d +++ b/runtime/druntime/src/core/sys/posix/sys/ioctl.d @@ -400,6 +400,11 @@ else version (DragonFlyBSD) else version (Solaris) { } +else version (Emscripten) +{ + enum FIONREAD = 0x541B; + enum FIONBIO = 0x5421; +} else version (CRuntime_WASI) { enum FIONREAD = 1; diff --git a/runtime/druntime/src/core/sys/posix/sys/ipc.d b/runtime/druntime/src/core/sys/posix/sys/ipc.d index 7d1472cd40d..f11bae61b90 100644 --- a/runtime/druntime/src/core/sys/posix/sys/ipc.d +++ b/runtime/druntime/src/core/sys/posix/sys/ipc.d @@ -14,7 +14,7 @@ */ module core.sys.posix.sys.ipc; -version (CRuntime_WASI) {} +version (WASI) {} else: import core.sys.posix.config; diff --git a/runtime/druntime/src/core/sys/posix/sys/mman.d b/runtime/druntime/src/core/sys/posix/sys/mman.d index aafef576670..2f07e3dc532 100644 --- a/runtime/druntime/src/core/sys/posix/sys/mman.d +++ b/runtime/druntime/src/core/sys/posix/sys/mman.d @@ -185,6 +185,14 @@ else version (DragonFlyBSD) else version (Solaris) { } +else version (Emscripten) +{ + enum POSIX_MADV_NORMAL = 0; + enum POSIX_MADV_RANDOM = 1; + enum POSIX_MADV_SEQUENTIAL = 2; + enum POSIX_MADV_WILLNEED = 3; + enum POSIX_MADV_DONTNEED = 4; +} else version (CRuntime_WASI) { enum POSIX_MADV_NORMAL = 0; @@ -257,6 +265,13 @@ else version (Solaris) enum PROT_WRITE = 0x02; enum PROT_EXEC = 0x04; } +else version (Emscripten) +{ + enum PROT_NONE = 0x00; + enum PROT_READ = 0x01; + enum PROT_WRITE = 0x02; + enum PROT_EXEC = 0x04; +} else version (CRuntime_WASI) { enum PROT_NONE = 0x00; @@ -512,6 +527,19 @@ else version (Solaris) enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; } +else version (Emscripten) +{ + enum MAP_SHARED = 0x0001; + enum MAP_PRIVATE = 0x0002; + enum MAP_FIXED = 0x0010; + enum MAP_ANON = 0x0020; + + enum MAP_FAILED = cast(void*)-1; + + enum MS_ASYNC = 0x0001; + enum MS_INVALIDATE = 0x0002; + enum MS_SYNC = 0x0004; +} else version (CRuntime_WASI) { enum MAP_SHARED = 0x0001; @@ -645,6 +673,11 @@ else version (Solaris) enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; } +else version (Emscripten) +{ + enum MCL_CURRENT = 0x0001; + enum MCL_FUTURE = 0x0002; +} else version (CRuntime_WASI) { enum MCL_CURRENT = 0x0001; diff --git a/runtime/druntime/src/core/sys/posix/sys/resource.d b/runtime/druntime/src/core/sys/posix/sys/resource.d index b147fced256..5b1c8a8428d 100644 --- a/runtime/druntime/src/core/sys/posix/sys/resource.d +++ b/runtime/druntime/src/core/sys/posix/sys/resource.d @@ -498,6 +498,8 @@ else version (Solaris) } else version (Emscripten) { + alias rlim_t = ulong; + struct rusage { timeval ru_utime; diff --git a/runtime/druntime/src/core/sys/posix/sys/select.d b/runtime/druntime/src/core/sys/posix/sys/select.d index b3c1395829a..0459b99550a 100644 --- a/runtime/druntime/src/core/sys/posix/sys/select.d +++ b/runtime/druntime/src/core/sys/posix/sys/select.d @@ -474,7 +474,7 @@ else version (CRuntime_Musl) { enum FD_SETSIZE = 1024; - alias fd_mask = ulong; + alias fd_mask = c_ulong; private { @@ -492,7 +492,7 @@ else version (CRuntime_Musl) } struct fd_set { - ulong[FD_SETSIZE / 8 / long.sizeof] fds_bits; + c_ulong[FD_SETSIZE / 8 / c_long.sizeof] fds_bits; } extern (D) void FD_CLR()( int fd, fd_set* fdset ) pure diff --git a/runtime/druntime/src/core/sys/posix/sys/shm.d b/runtime/druntime/src/core/sys/posix/sys/shm.d index 4a3aca91a23..cdf762589c0 100644 --- a/runtime/druntime/src/core/sys/posix/sys/shm.d +++ b/runtime/druntime/src/core/sys/posix/sys/shm.d @@ -14,7 +14,7 @@ */ module core.sys.posix.sys.shm; -version (CRuntime_WASI) {} +version (WASI) {} else: import core.sys.posix.config; diff --git a/runtime/druntime/src/core/sys/posix/sys/socket.d b/runtime/druntime/src/core/sys/posix/sys/socket.d index a673f363baf..0302907c5fc 100644 --- a/runtime/druntime/src/core/sys/posix/sys/socket.d +++ b/runtime/druntime/src/core/sys/posix/sys/socket.d @@ -1711,37 +1711,19 @@ else version (CRuntime_WASI) alias socklen_t = uint; alias sa_family_t = ushort; - version (Emscripten) + struct sockaddr { - struct sockaddr - { - sa_family_t sa_family; - char[14] sa_data = 0; - } + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t sa_family; - struct sockaddr_storage - { - sa_family_t ss_family; - char[128 - c_ulong.sizeof - sa_family_t.sizeof] __ss_padding = 0; - c_ulong __ss_align; - } + byte[0] sa_data; } - else - { - struct sockaddr - { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t sa_family; - - byte[0] sa_data; - } - struct sockaddr_storage - { - align(16) // __BIGGEST_ALIGNMENT__ on Wasm - sa_family_t ss_family; - byte[32] __ss_data; - } + struct sockaddr_storage + { + align(16) // __BIGGEST_ALIGNMENT__ on Wasm + sa_family_t ss_family; + byte[32] __ss_data; } struct msghdr diff --git a/runtime/druntime/src/core/sys/posix/sys/wait.d b/runtime/druntime/src/core/sys/posix/sys/wait.d index 99d300c2ccd..2953f945d70 100644 --- a/runtime/druntime/src/core/sys/posix/sys/wait.d +++ b/runtime/druntime/src/core/sys/posix/sys/wait.d @@ -14,7 +14,7 @@ */ module core.sys.posix.sys.wait; -version (CRuntime_WASI) {} +version (WASI) {} else: import core.sys.posix.config; diff --git a/runtime/druntime/src/core/sys/posix/time.d b/runtime/druntime/src/core/sys/posix/time.d index aad88d35ece..c110bbc9c2f 100644 --- a/runtime/druntime/src/core/sys/posix/time.d +++ b/runtime/druntime/src/core/sys/posix/time.d @@ -278,6 +278,14 @@ else version (Solaris) alias timestruc_t = timespec; } +else version (Emscripten) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} else version (CRuntime_WASI) { struct timespec @@ -543,43 +551,6 @@ else version (CRuntime_Musl) int timer_settime(timer_t, int, const scope itimerspec*, itimerspec*); int timer_getoverrun(timer_t); } -else version (Emscripten) -{ - alias clockid_t = int; - alias timer_t = void*; - - struct itimerspec - { - timespec it_interval; - timespec it_value; - } - - enum TIMER_ABSTIME = 1; - - enum CLOCK_REALTIME = 0; - enum CLOCK_PROCESS_CPUTIME_ID = 2; - enum CLOCK_THREAD_CPUTIME_ID = 3; - enum CLOCK_REALTIME_COARSE = 5; - enum CLOCK_BOOTTIME = 7; - enum CLOCK_REALTIME_ALARM = 8; - enum CLOCK_BOOTTIME_ALARM = 9; - enum CLOCK_SGI_CYCLE = 10; - enum CLOCK_TAI = 11; - - int nanosleep(const scope timespec*, timespec*); - - int clock_getres(clockid_t, timespec*); - int clock_gettime(clockid_t, timespec*); - int clock_settime(clockid_t, const scope timespec*); - int clock_nanosleep(clockid_t, int, const scope timespec*, timespec*); - int clock_getcpuclockid(pid_t, clockid_t *); - - //int timer_create(clockid_t, sigevent*, timer_t*); - int timer_delete(timer_t); - int timer_gettime(timer_t, itimerspec*); - int timer_settime(timer_t, int, const scope itimerspec*, itimerspec*); - int timer_getoverrun(timer_t); -} else version (CRuntime_WASI) { alias clockid_t = const __clockid*; diff --git a/runtime/druntime/src/core/thread/osthread.d b/runtime/druntime/src/core/thread/osthread.d index 5255ef61a02..03fc8a30b96 100644 --- a/runtime/druntime/src/core/thread/osthread.d +++ b/runtime/druntime/src/core/thread/osthread.d @@ -100,7 +100,7 @@ else version (Posix) static import core.sys.posix.pthread; import core.stdc.errno : EINTR, errno; - version (CRuntime_WASI) + version (WASI) import core.sys.posix.pthread : pthread_attr_destroy, pthread_attr_getstack, pthread_attr_init, pthread_attr_setstacksize, pthread_create, pthread_detach, pthread_join, pthread_self, sched_yield; diff --git a/runtime/druntime/src/rt/sections.d b/runtime/druntime/src/rt/sections.d index 2beeb2e06f5..2c569a2526e 100644 --- a/runtime/druntime/src/rt/sections.d +++ b/runtime/druntime/src/rt/sections.d @@ -24,6 +24,8 @@ else version (WatchOS) version (GNU) public import gcc.sections; +else version (WebAssembly) + public import rt.sections_wasm; else version (CRuntime_Glibc) public import rt.sections_elf_shared; else version (CRuntime_Musl) @@ -69,8 +71,6 @@ else version (CRuntime_Bionic) public import rt.sections_elf_shared; else version (CRuntime_UClibc) public import rt.sections_elf_shared; -else version (WebAssembly) - public import rt.sections_wasm; else static assert(0, "unimplemented"); diff --git a/runtime/druntime/src/rt/sections_elf_shared.d b/runtime/druntime/src/rt/sections_elf_shared.d index 585c451a1be..a75ff3c78d3 100644 --- a/runtime/druntime/src/rt/sections_elf_shared.d +++ b/runtime/druntime/src/rt/sections_elf_shared.d @@ -19,7 +19,8 @@ else version (TVOS) else version (WatchOS) version = Darwin; -version (CRuntime_Glibc) enum SharedELF = true; +version (Emscripten) enum SharedELF = false; +else version (CRuntime_Glibc) enum SharedELF = true; else version (CRuntime_Musl) enum SharedELF = true; else version (FreeBSD) enum SharedELF = true; else version (NetBSD) enum SharedELF = true; diff --git a/runtime/druntime/test/importc_compare/src/importc_compare.d b/runtime/druntime/test/importc_compare/src/importc_compare.d index 62f0feec8e4..608fe754324 100644 --- a/runtime/druntime/test/importc_compare/src/importc_compare.d +++ b/runtime/druntime/test/importc_compare/src/importc_compare.d @@ -73,6 +73,10 @@ immutable ErrorFilter[] knownProblems = [ ErrorFilter("core.sys.posix.sys.types.pthread_mutex_t", "", "CRuntime_WASI", 0, ""), ErrorFilter("core.sys.posix.sys.types.pthread_rwlock_t", "", "CRuntime_WASI", 0, ""), ErrorFilter("core.sys.posix.sys.types.pthread_barrier_t", "", "CRuntime_WASI", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_cond_t", "", "Emscripten", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_mutex_t", "", "Emscripten", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_rwlock_t", "", "Emscripten", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_barrier_t", "", "Emscripten", 0, ""), ]; struct ErrorFilter diff --git a/runtime/phobos b/runtime/phobos index 7b79b4a1c9e..27419a0108c 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 7b79b4a1c9e6ad2164f016c7789d39bc915f6fba +Subproject commit 27419a0108c511faa13643d11af35176fce810bd diff --git a/tests/codegen/emscripten.d b/tests/codegen/emscripten.d index cf72136bc3f..d705d55cc46 100644 --- a/tests/codegen/emscripten.d +++ b/tests/codegen/emscripten.d @@ -10,7 +10,7 @@ version (Emscripten) {} else static assert(0); version (WASI) {} else static assert(0); version (WASIp1) {} else static assert(0); version (Posix) {} else static assert(0); -version (CRuntime_WASI) {} else static assert(0); +version (CRuntime_Musl) {} else static assert(0); version (CppRuntime_LLVM) {} else static assert(0); From 06c29f286d117c62fad04e7ff07b6f5e8012b5ba Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 13 Aug 2026 20:26:12 +0200 Subject: [PATCH 13/25] [CI: Try mounting host timezone DB into node process] --- .github/actions/5a-emscripten/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 418f76bdc16..d754e646d13 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -18,6 +18,7 @@ runs: bootstrap-ldc/bin/ldc-build-runtime \ --ninja \ --dFlags="-mtriple=$triple" \ + --linkerFlags="--preload-file;/usr/share/zoneinfo@/tz" \ --ldcSrcDir="$PWD/ldc" \ --installWithSuffix="-$os-$arch" \ --testrunners \ @@ -41,6 +42,7 @@ runs: set -eux cd ../ldc-build-runtime.tmp + export TZDIR=/tz ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true ../emsdk/node/*/bin/node ./phobos2-test-runner.js || true From 346bf95bbeb6ab6777d73e7bf1ec05981c6c324b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 13 Aug 2026 22:33:19 +0200 Subject: [PATCH 14/25] [finish removal of wasm-ld linker special case] --- .github/actions/4b-test-lit/action.yml | 3 +-- tests/baremetal/wasm.d | 2 +- tests/baremetal/wasm2.d | 2 +- tests/codegen/wasm.d | 2 +- tests/lit.site.cfg.in | 10 ---------- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/.github/actions/4b-test-lit/action.yml b/.github/actions/4b-test-lit/action.yml index 77ac54d1559..c8aeed7344c 100644 --- a/.github/actions/4b-test-lit/action.yml +++ b/.github/actions/4b-test-lit/action.yml @@ -23,8 +23,7 @@ runs: fi cd ../build - # temporarily add LLVM bin dir to PATH, so that e.g. wasm-ld is found - PATH="$PWD/../llvm/bin:$PATH" ctest -V -R "lit-tests" + ctest -V -R "lit-tests" - name: 'Windows: Run LIT testsuite' if: runner.os == 'Windows' diff --git a/tests/baremetal/wasm.d b/tests/baremetal/wasm.d index 7146965eb8a..6a066e9da6e 100644 --- a/tests/baremetal/wasm.d +++ b/tests/baremetal/wasm.d @@ -1,7 +1,7 @@ // Compile and link directly to naked WebAssembly. // REQUIRES: target_WebAssembly -// REQUIRES: link_WebAssembly +// REQUIRES: internal_lld // RUN: %ldc -mtriple=wasm32-unknown-unknown -w %s extern(C): // no mangling diff --git a/tests/baremetal/wasm2.d b/tests/baremetal/wasm2.d index 1d2e37ae217..ee4caf4aaae 100644 --- a/tests/baremetal/wasm2.d +++ b/tests/baremetal/wasm2.d @@ -1,7 +1,7 @@ // A more complex naked wasm example using Phobos templates. // REQUIRES: target_WebAssembly -// REQUIRES: link_WebAssembly +// REQUIRES: internal_lld // RUN: %ldc -mtriple=wasm32-unknown-unknown -w -L--export-dynamic %s -of=%t.wasm diff --git a/tests/codegen/wasm.d b/tests/codegen/wasm.d index 9c2323348c3..25f48b93395 100644 --- a/tests/codegen/wasm.d +++ b/tests/codegen/wasm.d @@ -1,4 +1,4 @@ -// REQUIRES: target_WebAssembly, link_WebAssembly +// REQUIRES: target_WebAssembly, internal_lld // emit textual IR *and* compile & link diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index ed6d98153ba..6d1a67aecd2 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -132,16 +132,6 @@ if canDoLTO: if config.ldc_with_lld: config.available_features.add('internal_lld') -# Add "link_WebAssembly" feature if we can link wasm (-link-internally or wasm-ld in PATH). -if config.ldc_with_lld: - config.available_features.add('link_WebAssembly') -else: - try: - if (subprocess.call(["wasm-ld", "--version"]) == 0): - config.available_features.add('link_WebAssembly') - except OSError: - pass - config.target_triple = '(unused)' # test_exec_root: The root path where tests should be run. From 2d81f40eed25c50185200bba6641cdbb46e20e2f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Thu, 13 Aug 2026 23:57:52 +0200 Subject: [PATCH 15/25] [add -Xcc=-nostdlib to 55-target-wasm-naked.conf] --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53da9cf9ba1..75c06c27f3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -952,7 +952,7 @@ makeConfSection(NAME "51-target-wasm-base" ${wasm_eh_switches} ) -# Add a convenience config for *naked* wasm targets, omitting druntime/Phobos +# Add a convenience config for *naked* wasm targets, omitting druntime/Phobos/libc # (and preferring built-in lld for linking) automatically. set(wasm_naked_link_internally) if(LDC_WITH_LLD) @@ -962,6 +962,7 @@ makeConfSection(NAME "55-target-wasm-naked" SECTION "^wasm(32|64)-.*-(unknown|none)" SWITCHES -defaultlib= + -Xcc=-nostdlib ${wasm_naked_link_internally} LIB_DIRS OVERRIDE ) From 7ef92df3f3273bff2cc9f9d52bce22da4ffdb52e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 14 Aug 2026 00:12:20 +0200 Subject: [PATCH 16/25] [avoid warning wrt. ignored -Xcc switches when using -link-internally] --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 75c06c27f3a..b96a47657a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -954,16 +954,18 @@ makeConfSection(NAME "51-target-wasm-base" # Add a convenience config for *naked* wasm targets, omitting druntime/Phobos/libc # (and preferring built-in lld for linking) automatically. -set(wasm_naked_link_internally) +set(wasm_naked_switches) if(LDC_WITH_LLD) - set(wasm_naked_link_internally -link-internally) + list(APPEND wasm_naked_switches -link-internally) +else() + # clang as default cross-linker driver usually works, with -nostdlib + list(APPEND wasm_naked_switches -Xcc=-nostdlib) endif() makeConfSection(NAME "55-target-wasm-naked" SECTION "^wasm(32|64)-.*-(unknown|none)" SWITCHES -defaultlib= - -Xcc=-nostdlib - ${wasm_naked_link_internally} + ${wasm_naked_switches} LIB_DIRS OVERRIDE ) From ca0e64facf68a7bd30c60fee4b0bd44673b7081f Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 14 Aug 2026 13:58:14 +0200 Subject: [PATCH 17/25] [fix core.sync.event unittest failure] --- .github/actions/5a-emscripten/action.yml | 4 ++-- runtime/druntime/src/core/sync/event.d | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index d754e646d13..4c69302b208 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -33,8 +33,8 @@ runs: set -eux cd ../ldc-build-runtime.tmp - ../emsdk/node/*/bin/node ./druntime-test-runner-debug.js || true - ../emsdk/node/*/bin/node ./druntime-test-runner.js || true + ../emsdk/node/*/bin/node ./druntime-test-runner-debug.js + ../emsdk/node/*/bin/node ./druntime-test-runner.js - name: Run phobos unittests shell: bash diff --git a/runtime/druntime/src/core/sync/event.d b/runtime/druntime/src/core/sync/event.d index 9c8cb1b0250..237ba055857 100644 --- a/runtime/druntime/src/core/sync/event.d +++ b/runtime/druntime/src/core/sync/event.d @@ -269,7 +269,12 @@ nothrow @nogc: int result = 0; if (!m_state) { - if (tmout == Duration.max) + version (Emscripten) + { + // pthread_cond_[timed]wait() seems to be a stub always returning 0 + result = -1; + } + else if (tmout == Duration.max) { result = pthread_cond_wait(&m_cond, &m_mutex); } From 4459e8d47874358188db33644621ccdb3c7af4de Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 14 Aug 2026 14:50:03 +0200 Subject: [PATCH 18/25] [try using default Posix timezone data dir] --- .github/actions/5a-emscripten/action.yml | 3 +-- runtime/phobos | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 4c69302b208..1d728273b73 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -18,7 +18,7 @@ runs: bootstrap-ldc/bin/ldc-build-runtime \ --ninja \ --dFlags="-mtriple=$triple" \ - --linkerFlags="--preload-file;/usr/share/zoneinfo@/tz" \ + --linkerFlags="--preload-file;/usr/share/zoneinfo" \ --ldcSrcDir="$PWD/ldc" \ --installWithSuffix="-$os-$arch" \ --testrunners \ @@ -42,7 +42,6 @@ runs: set -eux cd ../ldc-build-runtime.tmp - export TZDIR=/tz ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true ../emsdk/node/*/bin/node ./phobos2-test-runner.js || true diff --git a/runtime/phobos b/runtime/phobos index 27419a0108c..a26e3c03bbe 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 27419a0108c511faa13643d11af35176fce810bd +Subproject commit a26e3c03bbec90c601894e8b8f77d8af37141ba0 From 2830c3949fd87ac7c1c2342440cd4b1c295bbbf7 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 14 Aug 2026 20:15:52 +0200 Subject: [PATCH 19/25] [Phobos: Hack around lacking TZ env var support] --- .github/actions/5a-emscripten/action.yml | 4 ++-- runtime/phobos | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/5a-emscripten/action.yml b/.github/actions/5a-emscripten/action.yml index 1d728273b73..44068eedf53 100644 --- a/.github/actions/5a-emscripten/action.yml +++ b/.github/actions/5a-emscripten/action.yml @@ -42,8 +42,8 @@ runs: set -eux cd ../ldc-build-runtime.tmp - ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js || true - ../emsdk/node/*/bin/node ./phobos2-test-runner.js || true + ../emsdk/node/*/bin/node ./phobos2-test-runner-debug.js + ../emsdk/node/*/bin/node ./phobos2-test-runner.js - name: Run `importc_compare` shell: bash diff --git a/runtime/phobos b/runtime/phobos index a26e3c03bbe..bdada788953 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit a26e3c03bbec90c601894e8b8f77d8af37141ba0 +Subproject commit bdada788953c2f6f199ce8eb52303c007cf337fa From 7cbdd31559d284acc6515195c0c9ce36b9edb960 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Aug 2026 15:14:50 +0200 Subject: [PATCH 20/25] [polish] --- .github/workflows/main.yml | 2 +- runtime/druntime/src/core/stdc/errno.d | 146 +++++++++++++- runtime/druntime/src/core/stdc/fenv.d | 2 +- runtime/druntime/src/core/stdc/wctype.d | 5 +- runtime/druntime/src/core/sys/posix/poll.d | 6 +- runtime/druntime/src/core/sys/posix/sched.d | 24 ++- runtime/druntime/src/core/sys/posix/signal.d | 183 ++++++++++++++---- .../druntime/src/core/sys/posix/sys/socket.d | 14 +- .../druntime/src/core/sys/posix/sys/types.d | 105 ++++------ runtime/druntime/src/core/sys/posix/time.d | 2 + runtime/druntime/src/rt/sections_elf_shared.d | 2 +- 11 files changed, 379 insertions(+), 112 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6781de0f1b7..1400f982947 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -440,7 +440,7 @@ jobs: arch: ${{ matrix.arch }} os: ${{ matrix.os }} is_addon: true - - name: 'Smoke-test add-on package' + - name: Smoke-test add-on package run: | set -eux # unpack addon archive into ../bootstrap-ldc diff --git a/runtime/druntime/src/core/stdc/errno.d b/runtime/druntime/src/core/stdc/errno.d index 73fbc5dc577..f2575a26eef 100644 --- a/runtime/druntime/src/core/stdc/errno.d +++ b/runtime/druntime/src/core/stdc/errno.d @@ -2328,7 +2328,7 @@ else version (Haiku) enum B_NO_TRANSLATOR = (B_TRANSLATION_ERROR_BASE + 1); enum B_ILLEGAL_DATA = (B_TRANSLATION_ERROR_BASE + 2); } -else version (WASI) +else version (CRuntime_WASI) { enum E2BIG = 1; enum EACCES = 2; @@ -2410,6 +2410,150 @@ else version (WASI) enum EOPNOTSUPP = ENOTSUP; enum EWOULDBLOCK = EAGAIN; } +else version (Emscripten) +{ + // mostly WASI compatible: + enum E2BIG = 1; + enum EACCES = 2; + enum EADDRINUSE = 3; + enum EADDRNOTAVAIL = 4; + enum EAFNOSUPPORT = 5; + enum EAGAIN = 6; + enum EALREADY = 7; + enum EBADF = 8; + enum EBADMSG = 9; + enum EBUSY = 10; + enum ECANCELED = 11; + enum ECHILD = 12; + enum ECONNABORTED = 13; + enum ECONNREFUSED = 14; + enum ECONNRESET = 15; + enum EDEADLK = 16; + enum EDESTADDRREQ = 17; + enum EDOM = 18; + enum EDQUOT = 19; + enum EEXIST = 20; + enum EFAULT = 21; + enum EFBIG = 22; + enum EHOSTUNREACH = 23; + enum EIDRM = 24; + enum EILSEQ = 25; + enum EINPROGRESS = 26; + enum EINTR = 27; + enum EINVAL = 28; + enum EIO = 29; + enum EISCONN = 30; + enum EISDIR = 31; + enum ELOOP = 32; + enum EMFILE = 33; + enum EMLINK = 34; + enum EMSGSIZE = 35; + enum EMULTIHOP = 36; + enum ENAMETOOLONG = 37; + enum ENETDOWN = 38; + enum ENETRESET = 39; + enum ENETUNREACH = 40; + enum ENFILE = 41; + enum ENOBUFS = 42; + enum ENODEV = 43; + enum ENOENT = 44; + enum ENOEXEC = 45; + enum ENOLCK = 46; + enum ENOLINK = 47; + enum ENOMEM = 48; + enum ENOMSG = 49; + enum ENOPROTOOPT = 50; + enum ENOSPC = 51; + enum ENOSYS = 52; + enum ENOTCONN = 53; + enum ENOTDIR = 54; + enum ENOTEMPTY = 55; + enum ENOTRECOVERABLE = 56; + enum ENOTSOCK = 57; + // different (see below): ENOTSUP + enum ENOTTY = 59; + enum ENXIO = 60; + enum EOVERFLOW = 61; + enum EOWNERDEAD = 62; + enum EPERM = 63; + enum EPIPE = 64; + enum EPROTO = 65; + enum EPROTONOSUPPORT = 66; + enum EPROTOTYPE = 67; + enum ERANGE = 68; + enum EROFS = 69; + enum ESPIPE = 70; + enum ESRCH = 71; + enum ESTALE = 72; + enum ETIMEDOUT = 73; + enum ETXTBSY = 74; + enum EXDEV = 75; + // not defined: ENOTCAPABLE + + // extra codes: + enum ENOSTR = 100; + enum EBFONT = 101; + enum EBADSLT = 102; + enum EBADRQC = 103; + enum ENOANO = 104; + enum ENOTBLK = 105; + enum ECHRNG = 106; + enum EL3HLT = 107; + enum EL3RST = 108; + enum ELNRNG = 109; + enum EUNATCH = 110; + enum ENOCSI = 111; + enum EL2HLT = 112; + enum EBADE = 113; + enum EBADR = 114; + enum EXFULL = 115; + enum ENODATA = 116; + enum ETIME = 117; + enum ENOSR = 118; + enum ENONET = 119; + enum ENOPKG = 120; + enum EREMOTE = 121; + enum EADV = 122; + enum ESRMNT = 123; + enum ECOMM = 124; + enum EDOTDOT = 125; + enum ENOTUNIQ = 126; + enum EBADFD = 127; + enum EREMCHG = 128; + enum ELIBACC = 129; + enum ELIBBAD = 130; + enum ELIBSCN = 131; + enum ELIBMAX = 132; + enum ELIBEXEC = 133; + enum ERESTART = 134; + enum ESTRPIPE = 135; + enum EUSERS = 136; + enum ESOCKTNOSUPPORT = 137; + enum EOPNOTSUPP = 138; + enum EPFNOSUPPORT = 139; + enum ESHUTDOWN = 140; + enum ETOOMANYREFS = 141; + enum EHOSTDOWN = 142; + enum EUCLEAN = 143; + enum ENOTNAM = 144; + enum ENAVAIL = 145; + enum EISNAM = 146; + enum EREMOTEIO = 147; + enum ENOMEDIUM = 148; + enum EMEDIUMTYPE = 149; + enum ENOKEY = 150; + enum EKEYEXPIRED = 151; + enum EKEYREVOKED = 152; + enum EKEYREJECTED = 153; + enum ERFKILL = 154; + enum EHWPOISON = 155; + enum EL2NSYNC = 156; + + // musl aliases: + enum EWOULDBLOCK = EAGAIN; + enum EDEADLOCK = EDEADLK; + enum ENOTSUP = EOPNOTSUPP; +} else { static assert(false, "Unsupported platform"); diff --git a/runtime/druntime/src/core/stdc/fenv.d b/runtime/druntime/src/core/stdc/fenv.d index 68c13bc053c..e95f878cc18 100644 --- a/runtime/druntime/src/core/stdc/fenv.d +++ b/runtime/druntime/src/core/stdc/fenv.d @@ -440,7 +440,7 @@ else version (CRuntime_Musl) } alias fexcept_t = uint; } - else version (RICV64) + else version (RISCV64) { alias fenv_t = uint; alias fexcept_t = uint; diff --git a/runtime/druntime/src/core/stdc/wctype.d b/runtime/druntime/src/core/stdc/wctype.d index 2b55f3decbb..88d97cc8ebb 100644 --- a/runtime/druntime/src/core/stdc/wctype.d +++ b/runtime/druntime/src/core/stdc/wctype.d @@ -32,7 +32,10 @@ version (CRuntime_Glibc) else version (CRuntime_Musl) { /// - alias wctype_t = c_ulong; + version (Emscripten) + alias wctype_t = uint; // for wasm64 too + else + alias wctype_t = c_ulong; /// alias wctrans_t = const(int)*; } diff --git a/runtime/druntime/src/core/sys/posix/poll.d b/runtime/druntime/src/core/sys/posix/poll.d index 5f3d315430f..344c24b3509 100644 --- a/runtime/druntime/src/core/sys/posix/poll.d +++ b/runtime/druntime/src/core/sys/posix/poll.d @@ -358,13 +358,13 @@ else version (Emscripten) POLLIN = 0x0001, POLLPRI = 0x0002, POLLOUT = 0x0004, + POLLERR = 0x0008, + POLLHUP = 0x0010, + POLLNVAL = 0x0020, POLLRDNORM = 0x0040, POLLRDBAND = 0x0080, POLLWRNORM = 0x0100, POLLWRBAND = 0x0200, - POLLERR = 0x0008, - POLLHUP = 0x0010, - POLLNVAL = 0x0020, } } else version (CRuntime_WASI) diff --git a/runtime/druntime/src/core/sys/posix/sched.d b/runtime/druntime/src/core/sys/posix/sched.d index cc89f54fd8f..3dd41400cc4 100644 --- a/runtime/druntime/src/core/sys/posix/sched.d +++ b/runtime/druntime/src/core/sys/posix/sched.d @@ -169,7 +169,29 @@ else version (Solaris) enum SCHED_FX = 6; enum _SCHED_NEXT = 7; } -else version (WASI) +else version (Emscripten) +{ + struct sched_param + { + int sched_priority; + int __reserved1; + struct __timespec32 + { + time_t __reserved1; + c_long __reserved2; + } + __timespec32[2] __reserved2; + int __reserved3; + } + + enum SCHED_OTHER = 0; + enum SCHED_FIFO = 1; + enum SCHED_RR = 2; + enum SCHED_BATCH = 3; + enum SCHED_IDLE = 5; + enum SCHED_RESET_ON_FORK = 0x40000000; +} +else version (CRuntime_WASI) { struct sched_param { diff --git a/runtime/druntime/src/core/sys/posix/signal.d b/runtime/druntime/src/core/sys/posix/signal.d index 9eed4fa8415..f404f42d412 100644 --- a/runtime/druntime/src/core/sys/posix/signal.d +++ b/runtime/druntime/src/core/sys/posix/signal.d @@ -425,30 +425,6 @@ version (linux) enum SIGUSR2 = 12; enum SIGURG = 23; } - else version (Emscripten) - { - //SIGABRT (defined in core.stdc.signal) - enum SIGALRM = 14; - enum SIGBUS = 7; - enum SIGCHLD = 17; - enum SIGCONT = 18; - //SIGFPE (defined in core.stdc.signal) - enum SIGHUP = 1; - //SIGILL (defined in core.stdc.signal) - //SIGINT (defined in core.stdc.signal) - enum SIGKILL = 9; - enum SIGPIPE = 13; - enum SIGQUIT = 3; - //SIGSEGV (defined in core.stdc.signal) - enum SIGSTOP = 19; - //SIGTERM (defined in core.stdc.signal) - enum SIGTSTP = 20; - enum SIGTTIN = 21; - enum SIGTTOU = 22; - enum SIGUSR1 = 10; - enum SIGUSR2 = 12; - enum SIGURG = 23; - } else static assert(0, "unimplemented"); } @@ -1529,17 +1505,85 @@ else version (Solaris) } else version (Emscripten) { + enum SIG_HOLD = cast(sigfn_t2) 2; + struct sigset_t { c_ulong[2] __bits; - } + enum SA_NOCLDSTOP = 1; + enum SIG_BLOCK = 0; enum SIG_UNBLOCK = 1; enum SIG_SETMASK = 2; - struct siginfo_t; // FIXME + struct siginfo_t + { + int si_signo; + int si_errno; + int si_code; + + union /* __si_fields */ + { + byte[128 - 2 * int.sizeof - c_long.sizeof] __pad; + + struct /* __si_common */ + { + union /* __first */ + { + struct /* __piduid */ + { + pid_t si_pid; + uid_t si_uid; + } + struct /* __timer */ + { + int si_timerid; + int si_overrun; + } + } + union /* __second */ + { + sigval si_value; + struct /* __sigchld */ + { + int si_status; + clock_t si_utime; + clock_t si_stime; + } + } + } + + struct /* __sigfault */ + { + void* si_addr; + short si_addr_lsb; + union /* __first */ + { + struct /* __addr_bnd */ + { + void* si_lower; + void* si_upper; + } + uint si_pkey; + } + } + + struct /* __sigpoll */ + { + c_long si_band; + int si_fd; + } + + struct /* __sigsys */ + { + void* si_call_addr; + int si_syscall; + uint si_arch; + } + } + } enum { @@ -1945,16 +1989,6 @@ version (linux) enum SIGXCPU = 24; enum SIGXFSZ = 25; } - else version (Emscripten) - { - enum SIGPOLL = 29; - enum SIGPROF = 27; - enum SIGSYS = 31; - enum SIGTRAP = 5; - enum SIGVTALRM = 26; - enum SIGXCPU = 24; - enum SIGXFSZ = 25; - } else static assert(0, "unimplemented"); @@ -2556,6 +2590,79 @@ else version (Emscripten) enum SIGXCPU = 24; enum SIGXFSZ = 25; enum SIGUNUSED = SIGSYS; + + enum SA_ONSTACK = 0x08000000; + enum SA_RESETHAND = 0x80000000; + enum SA_RESTART = 0x10000000; + enum SA_SIGINFO = 4; + enum SA_NOCLDWAIT = 2; + enum SA_NODEFER = 0x40000000; + + enum SA_NOMASK = SA_NODEFER; + enum SA_ONESHOT = SA_RESETHAND; + + enum + { + ILL_ILLOPC = 1, + ILL_ILLOPN, + ILL_ILLADR, + ILL_ILLTRP, + ILL_PRVOPC, + ILL_PRVREG, + ILL_COPROC, + ILL_BADSTK + } + + enum + { + FPE_INTDIV = 1, + FPE_INTOVF, + FPE_FLTDIV, + FPE_FLTOVF, + FPE_FLTUND, + FPE_FLTRES, + FPE_FLTINV, + FPE_FLTSUB + } + + enum + { + SEGV_MAPERR = 1, + SEGV_ACCERR + } + + enum + { + BUS_ADRALN = 1, + BUS_ADRERR, + BUS_OBJERR + } + + enum + { + TRAP_BRKPT = 1, + TRAP_TRACE + } + + enum + { + CLD_EXITED = 1, + CLD_KILLED, + CLD_DUMPED, + CLD_TRAPPED, + CLD_STOPPED, + CLD_CONTINUED + } + + enum + { + POLL_IN = 1, + POLL_OUT, + POLL_MSG, + POLL_ERR, + POLL_PRI, + POLL_HUP + } } else version (CRuntime_WASI) { @@ -3204,11 +3311,11 @@ else version (Emscripten) int sigev_signo; int sigev_notify; - union + union /* __sev_fields */ { byte[64 - 2 * int.sizeof - sigval.sizeof] __pad; pid_t sigev_notify_thread_id; - struct + struct /* __sev_thread */ { void function(sigval) sigev_notify_function; pthread_attr_t* sigev_notify_attributes; diff --git a/runtime/druntime/src/core/sys/posix/sys/socket.d b/runtime/druntime/src/core/sys/posix/sys/socket.d index 0302907c5fc..5bbcbf81f3a 100644 --- a/runtime/druntime/src/core/sys/posix/sys/socket.d +++ b/runtime/druntime/src/core/sys/posix/sys/socket.d @@ -1600,19 +1600,21 @@ else version (Solaris) } else version (Emscripten) { + // note: this is only a subset of the C declarations (covering what CRuntime_WASI does below) + alias socklen_t = uint; alias sa_family_t = ushort; struct sockaddr { sa_family_t sa_family; - char[14] sa_data = 0; + byte[14] sa_data; } struct sockaddr_storage { sa_family_t ss_family; - char[128 - c_ulong.sizeof - sa_family_t.sizeof] __ss_padding = 0; + byte[128 - c_ulong.sizeof - sa_family_t.sizeof] __ss_padding; c_ulong __ss_align; } @@ -1621,9 +1623,17 @@ else version (Emscripten) void* msg_name; socklen_t msg_namelen; iovec* msg_iov; + version (D_LP64) version (BigEndian) + int __pad1; int msg_iovlen; + version (D_LP64) version (LittleEndian) + int __pad1; void* msg_control; + version (D_LP64) version (BigEndian) + int __pad2; socklen_t msg_controllen; + version (D_LP64) version (LittleEndian) + int __pad2; int msg_flags; } diff --git a/runtime/druntime/src/core/sys/posix/sys/types.d b/runtime/druntime/src/core/sys/posix/sys/types.d index bd313f11c0b..00cb777fb08 100644 --- a/runtime/druntime/src/core/sys/posix/sys/types.d +++ b/runtime/druntime/src/core/sys/posix/sys/types.d @@ -330,13 +330,13 @@ else version (Solaris) } else version (Emscripten) { - alias blkcnt_t = int; + alias blkcnt_t = int; // for wasm64 too alias ino_t = ulong; alias off_t = long; - alias blksize_t = int; + alias blksize_t = int; // for wasm64 too - alias dev_t = uint; + alias dev_t = uint; // for wasm64 too alias gid_t = uint; alias mode_t = uint; @@ -344,7 +344,7 @@ else version (Emscripten) alias pid_t = int; //size_t (defined in core.stdc.stddef) - alias ssize_t = c_long; + alias ssize_t = ptrdiff_t; alias uid_t = uint; alias time_t = long; @@ -486,10 +486,10 @@ else version (Emscripten) alias fsblkcnt_t = ulong; alias fsfilcnt_t = ulong; - alias clock_t = int; + alias clock_t = int; // for wasm64 too alias id_t = uint; alias key_t = int; - alias suseconds_t = int; + alias suseconds_t = int; // for wasm64 too alias useconds_t = uint; } else version (CRuntime_WASI) @@ -808,65 +808,29 @@ version (CRuntime_Glibc) alias pthread_t = c_ulong; } -else version (Emscripten) -{ - struct pthread_attr_t - { - union - { - int[10] __i; - uint[10] __s; - } - const(char)* _a_transferredcanvases; - } - - union pthread_cond_t - { - int[12] __i; - void*[12] __p; - } - - union pthread_mutex_t - { - int[6] __i; - void*[6] __p; - } - - union pthread_rwlock_t - { - int[8] __i; - void*[8] __p; - } - - struct pthread_rwlockattr_t - { - uint[2] __attr; - } - - alias pthread_key_t = uint; - - struct pthread_condattr_t - { - uint __attr; - } - - struct pthread_mutexattr_t - { - uint __attr; - } - - alias pthread_once_t = int; - - alias pthread_t = c_ulong; -} else version (CRuntime_Musl) { version (D_LP64) { - union pthread_attr_t + version (Emscripten) { - int[14] __i; - ulong[7] __s; + struct pthread_attr_t + { + union + { + int[10] __i; + c_ulong[10] __s; + } + const(char)* _a_transferredcanvases; + } + } + else + { + union pthread_attr_t + { + int[14] __i; + ulong[7] __s; + } } union pthread_cond_t @@ -889,10 +853,25 @@ else version (CRuntime_Musl) } else { - union pthread_attr_t + version (Emscripten) { - int[9] __i; - uint[9] __s; + struct pthread_attr_t + { + union + { + int[10] __i; + c_ulong[10] __s; + } + const(char)* _a_transferredcanvases; + } + } + else + { + union pthread_attr_t + { + int[9] __i; + uint[9] __s; + } } union pthread_cond_t diff --git a/runtime/druntime/src/core/sys/posix/time.d b/runtime/druntime/src/core/sys/posix/time.d index c110bbc9c2f..6471206adea 100644 --- a/runtime/druntime/src/core/sys/posix/time.d +++ b/runtime/druntime/src/core/sys/posix/time.d @@ -283,7 +283,9 @@ else version (Emscripten) struct timespec { time_t tv_sec; + int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER == BIG_ENDIAN); c_long tv_nsec; + int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER != BIG_ENDIAN); } } else version (CRuntime_WASI) diff --git a/runtime/druntime/src/rt/sections_elf_shared.d b/runtime/druntime/src/rt/sections_elf_shared.d index a75ff3c78d3..a3c5d810005 100644 --- a/runtime/druntime/src/rt/sections_elf_shared.d +++ b/runtime/druntime/src/rt/sections_elf_shared.d @@ -19,7 +19,7 @@ else version (TVOS) else version (WatchOS) version = Darwin; -version (Emscripten) enum SharedELF = false; +version (WebAssembly) enum SharedELF = false; else version (CRuntime_Glibc) enum SharedELF = true; else version (CRuntime_Musl) enum SharedELF = true; else version (FreeBSD) enum SharedELF = true; From 9beaff69974ed326cb694f1d67449688f31f0e41 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Aug 2026 17:58:39 +0200 Subject: [PATCH 21/25] [changelog] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6745052666..1fd727a9f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,12 @@ - `-foptimize-nothrow` is the default setting when targeting Windows or WebAssembly, to make hitting https://github.com/ldc-developers/ldc/issues/3504 much less likely. - Support for [LLVM 22](https://releases.llvm.org/22.1.0/docs/ReleaseNotes.html). The prebuilt packages use v22.1.8. (#5097, #5102, #5172) - Minimum LLVM version raised to 18. (#5094) -- DRuntime and Phobos now support [WASI](https://github.com/WebAssembly/WASI) preview levels 1 and 2, based on [`wasi-libc`](https://github.com/WebAssembly/wasi-libc). Two caveats being that WASI is strictly single-threaded (currently), and that fibers are unavailable. (#5186, #5229, #5230, #5242) +- DRuntime and Phobos now support [WASI](https://github.com/WebAssembly/WASI) preview levels 1 and 2, based on [`wasi-libc`](https://github.com/WebAssembly/wasi-libc), as well as [Emscripten](https://emscripten.org/). Two caveats being that WASI (and our Emscripten support) is strictly single-threaded (currently), and that fibers are unavailable. (#5186, #5229, #5230, #5242, #5259) - New prebuilt **addon packages** (`ldc2-*-addon-.tar.xz`) for simple cross-compilation. Simply unpack them directly into your LDC installation directory, and then cross-compile via `-mtriple=…` (or `dub --target=…`): (#5240) - Windows: `x86_64-windows-msvc`, `i686-windows-msvc`; no SDK required. - Android: `aarch64-linux-android30`, `armv7a-linux-androideabi29`, `x86_64-linux-android29`, `i686-linux-android29`; needs an [Android NDK](https://developer.android.com/ndk/downloads) (r27) and its `toolchains/llvm/prebuilt/*/bin/` dir in your PATH. - WASI: `wasm32-unknown-wasip1`, `wasm32-unknown-wasip2`; needs a [WASI SDK](https://github.com/webassembly/wasi-sdk) and its `bin/` dir in your PATH. (#5207, #5242) + - Emscripten: `wasm32-unknown-emscripten`; needs a [Emscripten SDK](https://github.com/emscripten-core/emsdk) and its `upstream/emscripten/` dir in your PATH. (#5259) - The `@ldc.attributes.restrict` UDA is now supported for dynamic-array parameters too. Builtin array ops (`c[] = a[] * b[]` etc.) use it, enabling auto-vectorization. Note that for such array ops, the dlang spec prescribes that the slice on the left and any slices on the right must not overlap (https://dlang.org/spec/arrays.html#array-operations), and this knowledge is now used by the optimizer; e.g., `x[] *= x[]` is invalid. (#5148, #5168, #5176) - DMD-style inline assembly: `asm { naked; }` is now much less of a special case wrt. codegen - such naked functions are now emitted as if `asm { naked; }` was replaced with the `@naked` function UDA. IR-wise, they are not emitted as module-level asm blobs anymore, but regular IR functions. This should lift a few former `asm { naked; }`-specific restrictions and fixed at least one LTO issue. (#5041) - **dcompute**: Added support for Native device code Embedding (PTX and SPIR-V) into the host executable's `.rodata` section. (#5140) From 33d6afbf65ec69f439410578e3b4a57bcd8831a5 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 15 Aug 2026 22:41:08 +0200 Subject: [PATCH 22/25] [polish phobos] --- runtime/phobos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/phobos b/runtime/phobos index bdada788953..4d4842eff1f 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit bdada788953c2f6f199ce8eb52303c007cf337fa +Subproject commit 4d4842eff1f50b9edd90b9a7fc470a7b2fc4e85c From aad13389338cd9b5cae1aafce06c6f81f507ba71 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Aug 2026 11:30:17 +0200 Subject: [PATCH 23/25] [fix core.stdc.limits] --- runtime/druntime/src/core/stdc/limits.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/druntime/src/core/stdc/limits.d b/runtime/druntime/src/core/stdc/limits.d index 641515399c9..466273fc3f0 100644 --- a/runtime/druntime/src/core/stdc/limits.d +++ b/runtime/druntime/src/core/stdc/limits.d @@ -188,11 +188,11 @@ else version (Emscripten) /// enum MAX_INPUT = 255; /// - enum NAME_MAX = 14; + enum NAME_MAX = 255; /// - enum PATH_MAX = 256; + enum PATH_MAX = 4096; /// - enum PIPE_BUF = 512; + enum PIPE_BUF = 4096; } else version (CRuntime_WASI) { From 1956ff6243d907226a86e05240a2ff8772af146b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Aug 2026 15:07:39 +0200 Subject: [PATCH 24/25] [fix remaining mismatching constants reported by importc_compare, except for generally broken MB_LEN_MAX] --- runtime/druntime/src/core/stdc/math.d | 12 ++++++++++-- runtime/druntime/src/core/stdc/signal.d | 12 ++++++++++-- runtime/druntime/src/core/stdc/stddef.d | 5 +++++ runtime/druntime/src/core/stdc/wchar_.d | 12 ++++++++++-- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/runtime/druntime/src/core/stdc/math.d b/runtime/druntime/src/core/stdc/math.d index 9642a5d800d..e65bc80d913 100644 --- a/runtime/druntime/src/core/stdc/math.d +++ b/runtime/druntime/src/core/stdc/math.d @@ -214,8 +214,16 @@ else enum int MATH_ERRNO = 1; /// enum int MATH_ERREXCEPT = 2; -/// -enum int math_errhandling = MATH_ERRNO | MATH_ERREXCEPT; +version (Emscripten) +{ + /// + enum int math_errhandling = 2; +} +else +{ + /// + enum int math_errhandling = MATH_ERRNO | MATH_ERREXCEPT; +} version (none) { diff --git a/runtime/druntime/src/core/stdc/signal.d b/runtime/druntime/src/core/stdc/signal.d index 6abfea47c6c..81fc2c380b1 100644 --- a/runtime/druntime/src/core/stdc/signal.d +++ b/runtime/druntime/src/core/stdc/signal.d @@ -63,8 +63,16 @@ else version (Posix) enum SIG_ERR = cast(sigfn_t) -1; /// enum SIG_DFL = cast(sigfn_t) 0; - /// - enum SIG_IGN = cast(sigfn_t) 1; + version (Emscripten) + { + /// + enum SIG_IGN = cast(sigfn_t) -2; + } + else + { + /// + enum SIG_IGN = cast(sigfn_t) 1; + } // standard C signals /// diff --git a/runtime/druntime/src/core/stdc/stddef.d b/runtime/druntime/src/core/stdc/stddef.d index a147248e48d..d2ed7b3e747 100644 --- a/runtime/druntime/src/core/stdc/stddef.d +++ b/runtime/druntime/src/core/stdc/stddef.d @@ -29,6 +29,11 @@ version (Windows) /// alias wchar_t = wchar; } +else version (Emscripten) +{ + /// + alias wchar_t = int; +} else version (Posix) { /// diff --git a/runtime/druntime/src/core/stdc/wchar_.d b/runtime/druntime/src/core/stdc/wchar_.d index 6c9fc47a073..a382a873032 100644 --- a/runtime/druntime/src/core/stdc/wchar_.d +++ b/runtime/druntime/src/core/stdc/wchar_.d @@ -179,8 +179,16 @@ else /// alias wint_t = wchar_t; -/// -enum wchar_t WEOF = 0xFFFF; +version (Emscripten) +{ + /// + enum WEOF = uint.max; +} +else +{ + /// + enum wchar_t WEOF = 0xFFFF; +} version (CRuntime_Glibc) { From e18b0553fd79558ab559e1ae8423d612157ccf6d Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sun, 16 Aug 2026 15:28:14 +0200 Subject: [PATCH 25/25] [oh, Phobos depends on wchar_t being a Unicode char type, not int] --- runtime/druntime/src/core/stdc/stddef.d | 5 ---- runtime/druntime/src/core/stdc/stdint.d | 33 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/runtime/druntime/src/core/stdc/stddef.d b/runtime/druntime/src/core/stdc/stddef.d index d2ed7b3e747..a147248e48d 100644 --- a/runtime/druntime/src/core/stdc/stddef.d +++ b/runtime/druntime/src/core/stdc/stddef.d @@ -29,11 +29,6 @@ version (Windows) /// alias wchar_t = wchar; } -else version (Emscripten) -{ - /// - alias wchar_t = int; -} else version (Posix) { /// diff --git a/runtime/druntime/src/core/stdc/stdint.d b/runtime/druntime/src/core/stdc/stdint.d index 33db7a9cec6..eccaa7cd15d 100644 --- a/runtime/druntime/src/core/stdc/stdint.d +++ b/runtime/druntime/src/core/stdc/stdint.d @@ -537,15 +537,30 @@ enum sig_atomic_t SIG_ATOMIC_MAX = sig_atomic_t.max; /// enum size_t SIZE_MAX = size_t.max; -/// -enum wchar_t WCHAR_MIN = wchar_t.min; -/// -enum wchar_t WCHAR_MAX = wchar_t.max; - -/// -enum wint_t WINT_MIN = wint_t.min; -/// -enum wint_t WINT_MAX = wint_t.max; +version (Emscripten) // wchar_t/wint_t is actually int +{ + /// + enum WCHAR_MIN = int.min; + /// + enum WCHAR_MAX = int.max; + + /// + enum WINT_MIN = int.min; + /// + enum WINT_MAX = int.max; +} +else +{ + /// + enum wchar_t WCHAR_MIN = wchar_t.min; + /// + enum wchar_t WCHAR_MAX = wchar_t.max; + + /// + enum wint_t WINT_MIN = wint_t.min; + /// + enum wint_t WINT_MAX = wint_t.max; +} /// alias INT8_C = _typify!int8_t ;