1use alloc::{boxed::Box, format, rc::Rc, string::String, vec, vec::Vec};
2use core::{
3 any::{TypeId, type_name},
4 cell::{Ref, RefCell, RefMut, UnsafeCell},
5 fmt::{Debug, Display},
6 sync::atomic::Ordering,
7};
8use cubecl_common::format::type_name_sanitized;
9use cubecl_environment::{collections::HashMap, sync::Mutex};
10use derive_more::{Eq, PartialEq};
11use enumset::EnumSet;
12use pliron::{
13 attribute::AttrObj,
14 basic_block::BasicBlock,
15 builtin::{
16 attributes::{TypeAttr, VecAttr},
17 op_interfaces::{OneResultInterface, SingleBlockRegionInterface},
18 ops::{ConstantOp, FuncOp, ModuleOp},
19 type_interfaces::FunctionTypeInterface,
20 types::{FunctionType, UnitType},
21 },
22 context::{AuxDataIndex, Context},
23 debug_info::set_operation_result_name,
24 dict_key,
25 identifier::Identifier,
26 irbuild::{
27 inserter::{IRInserter, Inserter},
28 listener::DummyListener,
29 },
30 op::Op,
31 operation::Operation,
32 printable::Printable,
33 r#type::{TypeHandle, Typed, type_cast},
34 value::Value,
35};
36use portable_atomic::AtomicUsize;
37use spin::LazyLock;
38
39use crate::{
40 AddressSpace, AddressType, DeviceProperties, ElemType, FastMath, TargetProperties, TypeHash,
41 arena::DropBump,
42 attributes::{
43 ATTR_BUFFER_BINDING, ATTR_KEY_ARG_ATTRS, ATTR_TENSOR_MAP_BINDING, BoolAttr,
44 BufferBindingAttr, EntrypointAbiAttr, EntrypointInterface, FuncInterface, IndexAttr,
45 },
46 dialect::{
47 OperationPtrExt,
48 branch::{IfOp, ReturnOp, YieldOp},
49 memory::DeclareVariableOp,
50 vector::CompositeExtractOp,
51 },
52 interfaces::{ScalarType, TypedExt},
53 read_value,
54 settings::KernelSettings,
55 types::{PointerType, RuntimeArrayType, cuda::TensorMapType, scalar::BoolType},
56};
57
58pub type Types = HashMap<TypeId, ElemType>;
59pub type Sizes = HashMap<TypeId, usize>;
60
61pub type OpInserter = IRInserter<DummyListener>;
62
63#[derive(Clone)]
66enum CtxHandle {
67 Rc(Rc<UnsafeCell<Context>>),
68 Ref(*mut Context),
69}
70
71impl CtxHandle {
72 pub fn borrow(&self) -> &Context {
73 match self {
74 CtxHandle::Rc(cell) => unsafe { &*cell.get() },
75 CtxHandle::Ref(ptr) => unsafe { &**ptr },
76 }
77 }
78
79 #[allow(clippy::mut_from_ref)]
80 pub fn borrow_mut(&self) -> &mut Context {
81 match self {
82 CtxHandle::Rc(cell) => unsafe { &mut *cell.get() },
83 CtxHandle::Ref(ptr) => unsafe { &mut **ptr },
84 }
85 }
86}
87
88enum InserterHandle {
91 Owned(Box<UnsafeCell<dyn Inserter>>),
92 Ref(*mut dyn Inserter),
93}
94
95impl InserterHandle {
96 pub fn owned(inserter: impl Inserter + 'static) -> Self {
97 Self::Owned(Box::new(UnsafeCell::new(inserter)))
98 }
99
100 #[allow(clippy::mut_from_ref)]
101 pub fn borrow_mut(&self) -> &mut dyn Inserter {
102 match self {
103 InserterHandle::Owned(cell) => unsafe { &mut *cell.get() },
104 InserterHandle::Ref(ptr) => unsafe { &mut **ptr },
105 }
106 }
107}
108
109#[allow(missing_docs)]
117pub struct Scope {
118 ctx: CtxHandle,
119 inserter: InserterHandle,
120 expand_state: RefCell<ExpandState>,
121}
122
123#[derive(Clone, Copy, Default)]
124pub struct ExpandState {
125 pub may_return: bool,
126 pub may_break: bool,
127 pub inv_return_flag: Option<Value>,
129 pub inv_break_flag: Option<Value>,
131}
132
133impl Debug for Scope {
134 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135 f.debug_struct("Scope").finish()
136 }
137}
138
139#[track_caller]
140pub fn ident(name: impl Into<String>) -> Identifier {
141 Identifier::try_new(name.into()).unwrap()
142}
143
144pub struct GlobalState {
145 pub reference_arena: DropBump,
146 pub errors: Vec<String>,
147
148 pub module: ModuleOp,
149 pub module_inserter: OpInserter,
150 pub entry_func: FuncOp,
151 pub ident_unique_id: AtomicUsize,
152 pub typemap: Types,
153 pub sizemap: Sizes,
154 pub modes: InstructionModes,
155 pub target_properties: TargetProperties,
156 pub device_properties: Option<Rc<DeviceProperties>>,
157}
158
159impl GlobalState {
160 pub fn register_type<T: 'static>(&mut self, elem: ElemType) {
162 self.typemap.insert(TypeId::of::<T>(), elem);
163 }
164}
165
166dict_key!(ADDRESS_TYPE_KEY, "kernel_address_type");
167
168static TY_IDENTS: LazyLock<Mutex<HashMap<TypeId, Identifier>>> = LazyLock::new(Default::default);
169
170fn ty_ident<T: 'static>() -> Identifier {
171 let mut idents = TY_IDENTS.lock();
172 let ident = idents
173 .entry(TypeId::of::<T>())
174 .or_insert_with(|| Identifier::try_from(type_name_sanitized::<T>()).unwrap());
175 ident.clone()
176}
177
178fn ty_key<T: 'static>(ctx: &Context) -> Option<AuxDataIndex> {
179 let mut idents = TY_IDENTS.lock();
180 let ident = idents
181 .entry(TypeId::of::<T>())
182 .or_insert_with(|| Identifier::try_from(type_name_sanitized::<T>()).unwrap());
183 ctx.aux_data_map.get(ident).copied()
184}
185
186pub trait ContextExt {
187 fn aux_ty<T: 'static>(&self) -> &T;
188 fn aux_ty_mut<T: 'static>(&mut self) -> &mut T;
189 fn set_aux_ty<T: 'static>(&mut self, value: T);
190 fn set_address_type(&mut self, addr: AddressType);
191 fn address_type(&self) -> AddressType;
192}
193
194impl ContextExt for Context {
195 #[track_caller]
196 fn aux_ty<T: 'static>(&self) -> &T {
197 let key = ty_key::<T>(self)
198 .ok_or_else(|| format!("Key for {} should exist", type_name::<T>()))
199 .unwrap();
200 self.aux_data[key].downcast_ref().unwrap()
201 }
202
203 #[track_caller]
204 fn aux_ty_mut<T: 'static>(&mut self) -> &mut T {
205 let key = ty_key::<T>(self)
206 .ok_or_else(|| format!("Key for {} should exist", type_name::<T>()))
207 .unwrap();
208 self.aux_data[key].downcast_mut().unwrap()
209 }
210
211 fn set_aux_ty<T: 'static>(&mut self, value: T) {
212 if let Some(key) = ty_key::<T>(self) {
213 *self.aux_data.get_mut(key).unwrap() = Box::new(value);
214 } else {
215 let ident = ty_ident::<T>();
216 let key = self.aux_data.insert(Box::new(value));
217 self.aux_data_map.insert(ident, key);
218 }
219 }
220
221 fn set_address_type(&mut self, addr: AddressType) {
222 if let Some(key) = self.aux_data_map.get(&*ADDRESS_TYPE_KEY).copied() {
223 *self.aux_data.get_mut(key).unwrap() = Box::new(addr);
224 } else {
225 let key = self.aux_data.insert(Box::new(addr));
226 self.aux_data_map.insert(ADDRESS_TYPE_KEY.clone(), key);
227 }
228 }
229
230 fn address_type(&self) -> AddressType {
231 let key = self.aux_data_map[&*ADDRESS_TYPE_KEY];
232 *self.aux_data[key].downcast_ref::<AddressType>().unwrap()
233 }
234}
235
236pub trait FuncOpExt {
237 fn push_argument(&self, ctx: &Context, ty: TypeHandle) -> usize;
238 fn pop_argument(&self, ctx: &Context);
239 fn remove_argument(&self, ctx: &Context, arg_idx: usize);
240 fn return_type(&self, ctx: &Context) -> TypeHandle;
241}
242
243impl FuncOpExt for FuncOp {
244 fn push_argument(&self, ctx: &Context, ty: TypeHandle) -> usize {
245 let id = BasicBlock::push_argument(self.get_entry_block(ctx), ctx, ty);
246
247 let (mut arg_types, res_types) = {
248 let current_func_ty = self.get_type(ctx).deref(ctx);
249 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
250 (current_func_ty.arg_types(), current_func_ty.res_types())
251 };
252
253 arg_types.insert(id, ty);
254 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
255 self.set_attr_func_type(ctx, new_func_ty.into());
256 id
257 }
258
259 fn pop_argument(&self, ctx: &Context) {
260 let last_idx = self.get_entry_block(ctx).deref(ctx).get_num_arguments() - 1;
261 BasicBlock::pop_argument(self.get_entry_block(ctx), ctx);
262
263 let (mut arg_types, res_types) = {
264 let current_func_ty = self.get_type(ctx).deref(ctx);
265 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
266 (current_func_ty.arg_types(), current_func_ty.res_types())
267 };
268
269 arg_types.pop();
270 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
271 self.set_attr_func_type(ctx, new_func_ty.into());
272 let mut op = self.get_operation().deref_mut(ctx);
273 let arg_attrs = op.attributes.0.get_mut(&*ATTR_KEY_ARG_ATTRS);
274 if let Some(arg_attrs) = arg_attrs.and_then(|attr| attr.downcast_mut::<VecAttr>()) {
275 arg_attrs.0.truncate(last_idx);
276 }
277 }
278
279 fn remove_argument(&self, ctx: &Context, arg_idx: usize) {
280 BasicBlock::remove_argument(self.get_entry_block(ctx), ctx, arg_idx);
281
282 let (mut arg_types, res_types) = {
283 let current_func_ty = self.get_type(ctx).deref(ctx);
284 let current_func_ty = current_func_ty.downcast_ref::<FunctionType>().unwrap();
285 (current_func_ty.arg_types(), current_func_ty.res_types())
286 };
287
288 arg_types.remove(arg_idx);
289 let new_func_ty = FunctionType::get(ctx, arg_types, res_types).to_handle();
290 self.set_attr_func_type(ctx, new_func_ty.into());
291
292 let mut op = self.get_operation().deref_mut(ctx);
293 let arg_attrs = op.attributes.0.get_mut(&*ATTR_KEY_ARG_ATTRS);
294 if let Some(arg_attrs) = arg_attrs.and_then(|attr| attr.downcast_mut::<VecAttr>()) {
295 arg_attrs.0.remove(arg_idx);
296 }
297 }
298
299 fn return_type(&self, ctx: &Context) -> TypeHandle {
300 let ty = self.get_type(ctx).deref(ctx);
301 ty.downcast_ref::<FunctionType>().unwrap().res_types()[0]
302 }
303}
304
305fn new_context(settings: KernelSettings) -> Rc<UnsafeCell<Context>> {
306 let mut ctx = Context::default();
307 ctx.set_address_type(settings.address_type);
308
309 let module = ModuleOp::new(&mut ctx, ident("kernel"));
310 let module_block = module.get_body(&ctx, 0);
311 let mut module_inserter = OpInserter::new_at_block_end(module_block);
312
313 let entry_func_ty = FunctionType::get(&ctx, vec![], vec![UnitType::get(&ctx).into()]);
315 let entry_name = Identifier::try_new(settings.kernel_name).unwrap_or(ident("kernel_entry"));
316 let abi = EntrypointAbiAttr::new(settings.cube_dim, settings.cluster_dim);
317 let entry_func = FuncOp::new(&mut ctx, entry_name, entry_func_ty);
318 entry_func.set_entrypoint_abi(&mut ctx, abi);
319 module_inserter.append_op(&ctx, &entry_func);
320
321 let mut state = GlobalState {
322 reference_arena: Default::default(),
323 module,
324 module_inserter,
325 entry_func,
326 ident_unique_id: Default::default(),
327 typemap: Default::default(),
328 sizemap: Default::default(),
329 modes: Default::default(),
330 target_properties: Default::default(),
331 device_properties: Default::default(),
332 errors: Default::default(),
333 };
334 settings.address_type.register(&mut state);
335
336 ctx.set_aux_ty(state);
337 Rc::new(UnsafeCell::new(ctx))
338}
339
340fn dummy_context() -> Rc<UnsafeCell<Context>> {
344 let mut ctx = Context::default();
345
346 let module = ModuleOp::new(&mut ctx, ident("dummy_module"));
347 let module_block = module.get_body(&ctx, 0);
348 let mut module_inserter = OpInserter::new_at_block_end(module_block);
349
350 let entry_func_ty = FunctionType::get(&ctx, vec![], vec![UnitType::get(&ctx).into()]);
351 let entry_name = ident("dummy_entry");
352 let entry_func = FuncOp::new(&mut ctx, entry_name, entry_func_ty);
353 module_inserter.append_op(&ctx, &entry_func);
354
355 let state = GlobalState {
356 reference_arena: Default::default(),
357 module,
358 module_inserter,
359 entry_func,
360 ident_unique_id: Default::default(),
361 typemap: Default::default(),
362 sizemap: Default::default(),
363 modes: Default::default(),
364 target_properties: Default::default(),
365 device_properties: Default::default(),
366 errors: Default::default(),
367 };
368
369 ctx.set_aux_ty(state);
370 Rc::new(UnsafeCell::new(ctx))
371}
372
373impl Debug for GlobalState {
374 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
375 f.debug_struct("GlobalStateInner")
376 .field("reference_arena", &self.reference_arena)
377 .field("typemap", &self.typemap)
378 .field("sizemap", &self.sizemap)
379 .field("modes", &self.modes)
380 .field("target_properties", &self.target_properties)
381 .field("device_properties", &self.device_properties)
382 .finish()
383 }
384}
385
386#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
388#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, TypeHash)]
389pub struct InstructionModes {
390 pub fp_math_mode: EnumSet<FastMath>,
391}
392
393impl Scope {
394 pub fn device_properties(&self, properties: &DeviceProperties) {
396 self.state_mut().device_properties = Some(Rc::new(properties.clone()));
397 }
398
399 #[track_caller]
400 pub fn state(&self) -> &GlobalState {
401 self.ctx().aux_ty()
402 }
403
404 #[track_caller]
405 pub fn state_mut(&self) -> &mut GlobalState {
406 self.ctx_mut().aux_ty_mut()
407 }
408
409 fn ident_id(&self) -> usize {
410 self.state().ident_unique_id.fetch_add(1, Ordering::SeqCst)
411 }
412
413 #[track_caller]
414 pub fn expand_state(&self) -> Ref<'_, ExpandState> {
415 self.expand_state.borrow()
416 }
417
418 #[track_caller]
419 pub fn expand_state_mut(&self) -> RefMut<'_, ExpandState> {
420 self.expand_state.borrow_mut()
421 }
422
423 pub fn ctx(&self) -> &Context {
424 self.ctx.borrow()
425 }
426
427 pub fn ctx_mut(&self) -> &mut Context {
429 self.ctx.borrow_mut()
430 }
431
432 pub fn inserter(&self) -> &mut dyn Inserter {
433 self.inserter.borrow_mut()
434 }
435
436 pub fn root(settings: KernelSettings) -> Self {
440 let ctx = new_context(settings);
441 let mut inserter = {
442 let ctx = unsafe { &*ctx.get() };
443 let state = ctx.aux_ty::<GlobalState>();
444 let entry_block = state.entry_func.get_entry_block(ctx);
445 OpInserter::new_at_block_end(entry_block)
446 };
447 let return_flag =
448 init_bool_flag(unsafe { &mut *ctx.get() }, &mut inserter, "inv_return_flag");
449 Self {
450 ctx: CtxHandle::Rc(ctx),
451 inserter: InserterHandle::owned(inserter),
452 expand_state: RefCell::new(ExpandState {
453 may_break: false,
454 may_return: false,
455 inv_return_flag: Some(return_flag),
456 inv_break_flag: None,
457 }),
458 }
459 }
460
461 pub fn dummy() -> Self {
464 let ctx = dummy_context();
465 let inserter = {
466 let ctx = unsafe { &*ctx.get() };
467 let state = ctx.aux_ty::<GlobalState>();
468 let entry_block = state.entry_func.get_entry_block(ctx);
469 OpInserter::new_at_block_end(entry_block)
470 };
471 Self {
472 ctx: CtxHandle::Rc(ctx),
473 inserter: InserterHandle::owned(inserter),
474 expand_state: Default::default(),
475 }
476 }
477
478 pub fn from_context_and_inserter(
480 ctx: &mut Context,
481 inserter: &mut (impl Inserter + 'static),
482 ) -> Self {
483 let inserter: *mut dyn Inserter = inserter;
484 Self {
485 ctx: CtxHandle::Ref(ctx),
486 inserter: InserterHandle::Ref(inserter),
487 expand_state: RefCell::new(ExpandState {
488 may_return: false,
489 may_break: false,
490 inv_return_flag: None,
491 inv_break_flag: None,
492 }),
493 }
494 }
495
496 pub fn create_local_mut(
501 &self,
502 value_ty: impl Into<TypeHandle>,
503 init: Option<AttrObj>,
504 ) -> Value {
505 let value_ty = value_ty.into();
506 let ctx = self.ctx_mut();
507 let align = value_ty.align(ctx);
508 let op = DeclareVariableOp::new(ctx, value_ty, AddressSpace::Local, align, init);
509 let out = op.get_result(ctx);
510 self.inserter().append_op(ctx, &op);
511 out
512 }
513
514 pub fn create_shared(
516 &self,
517 value_ty: impl Into<TypeHandle>,
518 alignment: Option<usize>,
519 ) -> Value {
520 let value_ty = value_ty.into();
521 let ctx = self.ctx_mut();
522 let align = alignment.unwrap_or_else(|| value_ty.align(ctx));
523 let op = DeclareVariableOp::new(ctx, value_ty, AddressSpace::Shared, align, None);
524 let out = op.get_result(ctx);
525 self.inserter().append_op(ctx, &op);
526 out
527 }
528
529 pub fn func_ident(&self, label: Option<&str>) -> Identifier {
530 let unique_id = self.ident_id();
531 match label {
532 Some(label) => ident(format!("{label}_{unique_id}")),
533 None => ident(format!("func_{unique_id}")),
534 }
535 }
536
537 pub fn register_func(&self, func: FuncOp) {
539 let ctx = self.ctx();
540 let state = self.state_mut();
541 state.module_inserter.append_op(ctx, &func);
542 }
543
544 pub fn register(&self, op: &dyn Op) {
546 let ctx = self.ctx();
547 self.inserter().append_op(ctx, op);
548 }
549
550 pub fn register_with_result(&self, op: &dyn OneResultInterface) -> Value {
552 self.register(op);
553 op.get_result(self.ctx())
554 }
555
556 pub fn terminate_yield(&self) {
558 let block = self.inserter().get_insertion_block(self.ctx());
559 let block = block.expect("Should have insertion block");
560 if block.deref(self.ctx()).get_terminator(self.ctx()).is_none() {
561 self.register(&YieldOp::new(self.ctx_mut()));
562 }
563 }
564
565 pub fn set_break_return(&self, children: &[Scope]) {
566 self.set_may_break(children);
567 self.set_may_return(children);
568 }
569
570 pub fn set_may_return(&self, children: &[Scope]) {
571 let child_may_return = children.iter().any(|scope| scope.expand_state().may_return);
572 if child_may_return {
573 self.expand_state_mut().may_return = true;
574 let flag = self.expand_state().inv_return_flag;
575 self.predicate_on_flag(flag.expect("Can't return in rewrite context"));
576 }
577 }
578
579 pub fn set_may_break(&self, children: &[Scope]) {
580 let child_may_return = children.iter().any(|scope| scope.expand_state().may_break);
581 if child_may_return {
582 self.expand_state_mut().may_break = true;
583 let flag = self.expand_state().inv_break_flag;
584 self.predicate_on_flag(flag.expect("Should have break flag"));
585 }
586 }
587
588 fn predicate_on_flag(&self, flag: Value) {
589 let ctx = self.ctx_mut();
590 let cond = read_value(self, flag);
591 let predication = IfOp::new(ctx, cond);
592 let then_block = predication.then_block(ctx);
593 let else_block = predication.else_block(ctx);
594 let yield_ = YieldOp::new(ctx).get_operation();
595 yield_.insert_at_back(then_block, ctx);
596 let yield_ = YieldOp::new(ctx).get_operation();
597 yield_.insert_at_back(else_block, ctx);
598 self.register(&predication);
599 self.terminate_yield();
605 self.inserter()
606 .set_insertion_point_to_block_start(then_block);
607 }
608
609 pub fn create_kernel_ref<'a, T>(&self, value: T) -> &'a mut T
614 where
615 T: 'a,
616 {
617 let state = self.state_mut();
618 let reference = state.reference_arena.alloc(value);
619 unsafe { core::mem::transmute(reference) }
620 }
621
622 pub fn resolve_type<T: 'static>(&self) -> Option<ElemType> {
624 let state = self.state();
625 let result = state.typemap.get(&TypeId::of::<T>());
626
627 result.cloned()
628 }
629
630 pub fn resolve_size<T: 'static>(&self) -> Option<usize> {
632 let state = self.state();
633 let result = state.sizemap.get(&TypeId::of::<T>());
634
635 result.cloned()
636 }
637
638 pub fn register_type<T: 'static>(&self, elem: ElemType) {
640 self.state_mut().register_type::<T>(elem);
641 }
642
643 pub fn register_size<T: 'static>(&self, size: usize) {
645 let state = self.state_mut();
646
647 state.sizemap.insert(TypeId::of::<T>(), size);
648 }
649
650 pub fn register_value_type<T: 'static, N: 'static>(&self, value: impl Typed) {
652 let ty = value.get_type(self.ctx());
653 let scalar_ty = ty.scalar_ty(self.ctx());
654 let vector_size = ty.vector_size(self.ctx());
655 let storage_ty = {
656 let ctx = self.ctx();
657 let scalar_ty = scalar_ty.deref(ctx);
658 let scalar = type_cast::<dyn ScalarType>(&*scalar_ty).unwrap();
659 scalar.elem_type(ctx)
660 };
661 self.register_type::<T>(storage_ty);
662 self.register_size::<N>(vector_size);
663 }
664
665 pub fn child(&self, inserter: impl Inserter + 'static) -> Self {
667 Self {
668 ctx: self.ctx.clone(),
669 inserter: InserterHandle::owned(inserter),
670 expand_state: RefCell::new(ExpandState {
671 may_break: false,
672 may_return: false,
673 inv_return_flag: self.expand_state().inv_return_flag,
674 inv_break_flag: self.expand_state().inv_break_flag,
675 }),
676 }
677 }
678
679 pub fn loop_child(&self, inserter: impl Inserter + 'static) -> Self {
681 let break_flag = init_bool_flag(self.ctx_mut(), self.inserter(), "inv_break_flag");
682 Self {
683 ctx: self.ctx.clone(),
684 inserter: InserterHandle::owned(inserter),
685 expand_state: RefCell::new(ExpandState {
686 may_return: false,
687 may_break: false,
688 inv_return_flag: self.expand_state().inv_return_flag,
689 inv_break_flag: Some(break_flag),
690 }),
691 }
692 }
693
694 pub fn func_child(&self, mut inserter: impl Inserter + 'static) -> Self {
696 let return_flag = init_bool_flag(self.ctx_mut(), &mut inserter, "inv_return_flag");
697 Self {
698 ctx: self.ctx.clone(),
699 inserter: InserterHandle::owned(inserter),
700 expand_state: RefCell::new(ExpandState {
701 may_break: false,
702 may_return: false,
703 inv_return_flag: Some(return_flag),
704 inv_break_flag: None,
705 }),
706 }
707 }
708
709 pub fn push_error(&self, msg: impl Into<String>) {
711 self.state_mut().errors.push(msg.into());
712 }
713
714 pub fn pop_errors(&self) -> Vec<String> {
716 core::mem::take(&mut self.state_mut().errors)
717 }
718
719 pub fn global(
721 &self,
722 buffer_pos: usize,
723 ext_meta_pos: Option<usize>,
724 value_ty: TypeHandle,
725 ) -> Value {
726 let entry_func = self.state().entry_func;
727 let ctx = self.ctx_mut();
728
729 let ty_arr = RuntimeArrayType::get(ctx, value_ty);
730 let ty = PointerType::get(ctx, ty_arr.into(), AddressSpace::Global(buffer_pos));
731
732 let id = entry_func.push_argument(ctx, ty.to_handle());
733 entry_func.set_arg_attr(
734 ctx,
735 id,
736 &ATTR_BUFFER_BINDING,
737 Box::new(BufferBindingAttr::new(buffer_pos, ext_meta_pos)),
738 );
739
740 entry_func.get_entry_block(ctx).deref(ctx).get_argument(id)
741 }
742
743 pub fn tensor_map(&self, buffer_pos: usize, ext_meta_pos: usize) -> Value {
745 let entry_func = self.state().entry_func;
746 let ctx = self.ctx();
747 let ty = TensorMapType::get(ctx);
748
749 let id = entry_func.push_argument(ctx, ty.to_handle());
750 entry_func.set_arg_attr_unit(ctx, id, &ATTR_TENSOR_MAP_BINDING);
751 entry_func.set_arg_attr(
752 ctx,
753 id,
754 &ATTR_BUFFER_BINDING,
755 Box::new(BufferBindingAttr::new(buffer_pos, Some(ext_meta_pos))),
756 );
757 entry_func.get_entry_block(ctx).deref(ctx).get_argument(id)
758 }
759
760 pub fn kernel_arg(&self, idx: usize) -> Value {
761 let entry_block = self.state().entry_func.get_entry_block(self.ctx());
762 entry_block.deref(self.ctx()).get_argument(idx)
763 }
764
765 pub fn extract_field(&self, aggregate: Value, field: usize) -> Value {
766 let ctx = self.ctx_mut();
767 let op = CompositeExtractOp::new(ctx, aggregate, field);
768 self.register_with_result(&op)
769 }
770
771 pub fn const_usize(&self, value: usize) -> Value {
772 let op = ConstantOp::new(self.ctx_mut(), IndexAttr::new(value).into());
773 self.register_with_result(&op)
774 }
775
776 pub fn const_bool(&self, value: bool) -> Value {
777 let op = ConstantOp::new(self.ctx_mut(), BoolAttr::new(value).into());
778 self.register_with_result(&op)
779 }
780
781 pub fn into_context(self) -> Option<Context> {
782 let entry = self.state().entry_func.get_entry_block(self.ctx());
783 let term = entry.deref(self.ctx()).get_terminator(self.ctx());
784 let is_yield = term.is_some_and(|term| term.is_op::<YieldOp>(self.ctx()));
785 if let Some(term) = term
786 && is_yield
787 {
788 self.inserter().set_insertion_point_to_block_end(entry);
789 self.register(&ReturnOp::new(self.ctx_mut()));
790 Operation::erase(term, self.ctx_mut());
791 } else if term.is_none() {
792 self.inserter().set_insertion_point_to_block_end(entry);
793 self.register(&ReturnOp::new(self.ctx_mut()));
794 }
795 match self.ctx {
796 CtxHandle::Rc(ctx) => Some(Rc::into_inner(ctx)?.into_inner()),
797 CtxHandle::Ref(_) => None,
798 }
799 }
800}
801
802impl Display for Scope {
803 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
804 let ctx = self.ctx();
805 let state = self.state();
806 write!(f, "{}", state.module.disp(ctx))
807 }
808}
809
810fn init_bool_flag(ctx: &mut Context, inserter: &mut dyn Inserter, name: &str) -> Value {
811 let bool = TypeAttr::new(BoolType::get(ctx).to_handle());
812 let r#true = BoolAttr::new(true).into();
813 let flag = DeclareVariableOp::new(ctx, bool, AddressSpace::Local, 1, Some(r#true));
814 inserter.append_op(ctx, &flag);
815 set_operation_result_name(ctx, flag.get_operation(), 0, Some(ident(name)));
816 flag.get_result(ctx)
817}