libmir-cuda 0.3.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, bf16};
use runtime::kv::{BlockTable, KvWritePlan};

use super::{LayerPrefill, PrefillSignature, SessionLayer, SharedLayerPrefill};
use crate::{Error, PagedPrefillBatch, Result};

impl SessionLayer {
    pub(in crate::backend::model) const fn prefill_signature(&self) -> PrefillSignature {
        match self {
            Self::Moe(layer) => {
                let mut config = layer.template.config();
                if layer.template.experts_are_dense() {
                    config.attention.layer = 0;
                }
                PrefillSignature::Moe(config)
            },
            Self::Dense(layer) => {
                let mut config = layer.template.config();
                if !matches!(config.attention.projection_format, crate::ProjectionFormat::NvFp4) {
                    config.attention.layer = 0;
                }
                PrefillSignature::Dense(config)
            },
        }
    }

    pub(in crate::backend::model) fn instantiate_shared_prefill(
        &self,
        tokens: usize,
    ) -> Result<SharedLayerPrefill> {
        let signature = self.prefill_signature();
        let plan = match self {
            Self::Moe(layer) => layer.template.instantiate_prefill(tokens)?.into(),
            Self::Dense(layer) => layer.template.instantiate_prefill(tokens)?.into(),
        };
        Ok(SharedLayerPrefill::new(signature, plan))
    }

    pub(in crate::backend::model) fn execute_shared_prefill_batch(
        &mut self,
        prefill: LayerPrefill<'_>,
        input: &DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
        batch: &PagedPrefillBatch,
    ) -> Result<()> {
        match (self, prefill) {
            (Self::Moe(layer), LayerPrefill::Moe(prefill)) => {
                let decode = layer.decode.as_mut().ok_or(Error::InvalidDecoderKernel(
                    "CUDA prefill executor belongs to model graph",
                ))?;
                decode.execute_prefill_batch(prefill, input, batch, output)
            },
            (Self::Dense(layer), LayerPrefill::Dense(prefill)) => {
                let weights = layer.template.weights();
                let decode = layer.decode.as_mut().ok_or(Error::InvalidDecoderKernel(
                    "CUDA prefill executor belongs to model graph",
                ))?;
                prefill.execute_batch(decode, input, weights, batch, output)
            },
            _ => Err(Error::InvalidDecoderKernel(
                "shared CUDA prefill plan differs from decoder layer",
            )),
        }
    }

    pub(in crate::backend::model) fn execute_shared_prefill(
        &mut self,
        prefill: LayerPrefill<'_>,
        input: &DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
        plan: &KvWritePlan,
        table: &BlockTable,
        start: usize,
    ) -> Result<()> {
        match (self, prefill) {
            (Self::Moe(layer), LayerPrefill::Moe(prefill)) => layer
                .decode
                .as_mut()
                .ok_or(Error::InvalidDecoderKernel("CUDA prefill executor belongs to model graph"))?
                .execute_prefill(prefill, input, plan, table, start, output),
            (Self::Dense(layer), LayerPrefill::Dense(prefill)) => {
                let weights = layer.template.weights();
                let decode = layer.decode.as_mut().ok_or(Error::InvalidDecoderKernel(
                    "CUDA prefill executor belongs to model graph",
                ))?;
                prefill.execute(decode, input, weights, plan, table, start, output)
            },
            _ => Err(Error::InvalidDecoderKernel(
                "shared CUDA prefill plan differs from decoder layer",
            )),
        }
    }
}