Skip to content

Commit

Permalink
Speedup find_dead_code pass in control flow analysis (#2159)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mitchmindtree authored Jun 29, 2022
1 parent 987f950 commit 1dad396
Showing 1 changed file with 17 additions and 13 deletions.
30 changes: 17 additions & 13 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompileWarning> {
// 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] {
Expand Down

0 comments on commit 1dad396

Please sign in to comment.