logicaffeine_compile/spatial/lower/tensix/
mod.rs1mod args;
14mod cb;
15mod host;
16mod kernel;
17mod oracle;
18mod roles;
19
20pub use args::{ArgSlot, ArgSource, RuntimeArgLayout};
21pub use cb::{CbPlan, CbSlot};
22pub use host::mcast_corners;
23pub use roles::{KernelClass, Launch};
24
25use super::{
26 require_one_plan, require_verified, Census, SpatialArtifact, SpatialBackend, SpatialLowerError,
27};
28use crate::spatial::ir::*;
29use crate::spatial::target::{Fidelity, SpatialTarget};
30
31#[derive(Clone, Debug, PartialEq)]
32pub enum KernelRole {
33 Movement { engine: u8 },
34 Compute { fp32_acc: bool, fidelity: Fidelity },
35}
36
37#[derive(Clone, Debug, PartialEq)]
40pub struct Kernel {
41 pub name: String,
42 pub role: KernelRole,
43 pub source: String,
44 pub launches: Vec<Launch>,
45 pub compile_args: Vec<(String, u32)>,
46}
47
48impl Kernel {
49 pub fn sites(&self) -> Vec<Site> {
51 self.launches
52 .iter()
53 .flat_map(|l| l.sites.iter().copied())
54 .collect()
55 }
56}
57
58#[derive(Clone, Debug, PartialEq)]
59pub struct TensixArtifact {
60 pub name: String,
61 pub kernels: Vec<Kernel>,
62 pub host_cpp: String,
63 pub cmake: String,
64 pub runtime_arg_layout: RuntimeArgLayout,
65 pub oracle_c: String,
66 pub census: Census,
67 pub dprint_cores: Option<String>,
68 pub pass_marker: &'static str,
69 pub cfg_line: String,
71}
72
73pub fn bundle_files(art: &TensixArtifact) -> Vec<(String, String)> {
75 let mut files = vec![("host.cpp".to_string(), art.host_cpp.clone())];
76 for k in &art.kernels {
77 files.push((format!("kernels/{}", k.name), k.source.clone()));
78 }
79 files
80}
81
82pub fn bundle_dir_files(art: &TensixArtifact) -> Vec<(String, String)> {
84 let mut files = bundle_files(art);
85 files.push(("CMakeLists.txt".to_string(), art.cmake.clone()));
86 files.push(("logos_oracle.c".to_string(), art.oracle_c.clone()));
87 files
88}
89
90pub struct TensixBackend {
91 pub target: SpatialTarget,
92}
93
94impl TensixBackend {
95 pub fn new(target: SpatialTarget) -> Self {
96 TensixBackend { target }
97 }
98
99 pub fn printed_kernels(&self, p: &SpatialProgram) -> Result<Vec<Kernel>, SpatialLowerError> {
109 Ok(self.print_all(p)?.0)
110 }
111
112 pub fn compute_kernel_source(&self, p: &SpatialProgram) -> Result<String, SpatialLowerError> {
115 self.printed_kernels(p)?
116 .into_iter()
117 .find(|k| matches!(k.role, KernelRole::Compute { .. }))
118 .map(|k| k.source)
119 .ok_or(SpatialLowerError::Unsupported(
120 "a program with no compute kernel",
121 ))
122 }
123
124 #[allow(clippy::type_complexity)]
128 fn print_all(
129 &self,
130 p: &SpatialProgram,
131 ) -> Result<
132 (
133 Vec<Kernel>,
134 cb::CbPlan,
135 Vec<roles::KernelClass>,
136 RuntimeArgLayout,
137 bool,
138 Fidelity,
139 ),
140 SpatialLowerError,
141 > {
142 require_verified(p)?;
143 require_one_plan(p)?;
144 let target = &self.target;
145 if target.tile.rows != 32 || target.tile.cols != 32 {
146 return Err(SpatialLowerError::Unsupported("Tensix tiles are 32×32"));
147 }
148 let plan = p
149 .work
150 .plan
151 .ok_or(SpatialLowerError::Unsupported("a tiled plan"))?;
152 let exact_f32 = p.numerics == NumericsPolicy::Exact && plan.acc == AccPolicy::F32;
153 if matches!(plan.acc, AccPolicy::I32 | AccPolicy::I64) {
154 return Err(SpatialLowerError::Unsupported(
155 "integer accumulate on a float-primary matrix engine",
156 ));
157 }
158 let fidelity = target
159 .matmul_fidelity()
160 .ok_or(SpatialLowerError::Unsupported("a matrix engine"))?;
161 let cbs = cb::CbPlan::of(p, target)?;
162 let classes = roles::classes(p)?;
163 let mut layout = RuntimeArgLayout::default();
164 let mut kernels = Vec::new();
165 for class in &classes {
166 let site_slots = args::slots_for(p, class)?;
167 let (source, slots) =
168 kernel::print_kernel(p, target, &cbs, class, &site_slots, exact_f32)?;
169 let role = match class.engine {
170 Engine::Movement(e) => KernelRole::Movement { engine: e },
171 Engine::Compute => KernelRole::Compute {
172 fp32_acc: exact_f32,
173 fidelity,
174 },
175 };
176 kernels.push(Kernel {
177 name: class.file_name.clone(),
178 role,
179 source,
180 launches: class.launches.clone(),
181 compile_args: Vec::new(),
182 });
183 layout.per_kernel.push((class.file_name.clone(), slots));
184 }
185 Ok((kernels, cbs, classes, layout, exact_f32, fidelity))
186 }
187
188 pub fn lower_tensix(&self, p: &SpatialProgram) -> Result<TensixArtifact, SpatialLowerError> {
189 let target = &self.target;
190 let (kernels, cbs, classes, layout, exact_f32, fidelity) = self.print_all(p)?;
191 let cfg_line = host::cfg_line(p);
192 let host_cpp = host::print_host(
193 p, target, &cbs, &classes, &layout, exact_f32, fidelity, &cfg_line,
194 )?;
195 let cmake = host::print_cmake();
196 let oracle_c = oracle::print_oracle(p)?;
197 Ok(TensixArtifact {
198 name: p.name.clone(),
199 kernels,
200 host_cpp,
201 cmake,
202 runtime_arg_layout: layout,
203 oracle_c,
204 census: Census::of(p),
205 dprint_cores: None,
206 pass_marker: "LOGOS_MM_PASS",
207 cfg_line,
208 })
209 }
210}
211
212impl SpatialBackend for TensixBackend {
213 fn name(&self) -> &str {
214 self.target.name
215 }
216 fn target(&self) -> &SpatialTarget {
217 &self.target
218 }
219 fn lower(&self, p: &SpatialProgram) -> Result<SpatialArtifact, SpatialLowerError> {
220 Ok(SpatialArtifact::Tensix(self.lower_tensix(p)?))
221 }
222}
223
224pub(crate) fn template_name(p: &SpatialProgram, b: &Buffer) -> String {
226 let name = &b.name;
227 let base = name.split('@').next().unwrap_or(name);
228 let _ = p;
229 base.to_string()
230}
231
232pub(crate) fn template_of(b: &Buffer) -> BufferId {
234 b.origin.unwrap_or(b.id)
235}