1use alloc::vec::Vec;
2use cubecl_ir::{
3 ExpandState, ExpandValue, OpInserter,
4 dialect::{
5 branch::{ConditionOp, IfOp, RangeLoopOp, SwitchOp, UnreachableOp, WhileOp},
6 general::BoolAndOp,
7 },
8 pliron::{irbuild::inserter::Inserter, r#type::TypedHandle},
9 read_value,
10 rewrite::{op_insertion_point_in_block, set_inserter_before_terminator},
11};
12use pliron::{
13 basic_block::BasicBlock,
14 builtin::{
15 attributes::IntegerAttr,
16 op_interfaces::OneRegionInterface,
17 types::{IntegerType, Signedness},
18 },
19 irbuild::{
20 listener::DummyListener,
21 rewriter::{IRRewriter, Rewriter},
22 },
23 op::Op,
24 region::Region,
25 r#type::Typed,
26 utils::apint::{APInt, bw},
27 value::Value,
28};
29
30use crate::{
31 IntoRuntime,
32 frontend::{
33 CubePrimitive, ReadValue, RuntimeAssign, assign, assign_binop_expand, binary_expand,
34 init_mut_of_type,
35 },
36 prelude::{CubeEnum, ExpandTypeClone},
37};
38use crate::{ir::Scope, prelude::Assign};
39
40use super::{Int, NativeExpand};
41
42pub trait Iterable: Sized {
45 type Item;
46
47 fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, Self::Item));
57 fn expand_unroll(self, scope: &Scope, body: &mut dyn FnMut(&Scope, Self::Item));
64 fn const_len(&self) -> Option<usize> {
66 None
67 }
68}
69
70pub fn for_expand<I: Iterable>(
81 scope: &Scope,
82 range: I,
83 unroll: bool,
84 mut body: impl FnMut(&Scope, I::Item),
85) {
86 for_expand_dyn(scope, range, unroll, &mut body);
87}
88
89fn for_expand_dyn<I: Iterable>(
90 scope: &Scope,
91 range: I,
92 unroll: bool,
93 body: &mut dyn FnMut(&Scope, I::Item),
94) {
95 if unroll || range.const_len() == Some(1) {
96 range.expand_unroll(scope, body);
97 } else {
98 range.expand(scope, body);
99 }
100}
101
102pub fn if_expand(scope: &Scope, condition: NativeExpand<bool>, block: impl FnOnce(&Scope)) {
103 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
104 match comptime_cond {
105 Some(cond) => {
106 if cond {
107 block(scope);
108 }
109 }
110 None => {
111 let cond = condition.read_value(scope);
112 let if_op = IfOp::new(scope.ctx_mut(), cond);
113
114 let then_block = if_op.then_block(scope.ctx());
115 let then_child = scope.branch_child(OpInserter::new_at_block_end(then_block));
116 block(&then_child);
117 then_child.terminate_yield();
118
119 let else_block = if_op.else_block(scope.ctx());
120 let else_child = scope.branch_child(OpInserter::new_at_block_end(else_block));
121 else_child.terminate_yield();
122
123 scope.register(&if_op);
124 scope.update_flags_after_branch(&[then_child, else_child]);
125 }
126 }
127}
128
129#[allow(clippy::large_enum_variant)]
130pub enum IfElseExpand {
131 ComptimeThen,
132 ComptimeElse,
133 Runtime {
134 runtime_cond: NativeExpand<bool>,
135 if_op: IfOp,
136 then_child: Scope,
137 },
138}
139
140impl IfElseExpand {
141 pub fn or_else(self, scope: &Scope, else_block: impl FnOnce(&Scope)) {
142 match self {
143 Self::Runtime {
144 if_op, then_child, ..
145 } => {
146 let else_body = if_op.else_block(scope.ctx());
147 let else_child = scope.branch_child(OpInserter::new_at_block_end(else_body));
148 else_block(&else_child);
149 else_child.terminate_yield();
150
151 scope.register(&if_op);
152 scope.update_flags_after_branch(&[then_child, else_child]);
153 }
154 Self::ComptimeElse => else_block(scope),
155 Self::ComptimeThen => (),
156 }
157 }
158}
159
160pub fn if_else_expand(
161 scope: &Scope,
162 condition: NativeExpand<bool>,
163 then_block: impl FnOnce(&Scope),
164) -> IfElseExpand {
165 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
166 match comptime_cond {
167 Some(true) => {
168 then_block(scope);
169 IfElseExpand::ComptimeThen
170 }
171 Some(false) => IfElseExpand::ComptimeElse,
172 None => {
173 let cond = condition.read_value(scope);
174 let if_op = IfOp::new(scope.ctx_mut(), cond);
175 let if_block = if_op.then_block(scope.ctx());
176 let then_child = scope.branch_child(OpInserter::new_at_block_end(if_block));
177 then_block(&then_child);
178 then_child.terminate_yield();
179
180 IfElseExpand::Runtime {
181 runtime_cond: condition,
182 if_op,
183 then_child,
184 }
185 }
186 }
187}
188
189#[allow(clippy::large_enum_variant)]
190pub enum IfElseExprExpand<C: Assign> {
191 ComptimeThen(C),
192 ComptimeElse,
193 Runtime {
194 runtime_cond: NativeExpand<bool>,
195 out: C,
196 if_op: IfOp,
197 then_child: Scope,
198 },
199}
200
201impl<C: Assign> IfElseExprExpand<C> {
202 pub fn or_else<R: RuntimeAssign<Expand = C>>(
203 self,
204 scope: &Scope,
205 else_block: impl FnOnce(&Scope) -> R,
206 ) -> C {
207 match self {
208 Self::Runtime {
209 mut out,
210 if_op,
211 then_child,
212 ..
213 } => {
214 let else_body = if_op.else_block(scope.ctx());
215 let else_child = scope.branch_child(OpInserter::new_at_block_end(else_body));
216 let ret = else_block(&else_child);
217 out.__expand_assign_method(&else_child, ret.into_expand(scope));
218 else_child.terminate_yield();
219
220 scope.register(&if_op);
221 scope.update_flags_after_branch(&[then_child, else_child]);
222 out
223 }
224 Self::ComptimeElse => else_block(scope).into_expand(scope),
225 Self::ComptimeThen(ret) => ret,
226 }
227 }
228}
229
230pub fn if_else_expr_expand<C: RuntimeAssign>(
231 scope: &Scope,
232 condition: NativeExpand<bool>,
233 then_block: impl FnOnce(&Scope) -> C,
234) -> IfElseExprExpand<C::Expand> {
235 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
236 match comptime_cond {
237 Some(true) => {
238 let ret = then_block(scope);
239 IfElseExprExpand::ComptimeThen(ret.into_expand(scope))
240 }
241 Some(false) => IfElseExprExpand::ComptimeElse,
242 None => {
243 let cond = condition.read_value(scope);
244 let if_op = IfOp::new(scope.ctx_mut(), cond);
245 let then_body = if_op.then_block(scope.ctx());
246 let then_child = scope.branch_child(OpInserter::new_at_block_end(then_body));
247 let ret = then_block(&then_child);
248 let mut out = ret.init_mut(scope);
249 out.__expand_assign_method(&then_child, ret.into_expand(scope));
250 then_child.terminate_yield();
251
252 IfElseExprExpand::Runtime {
253 runtime_cond: condition,
254 out,
255 if_op,
256 then_child,
257 }
258 }
259 }
260}
261
262pub struct SwitchExpand<I: Int> {
263 switch_op: SwitchOp,
264 cases: Vec<I>,
265 children: Vec<Scope>,
266}
267
268impl<I: Int> SwitchExpand<I> {
269 pub fn case(mut self, scope: &Scope, value: impl Int, block: impl FnOnce(&Scope)) -> Self {
270 let value = I::from(value).unwrap();
271 self.cases.push(value);
272 let body = self.switch_op.append_case_block(scope.ctx_mut());
273 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
274 block(&case_child);
275 case_child.terminate_yield();
276 self.children.push(case_child);
277 self
278 }
279
280 pub fn finish(self, scope: &Scope) {
281 let cases = self.cases.into_iter().map(|case| {
282 let ty = I::__expand_as_type(scope);
283 let ty = TypedHandle::<IntegerType>::from_handle(ty, scope.ctx()).unwrap();
284 let width = bw(ty.deref(scope.ctx()).width() as usize);
285 IntegerAttr::new(ty, APInt::from_i128(case.to_i128().unwrap(), width))
286 });
287 self.switch_op.set_attr_cases(scope.ctx(), cases);
288 scope.register(&self.switch_op);
289 scope.update_flags_after_branch(&self.children);
290 }
291}
292
293pub fn switch_expand<I: Int>(
294 scope: &Scope,
295 value: NativeExpand<I>,
296 default_block: impl FnOnce(&Scope),
297) -> SwitchExpand<I> {
298 let value = value.read_value(scope);
299 let switch_op = SwitchOp::new(scope.ctx_mut(), value);
300
301 let default_body = switch_op.default_block(scope.ctx());
302 let default_child = scope.branch_child(OpInserter::new_at_block_end(default_body));
303 default_block(&default_child);
304 default_child.terminate_yield();
305
306 SwitchExpand {
307 switch_op,
308 cases: Vec::new(),
309 children: alloc::vec![default_child],
310 }
311}
312
313pub struct SwitchExpandExpr<I: Int, C: Assign> {
314 switch_op: SwitchOp,
315 cases: Vec<I>,
316 children: Vec<Scope>,
317 out: C,
318}
319
320impl<I: Int, C: Assign> SwitchExpandExpr<I, C> {
321 pub fn case<T: RuntimeAssign<Expand = C>>(
322 mut self,
323 scope: &Scope,
324 value: impl Int,
325 block: impl FnOnce(&Scope) -> T,
326 ) -> Self {
327 let value = I::from(value).unwrap();
328 self.cases.push(value);
329 let body = self.switch_op.append_case_block(scope.ctx_mut());
330 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
331 let ret = block(&case_child);
332 self.out
333 .__expand_assign_method(&case_child, ret.into_expand(scope));
334 case_child.terminate_yield();
335 self.children.push(case_child);
336 self
337 }
338
339 pub fn finish(self, scope: &Scope) -> C {
340 let cases = self.cases.into_iter().map(|case| {
341 let ty = I::__expand_as_type(scope);
342 let ty = TypedHandle::<IntegerType>::from_handle(ty, scope.ctx()).unwrap();
343 let width = bw(ty.deref(scope.ctx()).width() as usize);
344 IntegerAttr::new(ty, APInt::from_i128(case.to_i128().unwrap(), width))
345 });
346 self.switch_op.set_attr_cases(scope.ctx(), cases);
347 scope.register(&self.switch_op);
348 scope.update_flags_after_branch(&self.children);
349 self.out
350 }
351}
352
353pub fn switch_expand_expr<I: Int, C: RuntimeAssign>(
354 scope: &Scope,
355 value: NativeExpand<I>,
356 default_block: impl FnOnce(&Scope) -> C,
357) -> SwitchExpandExpr<I, C::Expand> {
358 let value = value.read_value(scope);
359 let switch_op = SwitchOp::new(scope.ctx_mut(), value);
360
361 let default_body = switch_op.default_block(scope.ctx());
362 let default_child = scope.branch_child(OpInserter::new_at_block_end(default_body));
363 let default = default_block(&default_child);
364 let mut out = default.init_mut(scope);
365 out.__expand_assign_method(&default_child, default.into_expand(scope));
366 default_child.terminate_yield();
367
368 SwitchExpandExpr {
369 switch_op,
370 cases: Vec::new(),
371 children: alloc::vec![default_child],
372 out,
373 }
374}
375
376#[allow(clippy::large_enum_variant)]
377pub enum MatchExpand<T: CubeEnum> {
378 ComptimeVariant {
379 variant: i32,
380 runtime_value: T::RuntimeValue,
381 matched: bool,
382 },
383 RuntimeVariant {
384 switch_op: SwitchOp,
385 cases: Vec<i32>,
386 children: Vec<Scope>,
387 has_default: bool,
388 runtime_value: T::RuntimeValue,
389 },
390}
391
392impl<T: CubeEnum> MatchExpand<T> {
393 pub fn case(
394 mut self,
395 scope: &Scope,
396 value: i32,
397 block: impl FnOnce(&Scope, T::RuntimeValue),
398 ) -> Self {
399 match &mut self {
400 Self::RuntimeVariant {
401 switch_op,
402 cases,
403 children,
404 runtime_value,
405 ..
406 } => {
407 cases.push(value);
408 let body = switch_op.append_case_block(scope.ctx_mut());
409 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
410 block(&case_child, (*runtime_value).clone_unchecked());
411 case_child.terminate_yield();
412 children.push(case_child);
413 }
414 Self::ComptimeVariant {
415 variant,
416 runtime_value,
417 matched,
418 } => {
419 if value == *variant {
420 block(scope, (*runtime_value).clone_unchecked());
421 *matched = true;
422 }
423 }
424 }
425 self
426 }
427
428 pub fn default(mut self, scope: &Scope, block: impl FnOnce(&Scope, T::RuntimeValue)) -> Self {
429 match &mut self {
430 Self::RuntimeVariant {
431 switch_op,
432 children,
433 runtime_value,
434 has_default,
435 ..
436 } => {
437 let body = switch_op.default_block(scope.ctx());
438 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
439 block(&case_child, (*runtime_value).clone_unchecked());
440 case_child.terminate_yield();
441 children.push(case_child);
442 *has_default = true;
443 }
444 Self::ComptimeVariant {
445 runtime_value,
446 matched,
447 ..
448 } => {
449 if !*matched {
450 block(scope, (*runtime_value).clone_unchecked());
451 *matched = true;
452 }
453 }
454 }
455 self
456 }
457
458 pub fn finish(self, scope: &Scope) {
459 match self {
460 MatchExpand::ComptimeVariant { .. } => {}
461 MatchExpand::RuntimeVariant {
462 switch_op,
463 cases,
464 children,
465 has_default,
466 ..
467 } => {
468 if !has_default {
469 let default_body = switch_op.default_block(scope.ctx());
470 let mut inserter = OpInserter::new_at_block_end(default_body);
471 let unreachable = UnreachableOp::new(scope.ctx_mut());
472 inserter.append_op(scope.ctx(), &unreachable);
473 }
474
475 let cases = cases.into_iter().map(|case| {
476 let ty = IntegerType::get(scope.ctx(), 32, Signedness::Unsigned);
477 IntegerAttr::new(ty, APInt::from_i32(case, bw(32)))
478 });
479 switch_op.set_attr_cases(scope.ctx(), cases);
480 scope.register(&switch_op);
481 scope.update_flags_after_branch(&children);
482 }
483 }
484 }
485}
486
487pub fn match_expand<T: CubeEnum>(
488 scope: &Scope,
489 value: T,
490 discriminant0: i32,
491 arm0: impl FnOnce(&Scope, T::RuntimeValue),
492) -> MatchExpand<T> {
493 let discriminant = value.discriminant();
494 match discriminant.constant() {
495 Some(const_variant) if const_variant.as_i32() == discriminant0 => {
496 let runtime_value = value.runtime_value();
497 arm0(scope, runtime_value.clone_unchecked());
498 MatchExpand::ComptimeVariant {
499 variant: const_variant.as_i32(),
500 runtime_value,
501 matched: true,
502 }
503 }
504 Some(const_variant) => MatchExpand::ComptimeVariant {
505 variant: const_variant.as_i32(),
506 runtime_value: value.runtime_value(),
507 matched: false,
508 },
509 None => {
510 let discriminant = discriminant.read_value(scope);
511 let runtime_value = value.runtime_value();
512
513 let switch_op = SwitchOp::new(scope.ctx_mut(), discriminant);
514 let body = switch_op.append_case_block(scope.ctx_mut());
515 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
516 arm0(&case_child, runtime_value.clone_unchecked());
517 case_child.terminate_yield();
518
519 MatchExpand::RuntimeVariant {
520 switch_op,
521 cases: alloc::vec![discriminant0],
522 children: alloc::vec![case_child],
523 has_default: false,
524 runtime_value,
525 }
526 }
527 }
528}
529
530#[allow(clippy::large_enum_variant)]
531pub enum MatchExpandExpr<T: CubeEnum, C: Assign> {
532 ComptimeVariant {
533 variant: i32,
534 runtime_value: T::RuntimeValue,
535 out: Option<C>,
536 matched: bool,
537 },
538 RuntimeVariant {
539 switch_op: SwitchOp,
540 cases: Vec<i32>,
541 children: Vec<Scope>,
542 has_default: bool,
543 out: C,
544 runtime_value: T::RuntimeValue,
545 },
546}
547
548impl<T: CubeEnum, C: Assign> MatchExpandExpr<T, C> {
549 pub fn case<R: RuntimeAssign<Expand = C>>(
550 mut self,
551 scope: &Scope,
552 value: i32,
553 block: impl FnOnce(&Scope, T::RuntimeValue) -> R,
554 ) -> Self {
555 match &mut self {
556 Self::RuntimeVariant {
557 switch_op,
558 cases,
559 children,
560 out,
561 runtime_value,
562 ..
563 } => {
564 cases.push(value);
565 let body = switch_op.append_case_block(scope.ctx_mut());
566 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
567 let ret_val = block(&case_child, (*runtime_value).clone_unchecked());
568 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
569 case_child.terminate_yield();
570 children.push(case_child);
571 }
572 Self::ComptimeVariant {
573 variant,
574 runtime_value,
575 out,
576 matched,
577 } => {
578 if value == *variant {
579 *out =
580 Some(block(scope, (*runtime_value).clone_unchecked()).into_expand(scope));
581 *matched = true;
582 }
583 }
584 }
585 self
586 }
587
588 pub fn default<R: RuntimeAssign<Expand = C>>(
589 mut self,
590 scope: &Scope,
591 block: impl FnOnce(&Scope, T::RuntimeValue) -> R,
592 ) -> Self {
593 match &mut self {
594 Self::RuntimeVariant {
595 switch_op,
596 children,
597 runtime_value,
598 out,
599 has_default,
600 ..
601 } => {
602 let body = switch_op.default_block(scope.ctx());
603 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
604 let ret_val = block(&case_child, (*runtime_value).clone_unchecked());
605 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
606 case_child.terminate_yield();
607 children.push(case_child);
608 *has_default = true;
609 }
610 Self::ComptimeVariant {
611 runtime_value,
612 out,
613 matched,
614 ..
615 } => {
616 if !*matched {
617 *out =
618 Some(block(scope, (*runtime_value).clone_unchecked()).into_expand(scope));
619 *matched = true;
620 }
621 }
622 }
623 self
624 }
625
626 pub fn finish(self, scope: &Scope) -> C {
627 match self {
628 MatchExpandExpr::ComptimeVariant { out, .. } => {
629 out.expect("At least one variant should be matched")
630 }
631 MatchExpandExpr::RuntimeVariant {
632 switch_op,
633 cases,
634 children,
635 has_default,
636 out,
637 ..
638 } => {
639 if !has_default {
640 let default_body = switch_op.default_block(scope.ctx());
641 let mut inserter = OpInserter::new_at_block_end(default_body);
642 let unreachable = UnreachableOp::new(scope.ctx_mut());
643 inserter.append_op(scope.ctx(), &unreachable);
644 }
645
646 let cases = cases.into_iter().map(|case| {
647 let ty = IntegerType::get(scope.ctx(), 32, Signedness::Unsigned);
648 IntegerAttr::new(ty, APInt::from_i32(case, bw(32)))
649 });
650 switch_op.set_attr_cases(scope.ctx(), cases);
651 scope.register(&switch_op);
652 scope.update_flags_after_branch(&children);
653
654 out
655 }
656 }
657 }
658}
659
660pub fn match_expand_expr<T: CubeEnum, C: RuntimeAssign>(
661 scope: &Scope,
662 value: T,
663 discriminant0: i32,
664 arm0: impl FnOnce(&Scope, T::RuntimeValue) -> C,
665) -> MatchExpandExpr<T, C::Expand> {
666 let discriminant = value.discriminant();
667 match discriminant.constant() {
668 Some(const_variant) if const_variant.as_i32() == discriminant0 => {
669 let runtime_value = value.runtime_value();
670 let out = arm0(scope, runtime_value.clone_unchecked());
671 MatchExpandExpr::ComptimeVariant {
672 variant: const_variant.as_i32(),
673 out: Some(out.into_expand(scope)),
674 runtime_value,
675 matched: true,
676 }
677 }
678 Some(const_variant) => MatchExpandExpr::ComptimeVariant {
679 variant: const_variant.as_i32(),
680 out: None,
681 runtime_value: value.runtime_value(),
682 matched: false,
683 },
684 None => {
685 let discriminant = discriminant.read_value(scope);
686 let runtime_value = value.runtime_value();
687
688 let switch_op = SwitchOp::new(scope.ctx_mut(), discriminant);
689 let body = switch_op.append_case_block(scope.ctx_mut());
690 let case_child = scope.branch_child(OpInserter::new_at_block_end(body));
691 let ret_val = arm0(&case_child, runtime_value.clone_unchecked());
692
693 let mut out = ret_val.init_mut(scope);
694 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
695 case_child.terminate_yield();
696
697 MatchExpandExpr::RuntimeVariant {
698 switch_op,
699 out,
700 cases: alloc::vec![discriminant0],
701 children: alloc::vec![case_child],
702 runtime_value,
703 has_default: false,
704 }
705 }
706 }
707}
708
709pub fn continue_expand(scope: &Scope) {
710 let inv_continue_flag = scope
711 .expand_state()
712 .inv_continue_flag
713 .expect("Should be in loop");
714 let false_ = false.__expand_runtime_method(scope).expand;
715 assign::expand_element(scope, false_, inv_continue_flag.into());
716 scope.expand_state_mut().may_continue = true;
717}
718
719pub fn break_expand(scope: &Scope) {
720 let inv_break_flag = scope
721 .expand_state()
722 .inv_break_flag
723 .expect("Should be in loop");
724 let false_ = false.__expand_runtime_method(scope).expand;
725 assign::expand_element(scope, false_, inv_break_flag.into());
726 scope.expand_state_mut().may_break = true;
727}
728
729pub fn return_expand(scope: &Scope) {
730 let inv_return_flag = scope.expand_state().inv_return_flag;
731 if let Some(inv_return_flag) = inv_return_flag {
732 let false_ = false.__expand_runtime_method(scope).expand;
733 assign::expand_element(scope, false_, inv_return_flag.into());
734 scope.expand_state_mut().may_return = true;
735 } else {
736 panic!("Can't return when no return flag has been set up")
737 }
738}
739
740pub fn return_with_value_expand<T: CubePrimitive>(
742 scope: &Scope,
743 value: NativeExpand<T>,
744) -> NativeExpand<T> {
745 let inv_return_flag = scope.expand_state().inv_return_flag;
746 if let Some(inv_return_flag) = inv_return_flag {
747 let return_value = scope.expand_state().return_value.expect("Should exist");
748 let false_ = false.__expand_runtime_method(scope).expand;
749
750 assign::expand_element(scope, false_, inv_return_flag.into());
751 assign::expand_element(scope, value.expand, return_value.into());
752 scope.expand_state_mut().may_return = true;
753 } else {
754 panic!("Can't return when no return flag has been set up")
755 }
756 value
757}
758
759pub fn terminate_expand(scope: &Scope) {
760 let inv_terminate_flag = scope.expand_state().inv_terminate_flag;
761 if let Some(inv_terminate_flag) = inv_terminate_flag {
762 let false_ = false.__expand_runtime_method(scope).expand;
763 assign::expand_element(scope, false_, inv_terminate_flag.into());
764 scope.expand_state_mut().may_terminate = true;
765 } else {
766 panic!("Can't terminate when no terminate flag has been set up")
767 }
768}
769
770#[diagnostic::on_unimplemented(
771 message = "Only runtime primitives can be returned. {Self} is not a primitive."
772)]
773pub trait EarlyReturnable {
774 fn init_value(scope: &Scope) -> Option<Value>;
775 fn from_value(scope: &Scope, value: Option<Value>) -> Self;
776}
777
778impl<T: CubePrimitive> EarlyReturnable for NativeExpand<T> {
779 fn init_value(scope: &Scope) -> Option<Value> {
780 Some(init_mut_of_type(scope, T::__expand_as_type(scope)).value(scope))
781 }
782 fn from_value(scope: &Scope, value: Option<Value>) -> Self {
783 read_value(scope, value.unwrap()).into()
784 }
785}
786impl EarlyReturnable for () {
787 fn init_value(_: &Scope) -> Option<Value> {
788 None
789 }
790 fn from_value(_: &Scope, _: Option<Value>) -> Self {}
791}
792
793pub fn setup_early_return<T: EarlyReturnable>(scope: &Scope, body: impl FnOnce(&Scope) -> T) -> T {
794 let value = T::init_value(scope);
795 let inserter = OpInserter::new(scope.inserter().get_insertion_point());
796 let child = scope.inlined_func_child(inserter, value);
797 body(&child);
798
799 let current_block = scope.inserter().get_insertion_block(scope.ctx()).unwrap();
800 let after_point = op_insertion_point_in_block(
801 scope.ctx(),
802 child.inserter().get_insertion_point(),
803 current_block,
804 );
805 if child.inserter().get_insertion_block(scope.ctx()) != Some(current_block) {
806 child.terminate_yield();
807 }
808
809 scope.inserter().set_insertion_point(after_point);
810 scope.update_flags_after_early_return(&[child]);
811 T::from_value(scope, value)
812}
813
814pub mod unreachable_unchecked {
815 use super::*;
816
817 pub fn expand(scope: &Scope) {
818 scope.register(&UnreachableOp::new(scope.ctx_mut()));
819 }
820}
821
822pub struct WhileBuilder {
826 while_op: WhileOp,
827 cond: ExpandValue,
828 cond_scope: Scope,
829}
830
831impl WhileBuilder {
832 pub fn new(scope: &Scope, mut cond: impl FnMut(&Scope) -> NativeExpand<bool>) -> Self {
833 let while_op = WhileOp::new(scope.ctx_mut());
834 let cond_scope = scope.branch_child(OpInserter::new_at_block_start(
835 while_op.before_block(scope.ctx()),
836 ));
837
838 WhileBuilder {
839 while_op,
840 cond: cond(&cond_scope).expand,
841 cond_scope,
842 }
843 }
844
845 pub fn with_body(self, scope: &Scope, mut block: impl FnMut(&Scope)) {
846 let Self {
847 while_op,
848 mut cond,
849 cond_scope,
850 } = self;
851
852 let body = while_op.after_block(scope.ctx());
853 let body = scope.loop_child(OpInserter::new_at_block_end(body));
854 block(&body);
855 body.terminate_yield();
856
857 let expand_state = *body.expand_state();
858 let break_flag = expand_state.inv_break_flag.unwrap().into();
859 let return_flag = expand_state.inv_return_flag.map(Into::into);
860 let terminate_flag = expand_state.inv_terminate_flag.map(Into::into);
861
862 if expand_state.may_break {
863 cond = binary_expand(&cond_scope, cond, break_flag, BoolAndOp::new);
864 }
865 if expand_state.may_return {
866 let return_flag = return_flag.unwrap();
867 cond = binary_expand(&cond_scope, cond, return_flag, BoolAndOp::new);
868 }
869 if expand_state.may_terminate {
870 let terminate_flag = terminate_flag.unwrap();
871 cond = binary_expand(&cond_scope, cond, terminate_flag, BoolAndOp::new);
872 }
873
874 let cond = cond.read_value(&cond_scope);
875 cond_scope.register(&ConditionOp::new(scope.ctx_mut(), cond));
876
877 scope.register(&while_op);
878 scope.update_flags_after_loop(&[cond_scope, body]);
879 }
880}
881
882pub(crate) fn register_range_loop<I: Int>(scope: &Scope, for_op: &RangeLoopOp, body: &Scope) {
884 let ctx = scope.ctx_mut();
885 let ExpandState {
886 may_terminate,
887 may_return,
888 may_break,
889 may_continue,
890 inv_terminate_flag,
891 inv_return_flag,
892 inv_break_flag,
893 inv_continue_flag: _,
894 return_value: _,
895 } = *body.expand_state();
896 if !may_break && !may_return && !may_terminate {
897 body.terminate_yield();
898 scope.register(for_op);
899 return;
900 }
901
902 let mut rewriter = IRRewriter::<DummyListener>::default();
903
904 let start = for_op.start(ctx);
905 let end = for_op.end(ctx);
906 let step = for_op.step(ctx);
907 let iter_var_old = for_op.iter_var(ctx);
908
909 let iter_var = ExpandValue::from(scope.create_local_mut(iter_var_old.get_type(ctx), None));
910 assign::expand_element(scope, start.into(), iter_var);
911
912 if may_continue {
916 set_inserter_before_terminator(body.inserter(), ctx, for_op.loop_body(ctx));
917 }
918 assign_binop_expand::<I>(
919 body,
920 &mut iter_var.into(),
921 step.into(),
922 I::__expand_native_add,
923 );
924 body.terminate_yield();
925
926 body.inserter()
927 .set_insertion_point_to_block_start(for_op.loop_body(ctx));
928 let iter_val = iter_var.read_value(body);
929 rewriter.replace_value_uses_with(ctx, iter_var_old, iter_val);
930 BasicBlock::remove_argument(for_op.loop_body(ctx), ctx, 0);
931
932 let while_op = WhileOp::new(ctx);
933
934 let cond_scope = scope.branch_child(OpInserter::new_at_block_start(
935 while_op.before_block(scope.ctx()),
936 ));
937
938 let mut cond = I::__expand_native_lt(&cond_scope, iter_var, end.into());
939 if may_break {
940 let inv_break_flag = inv_break_flag.unwrap().into();
941 cond = binary_expand(&cond_scope, cond, inv_break_flag, BoolAndOp::new);
942 }
943 if may_return {
944 let inv_return_flag = inv_return_flag.unwrap().into();
945 cond = binary_expand(&cond_scope, cond, inv_return_flag, BoolAndOp::new);
946 }
947 if may_terminate {
948 let inv_terminate_flag = inv_terminate_flag.unwrap().into();
949 cond = binary_expand(&cond_scope, cond, inv_terminate_flag, BoolAndOp::new);
950 }
951
952 let cond = cond.read_value(&cond_scope);
953 cond_scope.register(&ConditionOp::new(scope.ctx_mut(), cond));
954
955 rewriter.erase_region(ctx, while_op.after_region(ctx));
956 Region::move_to_op(for_op.get_region(ctx), while_op.get_operation(), ctx);
957 rewriter.erase_operation(ctx, for_op.get_operation());
958
959 scope.register(&while_op);
960}
961
962pub fn loop_expand(scope: &Scope, block: impl FnMut(&Scope)) {
964 WhileBuilder::new(scope, |scope| true.__expand_runtime_method(scope)).with_body(scope, block);
965}