From 1dad396810236ddd98526aebe01b0af9491f8136 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 29 Jun 2022 23:01:16 +1000 Subject: [PATCH] Speedup `find_dead_code` pass in control flow analysis (#2159) Fix slow `find_dead_code` pass in control flow analysis While waiting for the tests to pass on a PR I thought I'd have a quick look to see if I could find any quick wins for dead code analysis #1952. I noticed that that we're using `has_path_connecting` for every combination of node and entry point. This means we were re-checking the same nodes many, many times, searching from scratch each time and not re-using any of the knowledge of already visited nodes in each consecutive traversal. This commit refactors the approach to first collect all known live nodes into a set by traversing from the entry points. We re-use the same `Dfs` when searching from each entry in order to re-use its inner set of visited nodes and avoid re-searching sections of the graph that we've already visited. The dead nodes are those not contained in the live set after traversal. This reduces the time taken within the `find_dead_code` call when building the `std` library in debug from ~7.9 seconds down to ~3.3 milliseconds. 1000x+ speedup in DCA :) Hopefully this speeds up our CI a bit! Closes #1952. --- .../dead_code_analysis.rs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 548f4aae180..e09ccecca87 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -16,29 +16,33 @@ use crate::{ type_engine::{resolve_type, TypeInfo}, CompileError, CompileWarning, Ident, TreeType, Warning, }; +use std::collections::BTreeSet; use sway_types::{span::Span, Spanned}; use crate::semantic_analysis::TypedStorageDeclaration; -use petgraph::algo::has_path_connecting; use petgraph::prelude::NodeIndex; +use petgraph::visit::Dfs; impl ControlFlowGraph { pub(crate) fn find_dead_code(&self) -> Vec { - // dead code is code that has no path to the entry point - let mut dead_nodes = vec![]; - for destination in self.graph.node_indices() { - let mut is_connected = false; - for entry in &self.entry_points { - if has_path_connecting(&self.graph, *entry, destination, None) { - is_connected = true; - break; - } - } - if !is_connected { - dead_nodes.push(destination); + // Dead code is code that has no path to the entry point. + // Collect all connected nodes by traversing from the entries. + // The dead nodes are those we did not collect. + let mut connected = BTreeSet::new(); + let mut dfs = Dfs::empty(&self.graph); + for &entry in &self.entry_points { + dfs.move_to(entry); + while let Some(node) = dfs.next(&self.graph) { + connected.insert(node); } } + let dead_nodes: Vec<_> = self + .graph + .node_indices() + .filter(|n| !connected.contains(n)) + .collect(); + let dead_enum_variant_warnings = dead_nodes .iter() .filter_map(|x| match &self.graph[*x] {