1use core::ops::Range;
4
5use pliron::{
6 attribute::AttrObj,
7 basic_block::BasicBlock,
8 builtin::{attributes::IntegerAttr, op_interfaces, types::IntegerType},
9 combine::{self, Parser, between, parser::char::spaces, token},
10 common_traits::Named,
11 identifier::Identifier,
12 input_err,
13 irfmt::{
14 self,
15 parsers::{
16 block_opd_parser, delimited_list_parser, process_parsed_ssa_defs, spaced,
17 ssa_opd_parser,
18 },
19 printers::{iter_with_sep, list_with_sep},
20 },
21 location::Location,
22 op::OpObj,
23 opts::constants::BranchOpFoldInterface,
24 parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
25 printable::{Printable, indented_nl},
26 verify_err,
27};
28use thiserror::Error;
29
30use crate::{
31 NoMemoryEffect,
32 attributes::{BoolAttr, IntegerVecAttr, ZeroAttr},
33 prelude::*,
34 types::scalar::BoolType,
35};
36
37#[pliron_op(
38 name = "cf.branch",
39 format = "succ($0) `(` operands(CharSpace(`,`)) `)`",
40 interfaces = [
41 IsTerminatorInterface,
42 NResultsInterface<0>,
43 NSuccsInterface<1>,
44 OneSuccInterface
45 ],
46 verifier = "succ"
47)]
48#[op_traits(NoMemoryEffect)]
49pub struct BranchOp;
50
51#[op_interface_impl]
52impl BranchOpInterface for BranchOp {
53 fn verify_successor_operand_layout(&self, ctx: &Context) -> Result<()> {
54 <Self as OneSuccInterface>::verify(self, ctx)
55 }
56
57 fn successor_operand_range(&self, ctx: &Context, succ_idx: usize) -> Range<usize> {
58 assert!(succ_idx == 0, "BranchOp has exactly one successor");
59 0..self.get_operation().deref(ctx).get_num_operands()
60 }
61
62 fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
63 assert!(succ_idx == 0, "BrOp has exactly one successor");
64 Operation::push_operand(self.get_operation(), ctx, operand)
65 }
66
67 fn remove_successor_operand(
68 &self,
69 ctx: &mut Context,
70 succ_idx: usize,
71 opd_idx: usize,
72 ) -> Value {
73 assert!(succ_idx == 0, "BrOp has exactly one successor");
74 Operation::remove_operand(self.get_operation(), ctx, opd_idx)
75 }
76}
77
78impl BranchOp {
79 pub fn new(ctx: &mut Context, dest: Ptr<BasicBlock>, dest_opds: Vec<Value>) -> Self {
81 BranchOp {
82 op: Operation::new(
83 ctx,
84 Self::get_concrete_op_info(),
85 vec![],
86 dest_opds,
87 vec![dest],
88 0,
89 ),
90 }
91 }
92}
93
94#[pliron_op(
95 name = "cf.branch_conditional",
96 operands = (condition: BoolType),
97 verifier = "succ"
98)]
99#[op_interfaces(IsTerminatorInterface, NResultsInterface<0>, NSuccsInterface<2>)]
100#[op_traits(NoMemoryEffect)]
101pub struct BranchConditionalOp;
102impl BranchConditionalOp {
103 pub fn new(
105 ctx: &mut Context,
106 condition: Value,
107 true_dest: Ptr<BasicBlock>,
108 true_dest_opds: Vec<Value>,
109 false_dest: Ptr<BasicBlock>,
110 false_dest_opds: Vec<Value>,
111 ) -> Self {
112 let (operands, segment_sizes) =
113 Self::compute_segment_sizes(vec![vec![condition], true_dest_opds, false_dest_opds]);
114
115 let op = BranchConditionalOp {
116 op: Operation::new(
117 ctx,
118 Self::get_concrete_op_info(),
119 vec![],
120 operands,
121 vec![true_dest, false_dest],
122 0,
123 ),
124 };
125
126 op.set_operand_segment_sizes(ctx, segment_sizes);
128 op
129 }
130}
131
132#[op_interface_impl]
133impl OperandSegmentInterface for BranchConditionalOp {
134 fn expected_num_segments(&self, _ctx: &Context) -> Option<usize> {
135 Some(3)
137 }
138}
139
140impl Printable for BranchConditionalOp {
141 fn fmt(
142 &self,
143 ctx: &Context,
144 state: &pliron::printable::State,
145 f: &mut core::fmt::Formatter<'_>,
146 ) -> core::fmt::Result {
147 let op = self.get_operation().deref(ctx);
148 let condition = self.get_operand_condition(ctx);
149 let true_dest_opds = self.successor_operands(ctx, 0);
150 let false_dest_opds = self.successor_operands(ctx, 1);
151 let res = write!(
152 f,
153 "{} if {} ^{}({}) else ^{}({})",
154 Self::get_opid_static(),
155 condition.print(ctx, state),
156 op.get_successor(0).deref(ctx).unique_name(ctx),
157 iter_with_sep(
158 true_dest_opds.iter(),
159 pliron::printable::ListSeparator::CharSpace(',')
160 )
161 .print(ctx, state),
162 op.get_successor(1).deref(ctx).unique_name(ctx),
163 iter_with_sep(
164 false_dest_opds.iter(),
165 pliron::printable::ListSeparator::CharSpace(',')
166 )
167 .print(ctx, state),
168 );
169 res
170 }
171}
172
173impl Parsable for BranchConditionalOp {
174 type Arg = Vec<(Identifier, Location)>;
175 type Parsed = OpObj;
176 fn parse<'a>(
177 state_stream: &mut StateStream<'a>,
178 results: Self::Arg,
179 ) -> ParseResult<'a, Self::Parsed> {
180 if !results.is_empty() {
181 input_err!(
182 state_stream.loc(),
183 op_interfaces::NResultsVerifyErr(0, results.len())
184 )?
185 }
186
187 let r#if = irfmt::parsers::spaced::<StateStream, _>(combine::parser::char::string("if"));
189
190 let condition = ssa_opd_parser();
191
192 let true_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
193
194 let r_else =
195 irfmt::parsers::spaced::<StateStream, _>(combine::parser::char::string("else"));
196
197 let false_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
198
199 let final_parser = r#if
200 .with(spaced(condition))
201 .and(spaced(block_opd_parser()))
202 .and(true_operands)
203 .and(spaced(r_else).with(spaced(block_opd_parser()).and(false_operands)));
204
205 final_parser
206 .then(
207 move |(((condition, true_dest), true_dest_opds), (false_dest, false_dest_opds))| {
208 let results = results.clone();
209 combine::parser(move |parsable_state: &mut StateStream<'a>| {
210 let ctx = &mut parsable_state.state.ctx;
211 let op = BranchConditionalOp::new(
212 ctx,
213 condition,
214 true_dest,
215 true_dest_opds.clone(),
216 false_dest,
217 false_dest_opds.clone(),
218 );
219
220 process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
221 Ok(OpObj::new(op)).into_parse_result()
222 })
223 },
224 )
225 .parse_stream(state_stream)
226 .into()
227 }
228}
229
230#[op_interface_impl]
231impl BranchOpInterface for BranchConditionalOp {
232 fn verify_successor_operand_layout(&self, ctx: &Context) -> Result<()> {
233 <Self as OperandSegmentInterface>::verify(self, ctx)?;
234 <Self as NSuccsInterface<2>>::verify(self, ctx)
235 }
236
237 fn successor_operand_range(&self, ctx: &Context, succ_idx: usize) -> Range<usize> {
238 assert!(
239 succ_idx == 0 || succ_idx == 1,
240 "CondBrOp has exactly two successors"
241 );
242
243 self.segment_range(ctx, succ_idx + 1)
245 }
246
247 fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
248 self.push_to_segment(ctx, succ_idx + 1, operand)
250 }
251
252 fn remove_successor_operand(
253 &self,
254 ctx: &mut Context,
255 succ_idx: usize,
256 opd_idx: usize,
257 ) -> Value {
258 self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
260 }
261}
262
263#[pliron_op(
264 name = "cf.switch",
265 operands = (value: IntegerType),
266 attributes = (cf_switch_case_values: IntegerVecAttr)
267)]
268#[op_interfaces(IsTerminatorInterface, NResultsInterface<0>)]
269#[op_traits(NoMemoryEffect)]
270pub struct SwitchOp;
271
272#[op_interface_impl]
273impl OperandSegmentInterface for SwitchOp {
274 fn expected_num_segments(&self, ctx: &Context) -> Option<usize> {
275 Some(self.get_operation().deref(ctx).get_num_successors() + 1)
277 }
278}
279
280#[derive(Clone)]
282pub struct SwitchCase {
283 pub value: IntegerAttr,
285 pub dest: Ptr<BasicBlock>,
287 pub dest_opds: Vec<Value>,
289}
290
291impl Printable for SwitchCase {
292 fn fmt(
293 &self,
294 ctx: &Context,
295 state: &pliron::printable::State,
296 f: &mut core::fmt::Formatter<'_>,
297 ) -> core::fmt::Result {
298 write!(
299 f,
300 "{{ {}: ^{}({}) }}",
301 self.value.print(ctx, state),
302 self.dest.deref(ctx).unique_name(ctx),
303 list_with_sep(
304 &self.dest_opds,
305 pliron::printable::ListSeparator::CharSpace(',')
306 )
307 .print(ctx, state)
308 )
309 }
310}
311
312impl Parsable for SwitchCase {
313 type Arg = ();
314 type Parsed = Self;
315
316 fn parse<'a>(
317 state_stream: &mut StateStream<'a>,
318 _arg: Self::Arg,
319 ) -> ParseResult<'a, Self::Parsed> {
320 let mut parser = between(
321 token('{'),
322 token('}'),
323 (
324 spaced(IntegerAttr::parser(())),
325 spaced(token(':')),
326 spaced(block_opd_parser()),
327 delimited_list_parser('(', ')', ',', ssa_opd_parser()),
328 spaces(),
329 ),
330 );
331
332 let ((value, _colon, dest, dest_opds, _spaces), _) =
333 parser.parse_stream(state_stream).into_result()?;
334
335 Ok(SwitchCase {
336 value,
337 dest,
338 dest_opds,
339 })
340 .into_parse_result()
341 }
342}
343
344impl Printable for SwitchOp {
345 fn fmt(
346 &self,
347 ctx: &Context,
348 state: &pliron::printable::State,
349 f: &mut core::fmt::Formatter<'_>,
350 ) -> core::fmt::Result {
351 let op = self.get_operation().deref(ctx);
352 let value = self.get_operand_value(ctx);
353
354 let default_successor = op
355 .successors()
356 .next()
357 .expect("SwitchOp must have at least one successor");
358 let num_total_successors = op.get_num_successors();
359
360 write!(
361 f,
362 "{} {}, ^{}({})",
363 Self::get_opid_static(),
364 value.print(ctx, state),
365 default_successor.unique_name(ctx).print(ctx, state),
366 iter_with_sep(
367 self.successor_operands(ctx, 0).iter(),
368 pliron::printable::ListSeparator::CharSpace(',')
369 )
370 .print(ctx, state),
371 )?;
372
373 if num_total_successors < 2 {
374 writeln!(f, "[]")?;
375 return Ok(());
376 }
377
378 let cases = self.cases(ctx);
379
380 write!(f, "{}[", indented_nl(state))?;
381 {
382 let _indent = state.indent();
383 write!(f, "{}", indented_nl(state))?;
384 list_with_sep(&cases, pliron::printable::ListSeparator::CharNewline(','))
385 .fmt(ctx, state, f)?;
386 }
387 write!(f, "{}]", indented_nl(state))?;
388
389 Ok(())
390 }
391}
392
393impl Parsable for SwitchOp {
394 type Arg = Vec<(Identifier, Location)>;
395 type Parsed = OpObj;
396
397 fn parse<'a>(
398 state_stream: &mut StateStream<'a>,
399 arg: Self::Arg,
400 ) -> ParseResult<'a, Self::Parsed> {
401 if !arg.is_empty() {
402 input_err!(
403 state_stream.loc(),
404 op_interfaces::NResultsVerifyErr(0, arg.len())
405 )?
406 }
407
408 let condition = ssa_opd_parser().skip(spaced(token(',')));
410 let default_successor = block_opd_parser();
411 let default_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
412 let cases = delimited_list_parser('[', ']', ',', SwitchCase::parser(()));
413
414 let final_parser = spaced(condition)
415 .and(default_successor)
416 .skip(spaces())
417 .and(default_operands)
418 .skip(spaces())
419 .and(cases);
420
421 final_parser
422 .then(
423 move |(((condition, default_dest), default_dest_opds), cases)| {
424 let results = arg.clone();
425 combine::parser(move |parsable_state: &mut StateStream<'a>| {
426 let ctx = &mut parsable_state.state.ctx;
427 let op = SwitchOp::new(
428 ctx,
429 condition,
430 default_dest,
431 default_dest_opds.clone(),
432 cases.clone(),
433 );
434
435 process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
436 Ok(OpObj::new(op)).into_parse_result()
437 })
438 },
439 )
440 .parse_stream(state_stream)
441 .into()
442 }
443}
444
445impl SwitchOp {
446 pub fn new(
448 ctx: &mut Context,
449 condition: Value,
450 default_dest: Ptr<BasicBlock>,
451 default_dest_opds: Vec<Value>,
452 cases: Vec<SwitchCase>,
453 ) -> Self {
454 let case_values: Vec<IntegerAttr> = cases.iter().map(|case| case.value.clone()).collect();
455
456 let case_operands = cases
457 .iter()
458 .map(|case| case.dest_opds.clone())
459 .collect::<Vec<_>>();
460
461 let mut operand_segments = vec![vec![condition], default_dest_opds];
462 operand_segments.extend(case_operands);
463 let (operands, segment_sizes) = Self::compute_segment_sizes(operand_segments);
464
465 let case_dests = cases.iter().map(|case| case.dest);
466 let successors = vec![default_dest].into_iter().chain(case_dests).collect();
467 let op = SwitchOp {
468 op: Operation::new(
469 ctx,
470 Self::get_concrete_op_info(),
471 vec![],
472 operands,
473 successors,
474 0,
475 ),
476 };
477
478 op.set_operand_segment_sizes(ctx, segment_sizes);
480 op.set_attr_cf_switch_case_values(ctx, IntegerVecAttr(case_values));
482 op
483 }
484
485 pub fn cases(&self, ctx: &Context) -> Vec<SwitchCase> {
488 let case_values = &*self
489 .get_attr_cf_switch_case_values(ctx)
490 .expect("SwitchOp missing or incorrect case values attribute");
491
492 let op = self.get_operation().deref(ctx);
493 let successors = op.successors().skip(1);
495
496 successors
497 .zip(case_values.0.iter())
498 .enumerate()
499 .map(|(i, (dest, value))| {
500 let dest_opds = self.successor_operands(ctx, i + 1);
502 SwitchCase {
503 value: value.clone(),
504 dest,
505 dest_opds,
506 }
507 })
508 .collect()
509 }
510
511 pub fn default_dest(&self, ctx: &Context) -> Ptr<BasicBlock> {
513 self.get_operation().deref(ctx).get_successor(0)
514 }
515
516 pub fn default_dest_operands(&self, ctx: &Context) -> Vec<Value> {
518 self.successor_operands(ctx, 0)
519 }
520}
521
522#[op_interface_impl]
523impl BranchOpInterface for SwitchOp {
524 fn verify_successor_operand_layout(&self, ctx: &Context) -> Result<()> {
525 <Self as OperandSegmentInterface>::verify(self, ctx)
526 }
527
528 fn successor_operand_range(&self, ctx: &Context, succ_idx: usize) -> Range<usize> {
529 self.segment_range(ctx, succ_idx + 1)
531 }
532
533 fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
534 self.push_to_segment(ctx, succ_idx + 1, operand)
536 }
537
538 fn remove_successor_operand(
539 &self,
540 ctx: &mut Context,
541 succ_idx: usize,
542 opd_idx: usize,
543 ) -> Value {
544 self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
546 }
547}
548
549#[derive(Error, Debug)]
550pub enum SwitchOpVerifyErr {
551 #[error("SwitchOp has no or incorrect case values attribute")]
552 CaseValuesAttrErr,
553 #[error("SwitchOp has no or incorrect default destination")]
554 DefaultDestErr,
555}
556
557impl Verify for SwitchOp {
558 fn verify(&self, ctx: &Context) -> Result<()> {
559 let loc = self.loc(ctx);
560
561 let op = &*self.get_operation().deref(ctx);
562 if op.get_num_successors() < 1 {
563 verify_err!(loc.clone(), SwitchOpVerifyErr::DefaultDestErr)?;
564 }
565
566 Ok(())
567 }
568}
569
570#[op_interface_impl]
571impl BranchOpFoldInterface for BranchOp {
572 fn check_fold(&self, ctx: &Context, _operands: &[Option<AttrObj>]) -> Vec<Ptr<BasicBlock>> {
573 self.get_operation().deref(ctx).successors().collect()
574 }
575 fn fold_in_place(
576 &self,
577 _ctx: &mut Context,
578 _ops: &[Option<AttrObj>],
579 _rw: &mut dyn Rewriter,
580 ) -> IRStatus {
581 IRStatus::Unchanged
582 }
583}
584
585impl BranchConditionalOp {
586 fn possible_successor_indices(
587 &self,
588 ctx: &Context,
589 operands: &[Option<AttrObj>],
590 ) -> Vec<usize> {
591 let Some(cond_attr) = operands.first().unwrap().as_ref() else {
592 let num_successors = self.get_operation().deref(ctx).successors().count();
593 return (0..num_successors).collect();
594 };
595 let zero = cond_attr.downcast_ref::<ZeroAttr>().map(|_| false);
596 let bool = cond_attr.downcast_ref::<BoolAttr>().map(|it| it.0);
597 let Some(const_cond) = zero.or(bool) else {
598 let num_successors = self.get_operation().deref(ctx).successors().count();
599 return (0..num_successors).collect();
600 };
601 let taken = if const_cond { 0 } else { 1 };
602 vec![taken]
603 }
604}
605
606#[op_interface_impl]
607impl BranchOpFoldInterface for BranchConditionalOp {
608 fn check_fold(&self, ctx: &Context, operands: &[Option<AttrObj>]) -> Vec<Ptr<BasicBlock>> {
609 let successors: Vec<Ptr<BasicBlock>> =
610 self.get_operation().deref(ctx).successors().collect();
611
612 self.possible_successor_indices(ctx, operands)
613 .iter()
614 .map(|ind| successors[*ind])
615 .collect()
616 }
617
618 fn fold_in_place(
619 &self,
620 _ctx: &mut Context,
621 _ops: &[Option<AttrObj>],
622 _rewriter: &mut dyn Rewriter,
623 ) -> IRStatus {
624 IRStatus::Unchanged
625 }
626}
627
628#[op_interface_impl]
629impl BranchOpFoldInterface for SwitchOp {
630 fn check_fold(&self, ctx: &Context, operands: &[Option<AttrObj>]) -> Vec<Ptr<BasicBlock>> {
631 let successors: Vec<Ptr<BasicBlock>> =
632 self.get_operation().deref(ctx).successors().collect();
633 let Some(cond_attr) = operands.first().and_then(|o| o.as_ref()) else {
634 return successors;
635 };
636 let cond_int = cond_attr
637 .downcast_ref::<IntegerAttr>()
638 .expect("Switch condition operand must be an IntegerAttr")
639 .value();
640 let case_values = self
642 .get_attr_cf_switch_case_values(ctx)
643 .expect("SwitchOp missing case values attribute");
644 let taken = case_values
645 .0
646 .iter()
647 .position(|case| case.value() == cond_int)
648 .map(|i| i + 1)
649 .unwrap_or(0);
650 vec![successors[taken]]
651 }
652
653 fn fold_in_place(
654 &self,
655 _ctx: &mut Context,
656 _ops: &[Option<AttrObj>],
657 _rewriter: &mut dyn Rewriter,
658 ) -> IRStatus {
659 IRStatus::Unchanged
660 }
661}