Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# AGENTS.md

This file provides guidance to AI coding agents working with code in this repository.

## Project background

FloatWeaver is a header-mostly C++17 library for writing CUDA-style SIMD code that runs on the CPU: you write scalar-looking code against `VectorScalar<T, Width>` types and it compiles down to SSE/AVX/AVX512/NEON instructions (or a portable scalar fallback), with Eigen integration so `Eigen::Matrix<weaver::float4, 3, 1>` etc. work out of the box.

**Status**: the core SIMD types (bool/float/int × 4/8/16, ref/SSE/AVX/AVX512/NEON backends) and the Eigen adapter are implemented and have baseline Catch2 tests. Not yet done: examples, broader test coverage, user-facing documentation, and benchmarks — treat these as open work, not as things that don't belong here. There is also an open, undecided question about whether some standalone high-speed SVD tooling should live in this repo or a separate one under the same org — don't assume either answer without asking.

## Build & test commands

Dependencies (Eigen3, Catch2) are managed via Conan 2; do a full from-scratch build with:

```bash
conan install --build=missing -of build .
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=build/build/Release/generators/conan_toolchain.cmake
cmake --build build --config Release
```

(On Windows/multi-config generators the generators dir is `build/build/generators` instead — see `.github/workflows/build.yml` for the exact matrix.)

Run the whole test suite:

```bash
ctest --test-dir build --build-config Release
```

Run a single test / tag with Catch2 directly (faster than going through ctest), e.g. only float tests, or one named test case:

```bash
build/weaver_tests "[float]"
build/weaver_tests "Sqrt FloatN"
```

The vectorization backend used for the build is a CMake cache variable, not autodetected:

```bash
cmake -S . -B build -DWEAVER_VECTORIZE="AVX 2" # one of: Generic, SSE 4.2, AVX, AVX 2, AVX 512, NEON
```

This sets `WEAVER_VECTORIZE_*` compile definitions (see `cmake/config.cmake`) which in turn drive which per-ISA headers get pulled in (see Architecture below). When adding a new SIMD feature, it needs to be exercised under more than one backend to be considered validated — CI (`.github/workflows/build.yml`) builds gcc/clang/MSVC/emscripten across the matrix, but locally only one backend is active per configure.

## Architecture

**File naming = one axis per file.** Each SIMD type is split as `{category}{width}_{isa}.h`, e.g. `float8_avx.h`, `bool16_avx512.h`, `int4_neon.h`, plus a `*_ref.h` scalar-fallback variant for every width. There's no single "generic float vector" header — instead `include/weaver/eigen.h` (and equally `vector_types.h`'s consumers) `#if`-chains on `WEAVER_VECTORIZE_AVX512` / `_AVX` / `_SSE` / `_NEON` to pick exactly one full set of backend headers, falling back to `*_ref.h` for Generic builds. When adding a new op, it must be implemented consistently across all five backends (ref/SSE/AVX/AVX512/NEON) for a given width — a partial implementation will only surface as a build failure on the backends you didn't touch.

**Two-layer register model** (`include/weaver/vector_types.h`):
- `SimdRegister<Scalar, SimdExt>` is the thin, per-ISA-and-scalar-type wrapper around the actual intrinsic type (`__m128`, `float32x4_t`, `__mmask16`, ...). It only knows how to `set`/`get` lanes.
- `VectorScalarBase<Scalar, Width, SimdExt>` stacks `NrRegs = Width / NrValuesPerReg` of those registers to build wider logical vectors from narrower physical registers (e.g. a `float16` on SSE hardware is 4 `__m128` registers). `VectorScalar<Scalar, Width>` (defined per-ISA in the `{type}{width}_{isa}.h` files) is the public-facing type built on top of this.

**Operator codegen via macros**, not templates, because SIMD intrinsics for +, -, cmp, reductions etc. don't share a signature: `common.h` defines `WEAVER_SIMD_UNARY_OP` / `_BINARY_OP` / `_ASSIGN_OP` / `_COMP_OP` / `_QUERY_OP` / `_UNARY_REDUCTION_OP` / `_BINARY_REDUCTION_OP`, each expanding through `WEAVER_SIMD_P1_N` / `WEAVER_SIMD_P2_N` helper macros that unroll across the `NrRegs` registers for a given width `N` (4/8/16). Each per-width-per-ISA header instantiates these macros with the intrinsic function name for that op. `WEAVER_SIMD_VECTORSCALAR_SETUP(ISA)` wires up the constructors and type aliases (`Self`, `Bool`, `RegType`) each `VectorScalar` specialization needs.

**Free functions vs. members**: elementwise math (`sqrt`, `exp`, `log`, `min`, `max`, `select`, `equal`, `sgn`, ...) is exposed as free functions in `namespace weaver` (`vector_types.h`, bottom) that mostly just forward to a member of the same name — this mirrors `<cmath>`/Eigen calling conventions and is what makes generic code (templated on `Scalar`/`Width`) work uniformly across scalar and vector types.

**Eigen integration** (`include/weaver/eigen.h`): specializes `Eigen::NumTraits<weaver::floatN>` and the relevant `Eigen::numext` functions so `weaver::floatN`/`int N`/`boolN` can be used as the `Scalar` of an `Eigen::Matrix`. `select`/`cswap`/`cnswap` (`conditional.h`) are the masked-branchless primitives this whole model relies on instead of real control flow — any "CUDA-style" branch in user code should compile down to one of these rather than an `if`.

**`intrinsics_*.h`/`.cpp`** wrap raw platform intrinsics not otherwise exposed cleanly (lane extraction, mathfun-derived transcendentals in `src/detail/*_mathfun.h`, sourced from Julien Pommier / Giovanni Garberoglio / JishinMaster implementations). **`memory_*.h`** covers load/store/gather/scatter/interleave, which is why those get their own test files (`load.cpp`, `store.cpp`, `gather.cpp`, `scatter.cpp`, `interleave.cpp`) rather than living inside `float.cpp`/`int.cpp`.

**Tests** (`src/tests/`) use Catch2 v3. `src/tests/common.h` provides `WEAVER_SIMD_FLOATS` / `_INTS` / `_BOOLS` macros that inject a standard set of ascending/descending fixture vectors (`f4_asc`, `f8_desc`, ...) at all three widths into a test case — reuse these instead of hand-rolling fixtures. Tag tests with `[float]`/`[int]`/`[bool]`/`[operation]` etc. matching the existing convention so `-t "[tag]"` filtering keeps working.
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLAUDE.md

@AGENTS.md
105 changes: 105 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# FloatWeaver

FloatWeaver is a C++17 library for writing CUDA-style SIMD code that runs on the CPU. You write ordinary, scalar-looking code against vector types such as `weaver::float8`, and it compiles down to SSE, AVX, AVX-512 or NEON instructions, or to a portable scalar fallback.

The vector types also work as the scalar type of an Eigen matrix, so `Eigen::Matrix<weaver::float8, 3, 1>` is eight 3D vectors processed in lockstep.

```cpp
#include <weaver/vector_types.h>

weaver::float8 x{ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
weaver::float8 y{ 8.0f, 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };

weaver::float8 z = x + y; // one vector add per lane group
```

```cpp
#include <Eigen/Geometry>
#include <weaver/eigen.h>

Eigen::Matrix<weaver::float8, 3, 1> a{ weaver::float8{1.0f}, weaver::float8{2.0f}, weaver::float8{3.0f} };
Eigen::Matrix<weaver::float8, 3, 1> b{ weaver::float8{4.0f}, weaver::float8{5.0f}, weaver::float8{6.0f} };

Eigen::Matrix<weaver::float8, 3, 1> c = a.cross(b); // eight cross products at once
```

Branches are expressed with masked, branchless primitives (`select`, `cswap`, `cnswap`) instead of `if`, the same way divergent code is handled on a GPU.

## Features

- `float`, `int` and `bool` vectors at widths 4, 8 and 16 (`float4`, `int8`, `bool16`, ...).
- Arithmetic, comparison and reduction operators, plus `<cmath>`-style free functions (`sqrt`, `exp`, `log`, `min`, `max`, `sgn`, ...).
- Load, store, gather, scatter and interleave helpers.
- Eigen integration via `NumTraits` and `numext` specializations.
- Vectors wider than the hardware register are split across several registers automatically, so `float16` works on SSE and `float8` works on NEON.

## Supported backends

The instruction set is chosen at configure time with the `WEAVER_VECTORIZE` CMake cache variable. It is not autodetected.

| `WEAVER_VECTORIZE` | Target |
| --- | --- |
| `Generic` | Portable scalar fallback |
| `SSE 4.2` (default) | x86 / x86-64 |
| `AVX`, `AVX 2` | x86-64 |
| `AVX 512` | x86-64 |
| `NEON` | ARM / AArch64 |

CI builds and tests the library with GCC, Clang and MSVC on x86-64 and ARM64, and with Emscripten.

## Building

You need CMake 3.21 or newer and a C++17 compiler. The dependencies are [Eigen](https://eigen.tuxfamily.org) 3.4 and, for the tests, [Catch2](https://github.com/catchorg/Catch2) 3.

**With Conan 2:**

```bash
conan install --build=missing -of build .
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=build/build/Release/generators/conan_toolchain.cmake \
-DWEAVER_VECTORIZE="AVX 2"
cmake --build build --config Release
```

On multi-config generators (for example Visual Studio), the toolchain file is in `build/build/generators` instead.

**Without Conan:** if Eigen or Catch2 aren't found as packages, CMake falls back to the git submodules under `external/`:

```bash
git submodule update --init
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
```

**Running the tests:**

```bash
ctest --test-dir build --build-config Release
```

Tests are only built when FloatWeaver is the top-level project. To use it from another CMake project, add it with `add_subdirectory` and link against the `weaver` target.

## Documentation

The documentation is built with Doxygen, Breathe and Sphinx. It includes how-to guides that show the assembly each example compiles to. Build it locally with:

```bash
cmake -S . -B build -DWEAVER_BUILD_DOCS=ON # plus the options above
cmake --build build --target docs
```

The code examples in the docs live in [`doc/snippets`](doc/snippets) and are compiled and run as part of the test suite, so they stay in sync with the API.

## Origin

FloatWeaver started as the SIMD module of the [Visual Computing Library (VCL)](https://github.com/bfierz/vcl). Basil Fierz began VCL in 2014 to collect the algorithms and data structures from a PhD on visual simulation.

VCL grew into a large library covering many topics, but its SIMD types are useful on their own. FloatWeaver reimplements that module from scratch, along with parts of the math built on it, as a small standalone library. It targets C++17 instead of VCL's C++14 and uses a CMake build with Conan-managed dependencies. The core vector types and the Eigen adapter are complete. Examples, benchmarks and broader test coverage are still being brought over.

## Acknowledgements

The trigonometric, logarithm and power functions build on existing SIMD math libraries. The SSE and NEON versions come from [Julien Pommier's sse_mathfun](http://gruntthepeon.free.fr/ssemath/), the AVX version is by Giovanni Garberoglio, and the AVX-512 version comes from [JishinMaster's simd_utils](https://github.com/JishinMaster/simd_utils).

## License

FloatWeaver is released under the [MIT License](LICENSE).
Loading