cera 0.2.3

Rust-native LLM inference engine
Documentation
// Common WGSL declarations for cera kernels

#ifndef COMMON_DECLS_TMPL
#define COMMON_DECLS_TMPL

// Max workgroups per dimension (standard wgpu limit)
const MAX_WG: u32 = 65535u;

// Flatten a 2D workgroup ID into a linear index.
// Used when dispatches exceed MAX_WG in the X dimension.
fn get_wid(wid: vec3<u32>) -> u32 {
    return wid.x + wid.y * MAX_WG;
}

// Tree-reduce `shared_sum[0..256]` in-place; result lands in `shared_sum[0]`.
// Caller must have already populated `shared_sum[tid]` and issued a
// `workgroupBarrier()` so all writes are visible.
// NOTE: Requires `var<workgroup> shared_sum: array<f32, 256>;` to be defined
// in the calling shader.
#ifdef WG_SUM_REDUCE
fn workgroup_sum_reduce(tid: u32) {
    if tid < 128u { shared_sum[tid] += shared_sum[tid + 128u]; }
    workgroupBarrier();
    if tid < 64u { shared_sum[tid] += shared_sum[tid + 64u]; }
    workgroupBarrier();
    if tid < 32u { shared_sum[tid] += shared_sum[tid + 32u]; }
    workgroupBarrier();
    if tid < 16u { shared_sum[tid] += shared_sum[tid + 16u]; }
    workgroupBarrier();
    if tid < 8u { shared_sum[tid] += shared_sum[tid + 8u]; }
    workgroupBarrier();
    if tid < 4u { shared_sum[tid] += shared_sum[tid + 4u]; }
    workgroupBarrier();
    if tid < 2u { shared_sum[tid] += shared_sum[tid + 2u]; }
    workgroupBarrier();
    if tid < 1u { shared_sum[tid] += shared_sum[tid + 1u]; }
    workgroupBarrier();
}
#endif

// RoPE: Compute rotary angle for a given position and dimension.
fn rope_angle(pos: u32, d: u32, head_dim: u32, freq_base: f32) -> f32 {
    return f32(pos) * pow(freq_base, -2.0 * f32(d) / f32(head_dim));
}

// RoPE: Rotate a pair of values by a given angle.
fn rotate_rope(x0: f32, x1: f32, angle: f32) -> vec2<f32> {
    let cos_a = cos(angle);
    let sin_a = sin(angle);
    return vec2<f32>(
        x0 * cos_a - x1 * sin_a,
        x0 * sin_a + x1 * cos_a
    );
}

#ifdef BYTE_HELPERS
fn get_byte(value: u32, index: u32) -> u32 {
    return (value >> (index * 8)) & 0xFF;
}
#endif

// Q8_0 dequant helpers. Shared by gemv_q8_0 / gemm_q8_0.
//
// Contract: the including shader MUST declare the Q8_0 weight buffer as
//   @group(0) @binding(0) var<storage, read> a: array<u32>;
// (these helpers index `a` directly, relying on WGSL module-scope order
// independence).
//
// Q8_0 block layout (34 bytes per 32 elements):
//   bytes 0-1:  f16 scale
//   bytes 2-33: 32 signed i8 quants
#ifdef Q8_0_HELPERS
// Read a (possibly unaligned) u32 from the byte-addressed `a` buffer.
//
// When `byte_offset` is not 4-aligned this also touches `a[word_idx + 1]`.
// That index is always in bounds: a Q8_0 row is `nb * 34` bytes and the
// u32 buffer length is `ceil(row_bytes * m / 4)`. Because 34 is not a
// multiple of 4, rounding up always leaves at least one extra u32 of
// slack past the final quant word, so the +1 read never exceeds the
// buffer. Do not call this on a buffer whose length is not derived from
// whole Q8_0 rows.
fn get_u32_at(byte_offset: u32) -> u32 {
    let word_idx = byte_offset / 4u;
    let shift = (byte_offset & 3u) * 8u;
    let lo = a[word_idx];
    if shift == 0u {
        return lo;
    }
    let hi = a[word_idx + 1u];
    return (lo >> shift) | (hi << (32u - shift));
}

// Dot one Q8_0 block (32 weights) of row `row`, block `bi`, against the
// 32 staged activations in `xl`. `row_bytes` is the per-row byte stride
// (`nb * 34`). Returns the scaled partial sum for this block.
fn process_block_q8_0(
    row: u32,
    bi: u32,
    row_bytes: u32,
    xl: ptr<function, array<f32, 32>>,
) -> f32 {
    let block_byte = row * row_bytes + bi * 34u;
    let scale_bits = get_u32_at(block_byte) & 0xFFFFu;
    let scale = unpack2x16float(scale_bits).x;

    var sum = 0.0;
    for (var i = 0u; i < 32u; i += 4u) {
        let packed = get_u32_at(block_byte + 2u + i);
        sum += f32(bitcast<i32>((packed & 0x000000FFu) << 24u) >> 24u) * (*xl)[i + 0u];
        sum += f32(bitcast<i32>((packed & 0x0000FF00u) << 16u) >> 24u) * (*xl)[i + 1u];
        sum += f32(bitcast<i32>((packed & 0x00FF0000u) << 8u) >> 24u) * (*xl)[i + 2u];
        sum += f32(bitcast<i32>(packed & 0xFF000000u) >> 24u) * (*xl)[i + 3u];
    }

    return sum * scale;
}

#endif

#endif // COMMON_DECLS_TMPL