use crate::call::{Value, dtype_token, shape_of};
use rlx_ir::hir::{HirMut, HirNodeId};
use rlx_ir::op::{BinaryOp, CmpOp, ReduceOp};
use rlx_ir::{DType, HirGraphExt};
fn shape_source(dims: &[usize], dt: DType) -> String {
let ds: Vec<String> = dims.iter().map(|d| d.to_string()).collect();
format!(
"rlx_ir::Shape::new(&[{}], rlx_ir::DType::{})",
ds.join(", "),
dtype_token(dt)
)
}
fn cmp_variant(op: CmpOp) -> &'static str {
match op {
CmpOp::Eq => "Eq",
CmpOp::Ne => "Ne",
CmpOp::Lt => "Lt",
CmpOp::Le => "Le",
CmpOp::Gt => "Gt",
CmpOp::Ge => "Ge",
}
}
fn binop_variant(op: BinaryOp) -> &'static str {
match op {
BinaryOp::Add => "Add",
BinaryOp::Sub => "Sub",
BinaryOp::Mul => "Mul",
BinaryOp::Div => "Div",
BinaryOp::Max => "Max",
BinaryOp::Min => "Min",
BinaryOp::Pow => "Pow",
BinaryOp::Mod => "Mod",
BinaryOp::BitAnd => "BitAnd",
BinaryOp::BitOr => "BitOr",
BinaryOp::BitXor => "BitXor",
BinaryOp::Shl => "Shl",
BinaryOp::Shr => "Shr",
BinaryOp::Atan2 => "Atan2",
}
}
fn reduce_variant(op: ReduceOp) -> &'static str {
match op {
ReduceOp::Sum => "Sum",
ReduceOp::Mean => "Mean",
ReduceOp::Max => "Max",
ReduceOp::Min => "Min",
ReduceOp::Prod => "Prod",
}
}
fn usize_vec_source(v: &[usize]) -> String {
format!(
"vec![{}]",
v.iter()
.map(|d| format!("{d}usize"))
.collect::<Vec<_>>()
.join(", ")
)
}
fn i64_vec_source(v: &[i64]) -> String {
format!(
"vec![{}]",
v.iter()
.map(|d| format!("{d}i64"))
.collect::<Vec<_>>()
.join(", ")
)
}
macro_rules! define_node_ops {
( $(
$(#[$doc:meta])*
$variant:ident [ $($inp:ident),* $(,)? ] { $( $fname:ident : $fty:ty ),* $(,)? }
build = $opbody:expr,
src = $srcbody:expr
);+ $(;)? ) => {
#[derive(Debug, Clone)]
pub enum NodeOp {
$(
$(#[$doc])*
$variant {
$( $inp: Value, )*
$( $fname: $fty, )*
out: Vec<usize>,
out_dtype: DType,
},
)+
}
impl NodeOp {
pub fn build(
&self,
b: &mut HirMut,
resolve: &mut dyn FnMut(&str) -> anyhow::Result<HirNodeId>,
) -> anyhow::Result<HirNodeId> {
match self {
$(
NodeOp::$variant { $($inp,)* $($fname,)* out, out_dtype } => {
let inputs = vec![ $( resolve($inp)?, )* ];
let op: rlx_ir::Op = $opbody;
let _ = ($($fname,)*);
Ok(b.add_node(op, inputs, shape_of(out, *out_dtype)))
}
)+
}
}
pub fn emit(&self, res: &str, r: &dyn Fn(&str) -> String) -> String {
match self {
$(
NodeOp::$variant { $($inp,)* $($fname,)* out, out_dtype } => {
let inputs = vec![ $( r($inp), )* ].join(", ");
let op_src: String = $srcbody;
let _ = ($($fname,)*);
format!(
"let {res} = b.add_node({op_src}, vec![{inputs}], {});",
shape_source(out, *out_dtype)
)
}
)+
}
}
}
};
}
define_node_ops! {
Compare [a, b] { op: CmpOp }
build = rlx_ir::Op::Compare(*op),
src = format!("rlx_ir::Op::Compare(rlx_ir::op::CmpOp::{})", cmp_variant(*op));
Where [cond, a, b] { }
build = rlx_ir::Op::Where,
src = "rlx_ir::Op::Where".to_string();
Expand [x] { target: Vec<i64> }
build = rlx_ir::Op::Expand { target_shape: target.clone() },
src = format!("rlx_ir::Op::Expand {{ target_shape: {} }}", i64_vec_source(target));
BinaryShaped [a, b] { op: BinaryOp }
build = rlx_ir::Op::Binary(*op),
src = format!("rlx_ir::Op::Binary(rlx_ir::op::BinaryOp::{})", binop_variant(*op));
Pool [x] { kind: ReduceOp, kernel: Vec<usize>, stride: Vec<usize>, padding: Vec<usize> }
build = rlx_ir::Op::Pool {
kind: *kind,
kernel_size: kernel.clone(),
stride: stride.clone(),
padding: padding.clone(),
},
src = format!(
"rlx_ir::Op::Pool {{ kind: rlx_ir::op::ReduceOp::{}, kernel_size: {}, stride: {}, padding: {} }}",
reduce_variant(*kind), usize_vec_source(kernel), usize_vec_source(stride), usize_vec_source(padding)
);
TopK [x] { k: usize }
build = rlx_ir::Op::TopK { k: *k },
src = format!("rlx_ir::Op::TopK {{ k: {k} }}");
GroupNorm [x, gamma, beta] { num_groups: usize, eps: f32 }
build = rlx_ir::Op::GroupNorm { num_groups: *num_groups, eps: *eps },
src = format!("rlx_ir::Op::GroupNorm {{ num_groups: {num_groups}, eps: {eps}f32 }}");
ResizeNearest2x [x] { }
build = rlx_ir::Op::ResizeNearest2x,
src = "rlx_ir::Op::ResizeNearest2x".to_string();
}