use crate::cfg::{BlockId, Cfg};
use petgraph::graph::NodeIndex;
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathLimits {
pub max_length: usize,
pub max_paths: usize,
pub loop_unroll_limit: usize,
}
impl Default for PathLimits {
fn default() -> Self {
Self {
max_length: 1000,
max_paths: 10000,
loop_unroll_limit: 3,
}
}
}
impl PathLimits {
pub fn new(max_length: usize, max_paths: usize, loop_unroll_limit: usize) -> Self {
Self {
max_length,
max_paths,
loop_unroll_limit,
}
}
pub fn with_max_length(mut self, max_length: usize) -> Self {
self.max_length = max_length;
self
}
pub fn with_max_paths(mut self, max_paths: usize) -> Self {
self.max_paths = max_paths;
self
}
pub fn with_loop_unroll_limit(mut self, loop_unroll_limit: usize) -> Self {
self.loop_unroll_limit = loop_unroll_limit;
self
}
pub fn quick_analysis() -> Self {
Self {
max_length: 100,
max_paths: 1000,
loop_unroll_limit: 2,
}
}
pub fn thorough() -> Self {
Self {
max_length: 10000,
max_paths: 100000,
loop_unroll_limit: 5,
}
}
}
pub fn hash_path(blocks: &[BlockId]) -> String {
let mut hasher = blake3::Hasher::new();
hasher.update(&blocks.len().to_le_bytes());
for &block_id in blocks {
hasher.update(&block_id.to_le_bytes());
}
hasher.finalize().to_hex().to_string()
}
#[derive(Debug, Clone)]
pub struct EnumerationContext {
pub reachable_blocks: HashSet<BlockId>,
pub loop_headers: HashSet<NodeIndex>,
pub exits: HashSet<NodeIndex>,
}
impl EnumerationContext {
pub fn new(cfg: &Cfg) -> Self {
let reachable_nodes = crate::cfg::reachability::find_reachable(cfg);
let reachable_blocks: HashSet<BlockId> =
reachable_nodes.iter().map(|&idx| cfg[idx].id).collect();
let loop_headers = crate::cfg::loops::find_loop_headers(cfg);
let mut exits: HashSet<NodeIndex> =
crate::cfg::analysis::find_exits(cfg).into_iter().collect();
if exits.is_empty() {
for node in cfg.node_indices() {
if cfg.neighbors(node).next().is_none() {
exits.insert(node);
}
}
}
Self {
reachable_blocks,
loop_headers,
exits,
}
}
pub fn reachable_count(&self) -> usize {
self.reachable_blocks.len()
}
pub fn loop_count(&self) -> usize {
self.loop_headers.len()
}
pub fn exit_count(&self) -> usize {
self.exits.len()
}
pub fn is_reachable(&self, block_id: BlockId) -> bool {
self.reachable_blocks.contains(&block_id)
}
pub fn is_loop_header(&self, node: NodeIndex) -> bool {
self.loop_headers.contains(&node)
}
pub fn is_exit(&self, node: NodeIndex) -> bool {
self.exits.contains(&node)
}
}