use baracuda_kernels_types::{ArchSku, OperandDesc};
use fuel_graph::{Graph, NodeId};
use fuel_ir::probe::BackendId;
use fuel_kernel_seam::{JitBudget, JitRequest};
use crate::jit_adopt::dtype_to_element_kind;
use crate::kernel::KernelBindingTable;
pub fn jit_request_for_unplaceable_fused(
graph: &Graph,
id: NodeId,
backend: BackendId,
table: &KernelBindingTable,
arch: ArchSku,
budget: JitBudget,
) -> Option<JitRequest> {
let node = graph.node(id);
let fused_id = match &node.op {
fuel_graph::Op::Fused(fid, _) => *fid,
_ => return None,
};
let mut key_dtypes: Vec<fuel_ir::DType> =
node.inputs.iter().map(|&i| graph.node(i).dtype).collect();
key_dtypes.push(node.dtype);
if let Some(op_kind) =
crate::runtime_fused_kernels::static_fused_id_to_binding_table_op_kind(fused_id)
&& table.lookup(op_kind, &key_dtypes, backend).is_ok()
{
return None; }
let region = fuel_graph::registry::recipe_for(graph, id)?;
let mut operands: Vec<OperandDesc> = Vec::with_capacity(node.inputs.len() + 1);
for &input in &node.inputs {
operands.push(operand_desc(graph, input)?);
}
operands.push(operand_desc(graph, id)?);
Some(JitRequest::new(region, operands, arch, budget))
}
fn operand_desc(graph: &Graph, id: NodeId) -> Option<OperandDesc> {
let node = graph.node(id);
let dims = node.shape.dims();
let ek = dtype_to_element_kind(node.dtype)?;
let mut strides = vec![1i64; dims.len()];
for i in (0..dims.len().saturating_sub(1)).rev() {
strides[i] = strides[i + 1] * dims[i + 1] as i64;
}
let shape: Vec<i64> = dims.iter().map(|&d| d as i64).collect();
let align = node.dtype.size_in_bytes() as u32;
Some(OperandDesc::new(dims.len(), &shape, &strides, ek, align))
}
#[cfg(test)]
mod tests {
use super::*;
use fuel_graph::registry::{FusedOpParams, FusedOps};
use fuel_graph::{Node, Op};
use fuel_ir::{DType, Shape};
fn leaf(g: &mut Graph, dims: &[usize], dtype: DType) -> NodeId {
g.push(Node {
op: Op::Const,
inputs: vec![],
shape: Shape::from_dims(dims),
dtype,
})
}
fn paged_node(g: &mut Graph) -> (NodeId, FusedOpParams) {
let q = leaf(g, &[1, 4, 1, 4], DType::F32);
let kc = leaf(g, &[32, 4, 4, 4], DType::F32);
let vc = leaf(g, &[32, 4, 4, 4], DType::F32);
let bt = leaf(g, &[1, 8], DType::U32);
let cl = leaf(g, &[1], DType::U32);
let params = FusedOpParams::PagedAttn {
softmax_scale: 0.5,
block_size: 4,
softcap: None,
};
let id = g.push(Node {
op: Op::Fused(FusedOps::PAGED_ATTN, params.clone()),
inputs: vec![q, kc, vc, bt, cl],
shape: Shape::from_dims(&[1, 4, 1, 4]),
dtype: DType::F32,
});
(id, params)
}
#[test]
fn builds_a_request_for_a_fused_op_with_no_kernel_here() {
use baracuda_kernels_types::ElementKind;
let mut g = Graph::new();
let (id, _) = paged_node(&mut g);
let table = KernelBindingTable::new();
let req = jit_request_for_unplaceable_fused(
&g,
id,
BackendId::Cuda,
&table,
ArchSku::Sm89,
JitBudget {
max_compile_ms: 1_000,
},
)
.expect("a fused op with no kernel here yields a request");
assert_eq!(
req.operands.len(),
6,
"5 inputs + 1 output, bind order then output"
);
assert_eq!(req.operands[0].dtype, ElementKind::F32, "q is F32");
assert_eq!(
req.operands[3].dtype,
ElementKind::U32,
"block_table must be U32 — widening it to I32 makes every operand \
uniform and turns a real MixedDtype decline into a false accept",
);
assert_eq!(
req.operands[4].dtype,
ElementKind::U32,
"context_lens must be U32"
);
assert_eq!(req.operands[5].dtype, ElementKind::F32, "output is F32");
assert!(
matches!(req.region, fuel_graph::jit::PatternNode::Op { .. }),
"the region must be a real Op tree, not a bare bind",
);
}
#[test]
fn declines_a_non_fused_node() {
let mut g = Graph::new();
let c = leaf(&mut g, &[4], DType::F32);
let table = KernelBindingTable::new();
assert!(
jit_request_for_unplaceable_fused(
&g,
c,
BackendId::Cuda,
&table,
ArchSku::Sm89,
JitBudget {
max_compile_ms: 1_000
},
)
.is_none(),
"only fused ops have a region to hand over",
);
}
#[test]
fn declines_rather_than_approximating_an_unspellable_dtype() {
let mut g = Graph::new();
let q = leaf(&mut g, &[1, 4, 1, 4], DType::F32);
let kc = leaf(&mut g, &[32, 4, 4, 4], DType::F32);
let vc = leaf(&mut g, &[32, 4, 4, 4], DType::F32);
let bt = leaf(&mut g, &[1, 8], DType::I16);
let cl = leaf(&mut g, &[1], DType::U32);
let id = g.push(Node {
op: Op::Fused(
FusedOps::PAGED_ATTN,
FusedOpParams::PagedAttn {
softmax_scale: 0.5,
block_size: 4,
softcap: None,
},
),
inputs: vec![q, kc, vc, bt, cl],
shape: Shape::from_dims(&[1, 4, 1, 4]),
dtype: DType::F32,
});
let table = KernelBindingTable::new();
assert!(
jit_request_for_unplaceable_fused(
&g,
id,
BackendId::Cuda,
&table,
ArchSku::Sm89,
JitBudget {
max_compile_ms: 1_000
},
)
.is_none(),
"an unspellable operand dtype must decline, not approximate",
);
}
}