1use crate::{
2 EnumSet,
3 dialect::memory::StoreOp,
4 interfaces::control_flow::{SymbolOpInterface, SymbolVisibility},
5 rewrite::op_insertion_point_in_block,
6};
7use alloc::{boxed::Box, format, rc::Rc, string::String, sync::Arc, vec, vec::Vec};
8use core::{
9 any::{TypeId, type_name},
10 cell::{Ref, RefCell, RefMut, UnsafeCell},
11 fmt::{Debug, Display},
12 sync::atomic::Ordering,
13};
14use cubecl_common::format::type_name_sanitized;
15use cubecl_environment::{collections::HashMap, sync::Mutex};
16use derive_more::{Eq, PartialEq};
17use pliron::{
18 attribute::AttrObj,
19 basic_block::BasicBlock,
20 builtin::{
21 attributes::{TypeAttr, VecAttr},
22 given_names::set_operation_result_name,
23 op_interfaces::{OneResultInterface, SingleBlockRegionInterface},
24 ops::{ConstantOp, FuncOp, ModuleOp},
25 type_interfaces::FunctionTypeInterface,
26 types::{FunctionType, UnitType},
27 },
28 context::{AuxDataIndex, Context},
29 dict_key,
30 identifier::Identifier,
31 irbuild::inserter::{IRInserter, Inserter, OpInsertionPoint},
32 op::Op,
33 operation::Operation,
34 printable::Printable,
35 r#type::{TypeHandle, Typed, type_cast},
36 value::Value,
37};
38use portable_atomic::AtomicUsize;
39use spin::LazyLock;
40
41use crate::{
42 AddressSpace, AddressType, DeviceProperties, ElemType, FastMath, TargetProperties, TypeHash,
43 arena::DropBump,
44 attributes::{
45 ATTR_BUFFER_BINDING, ATTR_KEY_ARG_ATTRS, ATTR_TENSOR_MAP_BINDING, BoolAttr,
46 BufferBindingAttr, EntrypointAbiAttr, EntrypointInterface, FuncInterface, IndexAttr,
47 },
48 debug::{DebugState, LocationListener},
49 dialect::{
50 OperationPtrExt,
51 branch::{IfOp, ReturnOp, YieldOp},
52 memory::DeclareVariableOp,
53 vector::CompositeExtractOp,
54 },
55 interfaces::{ScalarType, TypedExt},
56 read_value,
57 settings::{DebugInfo, KernelSettings},
58 types::{PointerType, RuntimeArrayType, cuda::TensorMapType, scalar::BoolType},
59};
60
61pub type Types = HashMap<TypeId, ElemType>;
62pub type Sizes = HashMap<TypeId, usize>;
63
64pub type OpInserter = IRInserter<LocationListener>;
65
66#[derive(Clone)]
69enum CtxHandle {
70 Rc(Rc<UnsafeCell<Context>>),
71 Ref(*mut Context),
72}
73
74impl CtxHandle {
75 pub fn borrow(&self) -> &Context {
76 match self {
77 CtxHandle::Rc(cell) => unsafe { &*cell.get() },
78 CtxHandle::Ref(ptr) => unsafe { &**ptr },
79 }
80 }
81
82 #[allow(clippy::mut_from_ref)]
83 pub fn borrow_mut(&self) -> &mut Context {
84 match self {
85 CtxHandle::Rc(cell) => unsafe { &mut *cell.get() },
86 CtxHandle::Ref(ptr) => unsafe { &mut **ptr },
87 }
88 }
89}
90
91enum InserterHandle {
94 Owned(Box<UnsafeCell<dyn Inserter>>),
95 Ref(*mut dyn Inserter),
96}
97
98impl InserterHandle {
99 pub fn owned(inserter: impl Inserter + 'static) -> Self {
100 Self::Owned(Box::new(UnsafeCell::new(inserter)))
101 }
102
103 #[allow(clippy::mut_from_ref)]
104 pub fn borrow_mut(&self) -> &mut dyn Inserter {
105 match self {
106 InserterHandle::Owned(cell) => unsafe { &mut *cell.get() },
107 InserterHandle::Ref(ptr) => unsafe { &mut **ptr },
108 }
109 }
110}
111
112#[allow(missing_docs)]
120pub struct Scope {
121 ctx: CtxHandle,
122 inserter: InserterHandle,
123 expand_state: RefCell<ExpandState>,
124}
125
126#[derive(Clone, Copy, Default)]
127pub struct ExpandState {
128 pub may_terminate: bool,
129 pub may_return: bool,
130 pub may_break: bool,
131 pub may_continue: bool,
132 pub inv_terminate_flag: Option<Value>,
134 pub inv_return_flag: Option<Value>,
136 pub inv_break_flag: Option<Value>,
138 pub inv_continue_flag: Option<Value>,
140 pub return_value: Option<Value>,
142}
143
144impl Debug for Scope {
145 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146 f.debug_struct("Scope").finish()
147 }
148}
149
150#[track_caller]
151pub fn ident(name: impl Into<String>) -> Identifier {
152 Identifier::try_new(name.into()).unwrap()
153}
154
155pub struct GlobalState {
156 pub reference_arena: DropBump,
157 pub errors: Vec<String>,
158 pub warnings: Vec<String>,
159
160 pub module: ModuleOp,
161 pub module_inserter: OpInserter,
162 pub entry_func: FuncOp,
163 pub ident_unique_id: AtomicUsize,
164 pub typemap: Types,
165 pub sizemap: Sizes,
166 pub modes: InstructionModes,
167 pub target_properties: TargetProperties,
168 pub device_properties: Option<Arc<DeviceProperties>>,
169}
170
171unsafe impl Send for GlobalState {}
172
173impl GlobalState {
174 pub fn register_type<T: 'static>(&mut self, elem: ElemType) {
176 self.typemap.insert(TypeId::of::<T>(), elem);
177 }
178}
179
180dict_key!(ADDRESS_TYPE_KEY, "kernel_address_type");
181
182static TY_IDENTS: LazyLock<Mutex<HashMap<TypeId, Identifier>>> = LazyLock::new(Default::default);
183
184fn ty_ident<T: 'static>() -> Identifier {
185 let mut idents = TY_IDENTS.lock();
186 let ident = idents
187 .entry(TypeId::of::<T>())
188 .or_insert_with(|| Identifier::try_from(type_name_sanitized::<T>()).unwrap());
189 ident.clone()
190}
191
192fn ty_key<T: 'static>(ctx: &Context) -> Option<AuxDataIndex> {
193 let mut idents = TY_IDENTS.lock();
194 let ident = idents
195 .entry(TypeId::of::<T>())
196 .or_insert_with(|| Identifier::try_from(type_name_sanitized::<T>()).unwrap());
197 ctx.aux_data_map.get(ident).copied()
198}
199
200pub trait ContextExt {
201 fn aux_ty<T: Send + 'static>(&self) -> &T;
202 fn try_aux_ty<T: Send + 'static>(&self) -> Option<&T>;
203 fn aux_ty_mut<T: Send + 'static>(&mut self) -> &mut T;
204 fn try_aux_ty_mut<T: Send + 'static>(&mut self) -> Option<&mut T>;
205 fn set_aux_ty<T: Send + 'static>(&mut self, value: T);
206 fn set_address_type(&mut self, addr: AddressType);
207 fn address_type(&self) -> AddressType;
208}
209
210impl ContextExt for Context {
211 #[track_caller]
212 fn aux_ty<T: Send + 'static>(&self) -> &T {
213 self.try_aux_ty()
214 .ok_or_else(|| format!("Key for {} should exist", type_name::<T>()))
215 .unwrap()
216 }
217
218 fn try_aux_ty<T: Send + 'static>(&self) -> Option<&T> {
219 self.aux_data[ty_key::<T>(self)?].downcast_ref()
220 }
221
222 #[track_caller]
223 fn aux_ty_mut<T: Send + 'static>(&mut self) -> &mut T {
224 self.try_aux_ty_mut()
225 .ok_or_else(|| format!("Key for {} should exist", type_name::<T>()))
226 .unwrap()
227 }
228
229 fn try_aux_ty_mut<T: Send + 'static>(&mut self) -> Option<&mut T> {
230 let key = ty_key::<T>(self)?;
231 self.aux_data[key].downcast_mut()
232 }
233
234 fn set_aux_ty<T: Send + 'static>(&mut self, value: T) {
235 if let Some(key) = ty_key::<T>(self) {
236 *self.aux_data.get_mut(key).unwrap() = Box::new(value);
237 } else {
238 let ident = ty_ident::<T>();
239 let key = self.aux_data.insert(Box::new(value));
240 self.aux_data_map.insert(ident, key);
241 }
242 }
243
244 fn set_address_type(&mut self, addr: AddressType) {
245 if let Some(key) = self.aux_data_map.get(&ADDRESS_TYPE_KEY).copied() {
246 *self.aux_data.get_mut(key).unwrap() = Box::new(addr);
247 } else {
248 let key = self.aux_data.insert(Box::new(addr));
249 self.aux_data_map.insert(ADDRESS_TYPE_KEY.clone(), key);
250 }
251 }
252
253 fn address_type(&self) -> AddressType {
254 let key = self.aux_data_map[&ADDRESS_TYPE_KEY];
255 *self.aux_data[key].downcast_ref::<AddressType>().unwrap()
256 }
257}
258
259pub trait FuncOpExt {
260 fn push_argument(&self, ctx: &Context, ty: TypeHandle) -> usize;
261 fn pop_argument(&self, ctx: &Context);
262 fn remove_argument(&self, ctx: &Context, arg_idx: usize);
263 fn return_type(&self, ctx: &Context) -> TypeHandle;
264}
265
266impl FuncOpExt for FuncOp {
267 fn push_argument(&self, ctx: &Context, ty: TypeHandle) -> usize {
268 let id = BasicBlock::push_argument(self.get_entry_block(ctx), ctx, ty);
269
270 let (mut arg_types, res_types) = {
271 let current_func_ty = self.get_type(ctx).deref(ctx);
272 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
273 (current_func_ty.arg_types(), current_func_ty.res_types())
274 };
275
276 arg_types.insert(id, ty);
277 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
278 self.set_attr_builtin_func_type(ctx, new_func_ty.into());
279 id
280 }
281
282 fn pop_argument(&self, ctx: &Context) {
283 let last_idx = self.get_entry_block(ctx).deref(ctx).get_num_arguments() - 1;
284 BasicBlock::pop_argument(self.get_entry_block(ctx), ctx);
285
286 let (mut arg_types, res_types) = {
287 let current_func_ty = self.get_type(ctx).deref(ctx);
288 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
289 (current_func_ty.arg_types(), current_func_ty.res_types())
290 };
291
292 arg_types.pop();
293 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
294 self.set_attr_builtin_func_type(ctx, new_func_ty.into());
295 let mut op = self.get_operation().deref_mut(ctx);
296 let arg_attrs = op.attributes.0.get_mut(&ATTR_KEY_ARG_ATTRS);
297 if let Some(arg_attrs) = arg_attrs.and_then(|attr| attr.downcast_mut::<VecAttr>()) {
298 arg_attrs.0.truncate(last_idx);
299 }
300 }
301
302 fn remove_argument(&self, ctx: &Context, arg_idx: usize) {
303 BasicBlock::remove_argument(self.get_entry_block(ctx), ctx, arg_idx);
304
305 let (mut arg_types, res_types) = {
306 let current_func_ty = self.get_type(ctx).deref(ctx);
307 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
308 (current_func_ty.arg_types(), current_func_ty.res_types())
309 };
310
311 arg_types.remove(arg_idx);
312 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
313 self.set_attr_builtin_func_type(ctx, new_func_ty.into());
314
315 let mut op = self.get_operation().deref_mut(ctx);
316 let arg_attrs = op.attributes.0.get_mut(&ATTR_KEY_ARG_ATTRS);
317 if let Some(arg_attrs) = arg_attrs.and_then(|attr| attr.downcast_mut::<VecAttr>()) {
318 arg_attrs.0.remove(arg_idx);
319 }
320 }
321
322 fn return_type(&self, ctx: &Context) -> TypeHandle {
323 let ty = self.get_type(ctx).deref(ctx);
324 ty.downcast_ref::<FunctionType>().unwrap().res_types()[0]
325 }
326}
327
328fn new_context(settings: KernelSettings) -> Rc<UnsafeCell<Context>> {
329 let mut ctx = Context::default();
330 ctx.set_address_type(settings.address_type);
331
332 let module = ModuleOp::new(&mut ctx, ident("kernel"));
333 let module_block = module.get_body(&ctx, 0);
334 let mut module_inserter = OpInserter::new_at_block_end(module_block);
335
336 let entry_func_ty = FunctionType::get(&ctx, vec![], vec![UnitType::get(&ctx).into()]);
338 let entry_name = Identifier::try_new(settings.kernel_name).unwrap_or(ident("kernel_entry"));
339 let abi = EntrypointAbiAttr::new(settings.cube_dim, settings.cluster_dim);
340 let entry_func = FuncOp::new(&mut ctx, entry_name, entry_func_ty);
341 entry_func.set_visibility(&mut ctx, SymbolVisibility::Public);
342 entry_func.set_entrypoint_abi(&mut ctx, abi);
343 module_inserter.append_op(&ctx, &entry_func);
344
345 let mut state = GlobalState {
346 reference_arena: Default::default(),
347 module,
348 module_inserter,
349 entry_func,
350 ident_unique_id: Default::default(),
351 typemap: Default::default(),
352 sizemap: Default::default(),
353 modes: Default::default(),
354 target_properties: Default::default(),
355 device_properties: Default::default(),
356 errors: Default::default(),
357 warnings: Default::default(),
358 };
359 settings.address_type.register(&mut state);
360
361 ctx.set_aux_ty(state);
362 ctx.set_aux_ty(DebugState::new(settings.debug_info != DebugInfo::None));
363 Rc::new(UnsafeCell::new(ctx))
364}
365
366fn dummy_context() -> Rc<UnsafeCell<Context>> {
370 let mut ctx = Context::default();
371
372 let module = ModuleOp::new(&mut ctx, ident("dummy_module"));
373 let module_block = module.get_body(&ctx, 0);
374 let mut module_inserter = OpInserter::new_at_block_end(module_block);
375
376 let entry_func_ty = FunctionType::get(&ctx, vec![], vec![UnitType::get(&ctx).into()]);
377 let entry_name = ident("dummy_entry");
378 let entry_func = FuncOp::new(&mut ctx, entry_name, entry_func_ty);
379 module_inserter.append_op(&ctx, &entry_func);
380
381 let state = GlobalState {
382 reference_arena: Default::default(),
383 module,
384 module_inserter,
385 entry_func,
386 ident_unique_id: Default::default(),
387 typemap: Default::default(),
388 sizemap: Default::default(),
389 modes: Default::default(),
390 target_properties: Default::default(),
391 device_properties: Default::default(),
392 errors: Default::default(),
393 warnings: Default::default(),
394 };
395
396 ctx.set_aux_ty(state);
397 Rc::new(UnsafeCell::new(ctx))
398}
399
400impl Debug for GlobalState {
401 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
402 f.debug_struct("GlobalStateInner")
403 .field("reference_arena", &self.reference_arena)
404 .field("typemap", &self.typemap)
405 .field("sizemap", &self.sizemap)
406 .field("modes", &self.modes)
407 .field("target_properties", &self.target_properties)
408 .field("device_properties", &self.device_properties)
409 .finish()
410 }
411}
412
413#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
415#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, TypeHash)]
416pub struct InstructionModes {
417 pub fp_math_mode: EnumSet<FastMath>,
418}
419
420impl Scope {
421 pub fn device_properties(&self, properties: Arc<DeviceProperties>) {
424 self.state_mut().device_properties = Some(properties);
425 }
426
427 #[track_caller]
428 pub fn state(&self) -> &GlobalState {
429 self.ctx().aux_ty()
430 }
431
432 #[track_caller]
433 pub fn state_mut(&self) -> &mut GlobalState {
434 self.ctx_mut().aux_ty_mut()
435 }
436
437 pub fn debug_state(&self) -> Option<&mut DebugState> {
439 self.ctx_mut()
440 .try_aux_ty_mut::<DebugState>()
441 .filter(|debug| debug.is_enabled())
442 }
443
444 fn ident_id(&self) -> usize {
445 self.state().ident_unique_id.fetch_add(1, Ordering::SeqCst)
446 }
447
448 #[track_caller]
449 pub fn expand_state(&self) -> Ref<'_, ExpandState> {
450 self.expand_state.borrow()
451 }
452
453 #[track_caller]
454 pub fn expand_state_mut(&self) -> RefMut<'_, ExpandState> {
455 self.expand_state.borrow_mut()
456 }
457
458 pub fn ctx(&self) -> &Context {
459 self.ctx.borrow()
460 }
461
462 pub fn ctx_mut(&self) -> &mut Context {
464 self.ctx.borrow_mut()
465 }
466
467 pub fn inserter(&self) -> &mut dyn Inserter {
468 self.inserter.borrow_mut()
469 }
470
471 pub fn root(settings: KernelSettings) -> Self {
475 let ctx = new_context(settings);
476 let mut inserter = {
477 let ctx = unsafe { &*ctx.get() };
478 let state = ctx.aux_ty::<GlobalState>();
479 let entry_block = state.entry_func.get_entry_block(ctx);
480 OpInserter::new_at_block_end(entry_block)
481 };
482 let terminate_flag = init_bool_flag(
483 unsafe { &mut *ctx.get() },
484 &mut inserter,
485 "inv_terminate_flag",
486 );
487 Self {
488 ctx: CtxHandle::Rc(ctx),
489 inserter: InserterHandle::owned(inserter),
490 expand_state: RefCell::new(ExpandState {
491 inv_terminate_flag: Some(terminate_flag),
492 ..ExpandState::default()
493 }),
494 }
495 }
496
497 pub fn dummy() -> Self {
500 let ctx = dummy_context();
501 let inserter = {
502 let ctx = unsafe { &*ctx.get() };
503 let state = ctx.aux_ty::<GlobalState>();
504 let entry_block = state.entry_func.get_entry_block(ctx);
505 OpInserter::new_at_block_end(entry_block)
506 };
507 Self {
508 ctx: CtxHandle::Rc(ctx),
509 inserter: InserterHandle::owned(inserter),
510 expand_state: Default::default(),
511 }
512 }
513
514 pub fn from_context_and_inserter(
516 ctx: &mut Context,
517 inserter: &mut (impl Inserter + 'static),
518 ) -> Self {
519 let inserter: *mut dyn Inserter = inserter;
520 Self {
521 ctx: CtxHandle::Ref(ctx),
522 inserter: InserterHandle::Ref(inserter),
523 expand_state: RefCell::new(ExpandState::default()),
524 }
525 }
526
527 pub fn create_local_mut(
532 &self,
533 value_ty: impl Into<TypeHandle>,
534 init: Option<AttrObj>,
535 ) -> Value {
536 let value_ty = value_ty.into();
537 let ctx = self.ctx_mut();
538 let align = value_ty.align(ctx);
539 let op = DeclareVariableOp::new(ctx, value_ty, AddressSpace::Local, align, init);
540 let out = op.get_result(ctx);
541 self.inserter().append_op(ctx, &op);
542 out
543 }
544
545 pub fn create_shared(
547 &self,
548 value_ty: impl Into<TypeHandle>,
549 alignment: Option<usize>,
550 ) -> Value {
551 let value_ty = value_ty.into();
552 let ctx = self.ctx_mut();
553 let align = alignment.unwrap_or_else(|| value_ty.align(ctx));
554 let op = DeclareVariableOp::new(ctx, value_ty, AddressSpace::Shared, align, None);
555 let out = op.get_result(ctx);
556 self.inserter().append_op(ctx, &op);
557 out
558 }
559
560 pub fn func_ident(&self, label: Option<&str>) -> Identifier {
561 let unique_id = self.ident_id();
562 match label {
563 Some(label) => ident(format!("{label}_{unique_id}")),
564 None => ident(format!("func_{unique_id}")),
565 }
566 }
567
568 pub fn register_func(&self, func: FuncOp) {
570 let ctx = self.ctx_mut();
571 let state = self.state_mut();
572 func.set_visibility(ctx, SymbolVisibility::Private);
573 state.module_inserter.append_op(ctx, &func);
574 }
575
576 pub fn register(&self, op: &dyn Op) {
578 let ctx = self.ctx();
579 self.inserter().append_op(ctx, op);
580 }
581
582 pub fn register_with_result(&self, op: &dyn OneResultInterface) -> Value {
584 self.register(op);
585 op.get_result(self.ctx())
586 }
587
588 pub fn terminate_yield(&self) {
590 let block = self.inserter().get_insertion_block(self.ctx());
591 let block = block.expect("Should have insertion block");
592 if block.deref(self.ctx()).get_terminator(self.ctx()).is_none() {
593 self.register(&YieldOp::new(self.ctx_mut()));
594 }
595 }
596
597 pub fn update_flags_after_branch(&self, children: &[Scope]) {
598 self.set_may_continue(children);
599 self.set_may_break(children);
600 self.set_may_return(children);
601 self.set_may_terminate(children);
602 }
603
604 pub fn update_flags_after_loop(&self, children: &[Scope]) {
605 self.set_may_return(children);
606 self.set_may_terminate(children);
607 }
608
609 pub fn update_flags_after_early_return(&self, children: &[Scope]) {
610 self.set_may_terminate(children);
611 }
612
613 pub fn update_flags_before_unrolled_iteration(&self, body: &Scope) {
614 body.update_flags_before_unrolled_iteration_at(self.unrolled_insertion_point(body));
615 }
616
617 fn update_flags_before_unrolled_iteration_at(&self, insertion_point: OpInsertionPoint) {
618 self.inserter().set_insertion_point(insertion_point);
619
620 if self.expand_state().may_continue {
622 let flag = self.expand_state().inv_continue_flag.unwrap();
623 self.register(&StoreOp::new(self.ctx_mut(), flag, self.const_bool(true)));
624 self.expand_state_mut().may_continue = false;
625 }
626
627 self.set_may_break(core::slice::from_ref(self));
628 self.set_may_return(core::slice::from_ref(self));
629 self.set_may_terminate(core::slice::from_ref(self));
630 }
631
632 pub fn finalize_unrolled_loop(&self, body: &Scope) {
633 self.inserter()
634 .set_insertion_point(self.unrolled_insertion_point(body));
635
636 self.set_may_return(core::slice::from_ref(body));
637 self.set_may_terminate(core::slice::from_ref(body));
638 }
639
640 fn unrolled_insertion_point(&self, body: &Scope) -> OpInsertionPoint {
641 let current_block = self.inserter().get_insertion_block(self.ctx()).unwrap();
642 op_insertion_point_in_block(
643 self.ctx(),
644 body.inserter().get_insertion_point(),
645 current_block,
646 )
647 }
648
649 pub fn set_may_terminate(&self, children: &[Scope]) {
650 let child_may_terminate = children
651 .iter()
652 .any(|scope| scope.expand_state().may_terminate);
653 if child_may_terminate {
654 self.expand_state_mut().may_terminate = true;
655 let flag = self.expand_state().inv_terminate_flag;
656 self.predicate_on_flag(flag.expect("Can't terminate in rewrite context"));
657 }
658 }
659
660 pub fn set_may_return(&self, children: &[Scope]) {
661 let child_may_return = children.iter().any(|scope| scope.expand_state().may_return);
662 if child_may_return {
663 self.expand_state_mut().may_return = true;
664 let flag = self.expand_state().inv_return_flag;
665 self.predicate_on_flag(flag.expect("Can't return in rewrite context"));
666 }
667 }
668
669 pub fn set_may_break(&self, children: &[Scope]) {
670 let child_may_break = children.iter().any(|scope| scope.expand_state().may_break);
671 if child_may_break {
672 self.expand_state_mut().may_break = true;
673 let flag = self.expand_state().inv_break_flag;
674 self.predicate_on_flag(flag.expect("Should have break flag"));
675 }
676 }
677
678 pub fn set_may_continue(&self, children: &[Scope]) {
679 let child_may_continue = children
680 .iter()
681 .any(|scope| scope.expand_state().may_continue);
682 if child_may_continue {
683 self.expand_state_mut().may_continue = true;
684 let flag = self.expand_state().inv_continue_flag;
685 self.predicate_on_flag(flag.expect("Should have continue flag"));
686 }
687 }
688
689 fn predicate_on_flag(&self, flag: Value) {
690 let ctx = self.ctx_mut();
691 let cond = read_value(self, flag);
692 let predication = IfOp::new(ctx, cond);
693 let then_block = predication.then_block(ctx);
694 let else_block = predication.else_block(ctx);
695 let yield_ = YieldOp::new(ctx).get_operation();
696 yield_.insert_at_back(then_block, ctx);
697 let yield_ = YieldOp::new(ctx).get_operation();
698 yield_.insert_at_back(else_block, ctx);
699 self.register(&predication);
700 self.terminate_yield();
706 self.inserter()
707 .set_insertion_point_to_block_start(then_block);
708 }
709
710 pub fn create_kernel_ref<'a, T>(&self, value: T) -> &'a mut T
715 where
716 T: 'a,
717 {
718 let state = self.state_mut();
719 let reference = state.reference_arena.alloc(value);
720 unsafe { core::mem::transmute(reference) }
721 }
722
723 pub fn resolve_type<T: 'static>(&self) -> Option<ElemType> {
725 let state = self.state();
726 let result = state.typemap.get(&TypeId::of::<T>());
727
728 result.cloned()
729 }
730
731 pub fn resolve_size<T: 'static>(&self) -> Option<usize> {
733 let state = self.state();
734 let result = state.sizemap.get(&TypeId::of::<T>());
735
736 result.cloned()
737 }
738
739 pub fn register_type<T: 'static>(&self, elem: ElemType) {
741 self.state_mut().register_type::<T>(elem);
742 }
743
744 pub fn define_type<T: 'static>(&self, elem: ElemType) -> TypeDefinition<'_> {
748 let before = self.resolve_type::<T>();
749 self.register_type::<T>(elem);
750 TypeDefinition {
751 scope: self,
752 ty: TypeId::of::<T>(),
753 before,
754 }
755 }
756
757 pub fn define_size<T: 'static>(&self, size: usize) -> SizeDefinition<'_> {
761 let before = self.resolve_size::<T>();
762 self.register_size::<T>(size);
763 SizeDefinition {
764 scope: self,
765 size: TypeId::of::<T>(),
766 before,
767 }
768 }
769
770 pub fn register_size<T: 'static>(&self, size: usize) {
772 let state = self.state_mut();
773
774 state.sizemap.insert(TypeId::of::<T>(), size);
775 }
776
777 pub fn register_value_type<T: 'static, N: 'static>(&self, value: impl Typed) {
779 let ty = value.get_type(self.ctx());
780 let scalar_ty = ty.scalar_ty(self.ctx());
781 let vector_size = ty.vector_size(self.ctx());
782 let storage_ty = {
783 let ctx = self.ctx();
784 let scalar_ty = scalar_ty.deref(ctx);
785 let scalar = type_cast::<dyn ScalarType>(&*scalar_ty).unwrap();
786 scalar.elem_type(ctx)
787 };
788 self.register_type::<T>(storage_ty);
789 self.register_size::<N>(vector_size);
790 }
791
792 pub fn branch_child(&self, inserter: impl Inserter + 'static) -> Self {
794 Self {
795 ctx: self.ctx.clone(),
796 inserter: InserterHandle::owned(inserter),
797 expand_state: RefCell::new(ExpandState {
798 may_continue: false,
799 may_break: false,
800 may_return: false,
801 may_terminate: false,
802 inv_terminate_flag: self.expand_state().inv_terminate_flag,
803 inv_return_flag: self.expand_state().inv_return_flag,
804 inv_break_flag: self.expand_state().inv_break_flag,
805 inv_continue_flag: self.expand_state().inv_continue_flag,
806 return_value: self.expand_state().return_value,
807 }),
808 }
809 }
810
811 pub fn loop_child(&self, mut inserter: impl Inserter + 'static) -> Self {
813 let break_flag = init_bool_flag(self.ctx_mut(), self.inserter(), "inv_break_flag");
814 let continue_flag = init_bool_flag(self.ctx_mut(), &mut inserter, "inv_continue_flag");
815 Self {
816 ctx: self.ctx.clone(),
817 inserter: InserterHandle::owned(inserter),
818 expand_state: RefCell::new(ExpandState {
819 may_terminate: false,
820 may_return: false,
821 may_break: false,
822 may_continue: false,
823 inv_terminate_flag: self.expand_state().inv_terminate_flag,
824 inv_return_flag: self.expand_state().inv_return_flag,
825 inv_break_flag: Some(break_flag),
826 inv_continue_flag: Some(continue_flag),
827 return_value: self.expand_state().return_value,
828 }),
829 }
830 }
831
832 pub fn unrolled_loop_child(&self) -> Self {
834 let break_flag = init_bool_flag(self.ctx_mut(), self.inserter(), "inv_break_flag");
835 let continue_flag = init_bool_flag(self.ctx_mut(), self.inserter(), "inv_continue_flag");
836 let inserter = OpInserter::new(self.inserter().get_insertion_point());
837 Self {
838 ctx: self.ctx.clone(),
839 inserter: InserterHandle::owned(inserter),
840 expand_state: RefCell::new(ExpandState {
841 may_terminate: false,
842 may_return: false,
843 may_break: false,
844 may_continue: false,
845 inv_terminate_flag: self.expand_state().inv_terminate_flag,
846 inv_return_flag: self.expand_state().inv_return_flag,
847 inv_break_flag: Some(break_flag),
848 inv_continue_flag: Some(continue_flag),
849 return_value: self.expand_state().return_value,
850 }),
851 }
852 }
853
854 pub fn inlined_func_child(
856 &self,
857 mut inserter: impl Inserter + 'static,
858 return_value: Option<Value>,
859 ) -> Self {
860 let return_flag = init_bool_flag(self.ctx_mut(), &mut inserter, "inv_return_flag");
861 Self {
862 ctx: self.ctx.clone(),
863 inserter: InserterHandle::owned(inserter),
864 expand_state: RefCell::new(ExpandState {
865 may_continue: false,
866 may_break: false,
867 may_return: false,
868 may_terminate: false,
869 inv_terminate_flag: self.expand_state().inv_terminate_flag,
870 inv_return_flag: Some(return_flag),
871 inv_break_flag: None,
872 inv_continue_flag: None,
873 return_value: return_value,
874 }),
875 }
876 }
877
878 pub fn func_child(&self, mut inserter: impl Inserter + 'static) -> Self {
882 let terminate_flag = init_bool_flag(self.ctx_mut(), &mut inserter, "inv_terminate_flag");
883 Self {
884 ctx: self.ctx.clone(),
885 inserter: InserterHandle::owned(inserter),
886 expand_state: RefCell::new(ExpandState {
887 may_continue: false,
888 may_break: false,
889 may_return: false,
890 may_terminate: false,
891 inv_terminate_flag: Some(terminate_flag),
892 inv_return_flag: None,
893 inv_break_flag: None,
894 inv_continue_flag: None,
895 return_value: None,
896 }),
897 }
898 }
899
900 pub fn push_error(&self, msg: impl Into<String>) {
902 self.state_mut().errors.push(msg.into());
903 }
904
905 pub fn pop_errors(&self) -> Vec<String> {
907 core::mem::take(&mut self.state_mut().errors)
908 }
909
910 pub fn push_warning(&self, msg: impl Into<String>) {
912 self.state_mut().warnings.push(msg.into());
913 }
914
915 pub fn pop_warnings(&self) -> Vec<String> {
917 core::mem::take(&mut self.state_mut().warnings)
918 }
919
920 pub fn global(
922 &self,
923 buffer_pos: usize,
924 ext_meta_pos: Option<usize>,
925 value_ty: TypeHandle,
926 ) -> Value {
927 let entry_func = self.state().entry_func;
928 let ctx = self.ctx_mut();
929
930 let ty_arr = RuntimeArrayType::get(ctx, value_ty);
931 let ty = PointerType::get(ctx, ty_arr.into(), AddressSpace::Global(buffer_pos));
932
933 let id = entry_func.push_argument(ctx, ty.to_handle());
934 entry_func.set_arg_attr(
935 ctx,
936 id,
937 &ATTR_BUFFER_BINDING,
938 Box::new(BufferBindingAttr::new(buffer_pos, ext_meta_pos)),
939 );
940
941 entry_func.get_entry_block(ctx).deref(ctx).get_argument(id)
942 }
943
944 pub fn tensor_map(&self, buffer_pos: usize, ext_meta_pos: usize) -> Value {
946 let entry_func = self.state().entry_func;
947 let ctx = self.ctx();
948 let ty = TensorMapType::get(ctx);
949
950 let id = entry_func.push_argument(ctx, ty.to_handle());
951 entry_func.set_arg_attr_unit(ctx, id, &ATTR_TENSOR_MAP_BINDING);
952 entry_func.set_arg_attr(
953 ctx,
954 id,
955 &ATTR_BUFFER_BINDING,
956 Box::new(BufferBindingAttr::new(buffer_pos, Some(ext_meta_pos))),
957 );
958 entry_func.get_entry_block(ctx).deref(ctx).get_argument(id)
959 }
960
961 pub fn kernel_arg(&self, idx: usize) -> Value {
962 let entry_block = self.state().entry_func.get_entry_block(self.ctx());
963 entry_block.deref(self.ctx()).get_argument(idx)
964 }
965
966 pub fn extract_field(&self, aggregate: Value, field: usize) -> Value {
967 let ctx = self.ctx_mut();
968 let op = CompositeExtractOp::new(ctx, aggregate, field);
969 self.register_with_result(&op)
970 }
971
972 pub fn const_usize(&self, value: usize) -> Value {
973 let op = ConstantOp::new(self.ctx_mut(), Box::new(IndexAttr::new(value)));
974 self.register_with_result(&op)
975 }
976
977 pub fn const_bool(&self, value: bool) -> Value {
978 let op = ConstantOp::new(self.ctx_mut(), Box::new(BoolAttr::new(value)));
979 self.register_with_result(&op)
980 }
981
982 pub fn into_context(self) -> Option<Context> {
983 let entry = self.state().entry_func.get_entry_block(self.ctx());
984 let term = entry.deref(self.ctx()).get_terminator(self.ctx());
985 let is_yield = term.is_some_and(|term| term.is_op::<YieldOp>(self.ctx()));
986 if let Some(term) = term
987 && is_yield
988 {
989 self.inserter().set_insertion_point_to_block_end(entry);
990 self.register(&ReturnOp::new(self.ctx_mut()));
991 Operation::erase(term, self.ctx_mut());
992 } else if term.is_none() {
993 self.inserter().set_insertion_point_to_block_end(entry);
994 self.register(&ReturnOp::new(self.ctx_mut()));
995 }
996 match self.ctx {
997 CtxHandle::Rc(ctx) => Some(Rc::into_inner(ctx)?.into_inner()),
998 CtxHandle::Ref(_) => None,
999 }
1000 }
1001}
1002
1003impl Display for Scope {
1004 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1005 let ctx = self.ctx();
1006 let state = self.state();
1007 write!(f, "{}", state.module.disp(ctx))
1008 }
1009}
1010
1011fn init_bool_flag(ctx: &mut Context, inserter: &mut dyn Inserter, name: &str) -> Value {
1012 let bool = TypeAttr::new(BoolType::get(ctx).to_handle());
1013 let r#true = BoolAttr::new(true).into();
1014 let flag = DeclareVariableOp::new(ctx, bool, AddressSpace::Local, 1, Some(r#true));
1015 inserter.append_op(ctx, &flag);
1016 set_operation_result_name(ctx, flag.get_operation(), 0, Some(ident(name)));
1017 flag.get_result(ctx)
1018}
1019
1020pub struct TypeDefinition<'a> {
1022 scope: &'a Scope,
1023 ty: TypeId,
1024 before: Option<ElemType>,
1025}
1026
1027impl Drop for TypeDefinition<'_> {
1028 fn drop(&mut self) {
1029 if let Some(before) = self.before {
1030 self.scope.state_mut().typemap.insert(self.ty, before);
1031 }
1032 }
1033}
1034
1035pub struct SizeDefinition<'a> {
1037 scope: &'a Scope,
1038 size: TypeId,
1039 before: Option<usize>,
1040}
1041
1042impl Drop for SizeDefinition<'_> {
1043 fn drop(&mut self) {
1044 if let Some(before) = self.before {
1045 self.scope.state_mut().sizemap.insert(self.size, before);
1046 }
1047 }
1048}