vecmath: native WebAssembly SIMD128 backend - #4052
Merged
Merged
Conversation
… web build ran the SSE2 arm through emscripten's xmmintrin compat layer, where float->int conversion is a per-lane lrint loop (v_cvti_vec4i 86 instructions, v_cvt_roundi 170, v_floor 91) and min/max, select, abs and mul+add each cost two to four ops; the new arm answers with the ISA's own instructions (trunc_sat, floor/nearest, pmin/pmax with swapped operands for the SSE NaN and signed-zero rule, bitselect, f32x4.relaxed_madd for v_madd/v_nmsub under -mrelaxed-simd). vec4f/vec4i are clang typed vectors so the overload set stays distinct, selected on __wasm_simd128__ ahead of the __SSE2__ the compat layer predefines; daScriptC.h spells the same typedefs for C. Converts saturate and _x forms are the packed op (the NEON contract), everything else keeps the SSE rows. web/CMakeLists.txt and the dasImgui wasm archives drop -msse2 and gain DAS_WASM_RELAXED_SIMD / DAS_IMGUI_WASM_RELAXED_SIMD (default ON; an engine without relaxed SIMD refuses the module - Safari only behind a flag). Under emscripten the backend battery builds as node-runnable pairs under web/output/tests and the wasm_build lane runs both arms. Interpreter under node: the float->int/floor/round loop 26.4 -> 11.9 ms, n-bodies and the other profile samples unchanged within noise. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…_IMGUI_WASM_RELAXED_SIMD default OFF and cite the same ruling the JIT's feature string follows (modules/dasLLVM/ARCHITECTURE_TARGET_FEATURES.md sec.12) - WebKit implements none of the relaxed opcodes, one anywhere in the module makes Safari refuse it at validation, and the browser builds ship to every engine. The module now carries no relaxed opcode; the backend battery, the language suite under node and the converted-op timings (float->int/floor/round loop 11.8 ms) are unchanged, so the fused madd was buying nothing measurable in the interpreter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ompresses to pointers at the wasm feature string section, two restatements of dag_vecMath_wasm.h's own header block go, the relaxed-SIMD default and the web/dasImgui flag mirror become REVIEW.md rules, include/vecmath/CLAUDE.md says the folder is authored here for upstream and keeps the sibling backends' comment shape, the emscripten test block no longer names the native arm when the scalar backend is forced, and preflight.md's wasm_build row carries the node battery Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…asm.h joins the installed vecmath header set, the dasAudio convolution reverb takes its SIMD path on wasm and dasStbImage stops forcing its scalar paths there (both gated on the -msse2 the arc removed), the two test arms keep the memory64 and pthread link flags their objects were compiled with, the battery gains rows for the saturating converts, the pshufb key mask, the 64-bit shifts, mulhi16, cvt_byte, perm_zayx, the byte interleave, mat_43cu_from_mat44 and v_check_xz_all_true, the arm's reciprocal forms and is_neg_special delegate instead of repeating a body and the pair ops spell their lane fold through v_perm_xzac/ywbd, the vecmath notes correct the v_sel trap (every backend reads the sign bit; v_btsel is the per-bit select) and state the backend comment shape without an exemption, the hygiene skill's vendoring clause gains its outbound direction, the wasm_build lane runs the language suite before the battery, and the web, dasImgui, tests-cpp and workflows checklists carry the dragon's rewordings Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…uments: the workflows checklist's preflight duty splits into the row rule and the section rule (build.yml carries one section per job, not per matrix arm), the dasImgui checklist gains the relaxed-SIMD ban its web twin already had and both mirror rules key on the -m flag list alone so a default flip has one verdict, the vecmath notes say which comments travel upstream, and the hygiene skill's outbound clause is its own rule without a folder census Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Vecmath README updates are missing the -mnontrapping-fptoint requirement even though the wasm backend depends on trunc_sat conversions enabled by that feature.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a native WebAssembly SIMD128 backend for vecmath (replacing reliance on Emscripten’s SSE2 compatibility layer), updates wasm build flags accordingly, and extends CI/tests/docs to validate and explain the new wasm SIMD behavior (notably saturating float→int conversions on web builds).
Changes:
- Implement
include/vecmath/dag_vecMath_wasm.hand wire_TARGET_SIMD_WASMauto-detect +vec4dsupport for wasm. - Drop
-msse2fromweb/and dasImgui wasm builds; add an opt-in-mrelaxed-simdknob (default OFF for Safari compatibility). - Extend vecmath backend battery + wasm CI lane to run wasm-native and scalar arms under Node.
File summaries
| File | Description |
|---|---|
| web/REVIEW.md | Adds checklist constraints around relaxed-SIMD defaults and keeping wasm feature flags in sync with dasImgui. |
| web/CMakeLists.txt | Removes -msse2, adds DAS_WASM_RELAXED_SIMD option and -mrelaxed-simd gating. |
| tests-cpp/REVIEW.md | Clarifies “big” checklist applicability based on ctest labels rather than local CMake ownership. |
| tests-cpp/big/vecmath_backend/test_vecmath_backend.cpp | Updates backend battery semantics/guards for wasm (packed _x ops + saturating converts) and adds new pinned rows. |
| tests-cpp/big/vecmath_backend/CMakeLists.txt | Ensures wasm builds explicitly build/link the vecmath test arms into web/output/tests with appropriate link options. |
| skills/internal/preflight.md | Updates documentation of wasm_build.yml to include the Node-run language suite + vecmath battery. |
| skills/comment_style_hygiene.md | Adds a rule about preserving upstream comment/shape conventions for upstream-contributed code. |
| modules/dasStbImage/CMakeLists.txt | Removes wasm-only scalar-forcing defines now that -msse2 is no longer used on web builds. |
| modules/dasImgui/REVIEW.md | Mirrors relaxed-SIMD “default OFF” constraints and cross-folder flag sync requirements. |
| modules/dasImgui/CMakeLists.txt | Drops -msse2 and adds opt-in relaxed-SIMD flag for wasm archives. |
| modules/dasAudio/src/convolution_reverb.h | Enables wasm SIMD path via _TARGET_SIMD_WASM gating. |
| include/vecmath/usage.md | Updates vecmath usage docs to include wasm SIMD128 among supported backends. |
| include/vecmath/README.md | Updates high-level README to include wasm SIMD128 in supported targets and describes wasm flags (see review comment re: -mnontrapping-fptoint). |
| include/vecmath/dag_vecMathDecl.h | Adds _TARGET_SIMD_WASM auto-detect and wasm typed-vector definitions for vec4f/vec4i/vec4d. |
| include/vecmath/dag_vecMath.h | Includes the new wasm backend header when _TARGET_SIMD_WASM is selected. |
| include/vecmath/dag_vecMath_wasm.h | New wasm SIMD128 backend implementing vecmath primitive contract (including saturating float→int). |
| include/vecmath/dag_vecMath_double.h | Adds wasm SIMD128 implementation for the vec4d layer. |
| include/vecmath/dag_vecMath_const.h | Extends constant definitions to cover the wasm backend. |
| include/vecmath/dag_vecMath_common.h | Aligns shared helpers to treat wasm like SSE where semantics match. |
| include/vecmath/CLAUDE.md | Updates module overview and backend contract notes to include wasm. |
| include/daScript/daScriptC.h | Mirrors wasm target auto-detect and defines wasm vec4f/vec4i types for the C API. |
| CMakeLists.txt | Adds the new vecmath wasm header to the build’s vecmath source list. |
| .github/workflows/wasm_build.yml | Builds vecmath test arms explicitly and runs vecmath battery + language suite under Node. |
| .github/workflows/REVIEW.md | Tightens workflow-doc update requirements (row + per-workflow section in skills/internal/preflight.md). |
Review details
- Files reviewed: 24/24 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
borisbat
added a commit
that referenced
this pull request
Sep 17, 2026
…RC2: the bundle smoke compiles the DAP bridge, the watchdog's interpreter form, the LLVM-AOT driver and the LSP subtools, checks the static watchdog is present and answers --help and --lsp from the bundle, and launches the DAP bridge on an empty stdin; the release audit's utils phase gains rows for the DAP bridge's argument gate (a row now carries the exit code it expects) and both watchdog forms, and its telegram rows - stale since the echo-bot moved to its package repo at RC1 - become the crash example, whose native dascrash package installs from a local path and so proves the bundle's C++ build kit offline. The daslang driver no longer answers -h/--help placed after the -- separator, which stole every interpreted clargs tool's help (tests/fio pins it). The audit's per-file timeout is 180 s: a dasLLAMA example compiles cold in about 40 s alone and the 60 s cap tripped under a full worker set; examples/dasLLAMA/library/main.das is ruled integration-scaffold tier. CHANGELIST gains #4046 and #4052. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Behavior change: on the web build
int(x)of a float outside the 32-bit range now saturates (NaN gives 0) instead of answering INT_MIN - the NEON contract; nothing to do unless a script relied on the sentinel.Why. The web build ran vecmath's SSE2 arm through emscripten's
-msse2compat layer. There a float-to-int conversion is a per-lanelrintloop (86 wasm instructions forv_cvti_vec4i, 170 forv_cvt_roundi, 91 forv_floor), and min, max, select, abs and mul+add each cost two to four ops.What changes.
include/vecmath/dag_vecMath_wasm.himplements the backend contract on wasm SIMD128:trunc_sat,floor/nearest,pmin/pmaxwith swapped operands (the SSE NaN and signed-zero rule),bitselect, onei8x16.shuffleper permutation.vec4f/vec4iare clang typed vectors, selected on__wasm_simd128__ahead of the__SSE2__the compat layer predefines;daScriptC.hspells the same types for C.web/and the dasImgui wasm archives drop-msse2; relaxed SIMD is an opt-in knob, OFF because Safari refuses any relaxed opcode.wasm_buildlane builds both arms and runs them under node.-msse2.Observable behavior.
int(3e9)on the web build: INT_MIN -> INT_MAX (saturating, as on NEON)daslang_static.wasmcarries no relaxed opcode before or afterWhere to look. The operand swap in
v_min/v_max, the sign-bit shift inv_sel, the shift-count guards, andvd_cvt_to_vec4i's lane join indag_vecMath_double.h.Validation, claims, ledger
Validation
.github/workflows/wasm_build.ymljobwasm_build(the nativectest -L smallbuilds the same file withEXPECT_WASMundefined). Ran on Windows, one of the three matrix cells, inweb/cmake_temp:ninja test_vecmath_native test_vecmath_scalar, then fromweb/:"$EMSDK_NODE" --experimental-wasm-exnref output/tests/test_vecmath_native.jsand..._scalar.js- bothall vecmath backend checks passed, exit 0;"$EMSDK_NODE" --experimental-wasm-exnref test/dastest_wasm.js ../ ./output- 1696 passed, 0 failed.EMSDK_PYTHONpointed at python 3.13 (the emsdk-bundled 3.9 is below emscripten's floor). Linux and macOS cells: the lane's run on the head commit.ctest -C Release -L small150 passed (includesvecmath_nativeandvecmath_scalar, rerun after the new rows),dastest --test tests/language1758 passed. The preflighttestslane selected nothing (no.daschanged) and the full preflight chain was not run: the diff reaches only the wasm lane and the vecmath headers, whose x86 and NEON arms gain preprocessor gates and no code.llvm-objdump -d daslang_static.wasm | grep -c relaxedis 0 for both the baseline and this branch.Claims - stated, not tested
libDasModuleAudio/libDasModuleStbImageon thewasm_buildlane or a wrong reverb/decode result in the playground.DAS_WASM_MEMORY64=ON/DAS_WASM_PTHREADS=ONbuilds of the two test arms link: the link option list carries-sMEMORY64=2/-pthreadfrom the same knobs, not built here.v_madd/v_nmsub(-mrelaxed-simd, opt-in) compiles: built by hand once for the instruction-count probe, no lane builds it.Not done
dag_vecMath_wasm.hto DagorEngineprog/1stPartyLibs/vecmathafter this lands.__builtin_shufflevectorperm block with the NEON arm, moving thev_hmin/v_check_*/v_roundfamily intodag_vecMath_common.h, and thevd_dot*one-liners into the double header's shared tail - each touches the other backends' arms, which go upstream as they are.dag_vecMathDecl.handdaScriptC.h(pre-existing; a shared C-compatible header is the fold).web/has noARCHITECTURE.md; itsREADME.mdcannot take anchored facts, soweb/CMakeLists.txt's why-comments stay comments.+simd128,+nontrapping-fptoint) is unchanged; JIT-emitted code keeps mul+add, matching the runtime archive.