1use alloc::rc::Rc;
2
3use midenc_hir::{
4 derive::{EffectOpInterface, OpParser, OpPrinter, operation},
5 dialects::builtin::attributes::U32ArrayAttr,
6 effects::*,
7 parse::ParserExt,
8 patterns::RewritePatternSet,
9 print::AsmPrinter,
10 traits::*,
11 *,
12};
13
14use crate::ScfDialect;
15
16#[derive(OpPrinter, OpParser)]
28#[operation(
29 dialect = ScfDialect,
30 traits(SingleBlock, NoRegionArguments, HasRecursiveMemoryEffects),
31 implements(RegionBranchOpInterface, OpPrinter)
32)]
33pub struct If {
34 #[operand]
35 condition: Bool,
36 #[region(name = "then")]
37 then_body: Region,
38 #[region(name = "else")]
39 else_body: Region,
40 #[results]
41 returns: AnyType,
42}
43
44impl If {
45 pub fn then_yield(&self) -> UnsafeIntrusiveEntityRef<Yield> {
46 let terminator = self.then_body().entry().terminator().unwrap();
47 terminator
48 .try_downcast_op::<Yield>()
49 .expect("invalid hir.if then terminator: expected yield")
50 }
51
52 pub fn else_yield(&self) -> UnsafeIntrusiveEntityRef<Yield> {
53 let terminator = self.else_body().entry().terminator().unwrap();
54 terminator
55 .try_downcast_op::<Yield>()
56 .expect("invalid hir.if else terminator: expected yield")
57 }
58}
59
60impl Canonicalizable for If {
61 fn get_canonicalization_patterns(rewrites: &mut RewritePatternSet, context: Rc<Context>) {
62 rewrites.push(crate::canonicalization::ConvertTrivialIfToSelect::new(context.clone()));
63 rewrites.push(crate::canonicalization::IfRemoveUnusedResults::new(context.clone()));
64 rewrites.push(crate::canonicalization::FoldRedundantYields::new(context));
65 }
66}
67
68impl RegionBranchOpInterface for If {
69 fn get_entry_successor_regions(
70 &self,
71 operands: &[Option<AttributeRef>],
72 ) -> RegionSuccessorIter<'_> {
73 let condition = operands[0].as_ref().and_then(|v| v.borrow().as_bool());
74 let has_then = condition.is_none_or(|v| v);
75 let else_possible = condition.is_none_or(|v| !v);
76 let has_else = else_possible && !self.else_body().is_empty();
77
78 let mut infos = SmallVec::<[RegionSuccessorInfo; 2]>::default();
79 if has_then {
80 infos.push(RegionSuccessorInfo::Entering(self.then_body().as_region_ref()));
81 }
82
83 if else_possible {
84 if has_else {
85 infos.push(RegionSuccessorInfo::Entering(self.else_body().as_region_ref()));
86 } else {
87 infos.push(RegionSuccessorInfo::Returning(
89 self.results().all().iter().map(|v| v.borrow().as_value_ref()).collect(),
90 ));
91 }
92 }
93
94 RegionSuccessorIter::new(self.as_operation(), infos)
95 }
96
97 fn get_successor_regions(&self, point: RegionBranchPoint) -> RegionSuccessorIter<'_> {
98 match point {
99 RegionBranchPoint::Parent => {
100 let mut infos: SmallVec<[_; 2]> =
102 smallvec![RegionSuccessorInfo::Entering(self.then_body().as_region_ref())];
103 if !self.else_body().is_empty() {
105 infos.push(RegionSuccessorInfo::Entering(self.else_body().as_region_ref()));
106 }
107 RegionSuccessorIter::new(self.as_operation(), infos)
108 }
109 RegionBranchPoint::Child(_) => {
110 RegionSuccessorIter::new(
112 self.as_operation(),
113 [RegionSuccessorInfo::Returning(
114 self.results().all().iter().map(|v| v.borrow().as_value_ref()).collect(),
115 )],
116 )
117 }
118 }
119 }
120
121 fn get_region_invocation_bounds(
122 &self,
123 operands: &[Option<AttributeRef>],
124 ) -> SmallVec<[InvocationBounds; 1]> {
125 let condition = operands[0].as_ref().and_then(|v| v.borrow().as_bool());
126
127 if let Some(condition) = condition {
128 if condition {
129 smallvec![InvocationBounds::Exact(1), InvocationBounds::Never]
130 } else {
131 smallvec![InvocationBounds::Never, InvocationBounds::Exact(1)]
132 }
133 } else {
134 smallvec![InvocationBounds::NoMoreThan(1); 2]
136 }
137 }
138
139 #[inline(always)]
140 fn is_repetitive_region(&self, _index: usize) -> bool {
141 false
142 }
143
144 #[inline(always)]
145 fn has_loop(&self) -> bool {
146 false
147 }
148}
149
150#[derive(OpPrinter, OpParser)]
163#[operation(
164 dialect = ScfDialect,
165 traits(SingleBlock, HasRecursiveMemoryEffects),
166 implements(RegionBranchOpInterface, LoopLikeOpInterface, OpPrinter)
167)]
168pub struct While {
169 #[operands]
170 inits: AnyType,
171 #[region]
172 before: Region,
173 #[region]
174 after: Region,
175 #[results]
176 returns: AnyType,
177}
178
179impl While {
180 pub fn condition_op(&self) -> UnsafeIntrusiveEntityRef<Condition> {
181 let term = self
182 .before()
183 .entry()
184 .terminator()
185 .expect("expected before region to have a terminator");
186 term.try_downcast_op::<Condition>()
187 .expect("expected before region to terminate with hir.condition")
188 }
189
190 pub fn yield_op(&self) -> UnsafeIntrusiveEntityRef<Yield> {
191 let term = self
192 .after()
193 .entry()
194 .terminator()
195 .expect("expected after region to have a terminator");
196 term.try_downcast_op::<Yield>()
197 .expect("expected after region to terminate with hir.yield")
198 }
199}
200
201impl Canonicalizable for While {
202 fn get_canonicalization_patterns(rewrites: &mut RewritePatternSet, context: Rc<Context>) {
203 rewrites.push(crate::canonicalization::RemoveLoopInvariantArgsFromBeforeBlock::new(
204 context.clone(),
205 ));
206 rewrites.push(crate::canonicalization::WhileConditionTruth::new(context.clone()));
208 rewrites.push(crate::canonicalization::WhileUnusedResult::new(context.clone()));
209 rewrites.push(crate::canonicalization::WhileRemoveDuplicatedResults::new(context.clone()));
210 rewrites.push(crate::canonicalization::WhileRemoveUnusedArgs::new(context.clone()));
211 }
213}
214
215impl LoopLikeOpInterface for While {
216 fn get_region_iter_args(&self) -> Option<EntityRef<'_, [BlockArgumentRef]>> {
217 let entry = self.before().entry_block_ref()?;
218 Some(EntityRef::map(entry.borrow(), |block| block.arguments()))
219 }
220
221 fn get_loop_header_region(&self) -> RegionRef {
222 self.before().as_region_ref()
223 }
224
225 fn get_loop_regions(&self) -> SmallVec<[RegionRef; 2]> {
226 smallvec![self.before().as_region_ref(), self.after().as_region_ref()]
227 }
228
229 fn get_inits_mut(&mut self) -> OpOperandRangeMut<'_> {
230 self.inits_mut()
231 }
232
233 fn get_yielded_values_mut(&mut self) -> Option<EntityProjectionMut<'_, OpOperandRangeMut<'_>>> {
234 let mut yield_op = self
235 .after()
236 .entry()
237 .terminator()
238 .expect("invalid `while`: expected loop body to be terminated");
239
240 Some(EntityMut::project(yield_op.borrow_mut(), |op| op.operands_mut().group_mut(0)))
242 }
243}
244
245impl RegionBranchOpInterface for While {
246 #[inline]
247 fn get_entry_successor_operands(&self, _point: RegionBranchPoint) -> SuccessorOperandRange<'_> {
248 SuccessorOperandRange::forward(self.operands().all())
250 }
251
252 fn get_successor_regions(&self, point: RegionBranchPoint) -> RegionSuccessorIter<'_> {
253 match point {
254 RegionBranchPoint::Parent => {
255 RegionSuccessorIter::new(
258 self.as_operation(),
259 [RegionSuccessorInfo::Entering(self.before().as_region_ref())],
260 )
261 }
262 RegionBranchPoint::Child(region) => {
263 let before_region = self.before().as_region_ref();
264 let after_region = self.after().as_region_ref();
265 assert!(region == before_region || region == after_region);
266
267 if region == after_region {
270 RegionSuccessorIter::new(
271 self.as_operation(),
272 [RegionSuccessorInfo::Entering(before_region)],
273 )
274 } else {
275 RegionSuccessorIter::new(
276 self.as_operation(),
277 [
278 RegionSuccessorInfo::Returning(
279 self.results()
280 .all()
281 .iter()
282 .map(|r| r.borrow().as_value_ref())
283 .collect(),
284 ),
285 RegionSuccessorInfo::Entering(after_region),
286 ],
287 )
288 }
289 }
290 }
291 }
292
293 #[inline]
294 fn get_region_invocation_bounds(
295 &self,
296 _operands: &[Option<AttributeRef>],
297 ) -> SmallVec<[InvocationBounds; 1]> {
298 smallvec![InvocationBounds::Unknown; self.num_regions()]
299 }
300
301 #[inline(always)]
302 fn is_repetitive_region(&self, _index: usize) -> bool {
303 true
305 }
306
307 #[inline(always)]
308 fn has_loop(&self) -> bool {
309 true
310 }
311}
312
313#[operation(
339 dialect = ScfDialect,
340 traits(SingleBlock, HasRecursiveMemoryEffects),
341 implements(RegionBranchOpInterface, OpPrinter)
342)]
343pub struct IndexSwitch {
344 #[operand]
345 selector: UInt32,
346 #[attr]
347 cases: U32ArrayAttr,
348 #[region]
349 default_region: Region,
350}
351
352impl OpPrinter for IndexSwitch {
353 fn print(&self, printer: &mut AsmPrinter<'_>) {
354 use alloc::borrow::Cow;
355
356 use formatter::*;
357
358 printer.print_space();
359 printer.print_value_uses(ValueRange::<1>::Operands(&[self.selector().as_operand_ref()]));
360 printer.print_space();
361
362 for case in self.cases().iter() {
363 let index = self.get_case_index_for_selector(*case).unwrap();
364 let region = self.get_case_region(index);
365 *printer += nl() + const_text("case ") + display(*case) + const_text(" ");
366 printer.print_region(®ion.borrow());
367 }
368
369 *printer += nl() + const_text("default ");
370 printer.print_region(&self.default_region());
371
372 if self.op.has_attributes() {
373 printer.print_space();
374 printer.print_attribute_dictionary(
375 self.op.attributes().iter().map(|attr| *attr.as_named_attribute()),
376 );
377 }
378
379 printer.print_space();
380 printer.print_colon_type_list(
381 self.results().iter().map(|r| Cow::Owned(r.borrow().ty().clone())),
382 );
383 }
384}
385
386impl OpParser for IndexSwitch {
387 fn parse(state: &mut OperationState, parser: &mut dyn OpAsmParser<'_>) -> ParseResult {
388 use alloc::{format, vec};
389
390 use midenc_hir::{
391 diagnostics::{LabeledSpan, RelatedError, Report, Severity, miette::diagnostic},
392 dialects::builtin::attributes::Array,
393 parse::ParserError,
394 };
395
396 let selector = parser.parse_operand(true)?;
397 let selector = parser.resolve_operand(selector, Type::U32)?;
398 state.add_operand(selector);
399
400 let mut cases = Array::<u32>::default();
401 let mut regions = SmallVec::<[RegionRef; 2]>::default();
402 while parser.parse_optional_custom_keyword("case")?.is_some() {
403 let case_value = parser.parse_decimal_integer::<u32>()?;
404 if cases.contains(&case_value) {
405 return Err(ParserError::Report(RelatedError::new(Report::from(diagnostic!(
406 severity = Severity::Error,
407 labels = vec![LabeledSpan::at(
408 case_value.span(),
409 "this case selector has already been used"
410 )],
411 "invalid scf.index_switch operation"
412 )))));
413 }
414
415 let region = parser.context().create_region();
416 parser.parse_region(region, &[], false)?;
417
418 cases.push(case_value.into_inner());
419 regions.push(region);
420 }
421
422 parser.parse_custom_keyword("default")?;
423 let fallback_region = parser.context().create_region();
424 parser.parse_region(fallback_region, &[], false)?;
425
426 state
427 .add_attribute("cases", parser.context_rc().create_attribute::<U32ArrayAttr, _>(cases));
428 state.add_region(fallback_region);
431 for region in regions {
432 state.add_region(region);
433 }
434
435 parser.parse_optional_attribute_dict(&mut state.attrs)?;
436 parser.parse_colon_type_list(&mut state.results)?;
437
438 Ok(())
439 }
440}
441
442impl IndexSwitch {
443 pub fn num_cases(&self) -> usize {
444 self.cases().len()
445 }
446
447 pub fn get_default_block(&self) -> BlockRef {
448 self.default_region().entry_block_ref().expect("default region has no blocks")
449 }
450
451 pub fn get_case_index_for_selector(&self, selector: u32) -> Option<usize> {
452 self.cases().iter().position(|case| *case == selector)
453 }
454
455 #[track_caller]
456 pub fn get_case_block(&self, index: usize) -> BlockRef {
457 let block_ref = self.get_case_region(index).borrow().entry_block_ref();
458 match block_ref {
459 None => panic!("region for case {index} has no blocks"),
460 Some(block) => block,
461 }
462 }
463
464 #[track_caller]
465 pub fn get_case_region(&self, mut index: usize) -> RegionRef {
466 let mut next_region = self.regions().front().as_pointer();
467 let mut current_index = 0;
468 index += 1;
470 while let Some(region) = next_region.take() {
471 if index == current_index {
472 return region;
473 }
474 next_region = region.next();
475 current_index += 1;
476 }
477
478 panic!("invalid region index `{}`: out of bounds", index - 1)
479 }
480}
481
482impl RegionBranchOpInterface for IndexSwitch {
483 fn get_entry_successor_regions(
484 &self,
485 operands: &[Option<AttributeRef>],
486 ) -> RegionSuccessorIter<'_> {
487 let selector = operands[0].as_ref().and_then(|v| v.borrow().as_u32());
488 let selected = selector.map(|s| self.get_case_index_for_selector(s));
489
490 match selected {
491 None => {
492 let infos =
494 self.regions().iter().map(|r| RegionSuccessorInfo::Entering(r.as_region_ref()));
495 RegionSuccessorIter::new(self.as_operation(), infos)
496 }
497 Some(Some(selected)) => {
498 RegionSuccessorIter::new(
500 self.as_operation(),
501 [RegionSuccessorInfo::Entering(self.get_case_region(selected))],
502 )
503 }
504 Some(None) => {
505 RegionSuccessorIter::new(
507 self.as_operation(),
508 [RegionSuccessorInfo::Entering(self.default_region().as_region_ref())],
509 )
510 }
511 }
512 }
513
514 fn get_successor_regions(&self, point: RegionBranchPoint) -> RegionSuccessorIter<'_> {
515 match point {
516 RegionBranchPoint::Parent => {
517 let infos =
519 self.regions().iter().map(|r| RegionSuccessorInfo::Entering(r.as_region_ref()));
520 RegionSuccessorIter::new(self.as_operation(), infos)
521 }
522 RegionBranchPoint::Child(_) => {
523 RegionSuccessorIter::new(
525 self.as_operation(),
526 [RegionSuccessorInfo::Returning(
527 self.results().all().iter().map(|v| v.borrow().as_value_ref()).collect(),
528 )],
529 )
530 }
531 }
532 }
533
534 fn get_region_invocation_bounds(
535 &self,
536 operands: &[Option<AttributeRef>],
537 ) -> SmallVec<[InvocationBounds; 1]> {
538 let selector = operands[0].as_ref().and_then(|v| v.borrow().as_u32());
539
540 if let Some(selector) = selector {
541 let mut bounds = smallvec![InvocationBounds::Never; self.num_cases()];
542 let selected =
543 self.get_case_index_for_selector(selector).map(|idx| idx + 1).unwrap_or(0);
544 bounds[selected] = InvocationBounds::Exact(1);
545 bounds
546 } else {
547 smallvec![InvocationBounds::NoMoreThan(1); self.num_cases()]
549 }
550 }
551
552 #[inline(always)]
553 fn is_repetitive_region(&self, _index: usize) -> bool {
554 false
555 }
556
557 #[inline(always)]
558 fn has_loop(&self) -> bool {
559 false
560 }
561}
562
563impl Canonicalizable for IndexSwitch {
564 fn get_canonicalization_patterns(rewrites: &mut RewritePatternSet, context: Rc<Context>) {
565 rewrites.push(crate::canonicalization::FoldConstantIndexSwitch::new(context.clone()));
566 rewrites.push(crate::canonicalization::FoldRedundantYields::new(context.clone()));
567 rewrites.push(crate::canonicalization::IndexSwitchRemoveUnusedResults::new(context));
568 }
569}
570
571#[derive(EffectOpInterface, OpPrinter, OpParser)]
580#[operation(
581 dialect = ScfDialect,
582 traits(Terminator, ReturnLike),
583 implements(RegionBranchTerminatorOpInterface, MemoryEffectOpInterface, OpPrinter)
584)]
585pub struct Condition {
586 #[operand]
587 condition: Bool,
588 #[operands]
589 forwarded: AnyType,
590}
591
592impl RegionBranchTerminatorOpInterface for Condition {
593 #[inline]
594 fn get_successor_operands(&self, _point: RegionBranchPoint) -> SuccessorOperandRange<'_> {
595 SuccessorOperandRange::forward(self.forwarded())
596 }
597
598 #[inline]
599 fn get_mutable_successor_operands(
600 &mut self,
601 _point: RegionBranchPoint,
602 ) -> SuccessorOperandRangeMut<'_> {
603 SuccessorOperandRangeMut::forward(self.forwarded_mut())
604 }
605
606 fn get_successor_regions(
607 &self,
608 operands: &[Option<AttributeRef>],
609 ) -> SmallVec<[RegionSuccessorInfo; 2]> {
610 let cond = operands[0].as_ref().and_then(|v| v.borrow().as_bool());
618 let mut regions = SmallVec::<[RegionSuccessorInfo; 2]>::default();
619
620 let parent_op = self.parent_op().unwrap();
621 let parent_op = parent_op.borrow();
622 let while_op = parent_op
623 .downcast_ref::<While>()
624 .expect("expected `Condition` op to be a child of a `While` op");
625 let after_region = while_op.after().as_region_ref();
626
627 match cond {
629 None => {
630 regions.push(RegionSuccessorInfo::Entering(after_region));
631 regions.push(RegionSuccessorInfo::Returning(
632 while_op.results().all().iter().map(|r| r.borrow().as_value_ref()).collect(),
633 ));
634 }
635 Some(true) => {
636 regions.push(RegionSuccessorInfo::Entering(after_region));
637 }
638 Some(false) => {
639 regions.push(RegionSuccessorInfo::Returning(
640 while_op.results().all().iter().map(|r| r.borrow().as_value_ref()).collect(),
641 ));
642 }
643 }
644
645 regions
646 }
647}
648
649#[derive(EffectOpInterface, OpPrinter, OpParser)]
663#[operation(
664 dialect = ScfDialect,
665 traits(Terminator, ReturnLike, Pure, AlwaysSpeculatable),
666 implements(
667 RegionBranchTerminatorOpInterface,
668 MemoryEffectOpInterface,
669 OperandRangeRequirementOpInterface,
670 ConditionallySpeculatable,
671 OpPrinter,
672 )
673)]
674pub struct Yield {
675 #[operands]
676 yielded: AnyType,
677}
678
679impl OperandRangeRequirementOpInterface for Yield {
680 fn operand_range_requirement(&self, _operand_index: usize) -> OperandRangeRequirement {
681 OperandRangeRequirement::None
682 }
683}
684
685impl RegionBranchTerminatorOpInterface for Yield {
686 #[inline]
687 fn get_successor_operands(&self, _point: RegionBranchPoint) -> SuccessorOperandRange<'_> {
688 SuccessorOperandRange::forward(self.yielded())
689 }
690
691 fn get_mutable_successor_operands(
692 &mut self,
693 _point: RegionBranchPoint,
694 ) -> SuccessorOperandRangeMut<'_> {
695 SuccessorOperandRangeMut::forward(self.yielded_mut())
696 }
697
698 fn get_successor_regions(
699 &self,
700 _operands: &[Option<AttributeRef>],
701 ) -> SmallVec<[RegionSuccessorInfo; 2]> {
702 let parent_op = self.parent_op().unwrap();
709 let parent_op = parent_op.borrow();
710 if parent_op.is::<If>() || parent_op.is::<IndexSwitch>() {
711 smallvec![RegionSuccessorInfo::Returning(
712 parent_op.results().all().iter().map(|v| v.borrow().as_value_ref()).collect()
713 )]
714 } else if let Some(while_op) = parent_op.downcast_ref::<While>() {
715 let before_region = while_op.before().as_region_ref();
716 smallvec![RegionSuccessorInfo::Entering(before_region)]
717 } else {
718 panic!("unsupported parent operation for '{}': '{}'", self.name(), parent_op.name())
719 }
720 }
721}
722
723impl ConditionallySpeculatable for Yield {
724 fn speculatability(&self) -> Speculatability {
725 Speculatability::Speculatable
726 }
727}
728
729#[cfg(test)]
730mod tests {
731 use midenc_expect_test::expect;
732 use midenc_hir::{
733 diagnostics::Report, dialects::builtin::Function, testing::parse_function_fixpoint,
734 };
735
736 use super::*;
737
738 fn find_op<T: OpRegistration>(function: &Function) -> UnsafeIntrusiveEntityRef<T> {
740 function
741 .body()
742 .entry()
743 .body()
744 .iter()
745 .find_map(|op| op.as_operation_ref().try_downcast_op::<T>().ok())
746 .unwrap_or_else(|| {
747 panic!("expected a {} op in the function body", <T as OpRegistration>::full_name())
748 })
749 }
750
751 #[test]
756 fn parse_scf_while_round_trips() -> Result<(), Report> {
757 let context = Rc::new(Context::default());
758 let source = "\
759builtin.function public extern(\"C\") @count_to(%n: u32) -> u32 {
760 %zero = arith.constant 0 : u32;
761 %count = scf.while %zero before {
762 ^head(%i: u32):
763 %continue = arith.lt %i, %n;
764 scf.condition %continue, %i : (i1, u32);
765 } after {
766 ^body(%j: u32):
767 %next = arith.incr %j;
768 scf.yield %next : (u32);
769 } : (u32) -> u32;
770 builtin.ret %count : (u32);
771};";
772 let (function, printed) = parse_function_fixpoint(&context, "parse_scf_while.hir", source)?;
773 expect![[r#"
774 builtin.function public extern("C") @count_to(%0: u32) -> u32 {
775 %1 = arith.constant 0 : u32;
776 %6 = scf.while %1 before {
777 ^block2(%2: u32):
778 %3 = arith.lt %2, %0;
779 scf.condition %3, %2 : (i1, u32);
780 } after {
781 ^block3(%4: u32):
782 %5 = arith.incr %4;
783 scf.yield %5 : (u32);
784 } : (u32) -> (u32);
785 builtin.ret %6 : (u32);
786 };"#]]
787 .assert_eq(&printed);
788
789 let function = function.borrow();
790 let while_op = find_op::<While>(&function);
791 let while_op = while_op.borrow();
792 assert_eq!(while_op.inits().len(), 1);
793 assert_eq!(while_op.num_results(), 1);
794 assert_eq!(while_op.before().entry().num_arguments(), 1);
795 let condition = while_op.condition_op();
796 let condition = condition.borrow();
797 assert_eq!(condition.forwarded().len(), 1);
798
799 Ok(())
800 }
801
802 #[test]
805 fn parse_scf_condition_without_forwarded_operands() -> Result<(), Report> {
806 let context = Rc::new(Context::default());
807 let source = "\
808builtin.function public extern(\"C\") @spin(%n: u32) -> u32 {
809 %zero = arith.constant 0 : u32;
810 scf.while %zero before {
811 ^head(%i: u32):
812 %continue = arith.lt %i, %n;
813 scf.condition %continue : (i1);
814 } after {
815 ^body:
816 scf.yield %zero : (u32);
817 } : (u32) -> ();
818 builtin.ret %n : (u32);
819};";
820 let (function, printed) =
821 parse_function_fixpoint(&context, "parse_scf_condition_bare.hir", source)?;
822 expect![[r#"
823 builtin.function public extern("C") @spin(%0: u32) -> u32 {
824 %1 = arith.constant 0 : u32;
825 scf.while %1 before {
826 ^block2(%2: u32):
827 %3 = arith.lt %2, %0;
828 scf.condition %3 : (i1);
829 } after {
830 ^block3:
831 scf.yield %1 : (u32);
832 } : (u32) -> ();
833 builtin.ret %0 : (u32);
834 };"#]]
835 .assert_eq(&printed);
836
837 let function = function.borrow();
838 let while_op = find_op::<While>(&function);
839 let while_op = while_op.borrow();
840 assert_eq!(while_op.num_results(), 0);
841 let condition = while_op.condition_op();
842 let condition = condition.borrow();
843 assert_eq!(condition.forwarded().len(), 0);
844
845 Ok(())
846 }
847
848 #[test]
851 fn parse_multi_name_result_bindings() -> Result<(), Report> {
852 let context = Rc::new(Context::default());
853 let source = "\
854builtin.function public extern(\"C\") @pick(%c: i1, %a: u32, %b: u32) -> u32 {
855 %x, %y = scf.if %c then {
856 scf.yield %a, %b : (u32, u32);
857 } else {
858 scf.yield %b, %a : (u32, u32);
859 } : (i1) -> (u32, u32);
860 %sum = arith.add %x, %y <{ overflow = #builtin.overflow<unchecked> }>;
861 builtin.ret %sum : (u32);
862};";
863 let (function, printed) =
864 parse_function_fixpoint(&context, "parse_multi_result.hir", source)?;
865 expect![[r#"
866 builtin.function public extern("C") @pick(%0: i1, %1: u32, %2: u32) -> u32 {
867 %3, %4 = scf.if %0 then {
868 scf.yield %1, %2 : (u32, u32);
869 } else {
870 scf.yield %2, %1 : (u32, u32);
871 } : (i1) -> (u32, u32);
872 %5 = arith.add %3, %4 <{ overflow = #builtin.overflow<unchecked> }>;
873 builtin.ret %5 : (u32);
874 };"#]]
875 .assert_eq(&printed);
876
877 let function = function.borrow();
878 let if_op = find_op::<If>(&function);
879 let if_op = if_op.borrow();
880 assert_eq!(if_op.num_results(), 2);
881 for result in if_op.results().all().iter() {
883 assert_eq!(result.borrow().iter_uses().count(), 1);
884 }
885
886 Ok(())
887 }
888
889 #[test]
892 fn parse_index_switch_region_order() -> Result<(), Report> {
893 let context = Rc::new(Context::default());
894 let source = "\
895builtin.function public extern(\"C\") @dispatch(%sel: u32, %a: u32, %b: u32) -> u32 {
896 %r = scf.index_switch %sel
897 case 1 {
898 scf.yield %a : (u32);
899 }
900 default {
901 scf.yield %b : (u32);
902 } : u32;
903 builtin.ret %r : (u32);
904};";
905 let (function, printed) =
906 parse_function_fixpoint(&context, "parse_index_switch.hir", source)?;
907 expect![[r#"
908 builtin.function public extern("C") @dispatch(%0: u32, %1: u32, %2: u32) -> u32 {
909 %3 = scf.index_switch %0
910 case 1 {
911 scf.yield %1 : (u32);
912 }
913 default {
914 scf.yield %2 : (u32);
915 } : (u32);
916 builtin.ret %3 : (u32);
917 };"#]]
918 .assert_eq(&printed);
919
920 let function = function.borrow();
921 let body = function.body();
922 let entry = body.entry();
923 let arg_a = entry.arguments()[1] as ValueRef;
924 let arg_b = entry.arguments()[2] as ValueRef;
925
926 let yielded_value = |region: &Region| -> ValueRef {
927 let terminator = region.entry().terminator().unwrap();
928 let yield_op = terminator
929 .try_downcast_op::<Yield>()
930 .expect("expected region to terminate with scf.yield");
931 let yield_op = yield_op.borrow();
932 let yielded = yield_op.yielded();
933 let operand = yielded.iter().next().unwrap();
934 operand.borrow().as_value_ref()
935 };
936
937 let switch_op = find_op::<IndexSwitch>(&function);
938 let switch_op = switch_op.borrow();
939 assert_eq!(yielded_value(&switch_op.default_region()), arg_b);
942 let case_region = switch_op.get_case_region(0);
943 assert_eq!(yielded_value(&case_region.borrow()), arg_a);
944
945 Ok(())
946 }
947}