cera 0.5.4

Rust-native LLM inference engine
Documentation
// slang-entries: add_inplace scaled_add_inplace mul_inplace silu_mul_inplace
//
// Element-wise f32 operations, one Slang source for both GPU backends. Only the
// four entry points whose WGSL and MSL twins are identical are migrated here:
// `add_inplace`, `scaled_add_inplace`, `mul_inplace`, `silu_mul_inplace`. They
// share one binding contract (a in-place, b read, params) and have no per-target
// divergence, so the bodies are written once with no `__target_switch`.
//
// The handwritten `elementwise.metal` also carries four Metal-only entry points
// (`memcpy_f32`, `mul_out`, `cast_f32_to_f16`, `scale_f32`) that have no WGSL
// twin; `cast_f32_to_f16` in particular would force `enable f16` into the WGSL
// emission on adapters that never requested it. Those stay handwritten and are
// deliberately NOT part of this single-source port.
//
//   binding/buffer 0: a       f32, read-write, modified in place (n elements)
//   binding/buffer 1: b       f32, read (n elements)
//   binding/buffer 2: params  (n, scale_bits): scale_bits is the raw f32 bits of
//                             the scaled_add multiplier, unused by the others
//
// Dispatch: (ceil(n / 256), 1, 1) workgroups of 256.

[[vk::binding(0)]] RWStructuredBuffer<float> a_buf   : register(u0);
[[vk::binding(1)]] StructuredBuffer<float>   b_buf   : register(t1);
[[vk::binding(2)]] StructuredBuffer<uint2>   par_buf : register(t2);

[shader("compute")]
[numthreads(256, 1, 1)]
void add_inplace(uint3 gid : SV_DispatchThreadID) {
    uint i = gid.x;
    if (i >= par_buf[0].x) {
        return;
    }
    a_buf[i] = a_buf[i] + b_buf[i];
}

// Residual add with a scalar on the addend: a[i] += s * b[i], with `s` the f32
// bits in params.y. Granite 3.x sets `s` to its residual multiplier; every other
// arch passes s = 1.0, making this identical to `add_inplace`.
[shader("compute")]
[numthreads(256, 1, 1)]
void scaled_add_inplace(uint3 gid : SV_DispatchThreadID) {
    uint i = gid.x;
    if (i >= par_buf[0].x) {
        return;
    }
    a_buf[i] = a_buf[i] + asfloat(par_buf[0].y) * b_buf[i];
}

[shader("compute")]
[numthreads(256, 1, 1)]
void mul_inplace(uint3 gid : SV_DispatchThreadID) {
    uint i = gid.x;
    if (i >= par_buf[0].x) {
        return;
    }
    a_buf[i] = a_buf[i] * b_buf[i];
}

[shader("compute")]
[numthreads(256, 1, 1)]
void silu_mul_inplace(uint3 gid : SV_DispatchThreadID) {
    uint i = gid.x;
    if (i >= par_buf[0].x) {
        return;
    }
    float g = clamp(a_buf[i], -80.0f, 80.0f);
    a_buf[i] = (g / (1.0f + exp(-g))) * b_buf[i];
}