sonatina_codegen/optim/
gvn.rs

1//! This module contains a solver for complete Global Value Numbering.
2//!
3//! The algorithm here is based on Karthik Gargi.: A sparse algorithm for predicated global value numbering:
4//! PLDI 2002 Pages 45–56: <https://dl.acm.org/doi/10.1145/512529.512536>.
5//!
6//! Also, to accomplish complete GVN, we use the `ValuePhi` concept which is described in
7//! Rekha R. Pai.: Detection of Redundant Expressions: A Complete and Polynomial-Time Algorithm in SSA:
8//! APLAS 2015 pp49-65: <https://link.springer.com/chapter/10.1007/978-3-319-26529-2_4>
9
10use std::collections::BTreeSet;
11
12use cranelift_entity::{entity_impl, packed_option::PackedOption, PrimaryMap, SecondaryMap};
13use fxhash::{FxHashMap, FxHashSet};
14
15use crate::{
16    cfg::ControlFlowGraph,
17    domtree::{DomTree, DominatorTreeTraversable},
18};
19
20use sonatina_ir::{
21    func_cursor::{CursorLocation, FuncCursor, InsnInserter},
22    insn::{BinaryOp, CastOp, InsnData, UnaryOp},
23    Block, DataFlowGraph, Function, Immediate, Insn, Type, Value,
24};
25
26use super::{constant_folding, simplify_impl};
27
28///  An initial class that assigned to all values.
29///  If a value still has the initial class after value numbering, then the insn that defines the value is
30///  unreachable.
31const INITIAL_CLASS: Class = Class(0);
32
33/// The rank reserved for immediates.
34const IMMEDIATE_RANK: u32 = 0;
35
36/// A GVN solver.
37#[derive(Debug)]
38pub struct GvnSolver {
39    /// Store classes.
40    classes: PrimaryMap<Class, ClassData>,
41
42    /// Maps [`Value`] to [`GvnValueData`].
43    values: SecondaryMap<Value, GvnValueData>,
44
45    /// Maps [`GvnInsn`] to [`Class`].
46    insn_table: FxHashMap<GvnInsn, Class>,
47
48    /// Store edges.
49    edges: PrimaryMap<Edge, EdgeData>,
50
51    /// Maps [`Block`] to [`GvnBlockData`]
52    blocks: SecondaryMap<Block, GvnBlockData>,
53
54    value_phi_table: FxHashMap<ValuePhi, Class>,
55
56    /// Hold always available values, i.e. immediates or function arguments.
57    always_avail: Vec<Value>,
58}
59
60impl GvnSolver {
61    pub fn new() -> Self {
62        Self {
63            classes: PrimaryMap::default(),
64            values: SecondaryMap::default(),
65            insn_table: FxHashMap::default(),
66            edges: PrimaryMap::default(),
67            blocks: SecondaryMap::default(),
68            value_phi_table: FxHashMap::default(),
69            always_avail: Vec::default(),
70        }
71    }
72    /// The main entry point of the struct.
73    /// `cfg` and `domtree` is modified to reflect graph structure change.
74    pub fn run(&mut self, func: &mut Function, cfg: &mut ControlFlowGraph, domtree: &mut DomTree) {
75        self.clear();
76
77        // Return if the function has no blocks.
78        if func.layout.entry_block().is_none() {
79            return;
80        }
81
82        // Make dummy INITIAL_CLASS to which all values belong before congruence finding.
83        self.classes.push(ClassData {
84            values: BTreeSet::new(),
85            gvn_insn: GvnInsn::Value(Value(u32::MAX)),
86            value_phi: None,
87        });
88        debug_assert!(self.classes.len() == 1);
89
90        // Make and assign classes for immediate values.
91        for &value in func.dfg.immediates.values() {
92            self.assign_class_to_imm_value(value);
93        }
94
95        // Assign rank to function arguments and create class for them.
96        let mut rank = IMMEDIATE_RANK + 1;
97        for &arg in &func.arg_values {
98            self.values[arg].rank = rank;
99            rank += 1;
100            let gvn_insn = GvnInsn::Value(arg);
101            self.always_avail.push(arg);
102            let class = self.make_class(gvn_insn, None);
103            self.assign_class(arg, class);
104        }
105
106        // Iterate all insns in RPO to assign ranks and analyze edges information.
107        for &block in domtree.rpo() {
108            // Assign rank to the block.
109            self.blocks[block].rank = rank;
110            rank += 1;
111
112            for insn in func.layout.iter_insn(block) {
113                if let Some(insn_result) = func.dfg.insn_result(insn) {
114                    self.values[insn_result].rank = rank;
115                    rank += 1;
116                }
117
118                // Make edge data and attach the edge to the originating block and the destinating block.
119                match func.dfg.insn_data(insn) {
120                    InsnData::Jump { dests, .. } => {
121                        let dest = dests[0];
122                        self.add_edge_info(block, dest, None, None, None);
123                    }
124
125                    InsnData::Branch { args, dests } => {
126                        let cond = args[0];
127                        debug_assert_eq!(func.dfg.value_ty(cond), Type::I1);
128
129                        let then_block = dests[0];
130                        let else_block = dests[1];
131
132                        // Create predicate for each edges.
133                        // TODO: We need more elaborate representation of predicate.
134                        let then_predicate = func.dfg.value_insn(cond).map(|insn| {
135                            let insn_data = func.dfg.insn_data(insn).clone();
136                            self.perform_predicate_simplification(&mut func.dfg, insn_data)
137                        });
138                        let else_predicate = self.perform_predicate_simplification(
139                            &mut func.dfg,
140                            InsnData::unary(UnaryOp::Not, cond),
141                        );
142
143                        // Make immediates to which the predicate and `cond` is evaluated when the edge is selected.
144                        let then_imm = self.make_imm(&mut func.dfg, true);
145                        let else_imm = self.make_imm(&mut func.dfg, false);
146
147                        self.add_edge_info(
148                            block,
149                            then_block,
150                            Some(cond),
151                            then_predicate,
152                            Some(then_imm),
153                        );
154                        self.add_edge_info(
155                            block,
156                            else_block,
157                            Some(cond),
158                            Some(else_predicate),
159                            Some(else_imm),
160                        );
161                    }
162
163                    InsnData::BrTable {
164                        args,
165                        default,
166                        table,
167                    } => {
168                        // Add edge destinating to default block.
169                        if let Some(default) = default {
170                            self.add_edge_info(block, *default, None, None, None);
171                        }
172                        let cond = args[0];
173
174                        for (value, dest) in args[1..].iter().zip(table.iter()) {
175                            self.add_edge_info(block, *dest, Some(cond), None, Some(*value));
176                        }
177                    }
178
179                    _ => {}
180                }
181            }
182        }
183
184        // Make the entry block reachable.
185        let entry = func.layout.entry_block().unwrap();
186        self.blocks[entry].reachable = true;
187
188        // Reassign congruence classes until no more change happens.
189        // TODO: We don't need to iterate all insns, it's enough to iterate all `touched` insns as
190        // described in the paper.
191        let mut changed = true;
192        while changed {
193            changed = false;
194            for &block in domtree.rpo() {
195                // If block is unreachable, skip all insns in the block.
196                if !self.blocks[block].reachable {
197                    continue;
198                }
199
200                let mut next_insn = func.layout.first_insn_of(block);
201                while let Some(insn) = next_insn {
202                    // Reassign congruence class to the result value of the insn.
203                    if let Some(insn_result) = func.dfg.insn_result(insn) {
204                        changed |= self.reassign_congruence(func, domtree, insn, insn_result);
205                    }
206                    next_insn = func.layout.next_insn_of(insn);
207                }
208
209                // If insn is terminator, analyze it to update edge and block reachability.
210                if let Some(last_insn) = func.layout.last_insn_of(block) {
211                    changed |= self.analyze_last_insn(func, domtree, block, last_insn);
212                }
213            }
214        }
215
216        // Remove redundant insn and unreachable block.
217        let mut remover = RedundantCodeRemover::new(self);
218        remover.remove_redundant_code(func, cfg, domtree);
219    }
220
221    /// Clear all internal data of the solver.
222    pub fn clear(&mut self) {
223        self.classes.clear();
224        self.values.clear();
225        self.insn_table.clear();
226        self.edges.clear();
227        self.blocks.clear();
228        self.value_phi_table.clear();
229        self.always_avail.clear();
230    }
231
232    /// Analyze the last insn of the block.
233    /// This function updates reachability of the edges and blocks.
234    ///
235    /// Returns `true` if reachability is changed.
236    fn analyze_last_insn(
237        &mut self,
238        func: &Function,
239        domtree: &DomTree,
240        block: Block,
241        insn: Insn,
242    ) -> bool {
243        match func.dfg.insn_data(insn) {
244            InsnData::Jump { .. } => {
245                let out_edges = &self.blocks[block].out_edges;
246                debug_assert_eq!(out_edges.len(), 1);
247                let out_edge = out_edges[0];
248                self.mark_edge_reachable(out_edge)
249            }
250
251            InsnData::Branch {
252                args,
253                dests: [then_dest, else_dest],
254            } => {
255                let cond = args[0];
256                let out_edges = &self.blocks[block].out_edges;
257                debug_assert_eq!(out_edges.len(), 2);
258
259                let then_edge = *out_edges
260                    .iter()
261                    .find(|edge| self.edge_data(**edge).to == *then_dest)
262                    .unwrap();
263                let else_edge = *out_edges
264                    .iter()
265                    .find(|edge| self.edge_data(**edge).to == *else_dest)
266                    .unwrap();
267
268                // Try to infer reachability of edges.
269                if self.infer_edge_reachability(func, cond, then_edge, domtree) {
270                    self.mark_edge_reachable(then_edge)
271                } else if self.infer_edge_reachability(func, cond, else_edge, domtree) {
272                    self.mark_edge_reachable(else_edge)
273                } else {
274                    // Mark both edges if inference failed.
275                    let changed = self.mark_edge_reachable(then_edge);
276                    changed || self.mark_edge_reachable(else_edge)
277                }
278            }
279
280            InsnData::BrTable {
281                args,
282                default,
283                table,
284            } => {
285                let cond = args[0];
286
287                // Iterate table entry, if one of the entry value is congruent to cond, then mark
288                // the edge as reachable and stop marking.
289                for dest in table {
290                    let edge = self.find_edge(&self.blocks[block].out_edges, block, *dest);
291                    if self.infer_edge_reachability(func, cond, edge, domtree) {
292                        return self.mark_edge_reachable(edge);
293                    }
294                }
295
296                // If none of entry values is congruent to the cond, then mark all edges as
297                // reachable.
298                let mut changed = false;
299                if let Some(default) = default {
300                    let edge_to_default =
301                        self.find_edge(&self.blocks[block].out_edges, block, *default);
302                    changed |= self.mark_edge_reachable(edge_to_default);
303                }
304                for dest in table {
305                    let edge = self.find_edge(&self.blocks[block].out_edges, block, *dest);
306                    changed |= self.mark_edge_reachable(edge);
307                }
308
309                changed
310            }
311
312            _ => false,
313        }
314    }
315
316    /// Search and assign congruence class to the `insn_result`.
317    /// Returns `true` if congruence class is changed.
318    fn reassign_congruence(
319        &mut self,
320        func: &mut Function,
321        domtree: &DomTree,
322        insn: Insn,
323        insn_result: Value,
324    ) -> bool {
325        // Perform symbolic evaluation for the insn.
326        let block = func.layout.insn_block(insn);
327        let gvn_insn = self.perform_symbolic_evaluation(
328            func,
329            domtree,
330            func.dfg.insn_data(insn).clone(),
331            block,
332        );
333
334        // If insn has a side effect, create new class if the value still belongs to
335        // `INITIAL_CLASS`.
336        if func.dfg.has_side_effect(insn) {
337            if self.value_class(insn_result) == INITIAL_CLASS {
338                let class = self.make_class(gvn_insn, None);
339                self.assign_class(insn_result, class);
340                return true;
341            } else {
342                return false;
343            }
344        }
345
346        let mut changed = false;
347        let new_class = if let GvnInsn::Value(value) = &gvn_insn {
348            // If `gvn_insn` is value itself, just use the class of the value.
349            self.value_class(*value)
350        } else if let Some(class) = self.insn_table.get(&gvn_insn).copied() {
351            // If gvn_insn is already inserted, use corresponding class.
352
353            // We need to recompute value phi for the class to reflect reachability changes and
354            // leader changes.
355            changed |= self.recompute_value_phi(func, &gvn_insn, insn_result);
356            class
357        } else if let Some(value_phi) =
358            ValuePhiFinder::new(self, insn_result).compute_value_phi(func, &gvn_insn)
359        {
360            if let Some(class) = self.value_phi_table.get(&value_phi) {
361                *class
362            } else {
363                self.make_class(gvn_insn.clone(), Some(value_phi))
364            }
365        } else {
366            self.make_class(gvn_insn.clone(), None)
367        };
368
369        let old_class = self.value_class(insn_result);
370        if old_class == new_class {
371            changed
372        } else {
373            self.assign_class(insn_result, new_class);
374            true
375        }
376    }
377
378    /// Perform value phi computation for the value.
379    ///
380    /// Returns `true` if computed value phi is changed from the old value phi of the value class.
381    /// The computed value phi is assigned to the class's value phi.
382    fn recompute_value_phi(
383        &mut self,
384        func: &mut Function,
385        insn_data: &GvnInsn,
386        insn_result: Value,
387    ) -> bool {
388        let value_phi = ValuePhiFinder::new(self, insn_result).compute_value_phi(func, insn_data);
389        let class = self.value_class(insn_result);
390
391        let old_value_phi = &mut self.classes[class].value_phi;
392
393        if &value_phi == old_value_phi {
394            false
395        } else {
396            *old_value_phi = value_phi;
397            true
398        }
399    }
400
401    /// Mark the edge and its destinating block as reachable if they are still unreachable.
402    ///
403    /// Returns `true` if edge becomes reachable.
404    fn mark_edge_reachable(&mut self, edge: Edge) -> bool {
405        let edge_data = &mut self.edges[edge];
406        let dest = edge_data.to;
407
408        if !edge_data.reachable {
409            edge_data.reachable = true;
410            self.blocks[dest].reachable = true;
411            true
412        } else {
413            false
414        }
415    }
416
417    /// Returns `true` if the `edge` is inferred as being reachable.
418    /// If this function returns `true` then other outgoing edges from the same originating block
419    /// are guaranteed to be unreachable.
420    ///
421    /// Edge is inferred as reachable if inferred cond value is congruent to the `resolved_cond` of the edge.
422    fn infer_edge_reachability(
423        &self,
424        func: &Function,
425        cond: Value,
426        edge: Edge,
427        domtree: &DomTree,
428    ) -> bool {
429        let edge_data = &self.edges[edge];
430        // If `resolved_cond` doesn't exist, then the edge must be non conditional jump.
431        let resolved_cond = match edge_data.resolved_cond.expand() {
432            Some(cond) => cond,
433            None => return true,
434        };
435
436        let inferred_value = self.infer_value_at_block(func, domtree, cond, edge_data.from);
437        self.is_congruent_value(inferred_value, resolved_cond)
438    }
439
440    /// Returns representative value at the block by using edge's predicate.
441    ///
442    /// `value` can be inferred to `value2` if
443    /// 1. The predicate of dominant incoming edge is represented as `value1 == value2`.
444    /// 2. `value` is congruent to value1
445    /// 3. `value2`.rank < `value`.rank.
446    ///
447    /// This process is recursively applied while tracing idom chain.
448    /// If the dominant edge is back edge, then this process is stopped to avoid infinite loop.
449    ///
450    /// # Example
451    /// In the below example, `value` inside if block is inferred to `value2`.
452    ///
453    /// ```pseudo
454    /// let value2 = .. (rank0)
455    /// let value1 = .. (rank1)
456    /// let value = value1 + 0 (rank2)
457    /// if value1 == value2 {
458    ///     use value
459    /// }
460    /// ```
461    fn infer_value_at_block(
462        &self,
463        func: &Function,
464        domtree: &DomTree,
465        value: Value,
466        target_block: Block,
467    ) -> Value {
468        let mut rep_value = self.leader(value);
469
470        let mut current_block = Some(target_block);
471        // Try to infer value until the representative value becomes immediate or current block
472        // becomes `None`.
473        while current_block.is_some() && (!func.dfg.is_imm(rep_value)) {
474            let block = current_block.unwrap();
475            if let Some(dominant_edge) = self.dominant_reachable_in_edges(block) {
476                // Stop looking up the value to avoid infinite lookup loop.
477                if self.is_back_edge(dominant_edge) {
478                    break;
479                }
480
481                // If the value inference succeeds, restart inference with the new representative value.
482                if let Some(value) = self.infer_value_impl(dominant_edge, rep_value) {
483                    rep_value = value;
484                    current_block = Some(target_block);
485                } else {
486                    // If the inference failed, change the current block to the originating block
487                    // of the edge.
488                    current_block = Some(self.edge_data(dominant_edge).from);
489                }
490            } else {
491                // If no dominant edge found, change the current block to the immediate dominator
492                // of the current block.
493                current_block = domtree.idom_of(block);
494            }
495        }
496
497        rep_value
498    }
499
500    /// Returns representative value at the edge by using edge's predicate.
501    ///
502    /// This is used to infer phi arguments which is guaranteed to flow through the specified edge.
503    fn infer_value_at_edge(
504        &self,
505        func: &Function,
506        domtree: &DomTree,
507        value: Value,
508        edge: Edge,
509    ) -> Value {
510        let mut rep_value = self.leader(value);
511
512        if let Some(inferred_value) = self.infer_value_impl(edge, rep_value) {
513            rep_value = inferred_value;
514        }
515
516        self.infer_value_at_block(func, domtree, rep_value, self.edge_data(edge).from)
517    }
518
519    /// A helper function to infer value using edge predicate.
520    ///
521    /// Returns inferred `value2` iff
522    /// 1. The predicate of the `edge` is represented as `value1 == value2`.
523    /// 2. `value` is congruent to value1
524    /// 3. `value2`.rank < `value`.rank.
525    fn infer_value_impl(&self, edge: Edge, value: Value) -> Option<Value> {
526        let edge_data = self.edge_data(edge);
527        let edge_cond = edge_data.cond.expand()?;
528        // If value is congruent to edge cond value, then return resolved cond of the edge.
529        if self.is_congruent_value(edge_cond, value) {
530            return Some(edge_data.resolved_cond.unwrap());
531        }
532
533        let predicate = edge_data.predicate.as_ref()?;
534
535        match predicate {
536            InsnData::Binary {
537                code: BinaryOp::Eq,
538                args: [lhs, rhs],
539            } => {
540                if self.is_congruent_value(*lhs, value)
541                    && self.value_rank(*rhs) < self.value_rank(value)
542                {
543                    Some(*rhs)
544                } else if self.is_congruent_value(*rhs, value)
545                    && self.value_rank(*lhs) < self.value_rank(value)
546                {
547                    Some(*lhs)
548                } else {
549                    None
550                }
551            }
552            _ => None,
553        }
554    }
555
556    /// Returns reachable incoming edges of the block.
557    fn reachable_in_edges(&self, block: Block) -> impl Iterator<Item = &Edge> {
558        self.blocks[block]
559            .in_edges
560            .iter()
561            .filter(|edge| self.edge_data(**edge).reachable)
562    }
563
564    /// Returns unreachable outgoing edges of the block.
565    fn unreachable_out_edges(&self, block: Block) -> impl Iterator<Item = &Edge> {
566        self.blocks[block]
567            .out_edges
568            .iter()
569            .filter(|edge| !self.edge_data(**edge).reachable)
570    }
571
572    /// Returns the incoming edge if the edge is only one reachable edge to the block.
573    fn dominant_reachable_in_edges(&self, block: Block) -> Option<Edge> {
574        let mut edges = self.reachable_in_edges(block);
575
576        let dominant = edges.next()?;
577        if edges.next().is_none() {
578            Some(*dominant)
579        } else {
580            None
581        }
582    }
583
584    /// Returns `true` if two values are congruent.
585    /// NOTE: Returns `false` if both values belong to `INITIAL_CLASS`.
586    fn is_congruent_value(&self, lhs: Value, rhs: Value) -> bool {
587        let lhs_class = self.values[lhs].class;
588        let rhs_class = self.values[rhs].class;
589        (lhs_class == rhs_class) && (lhs_class != INITIAL_CLASS)
590    }
591
592    /// Perform symbolic evaluation for the `insn_data`.
593    fn perform_symbolic_evaluation(
594        &mut self,
595        func: &mut Function,
596        domtree: &DomTree,
597        insn_data: InsnData,
598        block: Block,
599    ) -> GvnInsn {
600        // Get canonicalized insn by swapping arguments with leader values.
601        //
602        // If the insn is commutative, then argument order is also canonicalized.
603        // And if the insn is phi, arguments from unreachable edges are omitted.
604        let insn_data = match insn_data {
605            InsnData::Unary { code, args: [arg] } => {
606                let arg = self.infer_value_at_block(func, domtree, arg, block);
607                InsnData::unary(code, arg)
608            }
609
610            InsnData::Binary {
611                code,
612                args: [lhs, rhs],
613            } => {
614                let mut lhs = self.infer_value_at_block(func, domtree, lhs, block);
615                let mut rhs = self.infer_value_at_block(func, domtree, rhs, block);
616                // Canonicalize the argument order if the insn is commutative.
617                if code.is_commutative() && self.value_rank(rhs) < self.value_rank(lhs) {
618                    std::mem::swap(&mut lhs, &mut rhs);
619                }
620
621                InsnData::binary(code, lhs, rhs)
622            }
623
624            InsnData::Cast {
625                code,
626                args: [arg],
627                ty,
628            } => {
629                let arg = self.infer_value_at_block(func, domtree, arg, block);
630                InsnData::cast(code, arg, ty)
631            }
632
633            InsnData::Store { .. }
634            | InsnData::Load { .. }
635            | InsnData::Call { .. }
636            | InsnData::Jump { .. }
637            | InsnData::Branch { .. }
638            | InsnData::BrTable { .. }
639            | InsnData::Alloca { .. }
640            | InsnData::Gep { .. }
641            | InsnData::Return { .. } => insn_data.clone(),
642
643            InsnData::Phi { values, blocks, ty } => {
644                let edges = &self.blocks[block].in_edges;
645
646                let mut phi_args = Vec::with_capacity(values.len());
647                for (&value, &from) in (values).iter().zip(blocks.iter()) {
648                    let edge = self.find_edge(edges, from, block);
649                    // Ignore an argument from an unreachable block.
650                    if !self.edge_data(edge).reachable {
651                        continue;
652                    }
653
654                    let inferred_value = self.infer_value_at_edge(func, domtree, value, edge);
655                    phi_args.push((inferred_value, from));
656                }
657
658                // Canonicalize the argument order by block rank.
659                phi_args.sort_unstable_by(|(_, block1), (_, block2)| {
660                    self.block_rank(*block1).cmp(&self.block_rank(*block2))
661                });
662
663                InsnData::Phi {
664                    values: phi_args.iter().map(|(value, _)| *value).collect(),
665                    blocks: phi_args.iter().map(|(_, block)| *block).collect(),
666                    ty,
667                }
668            }
669        };
670
671        if let Some(imm) = self.perform_constant_folding(func, &insn_data) {
672            GvnInsn::Value(imm)
673        } else if let Some(result) = self.perform_simplification(&mut func.dfg, &insn_data) {
674            result
675        } else {
676            GvnInsn::Insn(insn_data)
677        }
678    }
679
680    /// Perform constant folding.
681    fn perform_constant_folding(
682        &mut self,
683        func: &mut Function,
684        insn_data: &InsnData,
685    ) -> Option<Value> {
686        constant_folding::fold_constant(&func.dfg, insn_data)
687            .map(|imm| self.make_imm(&mut func.dfg, imm))
688    }
689
690    /// Perform insn simplification.
691    fn perform_simplification(
692        &mut self,
693        dfg: &mut DataFlowGraph,
694        insn_data: &InsnData,
695    ) -> Option<GvnInsn> {
696        simplify_impl::simplify_insn_data(dfg, insn_data.clone()).map(|res| match res {
697            simplify_impl::SimplifyResult::Value(value) => {
698                // Handle immediate specially because we need to assign a new class to the immediate.
699                // if the immediate is newly created in simplification process.
700                if let Some(imm) = dfg.value_imm(value) {
701                    GvnInsn::Value(self.make_imm(dfg, imm))
702                } else {
703                    GvnInsn::Value(value)
704                }
705            }
706            simplify_impl::SimplifyResult::Insn(insn) => GvnInsn::Insn(insn),
707        })
708    }
709
710    /// Perform predicate simplification.
711    fn perform_predicate_simplification(
712        &mut self,
713        dfg: &mut DataFlowGraph,
714        insn_data: InsnData,
715    ) -> InsnData {
716        if let Some(GvnInsn::Insn(insn)) = self.perform_simplification(dfg, &insn_data) {
717            insn
718        } else {
719            insn_data.clone()
720        }
721    }
722
723    /// Make edge data and append incoming/outgoing edges of corresponding blocks.
724    fn add_edge_info(
725        &mut self,
726        from: Block,
727        to: Block,
728        cond: Option<Value>,
729        predicate: Option<InsnData>,
730        resolved_cond: Option<Value>,
731    ) {
732        let edge_data = EdgeData {
733            from,
734            to,
735            cond: cond.into(),
736            predicate,
737            resolved_cond: resolved_cond.into(),
738            reachable: false,
739        };
740
741        // Append incoming/outgoing edges of corresponding blocks.
742        let edge = self.edges.push(edge_data);
743        self.blocks[to].in_edges.push(edge);
744        self.blocks[from].out_edges.push(edge);
745    }
746
747    /// Assign `class` to `value`.
748    fn assign_class(&mut self, value: Value, class: Class) {
749        let old_class = self.value_class(value);
750        if old_class == class {
751            return;
752        }
753
754        let value_rank = self.value_rank(value);
755
756        // Add value to the class.
757        let class_data = &mut self.classes[class];
758        class_data.values.insert((value_rank, value));
759        self.values[value].class = class;
760
761        // Remove `value` from `old` class.
762        let old_class_data = &mut self.classes[old_class];
763        old_class_data.values.remove(&(value_rank, value));
764
765        // Remove old insn and value phi if the class becomes empty.
766        if let Some(insn_class) = self.insn_table.get_mut(&old_class_data.gvn_insn) {
767            if *insn_class == old_class && old_class_data.values.is_empty() {
768                self.insn_table.remove(&old_class_data.gvn_insn);
769                if let Some(value_phi) = &old_class_data.value_phi {
770                    self.value_phi_table.remove(value_phi);
771                }
772            }
773        }
774    }
775
776    /// Make value from immediate, also make a corresponding congruence class and update
777    /// `insn_table` for the immediate value if they don't exist yet.
778    fn make_imm(&mut self, dfg: &mut DataFlowGraph, imm: impl Into<Immediate>) -> Value {
779        // `make_imm_value` return always same value for the same immediate.
780        let value = dfg.make_imm_value(imm);
781
782        // Assign new class to the `imm`.
783        self.assign_class_to_imm_value(value);
784
785        value
786    }
787
788    /// Make and assign class to immediate value.
789    fn assign_class_to_imm_value(&mut self, value: Value) {
790        let class = self.values[value].class;
791        let gvn_insn = GvnInsn::Value(value);
792
793        // If the congruence class for the value already exists, then return.
794        if class != INITIAL_CLASS {
795            debug_assert_eq!(self.values[value].rank, IMMEDIATE_RANK);
796            return;
797        }
798
799        // Set rank.
800        self.values[value].rank = IMMEDIATE_RANK;
801
802        // Add the immediate to `always_avail` value.
803        self.always_avail.push(value);
804
805        // Create a congruence class for the immediate.
806        let class = self.make_class(gvn_insn, None);
807        self.assign_class(value, class);
808    }
809
810    /// Returns `true` if the edge is backedge.
811    fn is_back_edge(&self, edge: Edge) -> bool {
812        let from = self.edge_data(edge).from;
813        let to = self.edge_data(edge).to;
814
815        self.blocks[to].rank <= self.blocks[from].rank
816    }
817
818    /// Returns the leader value of the congruent class to which `value` belongs.
819    fn leader(&self, value: Value) -> Value {
820        let class = self.values[value].class;
821
822        self.class_data(class).values.iter().next().unwrap().1
823    }
824
825    /// Returns the leader value of the congruent class.
826    /// This method is mainly for [`ValuePhiFinder`], use [`Self::leader`] whenever possible.
827    fn class_leader(&self, class: Class) -> Value {
828        self.class_data(class).values.iter().next().unwrap().1
829    }
830
831    /// Returns `EdgeData`.
832    fn edge_data(&self, edge: Edge) -> &EdgeData {
833        &self.edges[edge]
834    }
835
836    /// Returns `ClassData`.
837    fn class_data(&self, class: Class) -> &ClassData {
838        &self.classes[class]
839    }
840
841    /// Returns the class of the value.
842    fn value_class(&self, value: Value) -> Class {
843        self.values[value].class
844    }
845
846    /// Returns the rank of the value.
847    fn value_rank(&self, value: Value) -> u32 {
848        self.values[value].rank
849    }
850
851    /// Returns the rank of the block.
852    fn block_rank(&self, block: Block) -> u32 {
853        self.blocks[block].rank
854    }
855
856    /// Make a new class and update tables.
857    /// To assign the class to the value, use [`Self::assign_class`].
858    fn make_class(&mut self, gvn_insn: GvnInsn, value_phi: Option<ValuePhi>) -> Class {
859        let class_data = ClassData {
860            values: BTreeSet::new(),
861            gvn_insn: gvn_insn.clone(),
862            value_phi: value_phi.clone(),
863        };
864        let class = self.classes.push(class_data);
865
866        // Update tables.
867        self.insn_table.insert(gvn_insn, class);
868        if let Some(value_phi) = value_phi {
869            self.value_phi_table.insert(value_phi, class);
870        }
871
872        class
873    }
874
875    /// Find an edge that have corresponding `from` and `to` block.
876    /// This method panics if an edge isn't found.
877    fn find_edge(&self, edges: &[Edge], from: Block, to: Block) -> Edge {
878        edges
879            .iter()
880            .find(|edge| {
881                let data = self.edge_data(**edge);
882                data.from == from && data.to == to
883            })
884            .copied()
885            .unwrap()
886    }
887}
888
889impl Default for GvnSolver {
890    fn default() -> Self {
891        Self::new()
892    }
893}
894
895#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
896struct Class(u32);
897entity_impl!(Class);
898
899/// A congruence class.
900#[derive(Debug, Clone, PartialEq)]
901struct ClassData {
902    /// Values the class includes.
903    /// NOTE: We need sort the value by rank.
904    values: BTreeSet<(u32, Value)>,
905
906    /// Representative insn of the class.
907    gvn_insn: GvnInsn,
908
909    /// A value phi of the class.
910    value_phi: Option<ValuePhi>,
911}
912
913/// A collection of value data used by `GvnSolver`.
914#[derive(Debug, Clone, PartialEq)]
915struct GvnValueData {
916    /// A class to which the value belongs.
917    class: Class,
918
919    /// A rank of the value.
920    /// If the value is immediate, then the rank is 0, otherwise the rank is assigned to each value in RPO order.
921    /// This ensures `rankA` is defined earlier than `rankB` iff `rankA` < `rankB`.
922    rank: u32,
923}
924
925impl Default for GvnValueData {
926    fn default() -> Self {
927        Self {
928            class: INITIAL_CLASS,
929            rank: 0,
930        }
931    }
932}
933
934/// A insn data which represents canonicalized/simplified/folded insn.
935#[derive(Debug, Clone, PartialEq, Eq, Hash)]
936enum GvnInsn {
937    /// A value which occurs when insn is simplified/folded to value.
938    Value(Value),
939    /// An insn data.
940    Insn(InsnData),
941}
942
943impl From<Value> for GvnInsn {
944    fn from(value: Value) -> Self {
945        Self::Value(value)
946    }
947}
948
949impl From<InsnData> for GvnInsn {
950    fn from(insn: InsnData) -> Self {
951        Self::Insn(insn)
952    }
953}
954
955#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
956struct Edge(u32);
957entity_impl!(Edge);
958
959#[derive(Debug, Clone)]
960struct EdgeData {
961    /// An originating block.
962    from: Block,
963
964    /// A destinating block.
965    to: Block,
966
967    /// Hold a condition value if branch is conditional.
968    cond: PackedOption<Value>,
969
970    /// Hold a predicate insn if branch is conditional.
971    predicate: Option<InsnData>,
972
973    /// An immediate to which cond is resolved if the edge is selected.
974    resolved_cond: PackedOption<Value>,
975
976    /// `true` if the edge is reachable.
977    reachable: bool,
978}
979
980#[derive(Debug, Default, Clone)]
981struct GvnBlockData {
982    /// Incoming edges.
983    in_edges: Vec<Edge>,
984
985    /// Outgoing edges.
986    out_edges: Vec<Edge>,
987
988    /// `true` if the block is reachable.
989    reachable: bool,
990
991    /// A rank of the block.
992    /// The rank definition is same as `GvnValueData::rank`.
993    rank: u32,
994}
995
996/// This struct finds value phi that described in
997/// `Detection of Redundant Expressions: A Complete and Polynomial-Time Algorithm in SSA`.
998struct ValuePhiFinder<'a> {
999    solver: &'a mut GvnSolver,
1000    /// Hold visited values to prevent infinite loop.
1001    visited: FxHashSet<Value>,
1002}
1003
1004impl<'a> ValuePhiFinder<'a> {
1005    fn new(solver: &'a mut GvnSolver, insn_result: Value) -> Self {
1006        let mut visited = FxHashSet::default();
1007        visited.insert(insn_result);
1008        Self { solver, visited }
1009    }
1010
1011    /// Main entry of this struct.
1012    fn compute_value_phi(&mut self, func: &mut Function, gvn_insn: &GvnInsn) -> Option<ValuePhi> {
1013        // Break infinite loop if the block has been already visited.
1014        let insn_data = if let GvnInsn::Insn(insn_data) = gvn_insn {
1015            insn_data
1016        } else {
1017            return None;
1018        };
1019
1020        match insn_data {
1021            InsnData::Unary { code, args: [arg] } => {
1022                let arg = self.get_phi_of(func, *arg)?;
1023                self.compute_value_phi_for_unary(func, *code, arg)
1024            }
1025
1026            InsnData::Binary {
1027                code,
1028                args: [lhs, rhs],
1029            } => {
1030                let (lhs, rhs) = if lhs == rhs {
1031                    let lhs_phi = self.get_phi_of(func, *lhs)?;
1032                    let rhs_phi = lhs_phi.clone();
1033                    (lhs_phi, rhs_phi)
1034                } else {
1035                    match (self.get_phi_of(func, *lhs), self.get_phi_of(func, *rhs)) {
1036                        (Some(lhs_phi), Some(rhs_phi)) => (lhs_phi, rhs_phi),
1037                        (Some(lhs_phi), None) => (lhs_phi, ValuePhi::Value(*rhs)),
1038                        (None, Some(rhs_phi)) => (ValuePhi::Value(*lhs), rhs_phi),
1039                        (None, None) => return None,
1040                    }
1041                };
1042
1043                self.compute_value_phi_for_binary(func, *code, lhs, rhs)
1044            }
1045
1046            InsnData::Cast {
1047                code,
1048                args: [arg],
1049                ty,
1050            } => {
1051                let arg = self.get_phi_of(func, *arg)?;
1052                self.compute_value_phi_for_cast(func, *code, arg, *ty)
1053            }
1054
1055            _ => None,
1056        }
1057    }
1058
1059    fn compute_value_phi_for_unary(
1060        &mut self,
1061        func: &mut Function,
1062        code: UnaryOp,
1063        value_phi: ValuePhi,
1064    ) -> Option<ValuePhi> {
1065        let value_phi_insn = if let ValuePhi::PhiInsn(insn) = value_phi {
1066            insn
1067        } else {
1068            return None;
1069        };
1070
1071        let mut result =
1072            ValuePhiInsn::with_capacity(value_phi_insn.block, value_phi_insn.args.len());
1073
1074        for (arg, block) in value_phi_insn.args.into_iter() {
1075            match arg {
1076                ValuePhi::Value(value) => {
1077                    let query_insn = InsnData::unary(code, value);
1078                    let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1079                    result.args.push((phi_arg, block));
1080                }
1081                ValuePhi::PhiInsn(_) => {
1082                    let phi_arg = self.compute_value_phi_for_unary(func, code, arg)?;
1083                    result.args.push((phi_arg, block));
1084                }
1085            }
1086        }
1087
1088        Some(result.canonicalize())
1089    }
1090
1091    fn compute_value_phi_for_binary(
1092        &mut self,
1093        func: &mut Function,
1094        code: BinaryOp,
1095        lhs: ValuePhi,
1096        rhs: ValuePhi,
1097    ) -> Option<ValuePhi> {
1098        // Unpack the value phis and create args to lookup value phi.
1099        //
1100        // 1. In case both lhs and rhs are phi insn.
1101        // lhs := PhiInsn(lhs_arg1: block1, .., lhs_argN: blockN, phi_block)
1102        // rhs := PhiInsn(rhs_arg1: block1, .., rhs_argN: blockN, phi_block)
1103        // args := [((lhs_arg1, rhs_arg1): block1), .., ((lhs_argN, rhs_argN), BlockN), phi_block)]
1104        //
1105        // 2. In case lhs is a phi insn and rhs is a value.
1106        // lhs := PhiInsn(lhs_arg1: block1, .., lhs_argN: blockN, phi_block)
1107        // rhs := rhs_value
1108        // args := [((lhs_arg1, rhs_value): block1), .., ((lhs_argN, rhs_value), blockN), phi_block)]
1109        //
1110        // 3. In case rhs is a phi insn and lhs is a value.
1111        // Same as 2.
1112        //
1113        // 4. In case both args are value, then return `None`.
1114        let (args, phi_block): (Vec<_>, _) = match (lhs, rhs) {
1115            (ValuePhi::PhiInsn(lhs_phi), ValuePhi::PhiInsn(rhs_phi)) => {
1116                // If two blocks are not identical or number of phi args are not the same, return.
1117                if lhs_phi.block != rhs_phi.block || lhs_phi.args.len() != rhs_phi.args.len() {
1118                    return None;
1119                }
1120
1121                let mut args = Vec::with_capacity(lhs_phi.args.len());
1122                for ((lhs_arg, lhs_block), (rhs_arg, rhs_block)) in
1123                    lhs_phi.args.into_iter().zip(rhs_phi.args.into_iter())
1124                {
1125                    // If two blocks which phi arg passed through are not identical, return.
1126                    if lhs_block != rhs_block {
1127                        return None;
1128                    }
1129                    args.push(((lhs_arg, rhs_arg), lhs_block));
1130                }
1131                (args, lhs_phi.block)
1132            }
1133
1134            (ValuePhi::PhiInsn(lhs_phi), ValuePhi::Value(rhs_value)) => (
1135                lhs_phi
1136                    .args
1137                    .into_iter()
1138                    .map(|(phi_arg, block)| ((phi_arg, ValuePhi::Value(rhs_value)), block))
1139                    .collect(),
1140                lhs_phi.block,
1141            ),
1142
1143            (ValuePhi::Value(lhs_value), ValuePhi::PhiInsn(rhs_phi)) => (
1144                rhs_phi
1145                    .args
1146                    .into_iter()
1147                    .map(|(phi_arg, block)| ((ValuePhi::Value(lhs_value), phi_arg), block))
1148                    .collect(),
1149                rhs_phi.block,
1150            ),
1151
1152            (ValuePhi::Value(_), ValuePhi::Value(_)) => return None,
1153        };
1154
1155        let mut result = ValuePhiInsn::with_capacity(phi_block, args.len());
1156        for ((lhs_arg, rhs_arg), block) in args {
1157            match (lhs_arg, rhs_arg) {
1158                (ValuePhi::Value(mut lhs_value), ValuePhi::Value(mut rhs_value)) => {
1159                    if code.is_commutative()
1160                        && self.solver.value_rank(rhs_value) < self.solver.value_rank(lhs_value)
1161                    {
1162                        // Reorder args if the insn is commutative.
1163                        std::mem::swap(&mut lhs_value, &mut rhs_value);
1164                    }
1165                    let query_insn = InsnData::binary(code, lhs_value, rhs_value);
1166                    let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1167                    result.args.push((phi_arg, block));
1168                }
1169                (lhs_arg, rhs_arg) => {
1170                    let phi_arg =
1171                        self.compute_value_phi_for_binary(func, code, lhs_arg, rhs_arg)?;
1172                    result.args.push((phi_arg, block));
1173                }
1174            }
1175        }
1176
1177        Some(result.canonicalize())
1178    }
1179
1180    fn compute_value_phi_for_cast(
1181        &mut self,
1182        func: &mut Function,
1183        code: CastOp,
1184        value_phi: ValuePhi,
1185        ty: Type,
1186    ) -> Option<ValuePhi> {
1187        let value_phi_insn = if let ValuePhi::PhiInsn(insn) = value_phi {
1188            insn
1189        } else {
1190            return None;
1191        };
1192
1193        let mut result =
1194            ValuePhiInsn::with_capacity(value_phi_insn.block, value_phi_insn.args.len());
1195
1196        for (arg, block) in value_phi_insn.args.into_iter() {
1197            match arg {
1198                ValuePhi::Value(value) => {
1199                    let query_insn = InsnData::cast(code, value, ty);
1200                    let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1201                    result.args.push((phi_arg, block));
1202                }
1203                ValuePhi::PhiInsn(_) => {
1204                    let phi_arg = self.compute_value_phi_for_cast(func, code, arg, ty)?;
1205                    result.args.push((phi_arg, block));
1206                }
1207            }
1208        }
1209
1210        Some(result.canonicalize())
1211    }
1212
1213    /// Lookup value phi argument.
1214    fn lookup_value_phi_arg(&mut self, func: &mut Function, query: InsnData) -> Option<ValuePhi> {
1215        // Perform constant folding and insn simplification to canonicalize query.
1216        let query = if let Some(imm) = self.solver.perform_constant_folding(func, &query) {
1217            // If constant folding succeeds, no need to further query for the argument.
1218            return Some(ValuePhi::Value(imm));
1219        } else if let Some(simplified) = self.solver.perform_simplification(&mut func.dfg, &query) {
1220            // If query is simplified to a value, then no need to further query for the argument.
1221            if let GvnInsn::Value(value) = simplified {
1222                return Some(ValuePhi::Value(value));
1223            } else {
1224                simplified
1225            }
1226        } else {
1227            GvnInsn::Insn(query)
1228        };
1229
1230        // If class already exists for the query, return the leader value of the class.
1231        if let Some(class) = self.solver.insn_table.get(&query) {
1232            let leader = self.solver.class_leader(*class);
1233            return Some(ValuePhi::Value(leader));
1234        }
1235
1236        // Try to further compute value phi for the query insn.
1237        self.compute_value_phi(func, &query)
1238    }
1239
1240    /// Returns the `ValuePhi` if the value is defined by the phi insn or the class of the value
1241    /// is annotated with `ValuePhi`.
1242    /// The result reflects the reachability of the incoming edges,
1243    /// i.e. if a phi arg pass through an unreachable incoming edge, then the arg and blocks
1244    /// are omitted from the result.
1245    ///
1246    /// The result is sorted in the block order.
1247    fn get_phi_of(&mut self, func: &Function, value: Value) -> Option<ValuePhi> {
1248        if !self.visited.insert(value) {
1249            return None;
1250        }
1251
1252        let class = self.solver.value_class(value);
1253        let phi_block = func.layout.insn_block(func.dfg.value_insn(value)?);
1254
1255        // if the gvn_insn of the value class is phi, then create `ValuePhi::Insn` from its args.
1256        if let GvnInsn::Insn(InsnData::Phi { values, blocks, .. }) =
1257            &self.solver.classes[class].gvn_insn
1258        {
1259            let mut result = ValuePhiInsn::with_capacity(phi_block, values.len());
1260            let in_edges = &self.solver.blocks[phi_block].in_edges;
1261
1262            for (value, from) in values.iter().copied().zip(blocks.iter().copied()) {
1263                let edge = self.solver.find_edge(in_edges, from, phi_block);
1264                // If the corresponding edge is reachable, then add to the result.
1265                if self.solver.edges[edge].reachable {
1266                    result.args.push((ValuePhi::Value(value), from))
1267                }
1268            }
1269
1270            return Some(result.canonicalize());
1271        };
1272
1273        // If the value is annotated with value phi, then return it.
1274        let class = self.solver.value_class(value);
1275        match &self.solver.classes[class].value_phi {
1276            value_phi @ Some(ValuePhi::PhiInsn(..)) => value_phi.clone(),
1277            _ => None,
1278        }
1279    }
1280}
1281
1282/// Value phi that described in
1283/// `Detection of Redundant Expressions: A Complete and Polynomial-Time Algorithm in SSA`.
1284#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1285enum ValuePhi {
1286    /// `ValuePhi` may become value itself if the all arguments of the `ValuePhiInsn` is the same
1287    /// value.
1288    Value(Value),
1289    /// Phi instruction which is resolved to a "normal" phi insn in the later pass of `GvnSolver`.
1290    PhiInsn(ValuePhiInsn),
1291}
1292
1293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1294struct ValuePhiInsn {
1295    /// The Block where the phi insn should be inserted.
1296    block: Block,
1297    /// Argument of the value phi.
1298    args: Vec<(ValuePhi, Block)>,
1299}
1300
1301impl ValuePhiInsn {
1302    fn with_capacity(block: Block, cap: usize) -> Self {
1303        Self {
1304            block,
1305            args: Vec::with_capacity(cap),
1306        }
1307    }
1308
1309    /// Canonicalize the value phi insn and convert into value phi.
1310    fn canonicalize(mut self) -> ValuePhi {
1311        let first_arg = &self.args[0].0;
1312
1313        // If all arguments are the same, then return first argument.
1314        if self
1315            .args
1316            .iter()
1317            .all(|(value_phi, _)| value_phi == first_arg)
1318        {
1319            first_arg.clone()
1320        } else {
1321            // Sort arguments in block order.
1322            self.args.sort_by_key(|(_, block)| *block);
1323            ValuePhi::PhiInsn(self)
1324        }
1325    }
1326}
1327
1328/// A struct for redundant code/edge/block removal.
1329/// This struct also resolve value phis and insert phi insns to the appropriate block.
1330struct RedundantCodeRemover<'a> {
1331    solver: &'a GvnSolver,
1332
1333    /// Record pairs of available class and representative value in bottom of blocks.
1334    /// This is necessary to decide whether the args of inserted phi functions are dominated by its
1335    /// definitions.
1336    avail_set: SecondaryMap<Block, FxHashMap<Class, Value>>,
1337
1338    /// Record resolved value phis.
1339    resolved_value_phis: FxHashMap<ValuePhi, Value>,
1340}
1341
1342impl<'a> RedundantCodeRemover<'a> {
1343    fn new(solver: &'a GvnSolver) -> Self {
1344        Self {
1345            solver,
1346            avail_set: SecondaryMap::default(),
1347            resolved_value_phis: FxHashMap::default(),
1348        }
1349    }
1350
1351    /// The entry function of redundant code removal.
1352    ///
1353    /// This function
1354    /// 1. Removes unreachable edges and blocks.
1355    /// 2. Recomputes control flow graph and dominator tree.
1356    /// 3. Removes redundant code.
1357    /// 4. Resolves value phis and insert phi insns to the appropriate blocks. If value phi
1358    ///    resolution fails, then the value and its defining insn is left as is.
1359    fn remove_redundant_code(
1360        &mut self,
1361        func: &mut Function,
1362        cfg: &mut ControlFlowGraph,
1363        domtree: &mut DomTree,
1364    ) {
1365        // Remove unreachable edges and blocks before redundant code removal to calculate precise
1366        // dominator tree.
1367        self.remove_unreachable_edges(func);
1368
1369        // Recompute cfg and domtree.
1370        cfg.compute(func);
1371        domtree.compute(cfg);
1372
1373        // Compute domtree traversable.
1374        let mut domtree_traversable = DominatorTreeTraversable::default();
1375        domtree_traversable.compute(domtree);
1376
1377        let entry = func.layout.entry_block().unwrap();
1378        let mut blocks = vec![entry];
1379        while let Some(block) = blocks.pop() {
1380            blocks.extend_from_slice(domtree_traversable.children_of(block));
1381            self.remove_code_in_block(func, domtree, block);
1382        }
1383
1384        // Resolve value phis in the function.
1385        let mut next_block = Some(entry);
1386        while let Some(block) = next_block {
1387            self.resolve_value_phi_in_block(func, block);
1388            next_block = func.layout.next_block_of(block);
1389        }
1390    }
1391
1392    /// Remove redundant code in the block.
1393    fn remove_code_in_block(&mut self, func: &mut Function, domtree: &DomTree, block: Block) {
1394        // Get avail set in the top of the block.
1395        let mut avails = if block == func.layout.entry_block().unwrap() {
1396            // Insert always available values to avail set of the entry block.
1397            let mut avails = FxHashMap::default();
1398            for &avail in &self.solver.always_avail {
1399                let class = self.solver.value_class(avail);
1400                avails.insert(class, avail);
1401            }
1402            avails
1403        } else {
1404            // Use avails of the immediate dominator.
1405            let idom = domtree.idom_of(block).unwrap();
1406            self.avail_set[idom].clone()
1407        };
1408
1409        let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(block));
1410        loop {
1411            match inserter.loc() {
1412                CursorLocation::BlockTop(_) => {
1413                    inserter.proceed();
1414                }
1415
1416                CursorLocation::BlockBottom(..) | CursorLocation::NoWhere => {
1417                    break;
1418                }
1419
1420                CursorLocation::At(insn) => {
1421                    let block = inserter.block().unwrap();
1422                    if let Some(insn_result) = inserter.func().dfg.insn_result(insn) {
1423                        let class = self.solver.value_class(insn_result);
1424
1425                        // Use representative value if the class is in avail set.
1426                        if let Some(value) = avails.get(&class) {
1427                            inserter.func_mut().dfg.change_to_alias(insn_result, *value);
1428                            inserter.remove_insn();
1429                            continue;
1430                        }
1431
1432                        // Try rewrite phi insn to reflect edge's reachability.
1433                        self.rewrite_phi(inserter.func_mut(), insn, block);
1434                        avails.insert(class, insn_result);
1435                    }
1436
1437                    inserter.proceed();
1438                }
1439            }
1440        }
1441
1442        // Record avail set at the bottom of the block.
1443        self.avail_set[block] = avails;
1444    }
1445
1446    /// Resolve value phis in the block.
1447    fn resolve_value_phi_in_block(&mut self, func: &mut Function, block: Block) {
1448        let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(block));
1449        loop {
1450            match inserter.loc() {
1451                CursorLocation::BlockTop(_) => {
1452                    inserter.proceed();
1453                }
1454
1455                CursorLocation::BlockBottom(..) | CursorLocation::NoWhere => {
1456                    break;
1457                }
1458
1459                CursorLocation::At(insn) => {
1460                    let block = inserter.block().unwrap();
1461                    if let Some(insn_result) = inserter.func().dfg.insn_result(insn) {
1462                        // If value phi exists for the `insn_result` and its resolution succeeds,
1463                        // then use resolved phi value and remove insn.
1464                        let class = self.solver.value_class(insn_result);
1465                        if let Some(value_phi) = &self.solver.classes[class].value_phi {
1466                            let ty = inserter.func().dfg.value_ty(insn_result);
1467                            if self.is_value_phi_resolvable(value_phi, block) {
1468                                let value =
1469                                    self.resolve_value_phi(&mut inserter, value_phi, ty, block);
1470                                inserter.func_mut().dfg.change_to_alias(insn_result, value);
1471                                inserter.remove_insn();
1472                                continue;
1473                            }
1474                        }
1475                    }
1476
1477                    inserter.proceed();
1478                }
1479            }
1480        }
1481    }
1482
1483    /// Returns `true` if the `value_phi` can be resolved, i.e. all phi args are dominated by
1484    /// in the `block`.
1485    fn is_value_phi_resolvable(&self, value_phi: &ValuePhi, block: Block) -> bool {
1486        if self.resolved_value_phis.contains_key(value_phi) {
1487            return true;
1488        }
1489
1490        match value_phi {
1491            ValuePhi::Value(value) => {
1492                let class = self.solver.value_class(*value);
1493                self.avail_set[block].contains_key(&class)
1494            }
1495            ValuePhi::PhiInsn(phi_insn) => {
1496                for (value_phi, phi_block) in &phi_insn.args {
1497                    if !self.is_value_phi_resolvable(value_phi, *phi_block) {
1498                        return false;
1499                    }
1500                }
1501
1502                true
1503            }
1504        }
1505    }
1506
1507    /// Insert phi insn to appropriate location and returns the value that defined by
1508    /// the inserted phi insn.
1509    fn resolve_value_phi(
1510        &mut self,
1511        inserter: &mut InsnInserter,
1512        value_phi: &ValuePhi,
1513        ty: Type,
1514        block: Block,
1515    ) -> Value {
1516        debug_assert!(self.is_value_phi_resolvable(value_phi, block));
1517
1518        if let Some(value) = self.resolved_value_phis.get(value_phi) {
1519            return *value;
1520        }
1521
1522        match value_phi {
1523            ValuePhi::Value(value) => {
1524                let class = self.solver.value_class(*value);
1525                self.avail_set[block].get(&class).copied().unwrap()
1526            }
1527
1528            ValuePhi::PhiInsn(phi_insn) => {
1529                // Memorize current inserter loc to restore later.
1530                let current_inserter_loc = inserter.loc();
1531
1532                // Resolve phi value's arguments and append them to the newly `InsnData::Phi`.
1533                let mut phi = InsnData::phi(ty);
1534                for (value_phi, phi_block) in &phi_insn.args {
1535                    let resolved = self.resolve_value_phi(inserter, value_phi, ty, *phi_block);
1536                    phi.append_phi_arg(resolved, *phi_block);
1537                }
1538
1539                // Insert new phi insn to top of the phi_insn block.
1540                inserter.set_loc(CursorLocation::BlockTop(phi_insn.block));
1541                let insn = inserter.insert_insn_data(phi);
1542                let result = inserter.make_result(insn).unwrap();
1543                inserter.attach_result(insn, result);
1544
1545                // Restore the inserter loc.
1546                inserter.set_loc(current_inserter_loc);
1547
1548                // Store resolved value phis.
1549                self.resolved_value_phis.insert(value_phi.clone(), result);
1550
1551                // Returns inserted phi insn result.
1552                result
1553            }
1554        }
1555    }
1556
1557    /// Remove unreachable edges and blocks.
1558    fn remove_unreachable_edges(&self, func: &mut Function) {
1559        let entry_block = func.layout.entry_block().unwrap();
1560        let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(entry_block));
1561
1562        loop {
1563            match inserter.loc() {
1564                CursorLocation::BlockTop(block) => {
1565                    if !self.solver.blocks[block].reachable {
1566                        inserter.remove_block();
1567                    } else {
1568                        inserter.proceed();
1569                    }
1570                }
1571
1572                CursorLocation::BlockBottom(..) => inserter.proceed(),
1573
1574                CursorLocation::At(insn) => {
1575                    if inserter.func().dfg.is_branch(insn) {
1576                        let block = inserter.block().unwrap();
1577                        for &out_edge in self.solver.unreachable_out_edges(block) {
1578                            let edge_data = self.solver.edge_data(out_edge);
1579                            inserter
1580                                .func_mut()
1581                                .dfg
1582                                .remove_branch_dest(insn, edge_data.to);
1583                        }
1584                    }
1585                    inserter.proceed();
1586                }
1587
1588                CursorLocation::NoWhere => break,
1589            }
1590        }
1591    }
1592
1593    /// Rewrite phi insn when there is at least one unreachable incoming edge to the block.
1594    fn rewrite_phi(&self, func: &mut Function, insn: Insn, block: Block) {
1595        if !func.dfg.is_phi(insn) {
1596            return;
1597        }
1598
1599        let edges = &self.solver.blocks[block].in_edges;
1600        for &edge in edges {
1601            let edge_data = self.solver.edge_data(edge);
1602            // Remove phi arg if the edge is unreachable.
1603            if !edge_data.reachable {
1604                func.dfg.remove_phi_arg(insn, edge_data.from);
1605            }
1606        }
1607    }
1608}