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