use anyhow::{Result, bail};
use rlx_ir::op::{Activation, BinaryOp, MaskKind, ReduceOp};
use rlx_ir::{DType, Shape};
pub type Value = String;
#[derive(Debug, Clone)]
pub struct InputDef {
pub name: String,
pub shape: Vec<usize>,
pub dyn_dims: Vec<Option<u32>>,
pub dtype: DType,
}
impl InputDef {
pub fn hir_shape(&self, dtype: DType) -> Shape {
if self.dyn_dims.iter().all(Option::is_none) {
return shape_of(&self.shape, dtype);
}
let dims: Vec<rlx_ir::shape::Dim> = self
.shape
.iter()
.enumerate()
.map(|(ax, &s)| match self.dyn_dims.get(ax).copied().flatten() {
Some(sym) => rlx_ir::shape::Dim::Dynamic(sym),
None => rlx_ir::shape::Dim::Static(s),
})
.collect();
Shape::from_dims(&dims, dtype)
}
pub fn is_dynamic(&self) -> bool {
self.dyn_dims.iter().any(Option::is_some)
}
}
#[derive(Debug, Clone)]
pub struct ParamDef {
pub value_id: String,
pub key: String,
pub shape: Vec<usize>,
pub dtype: DType,
}
#[derive(Debug, Clone)]
pub enum Call {
Mm(Value, Value),
Binary(BinaryOp, Value, Value),
Act(Activation, Value),
Ln {
x: Value,
gamma: Value,
beta: Value,
eps: f32,
},
RmsNorm {
x: Value,
gamma: Value,
beta: Value,
eps: f32,
},
Reshape { x: Value, shape: Vec<i64> },
Transpose { x: Value, perm: Vec<usize> },
Narrow {
x: Value,
axis: usize,
start: usize,
len: usize,
},
Concat { xs: Vec<Value>, axis: usize },
Gather {
table: Value,
indices: Value,
axis: usize,
},
Softmax { x: Value, axis: i32 },
Reduce {
op: ReduceOp,
x: Value,
axes: Vec<usize>,
keep_dim: bool,
},
Cast { x: Value, to: DType },
Conv2d {
x: Value,
weight: Value,
kernel: [usize; 2],
stride: [usize; 2],
padding: [usize; 2],
groups: usize,
out: Vec<usize>,
out_dtype: DType,
},
Attention {
q: Value,
k: Value,
v: Value,
num_heads: usize,
head_dim: usize,
mask: MaskKind,
out: Vec<usize>,
out_dtype: DType,
},
Rope {
x: Value,
cos: Value,
sin: Value,
head_dim: usize,
},
Full {
value: f64,
shape: Vec<usize>,
dtype: DType,
},
ConvTranspose2d {
x: Value,
weight: Value,
kernel: [usize; 2],
stride: [usize; 2],
padding: [usize; 2],
dilation: [usize; 2],
output_padding: [usize; 2],
groups: usize,
out: Vec<usize>,
out_dtype: DType,
},
AttentionBias {
q: Value,
k: Value,
v: Value,
bias: Value,
num_heads: usize,
head_dim: usize,
out: Vec<usize>,
out_dtype: DType,
},
Iota {
rows: usize,
step: i64,
dtype: DType,
},
Arange {
start: f64,
step: f64,
len: usize,
dtype: DType,
},
Resize {
x: Value,
out_h: usize,
out_w: usize,
align_corners: bool,
cubic: bool,
antialias: bool,
out: Vec<usize>,
out_dtype: DType,
},
GridSample {
input: Value,
grid: Value,
mode: rlx_ir::hir::GridMode,
pad: rlx_ir::hir::GridPad,
align_corners: bool,
out: Vec<usize>,
out_dtype: DType,
},
Node(crate::nodeop::NodeOp),
Alias(Value),
}
#[derive(Debug, Clone)]
pub struct Instr {
pub result: Value,
pub call: Call,
pub note: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct Lowered {
pub name: String,
pub inputs: Vec<InputDef>,
pub params: Vec<ParamDef>,
pub zero_params: Vec<ParamDef>,
pub instrs: Vec<Instr>,
pub outputs: Vec<Value>,
pub source_histogram: Vec<(String, usize)>,
}
pub fn dtype_from_str(s: &str) -> Result<DType> {
Ok(match s {
"f32" => DType::F32,
"f16" => DType::F16,
"bf16" => DType::BF16,
"f64" => DType::F64,
"i8" => DType::I8,
"i16" => DType::I16,
"i32" => DType::I32,
"i64" => DType::I64,
"u8" => DType::U8,
"u32" => DType::U32,
"bool" => DType::Bool,
"c64" => DType::C64,
"c128" => DType::C128,
other => bail!("unsupported dtype token: {other:?}"),
})
}
pub fn dtype_token(dt: DType) -> &'static str {
match dt {
DType::F32 => "F32",
DType::F16 => "F16",
DType::BF16 => "BF16",
DType::F64 => "F64",
DType::I8 => "I8",
DType::I16 => "I16",
DType::I32 => "I32",
DType::I64 => "I64",
DType::U8 => "U8",
DType::U32 => "U32",
DType::Bool => "Bool",
DType::C64 => "C64",
DType::C128 => "C128",
}
}
pub fn shape_of(dims: &[usize], dt: DType) -> Shape {
Shape::new(dims, dt)
}
pub fn is_float_dtype(dt: DType) -> bool {
matches!(dt, DType::F32 | DType::F16 | DType::BF16 | DType::F64)
}
pub fn dims_usize(dims: &[i64]) -> Vec<usize> {
dims.iter().map(|&d| d.max(0) as usize).collect()
}
pub fn fill_bytes(value: f64, dtype: DType, numel: usize) -> Result<Vec<u8>> {
let one: Vec<u8> = match dtype {
DType::F32 => (value as f32).to_le_bytes().to_vec(),
DType::F64 => value.to_le_bytes().to_vec(),
DType::I64 => (value as i64).to_le_bytes().to_vec(),
DType::I32 => (value as i32).to_le_bytes().to_vec(),
DType::Bool => vec![(value != 0.0) as u8],
other => bail!("full/full_like with dtype {other:?} not supported"),
};
let mut out = Vec::with_capacity(one.len() * numel);
for _ in 0..numel {
out.extend_from_slice(&one);
}
Ok(out)
}