forked from KavrakiLab/vamp
-
Notifications
You must be signed in to change notification settings - Fork 0
wip: wasm init from codex #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
hashb
wants to merge
8
commits into
main
Choose a base branch
from
kautilya/wasm
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Conversation
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
Prompt: Below is a single, self‑contained prompt you can paste into Codex CLI. It tells the agent exactly what to do to implement Plan B: a native WebAssembly SIMD backend for VAMP, wire it into the build, and prove it works with a small smoke test. --- **Codex CLI prompt** You are working in my fork of [https://github.com/KavrakiLab/vamp](https://github.com/KavrakiLab/vamp). Implement a native WebAssembly SIMD backend and make the project build with Emscripten. Use the steps and acceptance criteria below. Do not ask me questions. Make reasonable assumptions and iterate until the build is green. ### Goal Add a `vector/wasm.hh` backend using `wasm_simd128.h`, enable it automatically when building under Emscripten with SIMD, then produce a minimal Node runnable smoke test that exercises the vector layer through at least one top level VAMP function that uses the vector backend on hot paths. Commit your changes as a single PR. ### Constraints * No Python bindings for this target. C++ only. * Use native Wasm SIMD intrinsics from `<wasm_simd128.h>`. Do not use emulation unless the compiler lacks SIMD support. * Keep the public vector API identical to the existing AVX and NEON backends so the rest of the code compiles unchanged. * If some ops in AVX/NEON are not used anywhere, you do not have to implement them now. Implement what the build requires, then add any obvious missing basics. * Keep exception behavior the same as in other backends. * Coding style, license headers, and namespaces must match the project. ### High level plan 1. Discover the vector abstraction: * Find where the vector backend is selected and how the interface looks. * Run: `git grep -n "vector/avx"`, `git grep -n "vector/neon"`, `git grep -n "namespace .*vector"`, and `git grep -n "VAMP_USE_AVX\|VAMP_USE_NEON"`. * Open `vector.hh`, `vector/avx.hh`, `vector/neon.hh` or their actual locations. Identify: * Core float vector type (likely 8-wide), mask type, and helper functions. * Required ops: load, store, broadcast, add, sub, mul, min, max, abs, fmadd or muladd, comparisons, select, bitwise and/or/xor/not, horizontal any/all, a testz-like mask intersection check, and any shuffle or permute used by collision checking or FK. 2. Create `src/impl/vamp/vector/wasm.hh` with the exact same interface used by AVX and NEON, implemented with Wasm intrinsics. * Include guard and the same namespace block used by the other backends. * Use `#include <wasm_simd128.h>`. * If the project assumes an 8-lane float vector, implement it as two `v128_t` registers: ```c++ struct f32x8 { v128_t lo, hi; }; struct mask8 { v128_t lo, hi; }; ``` * Implement the minimal but sufficient set of ops. Examples for style and naming, adjust to match the project interface: ```c++ // Constructors and memory static inline f32x8 load(const float* p) { return { wasm_v128_load(p), wasm_v128_load(p + 4) }; } static inline void store(float* p, f32x8 a) { wasm_v128_store(p, a.lo); wasm_v128_store(p + 4, a.hi); } static inline f32x8 splat(float x) { v128_t v = wasm_f32x4_splat(x); return { v, v }; } // Arithmetic inline f32x8 operator+(f32x8 a, f32x8 b) { return { wasm_f32x4_add(a.lo,b.lo), wasm_f32x4_add(a.hi,b.hi) }; } inline f32x8 operator-(f32x8 a, f32x8 b) { return { wasm_f32x4_sub(a.lo,b.lo), wasm_f32x4_sub(a.hi,b.hi) }; } inline f32x8 operator*(f32x8 a, f32x8 b) { return { wasm_f32x4_mul(a.lo,b.lo), wasm_f32x4_mul(a.hi,b.hi) }; } static inline f32x8 min(f32x8 a, f32x8 b) { return { wasm_f32x4_min(a.lo,b.lo), wasm_f32x4_min(a.hi,b.hi) }; } static inline f32x8 max(f32x8 a, f32x8 b) { return { wasm_f32x4_max(a.lo,b.lo), wasm_f32x4_max(a.hi,b.hi) }; } static inline f32x8 fmadd(f32x8 a, f32x8 b, f32x8 c) { // Wasm SIMD lacks fused op. Use a*b + c. return { wasm_f32x4_add(wasm_f32x4_mul(a.lo,b.lo), c.lo), wasm_f32x4_add(wasm_f32x4_mul(a.hi,b.hi), c.hi) }; } static inline f32x8 absval(f32x8 a) { v128_t mask = wasm_i32x4_splat(0x7fffffff); return { wasm_v128_and(a.lo, mask), wasm_v128_and(a.hi, mask) }; } // Comparisons to mask static inline mask8 cmplt(f32x8 a, f32x8 b) { return { wasm_f32x4_lt(a.lo,b.lo), wasm_f32x4_lt(a.hi,b.hi) }; } // Similarly for cmple, cmpgt, cmpge, cmpeq, cmpne // Bitwise and select static inline mask8 band(mask8 a, mask8 b) { return { wasm_v128_and(a.lo,b.lo), wasm_v128_and(a.hi,b.hi) }; } static inline mask8 bor(mask8 a, mask8 b) { return { wasm_v128_or(a.lo,b.lo), wasm_v128_or(a.hi,b.hi) }; } static inline mask8 bxor(mask8 a, mask8 b) { return { wasm_v128_xor(a.lo,b.lo), wasm_v128_xor(a.hi,b.hi) }; } static inline mask8 bnot(mask8 a) { v128_t all1 = wasm_i32x4_splat(-1); return { wasm_v128_xor(a.lo, all1), wasm_v128_xor(a.hi, all1) }; } static inline f32x8 select(mask8 m, f32x8 t, f32x8 f) { return { wasm_v128_bitselect(t.lo, f.lo, m.lo), wasm_v128_bitselect(t.hi, f.hi, m.hi) }; } // Horizontal queries static inline bool any(mask8 m) { return (wasm_i32x4_bitmask(m.lo) | wasm_i32x4_bitmask(m.hi)) != 0; } static inline bool all(mask8 m) { return (wasm_i32x4_bitmask(m.lo) == 0xF) && (wasm_i32x4_bitmask(m.hi) == 0xF); } static inline bool testz(mask8 a, mask8 b) { // like _mm256_testz_si256 v128_t lo = wasm_v128_and(a.lo, b.lo); v128_t hi = wasm_v128_and(a.hi, b.hi); return ((wasm_i32x4_bitmask(lo) | wasm_i32x4_bitmask(hi)) == 0); } ``` * If the project exposes integer vector helpers used as masks, add the minimal set based on i32x4 ops. Add shuffles only when compile errors indicate you need them. * Feature guard at top: ```c++ #if !(defined(__EMSCRIPTEN__) && defined(__wasm_simd128__)) #error "Wasm SIMD backend requires Emscripten with -msimd128" #endif ``` 3. Teach backend selection to pick Wasm when building with Emscripten and SIMD. * Edit the central selector, commonly `src/impl/vamp/vector/vector.hh` or similar. * Add: ```c++ #if defined(__EMSCRIPTEN__) && defined(__wasm_simd128__) #define VAMP_USE_WASM_SIMD 1 #endif ``` * Then include your new header in the same style as AVX or NEON: ```c++ #if VAMP_USE_WASM_SIMD #include "src/impl/vamp/vector/wasm.hh" #elif VAMP_USE_AVX #include "src/impl/vamp/vector/avx.hh" #elif VAMP_USE_NEON #include "src/impl/vamp/vector/neon.hh" #else #include "src/impl/vamp/vector/scalar.hh" #endif ``` Use the actual paths and macro names that exist. If there is no scalar fallback in the project, leave that branch out. 4. Update CMake to enable SIMD for Emscripten and disable Python. * In the top-level `CMakeLists.txt` or the place toolchain flags are set, add: ```cmake if (EMSCRIPTEN) add_compile_options(-msimd128 -mrelaxed-simd) add_link_options(-msimd128 -mrelaxed-simd) # Turn off Python extension modules for wasm set(VAMP_BUILD_PYTHON OFF CACHE BOOL "" FORCE) add_definitions(-DVAMP_USE_WASM_SIMD=1) endif() ``` * If the project uses feature checks, add a small test to verify `__wasm_simd128__` is defined. 5. Make RNG safe on Emscripten. * Search for xorshift or other x86 specific RNG code: `git grep -n "xorshift"`, `git grep -n "rng"`. * If there is an AVX specific RNG path, compile it out for Emscripten and fall back to the scalar or Halton sequence path already used on non-AVX builds. Use `#if !defined(__EMSCRIPTEN__)` guards where necessary. 6. Add a tiny browser-agnostic C entry point for testing. * Create `src/impl/vamp/bindings/wasm_smoke.cc` with a minimal C API that forces use of a path that exercises vector ops. For example, a function that computes a small piece of vector math, or even better, calls into any collision check or FK routine that already uses the vector layer. Expose one function with `extern "C"` and `EMSCRIPTEN_KEEPALIVE`. * Keep it header only usage from the existing library. Do not introduce third party deps. 7. Build for Node and run a smoke test. * Create a build dir and compile with Emscripten: ``` emcmake cmake -S . -B build-wasm -DCMAKE_BUILD_TYPE=Release -DVAMP_BUILD_PYTHON=OFF cmake --build build-wasm -j ``` * If you built a static lib, link the smoke binding into a runnable Node module: ``` em++ -O3 -msimd128 -mrelaxed-simd \ -s WASM=1 -s MODULARIZE=1 -s ENVIRONMENT=node \ -s EXPORTED_FUNCTIONS='["_vamp_wasm_smoke"]' \ -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \ -o build-wasm/vamp_smoke.mjs \ build-wasm/path/to/libvamp_core.a \ src/impl/vamp/bindings/wasm_smoke.cc ``` * Add `scripts/wasm_smoke.js` that imports the module, calls the function, and prints a simple “OK” plus a numeric checksum over the output so we can see it is not all zeros. 8. Iterate until it compiles. When the compiler yells about a missing vector op, implement it in `wasm.hh` in the same style. Keep the implementation minimal but correct. 9. Documentation. * Append a short “WebAssembly build” section to the README that lists one command block with `emcmake cmake` and `em++` flags, and states that Wasm SIMD is required. 10. Commit and open a PR. * Use clear, single-purpose commits. Suggested messages: * `vector: add wasm_simd128 backend` * `cmake: enable -msimd128 for Emscripten and select wasm backend` * `bindings: add wasm smoke test` * `docs: add Wasm build instructions` ### Acceptance criteria * Building with Emscripten in Release config succeeds on a clean checkout using the commands above. * The vector backend selected at compile time for Emscripten is the new Wasm SIMD backend, not AVX or NEON. * `scripts/wasm_smoke.js` runs under Node and prints “OK” with a stable checksum. * No changes are required to higher level VAMP code to adopt the backend. * CI or a local run proves the normal native build is still green. ### Deliverables * `src/impl/vamp/vector/wasm.hh` with a complete minimal implementation. * Selector changes that pick Wasm SIMD on Emscripten. * CMake changes that pass `-msimd128 -mrelaxed-simd`, disable Python, and define the backend macro. * A tiny `wasm_smoke.cc` entry point and `scripts/wasm_smoke.js` harness. * README update. * A PR link or a printed diff summary of all changed files. Start now.
emcmake cmake -S . -B build-wasm \ kautilya/wasm! -DCMAKE_BUILD_TYPE=Release \ -DVAMP_BUILD_PYTHON_BINDINGS=OFF \ -DVAMP_ARCH=wasm32 \ -DEigen3_DIR="$(brew --prefix eigen)/share/eigen3/cmake"
we are missing implementation of some SIMDVector methods like sin, cos log etc
emcmake cmake -S . -B build-wasm \ kautilya/wasm!? -DCMAKE_BUILD_TYPE=Release \ -DVAMP_BUILD_PYTHON_BINDINGS=OFF \ -DVAMP_ARCH=wasm32 \ -DEigen3_DIR="$(brew --prefix eigen)/share/eigen3/cmake" cmake --build build-wasm -j20 node --experimental-wasm-relaxed-simd scripts/wasm_planning.js
- Remove redundant operator+ overload for wasm_f32x4 (inconsistent with design pattern) - Add missing min() function to wasm_f32x4 (matches max() and used by clamp()) - Add missing rsqrt() function to wasm_f32x4 (reciprocal square root, present in other backends) These changes bring the WASM implementation in line with AVX and NEON backends. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Sculptor <sculptor@imbue.com>
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.
Prompt:
Below is a single, self‑contained prompt you can paste into Codex CLI. It tells the agent exactly what to do to implement Plan B: a native WebAssembly SIMD backend for VAMP, wire it into the build, and prove it works with a small smoke test.
Codex CLI prompt
You are working in my fork of https://github.com/KavrakiLab/vamp. Implement a native WebAssembly SIMD backend and make the project build with Emscripten. Use the steps and acceptance criteria below. Do not ask me questions. Make reasonable assumptions and iterate until the build is green.
Goal
Add a
vector/wasm.hhbackend usingwasm_simd128.h, enable it automatically when building under Emscripten with SIMD, then produce a minimal Node runnable smoke test that exercises the vector layer through at least one top level VAMP function that uses the vector backend on hot paths. Commit your changes as a single PR.Constraints
<wasm_simd128.h>. Do not use emulation unless the compiler lacks SIMD support.High level plan
Find where the vector backend is selected and how the interface looks.
git grep -n "vector/avx",git grep -n "vector/neon",git grep -n "namespace .*vector", andgit grep -n "VAMP_USE_AVX\|VAMP_USE_NEON".Open
vector.hh,vector/avx.hh,vector/neon.hhor their actual locations. Identify:src/impl/vamp/vector/wasm.hhwith the exact same interface used by AVX and NEON, implemented with Wasm intrinsics.Include guard and the same namespace block used by the other backends.
Use
#include <wasm_simd128.h>.If the project assumes an 8-lane float vector, implement it as two
v128_tregisters:c++ struct f32x8 { v128_t lo, hi; }; struct mask8 { v128_t lo, hi; };Implement the minimal but sufficient set of ops. Examples for style and naming, adjust to match the project interface:
If the project exposes integer vector helpers used as masks, add the minimal set based on i32x4 ops. Add shuffles only when compile errors indicate you need them.
Feature guard at top:
c++ #if !(defined(__EMSCRIPTEN__) && defined(__wasm_simd128__)) #error "Wasm SIMD backend requires Emscripten with -msimd128" #endifEdit the central selector, commonly
src/impl/vamp/vector/vector.hhor similar.Add:
c++ #if defined(__EMSCRIPTEN__) && defined(__wasm_simd128__) #define VAMP_USE_WASM_SIMD 1 #endifThen include your new header in the same style as AVX or NEON:
c++ #if VAMP_USE_WASM_SIMD #include "src/impl/vamp/vector/wasm.hh" #elif VAMP_USE_AVX #include "src/impl/vamp/vector/avx.hh" #elif VAMP_USE_NEON #include "src/impl/vamp/vector/neon.hh" #else #include "src/impl/vamp/vector/scalar.hh" #endifUse the actual paths and macro names that exist. If there is no scalar fallback in the project, leave that branch out.
In the top-level
CMakeLists.txtor the place toolchain flags are set, add:If the project uses feature checks, add a small test to verify
__wasm_simd128__is defined.git grep -n "xorshift",git grep -n "rng".#if !defined(__EMSCRIPTEN__)guards where necessary.src/impl/vamp/bindings/wasm_smoke.ccwith a minimal C API that forces use of a path that exercises vector ops. For example, a function that computes a small piece of vector math, or even better, calls into any collision check or FK routine that already uses the vector layer. Expose one function withextern "C"andEMSCRIPTEN_KEEPALIVE.Create a build dir and compile with Emscripten:
If you built a static lib, link the smoke binding into a runnable Node module:
Add
scripts/wasm_smoke.jsthat imports the module, calls the function, and prints a simple “OK” plus a numeric checksum over the output so we can see it is not all zeros.Iterate until it compiles. When the compiler yells about a missing vector op, implement it in
wasm.hhin the same style. Keep the implementation minimal but correct.Documentation.
emcmake cmakeandem++flags, and states that Wasm SIMD is required.Use clear, single-purpose commits. Suggested messages:
vector: add wasm_simd128 backendcmake: enable -msimd128 for Emscripten and select wasm backendbindings: add wasm smoke testdocs: add Wasm build instructionsAcceptance criteria
scripts/wasm_smoke.jsruns under Node and prints “OK” with a stable checksum.Deliverables
src/impl/vamp/vector/wasm.hhwith a complete minimal implementation.-msimd128 -mrelaxed-simd, disable Python, and define the backend macro.wasm_smoke.ccentry point andscripts/wasm_smoke.jsharness.Start now.