10bc0503fSJulian Seward //! A Constant-Phi-Node removal pass.
20bc0503fSJulian Seward 
30bc0503fSJulian Seward use crate::dominator_tree::DominatorTree;
4ae768805SNick Fitzgerald use crate::ir;
50bc0503fSJulian Seward use crate::ir::Function;
694ec88eaSChris Fallin use crate::ir::{Block, BlockArg, BlockCall, Inst, Value};
70bc0503fSJulian Seward use crate::timing;
8*76911c29SSSD use crate::{FxHashMap, FxHashSet};
9ae768805SNick Fitzgerald use bumpalo::Bump;
10ae768805SNick Fitzgerald use cranelift_entity::SecondaryMap;
11ae768805SNick Fitzgerald use smallvec::SmallVec;
120bc0503fSJulian Seward 
130bc0503fSJulian Seward // A note on notation.  For the sake of clarity, this file uses the phrase
140bc0503fSJulian Seward // "formal parameters" to mean the `Value`s listed in the block head, and
150bc0503fSJulian Seward // "actual parameters" to mean the `Value`s passed in a branch or a jump:
160bc0503fSJulian Seward //
170bc0503fSJulian Seward // block4(v16: i32, v18: i32):            <-- formal parameters
180bc0503fSJulian Seward //   ...
19a5698cedSTrevor Elliott //   brif v27, block7(v22, v24), block6   <-- actual parameters
200bc0503fSJulian Seward 
210bc0503fSJulian Seward // This transformation pass (conceptually) partitions all values in the
220bc0503fSJulian Seward // function into two groups:
230bc0503fSJulian Seward //
240bc0503fSJulian Seward // * Group A: values defined by block formal parameters, except for the entry block.
250bc0503fSJulian Seward //
260bc0503fSJulian Seward // * Group B: All other values: that is, values defined by instructions,
270bc0503fSJulian Seward //   and the formals of the entry block.
280bc0503fSJulian Seward //
290bc0503fSJulian Seward // For each value in Group A, it attempts to establish whether it will have
300bc0503fSJulian Seward // the value of exactly one member of Group B.  If so, the formal parameter is
310bc0503fSJulian Seward // deleted, all corresponding actual parameters (in jumps/branches to the
320bc0503fSJulian Seward // defining block) are deleted, and a rename is inserted.
330bc0503fSJulian Seward //
340bc0503fSJulian Seward // The entry block is special-cased because (1) we don't know what values flow
350bc0503fSJulian Seward // to its formals and (2) in any case we can't change its formals.
360bc0503fSJulian Seward //
370bc0503fSJulian Seward // Work proceeds in three phases.
380bc0503fSJulian Seward //
390bc0503fSJulian Seward // * Phase 1: examine all instructions.  For each block, make up a useful
400bc0503fSJulian Seward //   grab-bag of information, `BlockSummary`, that summarises the block's
410bc0503fSJulian Seward //   formals and jump/branch instruction.  This is used by Phases 2 and 3.
420bc0503fSJulian Seward //
430bc0503fSJulian Seward // * Phase 2: for each value in Group A, try to find a single Group B value
440bc0503fSJulian Seward //   that flows to it.  This is done using a classical iterative forward
450bc0503fSJulian Seward //   dataflow analysis over a simple constant-propagation style lattice.  It
460bc0503fSJulian Seward //   converges quickly in practice -- I have seen at most 4 iterations.  This
470bc0503fSJulian Seward //   is relatively cheap because the iteration is done over the
480bc0503fSJulian Seward //   `BlockSummary`s, and does not visit each instruction.  The resulting
490bc0503fSJulian Seward //   fixed point is stored in a `SolverState`.
500bc0503fSJulian Seward //
510bc0503fSJulian Seward // * Phase 3: using the `SolverState` and `BlockSummary`, edit the function to
520bc0503fSJulian Seward //   remove redundant formals and actuals, and to insert suitable renames.
530bc0503fSJulian Seward //
540bc0503fSJulian Seward // Note that the effectiveness of the analysis depends on on the fact that
550bc0503fSJulian Seward // there are no copy instructions in Cranelift's IR.  If there were, the
560bc0503fSJulian Seward // computation of `actual_absval` in Phase 2 would have to be extended to
570bc0503fSJulian Seward // chase through such copies.
580bc0503fSJulian Seward //
590bc0503fSJulian Seward // For large functions, the analysis cost using the new AArch64 backend is about
600bc0503fSJulian Seward // 0.6% of the non-optimising compile time, as measured by instruction counts.
610bc0503fSJulian Seward // This transformation usually pays for itself several times over, though, by
620bc0503fSJulian Seward // reducing the isel/regalloc cost downstream.  Gains of up to 7% have been
630bc0503fSJulian Seward // seen for large functions.
640bc0503fSJulian Seward 
658bd507dbSBenjamin Bouvier /// The `Value`s (Group B) that can flow to a formal parameter (Group A).
660bc0503fSJulian Seward #[derive(Clone, Copy, Debug, PartialEq)]
670bc0503fSJulian Seward enum AbstractValue {
688bd507dbSBenjamin Bouvier     /// Two or more values flow to this formal.
690bc0503fSJulian Seward     Many,
708bd507dbSBenjamin Bouvier 
718bd507dbSBenjamin Bouvier     /// Exactly one value, as stated, flows to this formal.  The `Value`s that
728bd507dbSBenjamin Bouvier     /// can appear here are exactly: `Value`s defined by `Inst`s, plus the
738bd507dbSBenjamin Bouvier     /// `Value`s defined by the formals of the entry block.  Note that this is
748bd507dbSBenjamin Bouvier     /// exactly the set of `Value`s that are *not* tracked in the solver below
758bd507dbSBenjamin Bouvier     /// (see `SolverState`).
760bc0503fSJulian Seward     One(Value /*Group B*/),
778bd507dbSBenjamin Bouvier 
788bd507dbSBenjamin Bouvier     /// No value flows to this formal.
790bc0503fSJulian Seward     None,
800bc0503fSJulian Seward }
810bc0503fSJulian Seward 
820bc0503fSJulian Seward impl AbstractValue {
join(self, other: AbstractValue) -> AbstractValue830bc0503fSJulian Seward     fn join(self, other: AbstractValue) -> AbstractValue {
840bc0503fSJulian Seward         match (self, other) {
850bc0503fSJulian Seward             // Joining with `None` has no effect
860bc0503fSJulian Seward             (AbstractValue::None, p2) => p2,
870bc0503fSJulian Seward             (p1, AbstractValue::None) => p1,
880bc0503fSJulian Seward             // Joining with `Many` produces `Many`
890bc0503fSJulian Seward             (AbstractValue::Many, _p2) => AbstractValue::Many,
900bc0503fSJulian Seward             (_p1, AbstractValue::Many) => AbstractValue::Many,
910bc0503fSJulian Seward             // The only interesting case
920bc0503fSJulian Seward             (AbstractValue::One(v1), AbstractValue::One(v2)) => {
930bc0503fSJulian Seward                 if v1 == v2 {
940bc0503fSJulian Seward                     AbstractValue::One(v1)
950bc0503fSJulian Seward                 } else {
960bc0503fSJulian Seward                     AbstractValue::Many
970bc0503fSJulian Seward                 }
980bc0503fSJulian Seward             }
990bc0503fSJulian Seward         }
1000bc0503fSJulian Seward     }
1018bd507dbSBenjamin Bouvier 
is_one(self) -> bool1020bc0503fSJulian Seward     fn is_one(self) -> bool {
1038bd507dbSBenjamin Bouvier         matches!(self, AbstractValue::One(_))
1040bc0503fSJulian Seward     }
1050bc0503fSJulian Seward }
1060bc0503fSJulian Seward 
107ae768805SNick Fitzgerald #[derive(Clone, Copy, Debug)]
108ae768805SNick Fitzgerald struct OutEdge<'a> {
109ae768805SNick Fitzgerald     /// An instruction that transfers control.
110ae768805SNick Fitzgerald     inst: Inst,
111b58a197dSTrevor Elliott     /// The index into branch_destinations for this instruction that corresponds
112b58a197dSTrevor Elliott     /// to this edge.
113b58a197dSTrevor Elliott     branch_index: u32,
114ae768805SNick Fitzgerald     /// The block that control is transferred to.
115ae768805SNick Fitzgerald     block: Block,
116ae768805SNick Fitzgerald     /// The arguments to that block.
117ae768805SNick Fitzgerald     ///
118ae768805SNick Fitzgerald     /// These values can be from both groups A and B.
11994ec88eaSChris Fallin     args: &'a [BlockArg],
120ae768805SNick Fitzgerald }
121ae768805SNick Fitzgerald 
122ae768805SNick Fitzgerald impl<'a> OutEdge<'a> {
123ae768805SNick Fitzgerald     /// Construct a new `OutEdge` for the given instruction.
124ae768805SNick Fitzgerald     ///
125ae768805SNick Fitzgerald     /// Returns `None` if this is an edge without any block arguments, which
126ae768805SNick Fitzgerald     /// means we can ignore it for this analysis's purposes.
127ae768805SNick Fitzgerald     #[inline]
new( bump: &'a Bump, dfg: &ir::DataFlowGraph, inst: Inst, branch_index: usize, block: BlockCall, ) -> Option<Self>128b58a197dSTrevor Elliott     fn new(
129b58a197dSTrevor Elliott         bump: &'a Bump,
130b58a197dSTrevor Elliott         dfg: &ir::DataFlowGraph,
131b58a197dSTrevor Elliott         inst: Inst,
132b58a197dSTrevor Elliott         branch_index: usize,
133b58a197dSTrevor Elliott         block: BlockCall,
134b58a197dSTrevor Elliott     ) -> Option<Self> {
13594ec88eaSChris Fallin         let inst_var_args = block.args(&dfg.value_lists);
136ae768805SNick Fitzgerald 
137ae768805SNick Fitzgerald         // Skip edges without params.
13894ec88eaSChris Fallin         if inst_var_args.len() == 0 {
139ae768805SNick Fitzgerald             return None;
140ae768805SNick Fitzgerald         }
141ae768805SNick Fitzgerald 
142ae768805SNick Fitzgerald         Some(OutEdge {
143ae768805SNick Fitzgerald             inst,
144b58a197dSTrevor Elliott             branch_index: branch_index as u32,
1451e6c13d8STrevor Elliott             block: block.block(&dfg.value_lists),
146ae768805SNick Fitzgerald             args: bump.alloc_slice_fill_iter(
14794ec88eaSChris Fallin                 inst_var_args.map(|arg| arg.map_value(|value| dfg.resolve_aliases(value))),
148ae768805SNick Fitzgerald             ),
149ae768805SNick Fitzgerald         })
150ae768805SNick Fitzgerald     }
151ae768805SNick Fitzgerald }
152ae768805SNick Fitzgerald 
1538bd507dbSBenjamin Bouvier /// For some block, a useful bundle of info.  The `Block` itself is not stored
1548bd507dbSBenjamin Bouvier /// here since it will be the key in the associated `FxHashMap` -- see
1558bd507dbSBenjamin Bouvier /// `summaries` below.  For the `SmallVec` tuning params: most blocks have
1568bd507dbSBenjamin Bouvier /// few parameters, hence `4`.  And almost all blocks have either one or two
1578bd507dbSBenjamin Bouvier /// successors, hence `2`.
158ae768805SNick Fitzgerald #[derive(Clone, Debug, Default)]
159ae768805SNick Fitzgerald struct BlockSummary<'a> {
160ae768805SNick Fitzgerald     /// Formal parameters for this `Block`.
161ae768805SNick Fitzgerald     ///
162ae768805SNick Fitzgerald     /// These values are from group A.
163ae768805SNick Fitzgerald     formals: &'a [Value],
1648bd507dbSBenjamin Bouvier 
165ae768805SNick Fitzgerald     /// Each outgoing edge from this block.
166ae768805SNick Fitzgerald     ///
167ae768805SNick Fitzgerald     /// We don't bother to include transfers that pass zero parameters
1688bd507dbSBenjamin Bouvier     /// since that makes more work for the solver for no purpose.
169ae768805SNick Fitzgerald     ///
17080c147d9STrevor Elliott     /// We optimize for the case where a branch instruction has up to two
17180c147d9STrevor Elliott     /// outgoing edges, as unconditional jumps and conditional branches are
17280c147d9STrevor Elliott     /// more prominent than br_table.
17380c147d9STrevor Elliott     dests: SmallVec<[OutEdge<'a>; 2]>,
1740bc0503fSJulian Seward }
175ae768805SNick Fitzgerald 
176ae768805SNick Fitzgerald impl<'a> BlockSummary<'a> {
177ae768805SNick Fitzgerald     /// Construct a new `BlockSummary`, using `values` as its backing storage.
178ae768805SNick Fitzgerald     #[inline]
new(bump: &'a Bump, formals: &[Value]) -> Self179ae768805SNick Fitzgerald     fn new(bump: &'a Bump, formals: &[Value]) -> Self {
1800bc0503fSJulian Seward         Self {
181ae768805SNick Fitzgerald             formals: bump.alloc_slice_copy(formals),
182ae768805SNick Fitzgerald             dests: Default::default(),
1830bc0503fSJulian Seward         }
1840bc0503fSJulian Seward     }
1850bc0503fSJulian Seward }
1860bc0503fSJulian Seward 
1878bd507dbSBenjamin Bouvier /// Solver state.  This holds a AbstractValue for each formal parameter, except
1888bd507dbSBenjamin Bouvier /// for those from the entry block.
1890bc0503fSJulian Seward struct SolverState {
1900bc0503fSJulian Seward     absvals: FxHashMap<Value /*Group A*/, AbstractValue>,
1910bc0503fSJulian Seward }
1928bd507dbSBenjamin Bouvier 
1930bc0503fSJulian Seward impl SolverState {
new() -> Self1940bc0503fSJulian Seward     fn new() -> Self {
1950bc0503fSJulian Seward         Self {
1960bc0503fSJulian Seward             absvals: FxHashMap::default(),
1970bc0503fSJulian Seward         }
1980bc0503fSJulian Seward     }
1998bd507dbSBenjamin Bouvier 
get(&self, actual: Value) -> AbstractValue2000bc0503fSJulian Seward     fn get(&self, actual: Value) -> AbstractValue {
2018bd507dbSBenjamin Bouvier         *self
2028bd507dbSBenjamin Bouvier             .absvals
2038bd507dbSBenjamin Bouvier             .get(&actual)
204a0442ea0SHamir Mahal             .unwrap_or_else(|| panic!("SolverState::get: formal param {actual:?} is untracked?!"))
2050bc0503fSJulian Seward     }
2068bd507dbSBenjamin Bouvier 
maybe_get(&self, actual: Value) -> Option<&AbstractValue>2070bc0503fSJulian Seward     fn maybe_get(&self, actual: Value) -> Option<&AbstractValue> {
2080bc0503fSJulian Seward         self.absvals.get(&actual)
2090bc0503fSJulian Seward     }
2108bd507dbSBenjamin Bouvier 
set(&mut self, actual: Value, lp: AbstractValue)2110bc0503fSJulian Seward     fn set(&mut self, actual: Value, lp: AbstractValue) {
2120bc0503fSJulian Seward         match self.absvals.insert(actual, lp) {
2130bc0503fSJulian Seward             Some(_old_lp) => {}
214a0442ea0SHamir Mahal             None => panic!("SolverState::set: formal param {actual:?} is untracked?!"),
2150bc0503fSJulian Seward         }
2160bc0503fSJulian Seward     }
2170bc0503fSJulian Seward }
2180bc0503fSJulian Seward 
2190bc0503fSJulian Seward /// Detect phis in `func` that will only ever produce one value, using a
2200bc0503fSJulian Seward /// classic forward dataflow analysis.  Then remove them.
2210bc0503fSJulian Seward #[inline(never)]
do_remove_constant_phis(func: &mut Function, domtree: &mut DominatorTree)2220bc0503fSJulian Seward pub fn do_remove_constant_phis(func: &mut Function, domtree: &mut DominatorTree) {
2230bc0503fSJulian Seward     let _tt = timing::remove_constant_phis();
2240bc0503fSJulian Seward     debug_assert!(domtree.is_valid());
2250bc0503fSJulian Seward 
2260bc0503fSJulian Seward     // Phase 1 of 3: for each block, make a summary containing all relevant
2270bc0503fSJulian Seward     // info.  The solver will iterate over the summaries, rather than having
2280bc0503fSJulian Seward     // to inspect each instruction in each block.
229ae768805SNick Fitzgerald     let bump =
2300889323aSSSD         Bump::with_capacity(domtree.cfg_postorder().len() * 4 * core::mem::size_of::<Value>());
231ae768805SNick Fitzgerald     let mut summaries =
232ae768805SNick Fitzgerald         SecondaryMap::<Block, BlockSummary>::with_capacity(domtree.cfg_postorder().len());
2330bc0503fSJulian Seward 
2343e0b7e50SKirpal Grewal     for b in domtree.cfg_rpo().copied() {
2358bd507dbSBenjamin Bouvier         let formals = func.dfg.block_params(b);
236ae768805SNick Fitzgerald         let mut summary = BlockSummary::new(&bump, formals);
2370bc0503fSJulian Seward 
2388bd507dbSBenjamin Bouvier         for inst in func.layout.block_insts(b) {
23980c147d9STrevor Elliott             for (ix, dest) in func.dfg.insts[inst]
24094ec88eaSChris Fallin                 .branch_destination(&func.dfg.jump_tables, &func.dfg.exception_tables)
24180c147d9STrevor Elliott                 .iter()
24280c147d9STrevor Elliott                 .enumerate()
24380c147d9STrevor Elliott             {
244b58a197dSTrevor Elliott                 if let Some(edge) = OutEdge::new(&bump, &func.dfg, inst, ix, *dest) {
245ae768805SNick Fitzgerald                     summary.dests.push(edge);
2460bc0503fSJulian Seward                 }
2470bc0503fSJulian Seward             }
2480bc0503fSJulian Seward         }
2490bc0503fSJulian Seward 
2500bc0503fSJulian Seward         // Ensure the invariant that all blocks (except for the entry) appear
2510bc0503fSJulian Seward         // in the summary, *unless* they have neither formals nor any
2520bc0503fSJulian Seward         // param-carrying branches/jumps.
2530bc0503fSJulian Seward         if formals.len() > 0 || summary.dests.len() > 0 {
254ae768805SNick Fitzgerald             summaries[b] = summary;
2550bc0503fSJulian Seward         }
2560bc0503fSJulian Seward     }
2570bc0503fSJulian Seward 
2580bc0503fSJulian Seward     // Phase 2 of 3: iterate over the summaries in reverse postorder,
2590bc0503fSJulian Seward     // computing new `AbstractValue`s for each tracked `Value`.  The set of
2600bc0503fSJulian Seward     // tracked `Value`s is exactly Group A as described above.
2610bc0503fSJulian Seward 
2620bc0503fSJulian Seward     let entry_block = func
2630bc0503fSJulian Seward         .layout
2640bc0503fSJulian Seward         .entry_block()
2650bc0503fSJulian Seward         .expect("remove_constant_phis: entry block unknown");
2660bc0503fSJulian Seward 
2670bc0503fSJulian Seward     // Set up initial solver state
2680bc0503fSJulian Seward     let mut state = SolverState::new();
2690bc0503fSJulian Seward 
2703e0b7e50SKirpal Grewal     for b in domtree.cfg_rpo().copied() {
2710bc0503fSJulian Seward         // For each block, get the formals
2728bd507dbSBenjamin Bouvier         if b == entry_block {
2730bc0503fSJulian Seward             continue;
2740bc0503fSJulian Seward         }
2758bd507dbSBenjamin Bouvier         let formals = func.dfg.block_params(b);
2760bc0503fSJulian Seward         for formal in formals {
2770bc0503fSJulian Seward             let mb_old_absval = state.absvals.insert(*formal, AbstractValue::None);
2780bc0503fSJulian Seward             assert!(mb_old_absval.is_none());
2790bc0503fSJulian Seward         }
2800bc0503fSJulian Seward     }
2810bc0503fSJulian Seward 
2820bc0503fSJulian Seward     // Solve: repeatedly traverse the blocks in reverse postorder, until there
2830bc0503fSJulian Seward     // are no changes.
2840bc0503fSJulian Seward     let mut iter_no = 0;
2850bc0503fSJulian Seward     loop {
2860bc0503fSJulian Seward         iter_no += 1;
2870bc0503fSJulian Seward         let mut changed = false;
2880bc0503fSJulian Seward 
2893e0b7e50SKirpal Grewal         for src in domtree.cfg_rpo().copied() {
290ae768805SNick Fitzgerald             let src_summary = &summaries[src];
291ae768805SNick Fitzgerald             for edge in &src_summary.dests {
292ae768805SNick Fitzgerald                 assert!(edge.block != entry_block);
2930bc0503fSJulian Seward                 // By contrast, the dst block must have a summary.  Phase 1
2940bc0503fSJulian Seward                 // will have only included an entry in `src_summary.dests` if
2950bc0503fSJulian Seward                 // that branch/jump carried at least one parameter.  So the
2960bc0503fSJulian Seward                 // dst block does take parameters, so it must have a summary.
297ae768805SNick Fitzgerald                 let dst_summary = &summaries[edge.block];
2980bc0503fSJulian Seward                 let dst_formals = &dst_summary.formals;
299ae768805SNick Fitzgerald                 assert_eq!(edge.args.len(), dst_formals.len());
300ae768805SNick Fitzgerald                 for (formal, actual) in dst_formals.iter().zip(edge.args) {
3010bc0503fSJulian Seward                     // Find the abstract value for `actual`.  If it is a block
3020bc0503fSJulian Seward                     // formal parameter then the most recent abstract value is
3030bc0503fSJulian Seward                     // to be found in the solver state.  If not, then it's a
3040bc0503fSJulian Seward                     // real value defining point (not a phi), in which case
3050bc0503fSJulian Seward                     // return it itself.
30694ec88eaSChris Fallin                     let actual_absval = match actual {
30794ec88eaSChris Fallin                         BlockArg::Value(actual) => match state.maybe_get(*actual) {
3080bc0503fSJulian Seward                             Some(pt) => *pt,
3090bc0503fSJulian Seward                             None => AbstractValue::One(*actual),
31094ec88eaSChris Fallin                         },
31194ec88eaSChris Fallin                         _ => AbstractValue::Many,
3120bc0503fSJulian Seward                     };
3130bc0503fSJulian Seward 
3140bc0503fSJulian Seward                     // And `join` the new value with the old.
3150bc0503fSJulian Seward                     let formal_absval_old = state.get(*formal);
3160bc0503fSJulian Seward                     let formal_absval_new = formal_absval_old.join(actual_absval);
3170bc0503fSJulian Seward                     if formal_absval_new != formal_absval_old {
3180bc0503fSJulian Seward                         changed = true;
3190bc0503fSJulian Seward                         state.set(*formal, formal_absval_new);
3200bc0503fSJulian Seward                     }
3210bc0503fSJulian Seward                 }
3220bc0503fSJulian Seward             }
3230bc0503fSJulian Seward         }
3240bc0503fSJulian Seward 
3250bc0503fSJulian Seward         if !changed {
3260bc0503fSJulian Seward             break;
3270bc0503fSJulian Seward         }
3280bc0503fSJulian Seward     }
3298bd507dbSBenjamin Bouvier 
3300bc0503fSJulian Seward     let mut n_consts = 0;
3310bc0503fSJulian Seward     for absval in state.absvals.values() {
3320bc0503fSJulian Seward         if absval.is_one() {
3330bc0503fSJulian Seward             n_consts += 1;
3340bc0503fSJulian Seward         }
3350bc0503fSJulian Seward     }
3360bc0503fSJulian Seward 
3370bc0503fSJulian Seward     // Phase 3 of 3: edit the function to remove constant formals, using the
3380bc0503fSJulian Seward     // summaries and the final solver state as a guide.
3390bc0503fSJulian Seward 
3400bc0503fSJulian Seward     // Make up a set of blocks that need editing.
3410bc0503fSJulian Seward     let mut need_editing = FxHashSet::<Block>::default();
342ae768805SNick Fitzgerald     for (block, summary) in summaries.iter() {
343ae768805SNick Fitzgerald         if block == entry_block {
3440bc0503fSJulian Seward             continue;
3450bc0503fSJulian Seward         }
346ae768805SNick Fitzgerald         for formal in summary.formals {
3470bc0503fSJulian Seward             let formal_absval = state.get(*formal);
3480bc0503fSJulian Seward             if formal_absval.is_one() {
349ae768805SNick Fitzgerald                 need_editing.insert(block);
3500bc0503fSJulian Seward                 break;
3510bc0503fSJulian Seward             }
3520bc0503fSJulian Seward         }
3530bc0503fSJulian Seward     }
3540bc0503fSJulian Seward 
3550bc0503fSJulian Seward     // Firstly, deal with the formals.  For each formal which is redundant,
3560bc0503fSJulian Seward     // remove it, and also add a reroute from it to the constant value which
3570bc0503fSJulian Seward     // it we know it to be.
3580bc0503fSJulian Seward     for b in &need_editing {
3590bc0503fSJulian Seward         let mut del_these = SmallVec::<[(Value, Value); 32]>::new();
3600bc0503fSJulian Seward         let formals: &[Value] = func.dfg.block_params(*b);
3610bc0503fSJulian Seward         for formal in formals {
3620bc0503fSJulian Seward             // The state must give an absval for `formal`.
3630bc0503fSJulian Seward             if let AbstractValue::One(replacement_val) = state.get(*formal) {
3640bc0503fSJulian Seward                 del_these.push((*formal, replacement_val));
3650bc0503fSJulian Seward             }
3660bc0503fSJulian Seward         }
3670bc0503fSJulian Seward         // We can delete the formals in any order.  However,
3680bc0503fSJulian Seward         // `remove_block_param` works by sliding backwards all arguments to
3698bd507dbSBenjamin Bouvier         // the right of the value it is asked to delete.  Hence when removing more
3700bc0503fSJulian Seward         // than one formal, it is significantly more efficient to ask it to
3718bd507dbSBenjamin Bouvier         // remove the rightmost formal first, and hence this `rev()`.
3728bd507dbSBenjamin Bouvier         for (redundant_formal, replacement_val) in del_these.into_iter().rev() {
3730bc0503fSJulian Seward             func.dfg.remove_block_param(redundant_formal);
3740bc0503fSJulian Seward             func.dfg.change_to_alias(redundant_formal, replacement_val);
3750bc0503fSJulian Seward         }
3760bc0503fSJulian Seward     }
3770bc0503fSJulian Seward 
3780bc0503fSJulian Seward     // Secondly, visit all branch insns.  If the destination has had its
3790bc0503fSJulian Seward     // formals changed, change the actuals accordingly.  Don't scan all insns,
3800bc0503fSJulian Seward     // rather just visit those as listed in the summaries we prepared earlier.
3811e6c13d8STrevor Elliott     let mut old_actuals = alloc::vec::Vec::new();
382ae768805SNick Fitzgerald     for summary in summaries.values() {
383ae768805SNick Fitzgerald         for edge in &summary.dests {
384ae768805SNick Fitzgerald             if !need_editing.contains(&edge.block) {
3850bc0503fSJulian Seward                 continue;
3860bc0503fSJulian Seward             }
3870bc0503fSJulian Seward 
3881e6c13d8STrevor Elliott             let dfg = &mut func.dfg;
38994ec88eaSChris Fallin             let dests = dfg.insts[edge.inst]
39094ec88eaSChris Fallin                 .branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables);
39180c147d9STrevor Elliott             let block = &mut dests[edge.branch_index as usize];
392b58a197dSTrevor Elliott 
39394ec88eaSChris Fallin             old_actuals.extend(block.args(&dfg.value_lists));
3940bc0503fSJulian Seward 
3950bc0503fSJulian Seward             // Check that the numbers of arguments make sense.
3961e6c13d8STrevor Elliott             let formals = &summaries[edge.block].formals;
3971e6c13d8STrevor Elliott             assert_eq!(formals.len(), old_actuals.len());
3980bc0503fSJulian Seward 
3991e6c13d8STrevor Elliott             // Filter out redundant block arguments.
4001e6c13d8STrevor Elliott             let mut formals = formals.iter();
4011e6c13d8STrevor Elliott             old_actuals.retain(|_| {
4021e6c13d8STrevor Elliott                 let formal_i = formals.next().unwrap();
4031e6c13d8STrevor Elliott                 !state.get(*formal_i).is_one()
4041e6c13d8STrevor Elliott             });
4050bc0503fSJulian Seward 
4061e6c13d8STrevor Elliott             // Replace the block with a new one that only includes the non-redundant arguments.
4071e6c13d8STrevor Elliott             // This leaks the value list from the old block,
4081e6c13d8STrevor Elliott             // https://github.com/bytecodealliance/wasmtime/issues/5451 for more information.
4091e6c13d8STrevor Elliott             let destination = block.block(&dfg.value_lists);
41094ec88eaSChris Fallin             *block = BlockCall::new(destination, old_actuals.drain(..), &mut dfg.value_lists);
4110bc0503fSJulian Seward         }
4120bc0503fSJulian Seward     }
4130bc0503fSJulian Seward 
4144f4edc7aSNikolay Volf     log::debug!(
4150bc0503fSJulian Seward         "do_remove_constant_phis: done, {} iters.   {} formals, of which {} const.",
4160bc0503fSJulian Seward         iter_no,
4170bc0503fSJulian Seward         state.absvals.len(),
4180bc0503fSJulian Seward         n_consts
4190bc0503fSJulian Seward     );
4200bc0503fSJulian Seward }
421