libmir-cuda 0.2.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, LaunchConfig, Stream, bf16, cuda_export};

use super::{ClampedRoutedKernels, narrow};
use crate::Result;

cuda_export!(pub(super) GateUpKernel = "libmir_cuda_clamped_routed_mxfp4_gate_up_bf16"(
    input: &DeviceBuffer<bf16>, blocks: &DeviceBuffer<u8>, scales: &DeviceBuffer<u8>,
    bias: &DeviceBuffer<bf16>, selected: &DeviceBuffer<u32>,
    output: &mut DeviceBuffer<bf16>, tokens: u32, top_k: u32, hidden: u32,
    intermediate: u32, limit: f32,
));
cuda_export!(pub(super) DownKernel = "libmir_cuda_clamped_routed_mxfp4_down_bf16"(
    input: &DeviceBuffer<bf16>, blocks: &DeviceBuffer<u8>, scales: &DeviceBuffer<u8>,
    bias: &DeviceBuffer<bf16>, selected: &DeviceBuffer<u32>,
    routing: &DeviceBuffer<bf16>, output: &mut DeviceBuffer<bf16>, tokens: u32,
    top_k: u32, hidden: u32, intermediate: u32,
));
cuda_export!(pub(super) GateUpMlxKernel = "libmir_cuda_clamped_routed_mlx_mxfp4_gate_up_bf16"(
    input: &DeviceBuffer<bf16>, gate_blocks: &DeviceBuffer<u32>,
    gate_scales: &DeviceBuffer<u8>, gate_bias: &DeviceBuffer<bf16>,
    up_blocks: &DeviceBuffer<u32>, up_scales: &DeviceBuffer<u8>,
    up_bias: &DeviceBuffer<bf16>, selected: &DeviceBuffer<u32>,
    output: &mut DeviceBuffer<bf16>, tokens: u32, top_k: u32, hidden: u32,
    intermediate: u32, limit: f32,
));
cuda_export!(pub(super) DownMlxKernel = "libmir_cuda_clamped_routed_mlx_mxfp4_down_bf16"(
    input: &DeviceBuffer<bf16>, blocks: &DeviceBuffer<u32>, scales: &DeviceBuffer<u8>,
    bias: &DeviceBuffer<bf16>, selected: &DeviceBuffer<u32>,
    routing: &DeviceBuffer<bf16>, output: &mut DeviceBuffer<bf16>, tokens: u32,
    top_k: u32, hidden: u32, intermediate: u32,
));

impl ClampedRoutedKernels {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn gate_up_native(
        &self,
        stream: &Stream,
        input: &DeviceBuffer<bf16>,
        blocks: &DeviceBuffer<u8>,
        scales: &DeviceBuffer<u8>,
        bias: &DeviceBuffer<bf16>,
        selected: &DeviceBuffer<u32>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        Ok(self.gate_up.launch(
            stream,
            self.gate_launch()?,
            (
                input,
                blocks,
                scales,
                bias,
                selected,
                output,
                narrow(self.spec.tokens)?,
                narrow(self.spec.top_k)?,
                narrow(self.spec.hidden)?,
                narrow(self.spec.intermediate)?,
                self.spec.swiglu_limit,
            ),
        )?)
    }

    #[allow(clippy::too_many_arguments)]
    pub(crate) fn down_native(
        &self,
        stream: &Stream,
        input: &DeviceBuffer<bf16>,
        blocks: &DeviceBuffer<u8>,
        scales: &DeviceBuffer<u8>,
        bias: &DeviceBuffer<bf16>,
        selected: &DeviceBuffer<u32>,
        routing: &DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        Ok(self.down.launch(
            stream,
            self.down_launch()?,
            (
                input,
                blocks,
                scales,
                bias,
                selected,
                routing,
                output,
                narrow(self.spec.tokens)?,
                narrow(self.spec.top_k)?,
                narrow(self.spec.hidden)?,
                narrow(self.spec.intermediate)?,
            ),
        )?)
    }

    #[allow(clippy::too_many_arguments)]
    pub(crate) fn gate_up_mlx(
        &self,
        stream: &Stream,
        input: &DeviceBuffer<bf16>,
        gate_blocks: &DeviceBuffer<u32>,
        gate_scales: &DeviceBuffer<u8>,
        gate_bias: &DeviceBuffer<bf16>,
        up_blocks: &DeviceBuffer<u32>,
        up_scales: &DeviceBuffer<u8>,
        up_bias: &DeviceBuffer<bf16>,
        selected: &DeviceBuffer<u32>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        Ok(self.gate_up_mlx.launch(
            stream,
            self.gate_launch()?,
            (
                input,
                gate_blocks,
                gate_scales,
                gate_bias,
                up_blocks,
                up_scales,
                up_bias,
                selected,
                output,
                narrow(self.spec.tokens)?,
                narrow(self.spec.top_k)?,
                narrow(self.spec.hidden)?,
                narrow(self.spec.intermediate)?,
                self.spec.swiglu_limit,
            ),
        )?)
    }

    #[allow(clippy::too_many_arguments)]
    pub(crate) fn down_mlx(
        &self,
        stream: &Stream,
        input: &DeviceBuffer<bf16>,
        blocks: &DeviceBuffer<u32>,
        scales: &DeviceBuffer<u8>,
        bias: &DeviceBuffer<bf16>,
        selected: &DeviceBuffer<u32>,
        routing: &DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        Ok(self.down_mlx.launch(
            stream,
            self.down_launch()?,
            (
                input,
                blocks,
                scales,
                bias,
                selected,
                routing,
                output,
                narrow(self.spec.tokens)?,
                narrow(self.spec.top_k)?,
                narrow(self.spec.hidden)?,
                narrow(self.spec.intermediate)?,
            ),
        )?)
    }

    fn gate_launch(&self) -> Result<LaunchConfig> {
        Ok(LaunchConfig {
            grid: (narrow(self.spec.intermediate * self.spec.tokens * self.spec.top_k)?, 1, 1),
            block: (32, 1, 1),
            shared_memory_bytes: 0,
        })
    }

    fn down_launch(&self) -> Result<LaunchConfig> {
        Ok(LaunchConfig {
            grid: (narrow(self.spec.hidden * self.spec.tokens)?, 1, 1),
            block: (32, 1, 1),
            shared_memory_bytes: 0,
        })
    }
}