cubecl-cpp 0.11.0-pre.4

CPP transpiler for CubeCL
Documentation
use cubecl_core::{
    self as cubecl,
    ir::{dialect::vector::*, interfaces::TypedExt, prelude::*},
    prelude::*,
};
use itertools::Itertools;

use crate::{
    shared::{
        binary::lower_binop, scoped_block, shared_op_with_out, ty::TypeExtCPP, unary::lower_unop,
    },
    target::{CtxTarget, Target},
};

shared_op_with_out!(CompositeConstructOp, |op, ctx| {
    let values = op.values(ctx).iter().map(|it| it.name(ctx)).join(", ");
    let ty = op.get_result(ctx).get_type(ctx).to_cpp(ctx);
    format!("{ty}{{{values}}}")
});

shared_op_with_out!(VectorBroadcastOp, |op, ctx| {
    let vec = op.get_result(ctx).vector_size(ctx);
    let values = (0..vec).map(|_| op.input(ctx).name(ctx)).join(", ");
    let ty = op.get_result(ctx).get_type(ctx).to_cpp(ctx);
    format!("{ty}{{{values}}}")
});

shared_op_with_out!(CompositeInsertOp, |op, ctx| {
    assert!(op.composite(ctx).is_vector(ctx));
    let vector_size = op.composite(ctx).vector_size(ctx);
    let vector = op.composite(ctx).name(ctx);
    let value = op.value(ctx).name(ctx);
    let index = op.index(ctx).0;
    let new_values = (0..vector_size)
        .map(|i| {
            if i == index {
                value.to_string()
            } else {
                format!("{vector}.i_{i}")
            }
        })
        .join(", ");
    format!("{{{new_values}}}")
});

shared_op_with_out!(CompositeExtractOp, |op, ctx| {
    assert!(op.composite(ctx).is_vector(ctx));
    let vector = op.composite(ctx).name(ctx);
    let index = op.index(ctx).0;
    format!("{vector}.i_{index}")
});

shared_op_with_out!(VectorInsertDynamicOp, |op, ctx| {
    let vector = op.vector(ctx).name(ctx);
    let value = op.value(ctx).name(ctx);
    let index = op.index(ctx).name(ctx);
    let elem_ty = op.value(ctx).get_type(ctx).to_cpp(ctx);
    let vector_ty = op.vector(ctx).get_type(ctx).to_cpp(ctx);
    scoped_block!(
        format!("{vector_ty} tmp = {vector};")
        format!("*(reinterpret_cast<{elem_ty}*>(&tmp) + {index}) = {value};")
        "return tmp;"
    )
});

shared_op_with_out!(VectorExtractDynamicOp, |op, ctx| {
    let vector = op.vector(ctx).name(ctx);
    let index = op.index(ctx).name(ctx);
    let elem_ty = op.get_result(ctx).get_type(ctx).to_cpp(ctx);
    format!("reinterpret_cast<const {elem_ty}*>(&{vector})[{index}]")
});

#[cube]
fn magnitude<T: Float, N: Size>(input: Vector<T, N>) -> T {
    let mut out = T::from_int(0);
    #[unroll]
    for i in 0..input.vector_size() {
        let val = input.extract(i);
        out += val * val;
    }
    out.sqrt()
}

#[cube]
fn normalize<T: Float, N: Size>(input: Vector<T, N>) -> Vector<T, N> {
    let mut norm = T::from_int(0);
    #[unroll]
    for i in 0..input.vector_size() {
        let val = input.extract(i);
        norm += val * val;
    }
    input * Vector::cast_from(norm.inverse_sqrt())
}

#[cube]
fn sum<T: Float, N: Size>(input: Vector<T, N>) -> T {
    let mut sum = T::from_int(0);
    #[unroll]
    for i in 0..input.vector_size() {
        sum += input.extract(i);
    }
    sum
}

#[cube]
fn dot<T: Float, N: Size>(lhs: Vector<T, N>, rhs: Vector<T, N>) -> T {
    sum(lhs * rhs)
}

lower_unop!(MagnitudeOp, magnitude, |_, ctx| {
    ctx.target() != Target::Metal
});

lower_unop!(NormalizeOp, normalize, |_, ctx| {
    ctx.target() != Target::Metal
});

lower_unop!(ISumOp, sum);
lower_unop!(FSumOp, sum);
lower_binop!(SDotOp, dot);
lower_binop!(UDotOp, dot);
lower_binop!(FDotOp, dot, |_, ctx| ctx.target() != Target::Metal);