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