1use core::ops::{Deref, DerefMut};
2use std::collections::VecDeque;
3
4use cubecl_core::{
5 ir::{self, Builtin, Id, ValueKind},
6 prelude::KernelDefinition,
7};
8use cubecl_opt::NodeIndex;
9use hashbrown::{HashMap, HashSet};
10use rspirv::{
11 dr,
12 spirv::{
13 self, BuiltIn, CooperativeMatrixLayout, CooperativeMatrixUse, MemoryAccess, Scope,
14 StorageClass, Word,
15 },
16};
17
18use crate::{
19 SpirvCompiler, SpirvTarget,
20 item::{Elem, Item},
21 value::ConstVal,
22};
23
24#[derive(Clone, Debug, Default)]
25pub struct CompilerState {
26 pub extra_funcs: HashMap<Id, FuncDefinition>,
27
28 pub scalar_bindings: HashMap<ir::StorageType, u32>,
29 pub params: Word,
30 pub info: Option<Buffer>,
31 pub cube_dims: Vec<Word>,
32 pub cube_size: Word,
33
34 pub loops: VecDeque<Loop>,
36
37 pub debug_types: HashSet<Word>,
38
39 pub base_lookups: LookupTables,
42 pub lookups: LookupTables,
44}
45
46impl Deref for CompilerState {
47 type Target = LookupTables;
48
49 fn deref(&self) -> &Self::Target {
50 &self.lookups
51 }
52}
53
54impl DerefMut for CompilerState {
55 fn deref_mut(&mut self) -> &mut Self::Target {
56 &mut self.lookups
57 }
58}
59
60#[derive(Clone, Debug, Default)]
61pub struct LookupTables {
62 pub buffers: Vec<Buffer>,
63 pub shared: HashMap<Id, SharedVal>,
64
65 pub globals: HashMap<Builtin, Word>,
66 pub loaded_builtins: HashMap<BuiltIn, Word>,
67 pub used_builtins: HashMap<BuiltIn, (Word, Item)>,
68
69 pub constants: HashMap<(ConstVal, Item), Word>,
70 pub values: HashMap<Id, Word>,
71 pub labels: HashMap<NodeIndex, Word>,
72 pub end_labels: HashMap<NodeIndex, Word>,
73}
74
75#[derive(Clone, Debug)]
76pub struct FuncDefinition {
77 pub type_id: Word,
78 pub id: Word,
79}
80
81#[derive(Clone, Debug)]
82pub struct SharedVal {
83 pub id: Word,
84 pub val_id: Word,
85 pub ptr_ty_id: Word,
86 pub item: Item,
87 pub offset: u32,
88 pub align: u32,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq)]
92#[allow(missing_docs)]
93pub struct Matrix {
94 pub id: Word,
95 pub ident: CooperativeMatrixUse,
96 pub scope: Scope,
97 pub m: u32,
98 pub n: u32,
99 pub k: u32,
100 pub elem: Elem,
101 pub layout: Option<CooperativeMatrixLayout>,
102}
103
104#[derive(Clone, Debug)]
105pub struct Loop {
106 pub header: Word,
107 pub continue_target: Word,
108 pub post: Word,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq)]
112pub struct Buffer {
113 pub id: Word,
114 pub struct_ty_id: Word,
115 pub struct_ptr_ty_id: Word,
116 pub arr_ty_id: Word,
117 pub arr_ptr_ty_id: Word,
118 pub storage_class: StorageClass,
119}
120
121impl<T: SpirvTarget> SpirvCompiler<T> {
122 pub fn init_base_state(&mut self, kernel: &mut KernelDefinition) {
123 let cube_dims = [kernel.cube_dim.x, kernel.cube_dim.y, kernel.cube_dim.z];
124 self.state.cube_dims = cube_dims.iter().map(|dim| self.const_u32(*dim)).collect();
125 self.state.cube_size = self.const_u32(cube_dims.iter().product());
126
127 let mut target = self.target.clone();
128
129 let max_vector_size = self.compilation_options.vulkan.max_vector_size;
130 for binding in &mut kernel.buffers {
131 if binding.value.ty.vector_size() > max_vector_size {
134 binding.value.ty = binding.value.ty.with_vector_size(max_vector_size);
135 }
136 }
137
138 let opt = self.opt.clone();
139 let mut visibility = opt.global_state.buffer_visibility.borrow_mut();
140 visibility.resize(kernel.num_global_buffers(), Default::default());
142 self.state.base_lookups.buffers =
143 target.generate_params(self, &kernel.buffers, &visibility);
144
145 let shared_liveness = self.shared_liveness.clone();
146 for alloc in shared_liveness.allocations.values() {
147 let smem_id = self.id();
148 let smem_ptr_ty_id = self.id();
149
150 let smem_val_id = if self.compilation_options.vulkan.supports_explicit_smem {
151 self.id()
152 } else {
153 smem_id
154 };
155
156 let item = self.compile_type(alloc.smem.value_ty);
157 self.state.base_lookups.shared.insert(
158 alloc.id,
159 SharedVal {
160 id: smem_id,
161 val_id: smem_val_id,
162 ptr_ty_id: smem_ptr_ty_id,
163 item,
164 offset: alloc.offset as u32,
165 align: alloc.smem.alignment as u32,
166 },
167 );
168 }
169
170 self.state.lookups = self.state.base_lookups.clone();
171 }
172
173 pub fn init_kernel_state(&mut self, kernel: KernelDefinition) {
174 self.state.scalar_bindings = kernel
175 .scalars
176 .into_iter()
177 .enumerate()
178 .map(|(i, arg)| (arg.ty, i as u32))
179 .collect();
180 self.state.lookups = self.state.base_lookups.clone();
181 }
182
183 fn dedup_const(&mut self, inst: &dr::Instruction) -> Option<Word> {
184 self.module_ref()
185 .types_global_values
186 .iter()
187 .find(|it| {
188 it.class == inst.class
189 && it.result_type == inst.result_type
190 && it.operands == inst.operands
191 })
192 .and_then(|it| it.result_id)
193 }
194
195 pub fn dedup_constant_bit32(&mut self, ty: Word, val: u32) -> Word {
196 let inst = dr::Instruction::new(
197 spirv::Op::Constant,
198 Some(ty),
199 None,
200 vec![dr::Operand::LiteralBit32(val)],
201 );
202 if let Some(id) = self.dedup_const(&inst) {
203 id
204 } else {
205 self.constant_bit32(ty, val)
206 }
207 }
208
209 pub fn dedup_constant_bit64(&mut self, ty: Word, val: u64) -> Word {
210 let inst = dr::Instruction::new(
211 spirv::Op::Constant,
212 Some(ty),
213 None,
214 vec![dr::Operand::LiteralBit64(val)],
215 );
216 if let Some(id) = self.dedup_const(&inst) {
217 id
218 } else {
219 self.constant_bit64(ty, val)
220 }
221 }
222
223 pub fn const_u32(&mut self, value: u32) -> Word {
224 let ty = Item::Scalar(Elem::Int(32, false));
225 let ty_id = ty.id(self);
226 self.dedup_constant_bit32(ty_id, value)
227 }
228
229 pub fn insert_builtin(
230 &mut self,
231 builtin: BuiltIn,
232 insert: impl FnOnce(&mut Self) -> Word,
233 ) -> Word {
234 if let Some(id) = self.state.loaded_builtins.get(&builtin) {
235 *id
236 } else {
237 let id = self.insert_in_setup(insert);
238 self.state.loaded_builtins.insert(builtin, id);
239 id
240 }
241 }
242
243 pub fn insert_global(
244 &mut self,
245 builtin: Builtin,
246 insert: impl FnOnce(&mut Self) -> Word,
247 ) -> Word {
248 if let Some(id) = self.state.globals.get(&builtin) {
249 *id
250 } else {
251 let id = self.insert_in_setup(insert);
252 self.state.globals.insert(builtin, id);
253 id
254 }
255 }
256
257 pub fn insert_in_setup(&mut self, insert: impl FnOnce(&mut Self) -> Word) -> Word {
258 let current_block = self.selected_block();
259 let setup = self.setup_block;
260 self.select_block(Some(setup)).unwrap();
261 let id = insert(self);
262 self.select_block(current_block).unwrap();
263 id
264 }
265
266 pub fn insert_in_root(&mut self, insert: impl FnOnce(&mut Self) -> Word) -> Word {
267 let current_block = self.selected_block();
268 self.select_block(None).unwrap();
269 let id = insert(self);
270 self.select_block(current_block).unwrap();
271 id
272 }
273
274 pub fn get_value(&mut self, id: Id) -> Word {
275 if let Some(existing) = self.state.values.get(&id) {
276 *existing
277 } else {
278 let word = self.id();
279 self.state.values.insert(id, word);
280 self.debug_val_name(word, id);
281 word
282 }
283 }
284
285 pub fn insert_value(&mut self, id: Id, word: Word) {
286 self.state.values.insert(id, word);
287 }
288
289 pub fn label(&mut self, block: NodeIndex) -> Word {
290 if let Some(existing) = self.state.labels.get(&block) {
291 *existing
292 } else {
293 let word = self.id();
294 self.debug_name(word, format!("bb{}", block.index()));
295 self.state.labels.insert(block, word);
296 word
297 }
298 }
299
300 pub fn end_label(&mut self, block: NodeIndex) -> Word {
301 if let Some(existing) = self.state.end_labels.get(&block) {
302 *existing
303 } else {
304 let word = self.label(block);
305 self.state.end_labels.insert(block, word);
306 word
307 }
308 }
309
310 pub fn init_function_param(&mut self, param: ir::Value, param_id: Word) {
311 let item = self.compile_type(param.ty);
312 match param.kind {
313 ValueKind::Constant(value) => {
314 let const_val = (value, item.clone()).into();
315 self.state.constants.insert((const_val, item), param_id);
316 }
317 ValueKind::Value { id } => {
318 self.state.values.insert(id, param_id);
319 }
320 }
321 }
322
323 pub fn end_function_and_reset_lookups(&mut self) {
324 self.builder.end_function().unwrap();
325 self.state.lookups = self.state.base_lookups.clone();
326 }
327
328 pub fn global_scalar(&mut self, id: Id, ty: ir::StorageType) -> Word {
329 self.insert_in_setup(|b| {
330 let field_id = b.const_u32(b.state.scalar_bindings[&ty]);
331 let offset = b.const_u32(id);
332 let item = b.compile_type(ir::Type::new(ty));
333 let align = item.size();
334 let ty_id = item.id(b);
335 let storage_class = T::info_storage_class(b);
336 let ptr_ty = Item::Pointer(storage_class, Box::new(item)).id(b);
337 let info = b.state.info.unwrap().id;
338 let access = b
339 .in_bounds_access_chain(ptr_ty, None, info, [field_id, offset])
340 .unwrap();
341 b.load(
342 ty_id,
343 None,
344 access,
345 Some(MemoryAccess::ALIGNED),
346 [align.into()],
347 )
348 .unwrap()
349 })
350 }
351}