1use alloc::collections::BTreeMap;
2use alloc::{borrow::Cow, rc::Rc, string::String, string::ToString, vec::Vec};
3use core::{
4 any::TypeId,
5 cell::{Ref, RefCell, RefMut},
6 fmt::Display,
7};
8use derive_more::{Eq, PartialEq};
9use enumset::EnumSet;
10use hashbrown::{HashMap, HashSet};
11use itertools::Itertools;
12
13use crate::{
14 AddressSpace, AggregateExtractOperands, CubeFnSource, DeviceProperties, FastMath, Function,
15 OpaqueType, Operation, OperationReflect, Processor, SourceLoc, StorageType, TargetProperties,
16 TypeHash, arena::DropBump,
17};
18
19use super::{Allocator, Id, Instruction, Type, Value, processing::ScopeProcessing};
20
21pub type TypeMap = HashMap<TypeId, StorageType>;
22pub type SizeMap = HashMap<TypeId, usize>;
23
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[derive(Debug, Clone, PartialEq, Eq, TypeHash)]
33#[allow(missing_docs)]
34pub struct Scope {
35 validation_errors: ValidationErrors,
36 pub depth: u8,
37 pub instructions: RefCell<Vec<Instruction>>,
38 pub return_value: Option<Value>,
39 pub locals: RefCell<Vec<Value>>,
40 pub debug: DebugInfo,
41
42 #[cfg_attr(feature = "serde", serde(skip))]
43 pub global_state: GlobalState,
44}
45
46pub type GlobalState = Rc<RefCell<GlobalStateInner>>;
47
48#[derive(Debug, PartialEq, Eq, TypeHash, Default)]
49pub struct GlobalStateInner {
50 #[partial_eq(skip)]
51 #[eq(skip)]
52 pub reference_arena: DropBump,
53 pub allocator: Allocator,
54
55 pub global_args: Vec<Value>,
56 pub functions: BTreeMap<Id, Function>,
57 pub typemap: TypeMap,
58 pub sizemap: SizeMap,
59 pub modes: InstructionModes,
60 pub target_properties: TargetProperties,
61 pub device_properties: Option<Rc<DeviceProperties>>,
62}
63
64impl GlobalStateInner {
65 pub fn clone_deep(&self) -> Self {
66 Self {
67 reference_arena: DropBump::new(),
68 allocator: self.allocator.clone_deep(),
69 global_args: self.global_args.clone(),
70 functions: self.functions.clone(),
71 typemap: self.typemap.clone(),
72 sizemap: self.sizemap.clone(),
73 modes: self.modes,
74 target_properties: self.target_properties.clone(),
75 device_properties: self.device_properties.clone(),
76 }
77 }
78}
79
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[derive(Debug, Clone, PartialEq, Eq, TypeHash)]
82pub struct ValidationErrors {
83 errors: Rc<RefCell<Vec<String>>>,
84}
85
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[derive(Debug, Clone, PartialEq, Eq, TypeHash)]
89pub struct DebugInfo {
90 pub enabled: bool,
91 pub sources: Rc<RefCell<HashSet<CubeFnSource>>>,
92 pub value_names: Rc<RefCell<HashMap<Value, Cow<'static, str>>>>,
93 pub source_loc: RefCell<Option<SourceLoc>>,
94 pub entry_loc: RefCell<Option<SourceLoc>>,
95}
96
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
99#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, TypeHash)]
100pub struct InstructionModes {
101 pub fp_math_mode: EnumSet<FastMath>,
102}
103
104impl core::hash::Hash for Scope {
105 fn hash<H: core::hash::Hasher>(&self, ra_expand_state: &mut H) {
106 self.depth.hash(ra_expand_state);
107 self.instructions.borrow().hash(ra_expand_state);
108 self.locals.borrow().hash(ra_expand_state);
109 }
110}
111
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TypeHash)]
114#[allow(missing_docs)]
115pub enum ReadingStrategy {
116 OutputLayout,
118 Plain,
120}
121
122impl Scope {
123 pub fn device_properties(&self, properties: &DeviceProperties) {
125 self.state_mut().device_properties = Some(Rc::new(properties.clone()));
126 }
127
128 pub fn state(&self) -> Ref<'_, GlobalStateInner> {
129 self.global_state.borrow()
130 }
131
132 pub fn state_mut(&self) -> RefMut<'_, GlobalStateInner> {
133 self.global_state.borrow_mut()
134 }
135
136 pub fn root(debug_enabled: bool) -> Self {
140 Self {
141 validation_errors: ValidationErrors {
142 errors: Rc::new(RefCell::new(Vec::new())),
143 },
144 depth: 0,
145 instructions: Default::default(),
146 return_value: None,
147 locals: Default::default(),
148 debug: DebugInfo {
149 enabled: debug_enabled,
150 sources: Default::default(),
151 value_names: Default::default(),
152 source_loc: Default::default(),
153 entry_loc: Default::default(),
154 },
155 global_state: Default::default(),
156 }
157 }
158
159 pub fn with_global_state(mut self, global_state: GlobalState) -> Self {
161 self.global_state = global_state;
162 self
163 }
164
165 pub fn create_value(&self, ty: Type) -> Value {
167 let id = self.new_local_index();
168 Value::new(id, ty)
169 }
170
171 pub fn create_local_mut(&self, value_ty: impl Into<Type>) -> Value {
173 let value_ty = value_ty.into();
174 let ty = Type::Pointer(value_ty.intern(), AddressSpace::Local);
175 let out = self.create_value(ty);
176 self.register(Instruction::new(
177 Operation::DeclareVariable {
178 value_ty,
179 addr_space: AddressSpace::Local,
180 alignment: value_ty.align(),
181 },
182 out,
183 ));
184 out
185 }
186
187 pub fn create_shared(&self, value_ty: impl Into<Type>, alignment: Option<usize>) -> Value {
189 let value_ty = value_ty.into();
190 let ty = Type::Pointer(value_ty.intern(), AddressSpace::Shared);
191 let out = self.create_value(ty);
192 self.register(Instruction::new(
193 Operation::DeclareVariable {
194 value_ty,
195 addr_space: AddressSpace::Shared,
196 alignment: alignment.unwrap_or_else(|| value_ty.align()),
197 },
198 out,
199 ));
200 out
201 }
202
203 pub fn create_function(&self, explicit_params: Vec<Value>, scope: Scope) -> Id {
205 let id = self.state().allocator.new_local_index();
206 self.state_mut().functions.insert(
207 id,
208 Function {
209 explicit_params,
210 scope,
211 },
212 );
213 id
214 }
215
216 pub fn register<T: Into<Instruction>>(&self, instruction: T) {
218 let mut inst = instruction.into();
219 inst.operation.sanitize_args(self);
220 inst.source_loc = self.debug.source_loc.borrow().clone();
221 inst.modes = self.state().modes;
222 self.instructions.borrow_mut().push(inst)
223 }
224
225 pub fn create_kernel_ref<'a, T>(&self, value: T) -> &'a mut T
230 where
231 T: 'a,
232 {
233 let mut state = self.state_mut();
234 let reference = state.reference_arena.alloc(value);
235 unsafe { core::mem::transmute(reference) }
236 }
237
238 pub fn resolve_type<T: 'static>(&self) -> Option<StorageType> {
240 let state = self.state();
241 let result = state.typemap.get(&TypeId::of::<T>());
242
243 result.cloned()
244 }
245
246 pub fn resolve_size<T: 'static>(&self) -> Option<usize> {
248 let state = self.state();
249 let result = state.sizemap.get(&TypeId::of::<T>());
250
251 result.cloned()
252 }
253
254 pub fn register_type<T: 'static>(&self, elem: StorageType) {
256 let mut state = self.state_mut();
257
258 state.typemap.insert(TypeId::of::<T>(), elem);
259 }
260
261 pub fn register_size<T: 'static>(&self, size: usize) {
263 let mut state = self.state_mut();
264
265 state.sizemap.insert(TypeId::of::<T>(), size);
266 }
267
268 pub fn child(&self) -> Self {
270 Self {
271 validation_errors: self.validation_errors.clone(),
272 depth: self.depth + 1,
273 instructions: Default::default(),
274 return_value: None,
275 locals: Default::default(),
276 debug: self.debug.clone(),
277 global_state: self.global_state.clone(),
278 }
279 }
280
281 pub fn push_error(&self, msg: impl Into<String>) {
283 self.validation_errors.errors.borrow_mut().push(msg.into());
284 }
285
286 pub fn pop_errors(&self) -> Vec<String> {
288 self.validation_errors.errors.replace_with(|_| Vec::new())
289 }
290
291 pub fn process<'a>(
298 &self,
299 processors: impl IntoIterator<Item = &'a dyn Processor>,
300 ) -> ScopeProcessing {
301 self.global_state.borrow_mut().reference_arena.reset();
302
303 let mut instructions = Vec::new();
304
305 for inst in self.instructions.borrow_mut().drain(..) {
306 instructions.push(inst);
307 }
308
309 let mut processing = ScopeProcessing {
310 instructions,
311 global_state: self.global_state.clone(),
312 };
313
314 for p in processors {
315 processing = p.transform(processing);
316 }
317
318 processing
319 }
320
321 pub fn new_local_index(&self) -> u32 {
322 self.state().allocator.new_local_index()
323 }
324
325 pub fn global(&self, id: Id, value_ty: Type) -> Value {
327 let ty_arr = Type::DynamicArray(value_ty.intern());
328 let ty = Type::Pointer(ty_arr.intern(), AddressSpace::Global(id));
329 let value = self.create_value(ty);
330 self.state_mut().global_args.insert(id as usize, value);
331 value
332 }
333
334 pub fn tensor_map(&self, id: Id) -> Value {
336 let ty = Type::Opaque(OpaqueType::TensorMap);
337 let value = self.create_value(ty);
338 self.state_mut().global_args.insert(id as usize, value);
339 value
340 }
341
342 pub fn update_source(&self, source: CubeFnSource) {
343 if self.debug.enabled {
344 self.debug.sources.borrow_mut().insert(source.clone());
345 *self.debug.source_loc.borrow_mut() = Some(SourceLoc {
346 line: source.line,
347 column: source.column,
348 source,
349 });
350 if self.debug.entry_loc.borrow().is_none() {
351 *self.debug.entry_loc.borrow_mut() = self.debug.source_loc.borrow().clone();
352 }
353 }
354 }
355
356 pub fn register_all(&self, instructions: impl IntoIterator<Item = Instruction>) {
357 self.instructions.borrow_mut().extend(instructions);
358 }
359
360 pub fn take_instructions(&self) -> Vec<Instruction> {
361 core::mem::take(&mut *self.instructions.borrow_mut())
362 }
363
364 pub fn update_span(&self, line: u32, col: u32) {
365 if let Some(loc) = self.debug.source_loc.borrow_mut().as_mut() {
366 loc.line = line;
367 loc.column = col;
368 }
369 }
370
371 pub fn update_value_name(&self, value: Value, name: impl Into<Cow<'static, str>>) {
372 if self.debug.enabled {
373 self.debug
374 .value_names
375 .borrow_mut()
376 .insert(value, name.into());
377 }
378 }
379
380 pub fn extract_field(&self, aggregate: Value, ty: Type, field: usize) -> Value {
381 if !matches!(aggregate.ty, Type::Aggregate(..)) {
382 panic!(
383 "Tried extracting field from non-aggregate {aggregate}.\nCurrent state:\n{}",
384 self.instructions.borrow().iter().join("\n")
385 )
386 }
387 let out = self.create_value(ty);
388 self.register(Instruction::new(
389 Operation::ExtractAggregateField(AggregateExtractOperands { aggregate, field }),
390 out,
391 ));
392 out
393 }
394}
395
396impl Display for Scope {
397 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
398 writeln!(f, "{{")?;
399 for instruction in self.instructions.borrow().iter() {
400 let instruction_str = instruction.to_string();
401 if !instruction_str.is_empty() {
402 writeln!(
403 f,
404 "{}{}",
405 " ".repeat(self.depth as usize + 1),
406 instruction_str,
407 )?;
408 }
409 }
410 write!(f, "{}}}", " ".repeat(self.depth as usize))?;
411 Ok(())
412 }
413}