use crate::analysis::cpa::lattice::JoinSemiLattice;
use crate::analysis::cpa::state::{AbstractState, MergeOutcome, Successor};
use jingle_sleigh::PcodeOperation;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::Display;
use std::iter::{empty, once};
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct BoundedBranchState {
pub instruction_count: usize,
pub branch_count: usize,
pub ops: usize,
pub max_instructions: Option<usize>,
pub max_branches: Option<usize>,
pub max_ops: Option<usize>,
}
impl BoundedBranchState {
pub fn new(max_count: usize) -> Self {
Self {
instruction_count: 0,
branch_count: 0,
ops: 0,
max_instructions: None,
max_branches: Some(max_count),
max_ops: None,
}
}
pub fn with_instruction_bound(max_instructions: usize) -> Self {
Self {
instruction_count: 0,
branch_count: 0,
ops: 0,
max_instructions: Some(max_instructions),
max_branches: None,
max_ops: None,
}
}
pub fn with_all_bounds(
max_instructions: Option<usize>,
max_branches: Option<usize>,
max_ops: Option<usize>,
) -> Self {
Self {
instruction_count: 0,
branch_count: 0,
ops: 0,
max_instructions,
max_branches,
max_ops,
}
}
fn is_at_bound(&self) -> bool {
if let Some(max_i) = self.max_instructions {
if self.instruction_count >= max_i {
return true;
}
}
if let Some(max_b) = self.max_branches {
if self.branch_count >= max_b {
return true;
}
}
if let Some(max_ops) = self.max_ops {
if self.ops >= max_ops {
return true;
}
}
false
}
}
impl PartialOrd for BoundedBranchState {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(self.instruction_count, self.branch_count, self.ops).partial_cmp(&(
other.instruction_count,
other.branch_count,
other.ops,
))
}
}
impl JoinSemiLattice for BoundedBranchState {
fn join(&mut self, other: &Self) {
self.instruction_count = self.instruction_count.min(other.instruction_count);
self.branch_count = self.branch_count.min(other.branch_count);
self.max_instructions = match (self.max_instructions, other.max_instructions) {
(Some(a), Some(b)) => Some(a.max(b)),
_ => None,
};
self.max_branches = match (self.max_branches, other.max_branches) {
(Some(a), Some(b)) => Some(a.max(b)),
_ => None,
};
self.instruction_count = self.instruction_count.max(other.instruction_count);
self.branch_count = self.branch_count.max(other.branch_count);
self.ops = self.ops.max(other.ops);
}
}
impl AbstractState for BoundedBranchState {
fn merge(&mut self, other: &Self) -> MergeOutcome {
self.merge_join(other)
}
fn stop<'a, T: Iterator<Item = &'a Self>>(&'a self, states: T) -> bool {
self.stop_sep(states)
}
fn transfer<'a, B: Borrow<PcodeOperation>>(&'a self, opcode: B) -> Successor<'a, Self> {
let opcode = opcode.borrow();
if self.is_at_bound() {
return empty().into();
}
let is_branch = opcode.branch_destination().is_some();
let is_fallthrough = matches!(opcode, PcodeOperation::Fallthrough { .. });
let should_count_branch = is_branch && !is_fallthrough;
let next_instruction_count = self.instruction_count + 1;
let next_branch_count = if should_count_branch {
self.branch_count + 1
} else {
self.branch_count
};
if match self.max_instructions {
Some(max) => next_instruction_count > max,
None => false,
} || match self.max_branches {
Some(max) => next_branch_count > max,
None => false,
} {
return empty().into();
}
let next = Self {
instruction_count: next_instruction_count,
branch_count: next_branch_count,
ops: self.ops + 1,
max_instructions: self.max_instructions,
max_branches: self.max_branches,
max_ops: self.max_ops,
};
once(next).into()
}
}
impl Display for BoundedBranchState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match (self.max_instructions, self.max_branches) {
(Some(mi), Some(mb)) => {
write!(
f,
"i:{}/{} b:{}/{}",
self.instruction_count, mi, self.branch_count, mb
)
}
(Some(mi), None) => write!(
f,
"i:{}/{} b:{}",
self.instruction_count, mi, self.branch_count
),
(None, Some(mb)) => write!(
f,
"i:{} b:{}/{}",
self.instruction_count, self.branch_count, mb
),
(None, None) => write!(f, "i:{} b:{}", self.instruction_count, self.branch_count),
}
}
}