Skip to main content

tenflowers_core/graph/optimization/passes/
dead_code.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::graph::{Graph, NodeId};
6use crate::Result;
7use std::cell::RefCell;
8use std::collections::HashSet;
9
10use super::pass_support::{get_node_inputs, get_node_outputs, OptimizationPass};
11
12/// Dead code elimination pass
13/// Removes nodes that don't contribute to any output
14pub struct DeadCodeEliminationPass {
15    pub(super) outputs: RefCell<HashSet<NodeId>>,
16}
17impl DeadCodeEliminationPass {
18    pub fn new() -> Self {
19        Self {
20            outputs: RefCell::new(HashSet::new()),
21        }
22    }
23    /// Construct a pass that additionally treats `outputs` as GC roots, so
24    /// nodes reachable only from these ids are preserved even if nothing
25    /// else about them would otherwise mark them as reachable.
26    pub fn with_outputs(outputs: HashSet<NodeId>) -> Self {
27        Self {
28            outputs: RefCell::new(outputs),
29        }
30    }
31    pub(super) fn is_output_node(&self, graph: &Graph, node_id: NodeId) -> bool {
32        let outputs = get_node_outputs(graph, node_id);
33        outputs.is_empty() || self.is_marked_as_output(node_id)
34    }
35    pub(super) fn is_marked_as_output(&self, node_id: NodeId) -> bool {
36        self.outputs.borrow().contains(&node_id)
37    }
38    #[allow(clippy::only_used_in_recursion)]
39    pub(super) fn mark_reachable(
40        &self,
41        graph: &Graph,
42        node_id: NodeId,
43        reachable: &mut HashSet<NodeId>,
44    ) {
45        if reachable.contains(&node_id) {
46            return;
47        }
48        reachable.insert(node_id);
49        for input_id in get_node_inputs(graph, node_id) {
50            self.mark_reachable(graph, input_id, reachable);
51        }
52    }
53}
54impl OptimizationPass for DeadCodeEliminationPass {
55    fn apply(&self, graph: &mut Graph) -> Result<bool> {
56        let mut changed = false;
57        let mut reachable = HashSet::new();
58        for node in graph.nodes() {
59            if self.is_output_node(graph, node.id) {
60                self.mark_reachable(graph, node.id, &mut reachable);
61            }
62        }
63        let mut nodes_to_remove = Vec::new();
64        for node in graph.nodes() {
65            if !reachable.contains(&node.id) {
66                nodes_to_remove.push(node.id);
67                changed = true;
68            }
69        }
70        for node_id in nodes_to_remove {
71            graph.remove_node(node_id)?;
72        }
73        Ok(changed)
74    }
75    fn name(&self) -> &str {
76        "DeadCodeElimination"
77    }
78    fn is_applicable(&self, graph: &Graph) -> bool {
79        graph.node_count() > 0
80    }
81    fn priority(&self) -> u32 {
82        50
83    }
84    fn set_outputs(&self, outputs: &HashSet<NodeId>) {
85        *self.outputs.borrow_mut() = outputs.clone();
86    }
87}
88impl Default for DeadCodeEliminationPass {
89    fn default() -> Self {
90        Self::new()
91    }
92}