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