Find the best simplification of the given skeleton instruction, if any, by consulting our `simplify_skeleton` ISLE rules.
(&mut self, inst: Inst)
| 576 | /// Find the best simplification of the given skeleton instruction, if any, |
| 577 | /// by consulting our `simplify_skeleton` ISLE rules. |
| 578 | fn simplify_skeleton_inst(&mut self, inst: Inst) -> Option<SkeletonInstSimplification> { |
| 579 | // NB: we support simplifying branch terminators (e.g. `brif` with a |
| 580 | // constant condition into `jump`). This can make blocks unreachable, |
| 581 | // but a separate `eliminate_unreachable_code` pass handles removing |
| 582 | // them after the egraph pass completes. |
| 583 | // |
| 584 | // We do NOT yet support simplifying non-terminators into terminators |
| 585 | // (e.g. `trapz` into `trap`) because that would introduce a |
| 586 | // terminator in the middle of a block, requiring removal of trailing |
| 587 | // instructions and their value definitions. |
| 588 | |
| 589 | let mut guard = TakeAndReplace::new(self, |x| &mut x.optimized_insts); |
| 590 | let (ctx, optimized_insts) = guard.get(); |
| 591 | |
| 592 | crate::opts::generated_code::constructor_simplify_skeleton( |
| 593 | &mut IsleContext { ctx }, |
| 594 | inst, |
| 595 | optimized_insts, |
| 596 | ); |
| 597 | |
| 598 | let simplifications_len = optimized_insts.len(); |
| 599 | log::trace!(" -> simplify_skeleton: yielded {simplifications_len} simplification(s)"); |
| 600 | if simplifications_len > MATCHES_LIMIT { |
| 601 | log::trace!(" too many candidate simplifications; truncating to {MATCHES_LIMIT}"); |
| 602 | optimized_insts.truncate(MATCHES_LIMIT); |
| 603 | } |
| 604 | |
| 605 | // Find the best simplification, if any, from our candidates. |
| 606 | // |
| 607 | // Unlike simplifying pure values, we do not add side-effectful |
| 608 | // instructions to the egraph, nor do we extract the best version via |
| 609 | // dynamic programming and considering the costs of operands. Instead, |
| 610 | // we greedily choose the best simplification. This is because there is |
| 611 | // an impedance mismatch: the egraph and our pure rewrites are centered |
| 612 | // around *values*, but we don't represent side-effects with values, we |
| 613 | // represent them implicitly in their *instructions*. |
| 614 | // |
| 615 | // The initial best choice is "no simplification, just use the original |
| 616 | // instruction" which has the original instruction's cost. |
| 617 | let mut best = None; |
| 618 | let mut best_cost = cost::Cost::of_skeleton_op( |
| 619 | ctx.func.dfg.insts[inst].opcode(), |
| 620 | ctx.func.dfg.inst_args(inst).len(), |
| 621 | ); |
| 622 | while let Some(simplification) = optimized_insts.pop() { |
| 623 | let (new_inst, new_val) = match simplification { |
| 624 | // We can't do better than completely removing the skeleton |
| 625 | // instruction, so short-cicuit the loop and eagerly return the |
| 626 | // `Remove*` simplifications. |
| 627 | SkeletonInstSimplification::Remove => { |
| 628 | log::trace!(" -> simplify_skeleton: remove inst"); |
| 629 | debug_assert!(ctx.func.dfg.inst_results(inst).is_empty()); |
| 630 | return Some(simplification); |
| 631 | } |
| 632 | SkeletonInstSimplification::RemoveWithVal { val } => { |
| 633 | log::trace!(" -> simplify_skeleton: remove inst and use {val} as its result"); |
| 634 | if cfg!(debug_assertions) { |
| 635 | let results = ctx.func.dfg.inst_results(inst); |
no test coverage detected