logicaffeine_compile/spatial/
shim.rs1use super::target::SpatialTarget;
12use crate::tensor_graph::graph::{Op, Sym, TensorGraph};
13use crate::tensor_graph::numerics::{holds_accumulator, Bound, Contract, Keep};
14use crate::tensor_graph::shim::{Shim, Support};
15use crate::tensor_graph::symbols::ClassId;
16
17pub struct SpatialShim<'a> {
19 pub target: &'a SpatialTarget,
20}
21
22impl Shim for SpatialShim<'_> {
23 fn name(&self) -> String {
24 format!("spatial:{}", self.target.name)
25 }
26
27 fn supports(&self, class: ClassId) -> Support {
28 use ClassId::*;
29 use Support::{Decompose, Native, Refuse};
30 match class {
31 NormWeight | FlashDecode | FlashPrefill | GemmDequantInkernel | RouterTopk | MoeGateUpAct | MoeDownCombine | GdnStep => Native,
32 PersistentLayerMegakernel => Native,
33 AddNorm | NormThenAdd | NormScale | NormWeightOffset | NormQuant | AddNormQuant | DualNorm | GatedNorm | EmbedNorm => Decompose,
34 EmbedScale | GemvNormEpilogue | PrefillNormEpilogues | AttnResidualNorm | RecomputeFanout | QkNormRope => Decompose,
35 QkNormRopeKvAppend | RopeKvAppend | RopeQkOneLaunch | QkNormRopeGateSplit | QkvHeadEpilogue | HorizontalNormRopeInsert => Decompose,
36 CrossLayerLaunch | Mla | MlaPrologue | AttnOutputQuant | AttnOutputGate | RowWriteQuant | MlaUnabsorbInFold | GluActMul | GluActMulQuant => Decompose,
37 ElementwiseRegions => Native,
38 GemvGateUpGlu | ElementwiseChains | ReluSqr | MaskedExpertActivation | GemmBias | GemmScale => Decompose,
39 MergedQkv | MergedGateUp | AffineInputFold | RouterSharedGateMerge | MoeTokenSort | MoeGatherInOperand => Decompose,
40 MoeDeferredFinalize | SharedExpertFusion | GdnInputEpilogue | GdnDecodeWhole | GdnPostConvPrep | ConvBiasSilu => Decompose,
41 ConvParts | GdnGateProjections | GdnGating | FusedSampling | LastLayerRows | AllreduceNorm => Decompose,
42 ConvSiluL2norm | GdnOutputQuant | GemvQuant | ElementwiseQuant => Decompose,
43 StridedRead | RowWrite | JoinInPlace | StridedValues => Refuse("a section reads and writes each tensor through its own access map"),
44 Softcap => Native,
45 AttentionVariantsInline => Refuse("the attention sections attend a prefix of the cache, with no window or sink"),
46 MixedBatchAttention | SparseAttention => Refuse("the attention sections attend every cached row of one sequence's prefix"),
47 AttnGqaRowPacking | AttnLseMerge => Refuse("an attention section holds its scores whole, never in chunks"),
48 GroupedTopk => Refuse("the expert sections route by one top-k over every expert"),
49 Sampling => Refuse("a spatial step picks its token by arg-max"),
50 KvCacheQuant | HadamardRotation | CompensatedResidual => Refuse("a spatial chip holds its values in the precision the target's datapath reads"),
51 HostResidentKv | MoeDirectPairs => Refuse("a spatial program places every tensor on the chip's own memory"),
52 CudaGraphs => Refuse("a spatial program is placed and scheduled whole, never captured as launches"),
53 }
54 }
55
56 fn fits(&self, op: &Op, g: &TensorGraph) -> Result<(), String> {
57 holds_accumulator(op, g, self.target.matmul_acc_int_bits())?;
58 match &op.sym {
59 Sym::Prim(_) => Ok(()),
60 Sym::View { .. } => Err("a section reads a row of a table through its own access map, never a view of it".to_string()),
61 Sym::Quantize(_) => Err("a spatial chip reads no activation quantized".to_string()),
62 Sym::Fused(f) if f.class == ClassId::ElementwiseRegions => {
63 let read_once = f.parts.iter().filter(|m| m.out != op.out).all(|m| f.parts.iter().flat_map(|p| &p.ins).filter(|t| **t == m.out).count() == 1);
66 if op.keeps.is_empty() && read_once {
67 Ok(())
68 } else {
69 Err("a region is one section where each value it makes is read once within it and none is written out".to_string())
70 }
71 }
72 Sym::Fused(_) => Err("a spatial chip runs every fused op as its parts".to_string()),
73 }
74 }
75
76 fn numerics(&self) -> Option<Contract> {
77 use crate::codegen_sva::fabric_target::Precision;
78 let keep = if self.target.matmul_supports(Precision::Bf16) {
80 Keep::Bf16
81 } else if self.target.matmul_supports(Precision::Fp16) {
82 Keep::F16
83 } else {
84 Keep::F32
85 };
86 let point = format!("spatial:{}", self.target.name);
87 let fused = Bound { of_block: 0.0, of_value: crate::spatial::equiv::APPROX_REL_TOL, floor: crate::spatial::equiv::APPROX_REL_TOL };
90 Some(Contract { point, activations: None, cache: keep, state: keep, attention: None, fused, accumulator_bits: self.target.matmul_acc_int_bits() })
91 }
92}