Skip to content

SIMD (std::simd)

std/simd/ is a portable SIMD library built entirely on vec<T, N>, the compiler's native vector type — there is no hand-written per-instruction implementation anywhere in this library. Elementwise arithmetic and lane access already work on any vec<T,N> through ordinary operator/subscript syntax; this library adds convenient ISA-agnostic type names, pointer load/store, broadcast, horizontal reductions, and fused multiply-add on top.

c
#include <std/simd/simd.h>

int main() {
    float src[4] = {1.0, 5.0, 3.0, 2.0};
    float dst[4];

    unsafe {
        f32x4 v = simd_load_f32x4(src);
        f32x4 doubled = v + v;
        simd_store_f32x4(doubled, dst);

        float total = simd_hsum_f32x4(v);   // horizontal sum
        f32x4 a = simd_splat_f32x4(2.0);    // broadcast
        f32x4 fma = simd_fma_f32x4(a, a, v); // a*a + v
    }
    return 0;
}

Load/store take a raw pointer, so they require unsafe { } at the call site like any other raw-pointer access in SafeC.

Type Aliases

Naming convention: <elem><bits>x<lanes>. Widths cover every target's baseline SIMD register (128-bit: SSE, NEON, WASM SIMD128, RVV with VLEN≥128) plus the common 256-bit case (AVX2 — on targets without a native 256-bit register, LLVM legalizes it into two 128-bit ops, still correct, just not a single instruction).

AliasUnderlying type
f32x4, f32x8vec<float, 4>, vec<float, 8>
f64x2, f64x4vec<double, 2>, vec<double, 4>
i32x4, i32x8vec<int, 4>, vec<int, 8>
i64x2, i64x4vec<long long, 2>, vec<long long, 4>
i16x8, i16x16vec<short, 8>, vec<short, 16>
i8x16, i8x32vec<signed char, 16>, vec<signed char, 32>
u32x4, u32x8vec<unsigned int, 4>, vec<unsigned int, 8>
u8x16, u8x32vec<unsigned char, 16>, vec<unsigned char, 32>

Functions

FunctionDescription
simd_load_<type>(const T* p)Load from a raw pointer — unaligned by construction, safe on any pointer
simd_store_<type>(v, T* p)Store to a raw pointer
simd_splat_<type>(x)Broadcast a scalar to every lane
simd_fma_<type>(a, b, c)Fused multiply-add: a*b + c
simd_min_<type>(a, b) / simd_max_<type>(a, b)Elementwise min/max
simd_hsum_<type>(v)Horizontal sum — reduce all lanes to one scalar
simd_hmin_<type>(v) / simd_hmax_<type>(v)Horizontal min/max

Not every function is defined for every type alias — see std/simd/simd.h for the exact set (e.g. simd_hmin/simd_hmax are float-only; integer types have simd_hsum but not the horizontal min/max variants).

Per-ISA Convenience Headers

Eight thin headers re-export the same portable types under architecture-idiomatic names — each is pure typedefs and documentation, containing no separate logic, and each has been verified against real generated code for its target (disassembled llc output, not just "compiles"):

HeaderTargetNative mapping
std/simd/x86_64.hx86_64m128/m128i/m128d (SSE/SSE2), m256/m256i/m256d (AVX/AVX2)
std/simd/aarch64.hAArch64128-bit NEON registers
std/simd/riscv.hRISC-V (+v)128-bit RVV register groups (baseline zve* width; a true scalable vector length isn't modeled — see the header for why)
std/simd/wasm.hWebAssembly128-bit v128 (SIMD128 proposal)
std/simd/spirv.hSPIR-VReal OpTypeVector/OpFAdd in a compute kernel body — see the header's caveat about SPIR-V's no-host-libc execution model
std/simd/cortex_m.hARM Cortex-MMVE (M55/M85) type aliases + the DSP-extension dsp_* functions (M4/M7) — see Bare-Metal
std/simd/cuda.hCUDA (NVPTX)Real PTX vector-lane codegen — GPU scalarizes to N scalar ops, see caveat below
std/simd/rocm.hROCm (AMDGPU)Real GCN vector-ALU codegen — same scalarization caveat

GPU targets: SIMT, not SIMD-in-one-instruction

PTX and GCN have no packed-arithmetic instruction the way SSE/NEON do — a GPU's parallelism comes from running many threads in lockstep, not wide registers within one thread. vec<float,4> + vec<float,4> still compiles correctly on CUDA/ROCm targets, it just becomes four independent scalar ops rather than one wide instruction (confirmed: real generated PTX shows four add.rn.f32 instructions, real GCN shows four v_add_f32_e32 instructions). std::simd's CUDA/ROCm headers only provide the portable arithmetic types usable within a kernel body (device memory, no host libc) — they don't define kernel entry points (__global__/ ptx_kernel/amdgpu_kernel calling convention plus launch-configuration support is a separate concern this library doesn't address).

Metal Shading Language

Not supported. Apple's Metal compiler has no LLVM backend upstream — unlike NVPTX/AMDGPU/SPIR-V, which are real LLVM targets --target can select, Metal is a separate, closed toolchain. The only interop path from this library's SPIR-V output is a third-party translator (e.g. SPIRV-Cross), not something safec or std::simd does directly.

Released under the MIT License.