use crate::kernel::{BOp, IDX_T, MoveOp, UOp};
use crate::slab::SlabId;
use crate::{BLUE, BOLD, CYAN, GREEN, GREY, MAGENTA, ORANGE, RED, RESET, YELLOW};
use crate::{
DType, Map,
kernel::{Kernel, Op, OpId},
};
impl Kernel {
pub fn debug(&self) {
let remap_ids = false;
let mut indent = String::from(" ");
let bounds = self.compute_bounds();
let mut dtypes: Map<OpId, DType> = Map::default();
let mut op_id = self.head;
let mut has_loops = false;
let mut id_map = Map::default();
let mut max_id = OpId::ZERO;
let colorless = std::env::var("AGENT").map_or(false, |v| v == "1");
let (bold, blue, cyan, green, grey, magenta, orange, red, reset, yellow) = if colorless {
("", "", "", "", "", "", "", "", "", "")
} else {
(BOLD, BLUE, CYAN, GREEN, GREY, MAGENTA, ORANGE, RED, RESET, YELLOW)
};
while !op_id.is_null() {
max_id.inc();
let out_id = if remap_ids {
id_map.insert(op_id, max_id);
max_id
} else {
id_map.insert(op_id, op_id);
op_id
};
match *self.at(op_id) {
Op::ConstView(ref x) => {
let value = x.0;
let view = &x.1;
let dtype = value.dtype();
dtypes.insert(op_id, dtype);
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}{value}{reset} {view}");
}
Op::LoadView(ref x) => {
let dtype = x.0;
let view = &x.1;
dtypes.insert(op_id, dtype);
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}load{reset} {view}");
}
Op::StoreView { src, dtype, .. } => {
let src = id_map[&src];
dtypes.insert(op_id, dtype);
println!("{indent}{cyan}store{reset} r{src}");
}
Op::Reduce { x, rop, n_axes, .. } => {
let dtype = dtypes[&x];
dtypes.insert(op_id, dtype);
let x = id_map[&x];
if has_loops {
for _ in 0..n_axes * 2 {
indent.pop();
}
}
println!(
"{indent}r{out_id}{grey}: {dtype}{reset} = {red}reduce {}{reset} r{x}, dims={n_axes:?} {}",
match rop {
BOp::Add => "sum",
BOp::Max => "max",
BOp::Mul => "prod",
_ => unreachable!(),
},
dtypes[&op_id]
);
}
Op::Define { dtype, scope, ro, len, .. } => {
dtypes.insert(op_id, dtype);
let ro = if ro { "" } else { "mut " };
println!("{indent}{red}r{out_id}{reset}{grey}: {dtype}{reset} = {yellow}def {ro}{reset}{scope}, len={len}");
}
Op::Const(value) => {
let dtype = value.dtype();
dtypes.insert(op_id, dtype);
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {magenta}{value}{reset}");
}
Op::Load { src, index, layout } => {
let dtype = dtypes[&src];
dtypes.insert(op_id, dtype);
let (lb, ub) = bounds.get(&index).copied().unwrap_or((0, 0));
let src = id_map.get(&src).copied().unwrap_or(OpId::NULL);
let index = id_map.get(&index).copied().unwrap_or(OpId::NULL);
println!(
"{indent}r{out_id}{grey}: {dtype}{reset} = {red}r{src}{reset}[r{index} @ {layout}] // {lb}..={ub} {green}load{reset}"
);
}
Op::Store { dst, x, index, layout } => {
let dtype = dtypes[&x];
dtypes.insert(op_id, dtype);
let (lb, ub) = bounds.get(&index).copied().unwrap_or((0, 0));
let dst = id_map.get(&dst).copied().unwrap_or(OpId::NULL);
let index = id_map.get(&index).copied().unwrap_or(OpId::NULL);
let x = id_map.get(&x).copied().unwrap_or(OpId::NULL);
println!("{indent}{red}r{dst}{reset}[r{index} @ {layout}] = r{x} // {lb}..={ub} {red}store{reset}");
}
Op::Cast { x, dtype } => {
dtypes.insert(op_id, dtype);
let x = id_map.get(&x).copied().unwrap_or(OpId::NULL);
if let Some((lb, ub)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {dtype}(r{x}) // {lb}..={ub}");
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {dtype}(r{x})");
}
}
Op::Unary { x, uop, .. } => {
let dtype = dtypes[&x];
dtypes.insert(op_id, dtype);
let (op1, op2) = match uop {
UOp::Neg => ("-", ""),
UOp::BitNot => ("~", ""),
UOp::Exp => ("exp(", ")"),
UOp::Exp2 => ("exp2(", ")"),
UOp::Ln => ("ln(", ")"),
UOp::Log2 => ("log2(", ")"),
UOp::Reciprocal => ("1/", ""),
UOp::Sqrt => ("sqrt(", ")"),
UOp::Sin => ("sin(", ")"),
UOp::Cos => ("cos(", ")"),
UOp::Floor => ("floor(", ")"),
UOp::Trunc => ("trunc(", ")"),
UOp::Abs => ("abs(", ")"),
};
let x = id_map.get(&x).copied().unwrap_or(OpId::NULL);
if let Some((lb, ub)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {op1}r{x}{op2} // {lb}..={ub}");
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {op1}r{x}{op2}");
}
}
Op::Binary { x, y, bop, .. } => {
let dtype = if bop.returns_bool() { DType::Bool } else { dtypes[&x] };
dtypes.insert(op_id, dtype);
let (op1, op2, op3) = match bop {
BOp::Add => ("", " + ", ""),
BOp::Sub => ("", " - ", ""),
BOp::Mul => ("", " * ", ""),
BOp::Div => ("", " / ", ""),
BOp::Pow => ("pow(", ", ", ")"),
BOp::Mod => ("", " % ", ""),
BOp::Cmplt => ("", " < ", ""),
BOp::Cmpgt => ("", " > ", ""),
BOp::Max => ("max(", ", ", ")"),
BOp::Or => ("", " || ", ""),
BOp::And => ("", " && ", ""),
BOp::BitXor => ("", " ^ ", ""),
BOp::BitOr => ("", " | ", ""),
BOp::BitAnd => ("", " & ", ""),
BOp::BitShiftLeft => ("", " << ", ""),
BOp::BitShiftRight => ("", " >> ", ""),
BOp::NotEq => ("", " != ", ""),
BOp::Eq => ("", " == ", ""),
};
let x_r = id_map.get(&x).copied().unwrap_or(OpId::NULL);
let y_r = id_map.get(&y).copied().unwrap_or(OpId::NULL);
let x = if let Op::Const(c) = self.ops[x].op {
format!("{c}")
} else {
format!("r{x_r}")
};
let y = if let Op::Const(c) = self.ops[y].op {
format!("{c}")
} else {
format!("r{y_r}")
};
if let Some((lb, ub)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {op1}{x}{op2}{y}{op3} // {lb}..={ub}");
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {op1}{x}{op2}{y}{op3}");
}
}
Op::Mad { x, y, z } => {
let dtype = dtypes[&x];
dtypes.insert(op_id, dtype);
let x = id_map.get(&x).copied().unwrap_or(OpId::NULL);
let y = id_map.get(&y).copied().unwrap_or(OpId::NULL);
let z = id_map.get(&z).copied().unwrap_or(OpId::NULL);
if let Some((l, u)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = r{x} * r{y} + r{z} // {l}..={u}");
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = r{x} * r{y} + r{z}");
}
}
Op::Wmma { dims, layout, dtype, c, a, b } => {
let cdtype = dtypes[&c];
dtypes.insert(op_id, cdtype);
let a = id_map.get(&a).copied().unwrap_or(OpId::NULL);
let b = id_map.get(&b).copied().unwrap_or(OpId::NULL);
let c = id_map.get(&c).copied().unwrap_or(OpId::NULL);
println!(
"{indent}r{out_id}{grey}: {cdtype}{reset} = {orange}wmma{reset}.{dims:?}.{layout:?}.{dtype:?}(c={c}, a={a}, b={b})",
);
}
Op::Index { len, axis, scope } => {
dtypes.insert(op_id, IDX_T);
let ub = len - 1;
println!("{indent}r{out_id}{grey}: {IDX_T}{reset} = {blue}{scope}_index({axis}){reset} // 0..={ub}");
}
Op::Loop { len } => {
has_loops = true;
let dtype = dtypes[&len];
dtypes.insert(op_id, dtype);
let len = id_map.get(&len).copied().unwrap_or(OpId::NULL);
if let Some((l, u)) = bounds.get(&op_id) {
println!("{indent}{bold}for{reset} r{out_id} in 0..r{len} {{ // {l}..={}", u);
} else {
println!("{indent}{bold}for{reset} r{out_id} in 0..r{len} {{");
}
indent += " ";
}
Op::If { condition } => {
let condition = id_map.get(&condition).copied().unwrap_or(OpId::NULL);
println!("{indent}{bold}if{reset} r{condition} {{");
indent += " ";
}
Op::EndIf | Op::EndLoop => {
if indent.len() > 1 {
indent.pop();
indent.pop();
}
println!("{indent}}}");
}
Op::Vectorize { ref ops } => {
let dtype = dtypes[&ops[0]];
dtypes.insert(op_id, dtype);
let ops: Vec<OpId> = ops.iter().map(|x| id_map.get(x).copied().unwrap_or(OpId::NULL)).collect();
if let Some((lb, ub)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {orange}vec{reset}{ops:?} // {lb}..={ub}");
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {orange}vec{reset}{ops:?}");
}
}
Op::Devectorize { vec, idx } => {
let dtype = dtypes[&vec];
dtypes.insert(op_id, dtype);
let vec = id_map.get(&vec).copied().unwrap_or(OpId::NULL);
if let Some((l, u)) = bounds.get(&op_id) {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = r{vec}{orange}.s{idx}{reset} // {l}..={u}",);
} else {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = r{vec}{orange}.s{idx}{reset}");
}
}
Op::Move { x, ref mop } => {
let dtype = dtypes[&x];
dtypes.insert(op_id, dtype);
let x = id_map.get(&x).copied().unwrap_or(OpId::NULL);
match mop.as_ref() {
MoveOp::Reshape { shape } => {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}reshape{reset} r{x} -> {shape:?}");
}
MoveOp::Expand { shape } => {
println!("{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}expand{reset} r{x} -> {shape:?}");
}
MoveOp::Permute { axes, shape } => {
println!(
"{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}permute{reset} r{x} axes={axes:?} -> {shape:?}",
);
}
MoveOp::Pad { padding, shape } => {
println!(
"{indent}r{out_id}{grey}: {dtype}{reset} = {cyan}pad{reset} r{x} padding={padding:?} -> {shape:?}",
);
}
}
}
Op::Barrier => {
println!("{indent}barrier");
}
}
op_id = self.ops[op_id].next;
}
println!()
}
}