diff --git a/crates/analyzers/src/func_analyzer/mod.rs b/crates/analyzers/src/func_analyzer/mod.rs index bef2a700..f41db5cd 100644 --- a/crates/analyzers/src/func_analyzer/mod.rs +++ b/crates/analyzers/src/func_analyzer/mod.rs @@ -56,7 +56,7 @@ impl<'a> FunctionVarsBoundAnalysis { let deps = ctx.ctx_deps(analyzer).unwrap(); let deps = deps .iter() - .map(|var| (var.display_name(analyzer).unwrap(), var)) + .map(|var| (var.as_controllable_name(analyzer).unwrap(), var)) .collect::>(); // create the bound strings // let atoms = ctx.dep_atoms(analyzer).unwrap(); diff --git a/crates/analyzers/src/var_analyzer/mod.rs b/crates/analyzers/src/var_analyzer/mod.rs index 9b14027b..9fac71ef 100644 --- a/crates/analyzers/src/var_analyzer/mod.rs +++ b/crates/analyzers/src/var_analyzer/mod.rs @@ -240,7 +240,7 @@ pub trait VarBoundAnalyzer: Search + AnalyzerBackend + Sized { if let Some(next_range) = curr.ref_range(self).unwrap() { let nr_min = next_range.evaled_range_min(self).unwrap(); let nr_max = next_range.evaled_range_max(self).unwrap(); - let nr_excl = &next_range.exclusions; + let nr_excl = &next_range.range_exclusions(); // check if there was a bound change if report_config.show_all_lines @@ -266,7 +266,7 @@ pub trait VarBoundAnalyzer: Search + AnalyzerBackend + Sized { if let Some(next_range) = next.ref_range(self).unwrap() { let nr_min = next_range.evaled_range_min(self).unwrap(); let nr_max = next_range.evaled_range_max(self).unwrap(); - let nr_excl = &next_range.exclusions; + let nr_excl = &next_range.range_exclusions(); // check if there was a bound change if report_config.show_all_lines diff --git a/crates/graph/src/nodes/context/solving.rs b/crates/graph/src/nodes/context/solving.rs index bebaf6ce..35f927bf 100644 --- a/crates/graph/src/nodes/context/solving.rs +++ b/crates/graph/src/nodes/context/solving.rs @@ -1,3 +1,5 @@ +use crate::FlattenedRange; +use crate::elem::Elem; use crate::SolcRange; use crate::{ as_dot_str, @@ -33,7 +35,7 @@ impl ContextNode { let mut ranges = BTreeMap::default(); deps.iter().try_for_each(|dep| { let mut range = dep.range(analyzer)?.unwrap(); - let r: Cow<'_, SolcRange> = range.flattened_range(analyzer)?; + let r: Cow<'_, _> = range.flattened_range(analyzer)?; ranges.insert(*dep, r.into_owned()); Ok(()) })?; @@ -41,10 +43,10 @@ impl ContextNode { Ok(ranges .iter() .filter_map(|(_dep, range)| { - if let Some(atom) = range.min.atomize(analyzer) { + if let Some(atom) = Elem::Arena(range.min).atomize(analyzer) { Some(atom) } else { - range.max.atomize(analyzer) + Elem::Arena(range.max).atomize(analyzer) } }) .collect::>()) @@ -89,12 +91,13 @@ impl ContextNode { // dep.cache_flattened_range(analyzer)?; let mut range = dep.range(analyzer)?.unwrap(); let r = range.flattened_range(analyzer)?.into_owned(); + tracing::trace!("flattened: {}", >::into(r.clone()).as_dot_str(analyzer)); // add the atomic constraint - if let Some(atom) = r.min.atomize(analyzer) { + if let Some(atom) = Elem::Arena(r.min).atomize(analyzer) { let mut solver = std::mem::take(&mut self.underlying_mut(analyzer)?.dl_solver); solver.add_constraints(vec![atom], analyzer); self.underlying_mut(analyzer)?.dl_solver = solver; - } else if let Some(atom) = r.max.atomize(analyzer) { + } else if let Some(atom) = Elem::Arena(r.max).atomize(analyzer) { let mut solver = std::mem::take(&mut self.underlying_mut(analyzer)?.dl_solver); solver.add_constraints(vec![atom], analyzer); self.underlying_mut(analyzer)?.dl_solver = solver; diff --git a/crates/graph/src/nodes/context/var/ranging.rs b/crates/graph/src/nodes/context/var/ranging.rs index 2a1c86c1..0cece95e 100644 --- a/crates/graph/src/nodes/context/var/ranging.rs +++ b/crates/graph/src/nodes/context/var/ranging.rs @@ -203,6 +203,8 @@ impl ContextVarNode { new_min.arenaize(analyzer)?; + + // new_min.cache_flatten(analyzer)?; // new_min.cache_minimize(analyzer)?; @@ -279,7 +281,7 @@ impl ContextVarNode { pub fn set_range_exclusions( &self, analyzer: &mut impl GraphBackend, - mut new_exclusions: Vec>, + mut new_exclusions: Vec, ) -> Result<(), GraphError> { tracing::trace!( "setting range exclusions for {}", @@ -292,9 +294,10 @@ impl ContextVarNode { None }; - new_exclusions - .iter_mut() - .try_for_each(|excl| excl.arenaize(analyzer))?; + // let new_exclusions = new_exclusions + // .into_iter() + // .map(|excl| analyzer.range_arena_idx_or_upsert(excl)) + // .collect(); self.underlying_mut(analyzer)? .set_range_exclusions(new_exclusions, fallback)?; @@ -366,7 +369,7 @@ impl ContextVarNode { pub fn try_set_range_exclusions( &self, analyzer: &mut impl GraphBackend, - mut new_exclusions: Vec>, + mut new_exclusions: Vec, ) -> Result { tracing::trace!( "setting range exclusions for: {}", @@ -379,9 +382,10 @@ impl ContextVarNode { None }; - new_exclusions - .iter_mut() - .try_for_each(|excl| excl.arenaize(analyzer))?; + // let new_exclusions = new_exclusions + // .into_iter() + // .map(|excl| analyzer.range_arena_idx_or_upsert(excl)) + // .collect(); Ok(self .underlying_mut(analyzer)? diff --git a/crates/graph/src/nodes/context/var/underlying.rs b/crates/graph/src/nodes/context/var/underlying.rs index 840b0a79..76a29932 100644 --- a/crates/graph/src/nodes/context/var/underlying.rs +++ b/crates/graph/src/nodes/context/var/underlying.rs @@ -357,7 +357,7 @@ impl ContextVar { pub fn set_range_exclusions( &mut self, - new_exclusions: Vec>, + new_exclusions: Vec, fallback_range: Option, ) -> Result<(), GraphError> { match &mut self.ty { @@ -414,7 +414,7 @@ impl ContextVar { pub fn try_set_range_exclusions( &mut self, - new_exclusions: Vec>, + new_exclusions: Vec, fallback_range: Option, ) -> bool { match &mut self.ty { diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index 3dcbdfd7..ee839759 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -27,7 +27,8 @@ impl ContextNode { underlying_mut .expr_ret_stack .iter() - .for_each(|elem| println!("{}", elem.debug_str(analyzer))); + .enumerate() + .for_each(|(i, elem)| println!("{i}. {}", elem.debug_str(analyzer))); Ok(()) } @@ -212,6 +213,10 @@ impl ContextNode { loc: Loc, analyzer: &mut (impl GraphBackend + AnalyzerBackend), ) -> Result { + tracing::trace!( + "moving expr to {}", + self.path(analyzer) + ); match expr { ExprRet::SingleLiteral(var) => Ok(ExprRet::SingleLiteral( self.maybe_move_var(var.into(), loc, analyzer)?.into(), @@ -239,6 +244,11 @@ impl ContextNode { let var = var.latest_version(analyzer); if let Some(ctx) = var.maybe_ctx(analyzer) { if ctx != *self { + tracing::trace!( + "moving var {} from {}", + ctx.path(analyzer), + self.path(analyzer) + ); let mut new_cvar = var.latest_version(analyzer).underlying(analyzer)?.clone(); new_cvar.loc = Some(loc); diff --git a/crates/graph/src/range/elem/concrete.rs b/crates/graph/src/range/elem/concrete.rs index aad962f5..97a91ec5 100644 --- a/crates/graph/src/range/elem/concrete.rs +++ b/crates/graph/src/range/elem/concrete.rs @@ -62,6 +62,15 @@ impl RangeElem for RangeConcrete { Ok(false) } + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result { + Ok(false) + } + fn flatten( &self, _maximize: bool, diff --git a/crates/graph/src/range/elem/elem_enum.rs b/crates/graph/src/range/elem/elem_enum.rs index 18106228..a34d292a 100644 --- a/crates/graph/src/range/elem/elem_enum.rs +++ b/crates/graph/src/range/elem/elem_enum.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use crate::elem::MinMaxed; use std::cell::RefCell; use std::rc::Rc; @@ -10,14 +9,14 @@ use crate::{ GraphBackend, GraphError, }; use solang_parser::pt::Loc; -use std::hash::Hash; -use std::hash::Hasher; use shared::{NodeIdx, RangeArenaIdx}; use ethers_core::types::I256; +use tracing::instrument; use std::{ + hash::{Hash,Hasher}, collections::BTreeMap, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}, }; @@ -562,6 +561,113 @@ impl Elem { Self::Arena(_) => todo!(), } } + + pub fn arenaized_flattened(&self, max: bool, analyzer: &impl GraphBackend) -> Option>> { + if let Some(idx) = analyzer.range_arena_idx(self) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + match &*t { + Elem::Expr(ref arenaized) => { + if max { + arenaized.flattened_max.clone() + } else { + arenaized.flattened_min.clone() + } + } + Elem::Reference(ref arenaized) => { + if max { + arenaized.flattened_max.clone() + } else { + arenaized.flattened_min.clone() + } + } + Elem::ConcreteDyn(ref arenaized) => { + if max { + arenaized.flattened_max.clone() + } else { + arenaized.flattened_min.clone() + } + } + c @ Elem::Concrete(_) => { + Some(Box::new(c.clone())) + } + c @ Elem::Null => { + Some(Box::new(c.clone())) + } + a @ Elem::Arena(_) => { + a.arenaized_flattened(max, analyzer) + } + } + } else { + None + } + } else { + None + } + } + + pub fn set_arenaized_flattened(&self, max: bool, elem: &Elem, analyzer: &impl GraphBackend) { + if let Some(idx) = analyzer.range_arena_idx(self) { + if let Ok(mut t) = analyzer.range_arena().ranges[idx].try_borrow_mut() { + match &mut *t { + Elem::Expr(ref mut arenaized) => { + if max { + arenaized.flattened_max = Some(Box::new(elem.clone())); + } else { + arenaized.flattened_min = Some(Box::new(elem.clone())); + } + } + Elem::Reference(ref mut arenaized) => { + if max { + arenaized.flattened_max = Some(Box::new(elem.clone())); + } else { + arenaized.flattened_min = Some(Box::new(elem.clone())); + } + } + Elem::ConcreteDyn(ref mut arenaized) => { + if max { + arenaized.flattened_max = Some(Box::new(elem.clone())); + } else { + arenaized.flattened_min = Some(Box::new(elem.clone())); + } + } + _ => {} + } + + } + } + } + + pub fn set_arenaized_cache(&self, max: bool, elem: &Elem, analyzer: &impl GraphBackend) { + if let Some(idx) = analyzer.range_arena_idx(self) { + if let Ok(mut t) = analyzer.range_arena().ranges[idx].try_borrow_mut() { + match &mut *t { + Elem::Expr(ref mut arenaized) => { + if max { + arenaized.maximized = Some(MinMaxed::Maximized(Box::new(elem.clone()))); + } else { + arenaized.minimized = Some(MinMaxed::Minimized(Box::new(elem.clone()))); + } + } + Elem::Reference(ref mut arenaized) => { + if max { + arenaized.maximized = Some(MinMaxed::Maximized(Box::new(elem.clone()))); + } else { + arenaized.minimized = Some(MinMaxed::Minimized(Box::new(elem.clone()))); + } + } + Elem::ConcreteDyn(ref mut arenaized) => { + if max { + arenaized.maximized = Some(MinMaxed::Maximized(Box::new(elem.clone()))); + } else { + arenaized.minimized = Some(MinMaxed::Minimized(Box::new(elem.clone()))); + } + } + _ => {} + } + + } + } + } } impl From for Elem { @@ -672,6 +778,7 @@ impl RangeElem for Elem { } } + #[instrument(level = "trace", skip_all)] fn flatten( &self, maximize: bool, @@ -686,35 +793,36 @@ impl RangeElem for Elem { Self::Arena(_) => { let de = self.dearenaize(analyzer); let res = de.borrow().flatten(maximize, analyzer)?; - match &mut *de.borrow_mut() { - Self::Reference(ref mut d) => { - if maximize { - d.flattened_max = Some(Box::new(res.clone())); - } else { - d.flattened_min = Some(Box::new(res.clone())); - } - } - Self::Expr(ref mut expr) => { - if maximize { - expr.flattened_max = Some(Box::new(res.clone())); - } else { - expr.flattened_min = Some(Box::new(res.clone())); - } - } - Self::ConcreteDyn(ref mut d) => { - if maximize { - d.flattened_max = Some(Box::new(res.clone())); - } else { - d.flattened_min = Some(Box::new(res.clone())); - } - } - _ => {} - } + // match &mut *de.borrow_mut() { + // Self::Reference(ref mut d) => { + // if maximize { + // d.flattened_max = Some(Box::new(res)); + // } else { + // d.flattened_min = Some(Box::new(res)); + // } + // } + // Self::Expr(ref mut expr) => { + // if maximize { + // expr.flattened_max = Some(Box::new(res)); + // } else { + // expr.flattened_min = Some(Box::new(res)); + // } + // } + // Self::ConcreteDyn(ref mut d) => { + // if maximize { + // d.flattened_max = Some(Box::new(res)); + // } else { + // d.flattened_min = Some(Box::new(res)); + // } + // } + // _ => {} + // } Ok(res) }, } } + #[instrument(level = "trace", skip_all)] fn cache_flatten(&mut self, analyzer: &mut impl GraphBackend) -> Result<(), GraphError> { if self.is_flatten_cached(analyzer) { return Ok(()); @@ -726,15 +834,43 @@ impl RangeElem for Elem { Self::Expr(expr) => expr.cache_flatten(analyzer), Self::ConcreteDyn(d) => d.cache_flatten(analyzer), Self::Null => Ok(()), - Self::Arena(_idx) => { - let mut dearenaized = self.dearenaize(analyzer).borrow().clone(); - dearenaized.cache_flatten(analyzer)?; - *self.dearenaize(analyzer).borrow_mut() = dearenaized; + Self::Arena(idx) => { + tracing::trace!("flattening for arena idx: {idx}"); + let dearenaized = self.dearenaize(analyzer); + let (min, max) = { + let Ok(t) = dearenaized.try_borrow() else { + return Ok(()) + }; + + let min = t.flatten(false, analyzer)?; + let max = t.flatten(true, analyzer)?; + (min, max) + }; + + match &mut *dearenaized.borrow_mut() { + Self::Reference(ref mut d) => { + d.flattened_min = Some(Box::new(min)); + d.flattened_max = Some(Box::new(max)); + } + Self::Expr(ref mut expr) => { + expr.flattened_min = Some(Box::new(min)); + expr.flattened_max = Some(Box::new(max)); + } + Self::ConcreteDyn(ref mut d) => { + d.flattened_min = Some(Box::new(min)); + d.flattened_max = Some(Box::new(max)); + } + _ => {} + } + // let mut dearenaized = self.dearenaize(analyzer).borrow().clone(); + // dearenaized.cache_flatten(analyzer)?; + // *self.dearenaize(analyzer).borrow_mut() = dearenaized; Ok(()) } } } + #[instrument(level = "trace", skip_all)] fn is_flatten_cached(&self, analyzer: &impl GraphBackend) -> bool { match self { Self::Reference(d) => d.is_flatten_cached(analyzer), @@ -742,18 +878,30 @@ impl RangeElem for Elem { Self::Expr(expr) => expr.is_flatten_cached(analyzer), Self::ConcreteDyn(d) => d.is_flatten_cached(analyzer), Self::Null => true, - Self::Arena(_) => self.dearenaize(analyzer).borrow().is_flatten_cached(analyzer), + Self::Arena(_idx) => { + if let Ok(t) = self.dearenaize(analyzer).try_borrow() { + t.is_flatten_cached(analyzer) + } else { + false + } + }, } } fn is_min_max_cached(&self, analyzer: &impl GraphBackend) -> (bool, bool) { match self { Self::Reference(d) => d.is_min_max_cached(analyzer), - Self::Concrete(c) => c.is_min_max_cached(analyzer), + Self::Concrete(_c) => (true, true), Self::Expr(expr) => expr.is_min_max_cached(analyzer), Self::ConcreteDyn(d) => d.is_min_max_cached(analyzer), Self::Null => (true, true), - Self::Arena(_) => self.dearenaize(analyzer).borrow().is_min_max_cached(analyzer), + Self::Arena(_) => { + if let Ok(t) = self.dearenaize(analyzer).try_borrow() { + t.is_min_max_cached(analyzer) + } else { + (false, false) + } + }, } } @@ -764,7 +912,13 @@ impl RangeElem for Elem { Self::Expr(expr) => expr.dependent_on(analyzer), Self::ConcreteDyn(d) => d.dependent_on(analyzer), Self::Null => vec![], - Self::Arena(_) => self.dearenaize(analyzer).borrow().dependent_on(analyzer), + Self::Arena(_) => { + if let Ok(t) = self.dearenaize(analyzer).try_borrow() { + t.dependent_on(analyzer) + } else { + vec![] + } + } } } @@ -797,6 +951,22 @@ impl RangeElem for Elem { } } + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result { + match self { + Self::Reference(d) => d.depends_on(var, seen, analyzer), + Self::Concrete(_) => Ok(false), + Self::Expr(expr) => expr.depends_on(var, seen, analyzer), + Self::ConcreteDyn(d) => d.depends_on(var, seen, analyzer), + Self::Null => Ok(false), + Self::Arena(_) => self.dearenaize(analyzer).borrow().depends_on(var, seen, analyzer), + } + } + fn filter_recursion( &mut self, node_idx: NodeIdx, @@ -824,6 +994,7 @@ impl RangeElem for Elem { if let Some(idx) = analyzer.range_arena_idx(self) { let (_min, max) = Elem::Arena(idx).is_min_max_cached(analyzer); if max { + tracing::trace!("maximize cache hit"); match &*analyzer.range_arena().ranges[idx].borrow() { Reference(dy) => return dy.maximize(analyzer), Concrete(inner) => return inner.maximize(analyzer), @@ -844,19 +1015,32 @@ impl RangeElem for Elem { Null => Elem::Null, Arena(_) => { let dearenaized = self.dearenaize(analyzer); - let res = dearenaized.borrow().maximize(analyzer)?; + let res = { + let Ok(t) = dearenaized.try_borrow() else { + return Ok(self.clone()) + }; + t.maximize(analyzer)? + }; + match &mut *dearenaized.borrow_mut() { Self::Reference(ref mut d) => { + tracing::trace!("maximize cache MISS: {self}"); d.maximized = Some(MinMaxed::Maximized(Box::new(res.clone()))); } Self::Expr(ref mut expr) => { + tracing::trace!("maximize cache MISS: {self}"); expr.maximized = Some(MinMaxed::Maximized(Box::new(res.clone()))); } Self::ConcreteDyn(ref mut d) => { + tracing::trace!("maximize cache MISS: {self}"); d.maximized = Some(MinMaxed::Maximized(Box::new(res.clone()))); } _ => {} } + + let (_min, max) = self.is_min_max_cached(analyzer); + assert!(max, "????"); + res }, }; @@ -869,6 +1053,7 @@ impl RangeElem for Elem { if let Some(idx) = analyzer.range_arena_idx(self) { let (min, _max) = Elem::Arena(idx).is_min_max_cached(analyzer); if min { + tracing::trace!("minimize cache hit"); match &*analyzer.range_arena().ranges[idx].borrow() { Reference(dy) => return dy.minimize(analyzer), Concrete(inner) => return inner.minimize(analyzer), @@ -888,19 +1073,31 @@ impl RangeElem for Elem { Null => Elem::Null, Arena(_) => { let dearenaized = self.dearenaize(analyzer); - let res = dearenaized.borrow().minimize(analyzer)?; + let res = { + let Ok(t) = dearenaized.try_borrow() else { + return Ok(self.clone()) + }; + t.minimize(analyzer)? + }; + match &mut *dearenaized.borrow_mut() { Self::Reference(ref mut d) => { - d.maximized = Some(MinMaxed::Minimized(Box::new(res.clone()))); + tracing::trace!("minimize cache MISS: {self}"); + d.minimized = Some(MinMaxed::Minimized(Box::new(res.clone()))); } Self::Expr(ref mut expr) => { + tracing::trace!("minimize cache MISS: {self}"); expr.minimized = Some(MinMaxed::Minimized(Box::new(res.clone()))); } Self::ConcreteDyn(ref mut d) => { + tracing::trace!("minimize cache MISS: {self}"); d.minimized = Some(MinMaxed::Minimized(Box::new(res.clone()))); } _ => {} } + + let (min, _max) = self.is_min_max_cached(analyzer); + assert!(min, "????"); res }, }; @@ -913,53 +1110,72 @@ impl RangeElem for Elem { ) -> Result, GraphError> { use Elem::*; - match self { - Arena(_) => {} - _ => { - if let Some(idx) = analyzer.range_arena_idx(self) { - if Elem::Arena(idx).is_flatten_cached(analyzer) { - match &*analyzer.range_arena().ranges[idx].borrow() { - Reference(dy) => return dy.simplify_maximize(analyzer), - Concrete(inner) => return inner.simplify_maximize(analyzer), - ConcreteDyn(inner) => return inner.simplify_maximize(analyzer), - Expr(expr) => return expr.simplify_maximize(analyzer), - Null => return Ok(Elem::Null), - _ => {} - } + if let Some(idx) = analyzer.range_arena_idx(self) { + match &*analyzer.range_arena().ranges[idx].borrow() { + Reference(dy) => { + if let Some(max) = &dy.flattened_max { + return Ok(*max.clone()) + } + }, + c @ Concrete(_) => return Ok(c.clone()), + ConcreteDyn(inner) => { + if let Some(max) = &inner.flattened_max { + return Ok(*max.clone()) } } + Expr(expr) => { + if let Some(max) = &expr.flattened_max { + return Ok(*max.clone()) + } + }, + Null => return Ok(Elem::Null), + _ => {} } } - match self { Reference(dy) => dy.simplify_maximize(analyzer), Concrete(inner) => inner.simplify_maximize(analyzer), ConcreteDyn(inner) => inner.simplify_maximize(analyzer), Expr(expr) => match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { MaybeCollapsed::Collapsed(collapsed) => { - collapsed.simplify_maximize(analyzer) + let res = collapsed.simplify_maximize(analyzer)?; + collapsed.set_arenaized_flattened(true, &res, analyzer); + Ok(res) } - _ => expr.simplify_maximize(analyzer), + _ => { + let res = expr.simplify_maximize(analyzer)?; + expr.set_arenaized_flattened(true, res.clone(), analyzer); + Ok(res) + }, }, Null => Ok(Elem::Null), Arena(_) => { let dearenaized = self.dearenaize(analyzer); let flat = dearenaized.borrow().flatten(true, analyzer)?; - let res = flat.simplify_maximize(analyzer)?; + let max = flat.simplify_maximize(analyzer)?; + // let min = flat.simplify_minimize(analyzer)?; match &mut *dearenaized.borrow_mut() { Self::Reference(ref mut d) => { - d.flattened_max = Some(Box::new(res.clone())); + tracing::trace!("simplify maximize cache MISS: {self}"); + d.flattened_max = Some(Box::new(max.clone())); + // d.flattened_min = Some(Box::new(min.clone())); } Self::Expr(ref mut expr) => { - expr.flattened_max = Some(Box::new(res.clone())); + tracing::trace!("simplify maximize cache MISS: {self}"); + expr.flattened_max = Some(Box::new(max.clone())); + // expr.flattened_min = Some(Box::new(min.clone())); } Self::ConcreteDyn(ref mut d) => { - d.flattened_max = Some(Box::new(res.clone())); + tracing::trace!("simplify maximize cache MISS: {self}"); + d.flattened_max = Some(Box::new(max.clone())); + // d.flattened_min = Some(Box::new(min.clone())); } _ => {} } - Ok(res) + + // assert!(self.is_flatten_cached(analyzer), "????"); + Ok(max) } } } @@ -970,21 +1186,26 @@ impl RangeElem for Elem { ) -> Result, GraphError> { use Elem::*; - match self { - Arena(_) => {} - _ => { - if let Some(idx) = analyzer.range_arena_idx(self) { - if Elem::Arena(idx).is_flatten_cached(analyzer) { - match &*analyzer.range_arena().ranges[idx].borrow() { - Reference(dy) => return dy.simplify_minimize(analyzer), - Concrete(inner) => return inner.simplify_minimize(analyzer), - ConcreteDyn(inner) => return inner.simplify_minimize(analyzer), - Expr(expr) => return expr.simplify_minimize(analyzer), - Null => return Ok(Elem::Null), - _ => {} - } + if let Some(idx) = analyzer.range_arena_idx(self) { + match &*analyzer.range_arena().ranges[idx].borrow() { + Reference(dy) => { + if let Some(min) = &dy.flattened_min { + return Ok(*min.clone()) + } + }, + c @ Concrete(_) => return Ok(c.clone()), + ConcreteDyn(inner) => { + if let Some(min) = &inner.flattened_min { + return Ok(*min.clone()) } } + Expr(expr) => { + if let Some(min) = &expr.flattened_min { + return Ok(*min.clone()) + } + }, + Null => return Ok(Elem::Null), + _ => {} } } @@ -994,28 +1215,38 @@ impl RangeElem for Elem { ConcreteDyn(inner) => inner.simplify_minimize(analyzer), Expr(expr) => match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { MaybeCollapsed::Collapsed(collapsed) => { - collapsed.simplify_minimize(analyzer) + let res = collapsed.simplify_minimize(analyzer)?; + collapsed.set_arenaized_flattened(false, &res, analyzer); + Ok(res) } - _ => expr.simplify_minimize(analyzer), + _ => { + let res = expr.simplify_minimize(analyzer)?; + expr.set_arenaized_flattened(false, res.clone(), analyzer); + Ok(res) + }, }, Null => Ok(Elem::Null), Arena(_) => { let dearenaized = self.dearenaize(analyzer); - let flat = dearenaized.borrow().flatten(true, analyzer)?; - let res = flat.simplify_minimize(analyzer)?; + let flat = dearenaized.borrow().flatten(false, analyzer)?; + let min = flat.simplify_minimize(analyzer)?; match &mut *dearenaized.borrow_mut() { Self::Reference(ref mut d) => { - d.flattened_min = Some(Box::new(res.clone())); + tracing::trace!("simplify minimize cache MISS: {self}"); + d.flattened_min = Some(Box::new(min.clone())); } Self::Expr(ref mut expr) => { - expr.flattened_min = Some(Box::new(res.clone())); + tracing::trace!("simplify minimize cache MISS: {self}"); + expr.flattened_min = Some(Box::new(min.clone())); } - Self::ConcreteDyn(d) => { - d.flattened_min = Some(Box::new(res.clone())); + Self::ConcreteDyn(ref mut d) => { + tracing::trace!("simplify minimize cache MISS: {self}"); + d.flattened_min = Some(Box::new(min.clone())); } _ => {} } - Ok(res) + + Ok(min) } }?; @@ -1030,16 +1261,26 @@ impl RangeElem for Elem { ConcreteDyn(inner) => inner.cache_maximize(analyzer), Expr(expr) => match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { MaybeCollapsed::Collapsed(mut collapsed) => { - collapsed.cache_minimize(analyzer)?; + collapsed.cache_maximize(analyzer)?; + let max = collapsed.maximize(analyzer)?; + self.set_arenaized_flattened(true, &max, analyzer); *self = collapsed; Ok(()) } - _ => expr.cache_maximize(analyzer), + _ => { + expr.cache_maximize(analyzer)?; + let max = expr.maximize(analyzer)?; + self.set_arenaized_flattened(true, &max, analyzer); + Ok(()) + }, }, Null => Ok(()), Arena(_idx) => { let dearenaized = self.dearenaize(analyzer); - dearenaized.borrow_mut().cache_maximize(analyzer)?; + if let Ok(mut t) = dearenaized.try_borrow_mut() { + t.cache_maximize(analyzer)?; + } + Ok(()) } } @@ -1054,15 +1295,25 @@ impl RangeElem for Elem { Expr(expr) => match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { MaybeCollapsed::Collapsed(mut collapsed) => { collapsed.cache_minimize(analyzer)?; + let min = collapsed.minimize(analyzer)?; + self.set_arenaized_flattened(false, &min, analyzer); *self = collapsed; Ok(()) } - _ => expr.cache_minimize(analyzer), + _ => { + expr.cache_minimize(analyzer)?; + let min = expr.minimize(analyzer)?; + self.set_arenaized_flattened(false, &min, analyzer); + Ok(()) + }, }, Null => Ok(()), Arena(_idx) => { let dearenaized = self.dearenaize(analyzer); - dearenaized.borrow_mut().cache_minimize(analyzer)?; + if let Ok(mut t) = dearenaized.try_borrow_mut() { + t.cache_minimize(analyzer)?; + } + Ok(()) } } diff --git a/crates/graph/src/range/elem/elem_trait.rs b/crates/graph/src/range/elem/elem_trait.rs index 1184558a..1ee2bd19 100644 --- a/crates/graph/src/range/elem/elem_trait.rs +++ b/crates/graph/src/range/elem/elem_trait.rs @@ -66,6 +66,13 @@ pub trait RangeElem { seen: &mut Vec, analyzer: &impl GraphBackend, ) -> Result; + + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result; /// Attempts to replace range elements that form a cyclic dependency by replacing /// it with a new node. Ideally no cyclic dependencies occur in ranges as of now /// but in theory it can make sense. diff --git a/crates/graph/src/range/elem/expr.rs b/crates/graph/src/range/elem/expr.rs index 39147265..87c379b6 100644 --- a/crates/graph/src/range/elem/expr.rs +++ b/crates/graph/src/range/elem/expr.rs @@ -111,7 +111,10 @@ impl RangeExpr { pub fn arenaized_cache(&self, max: bool, analyzer: &impl GraphBackend) -> Option> { if let Some(idx) = self.arena_idx(analyzer) { - let Elem::Expr(ref arenaized) = *analyzer.range_arena().ranges[idx].borrow() else { + let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() else { + return None; + }; + let Elem::Expr(ref arenaized) = *t else { return None; }; return if max { @@ -125,7 +128,10 @@ impl RangeExpr { pub fn arenaized_flat_cache(&self, max: bool, analyzer: &impl GraphBackend) -> Option>> { if let Some(idx) = self.arena_idx(analyzer) { - let Elem::Expr(ref arenaized) = *analyzer.range_arena().ranges[idx].borrow() else { + let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() else { + return None; + }; + let Elem::Expr(ref arenaized) = *t else { return None; }; return if max { @@ -182,10 +188,11 @@ impl RangeElem for RangeExpr { // self.lhs.clone().arenaize(analyzer)?; // self.rhs.clone().arenaize(analyzer)?; if self.arena_idx(analyzer).is_none() { - let mut self_copy = self.clone(); - *self_copy.lhs = *Box::new(Elem::Arena(analyzer.range_arena_idx_or_upsert(*self.lhs.clone()))); - *self_copy.rhs = *Box::new(Elem::Arena(analyzer.range_arena_idx_or_upsert(*self.rhs.clone()))); - let _ = analyzer.range_arena_idx_or_upsert(Elem::Expr(self_copy)); + let lhs = std::mem::take(&mut self.lhs); + let rhs = std::mem::take(&mut self.rhs); + self.lhs = Box::new(Elem::Arena(analyzer.range_arena_idx_or_upsert(*lhs))); + self.rhs = Box::new(Elem::Arena(analyzer.range_arena_idx_or_upsert(*rhs))); + let _ = analyzer.range_arena_idx_or_upsert(Elem::Expr(self.clone())); } Ok(()) } @@ -218,12 +225,42 @@ impl RangeElem for RangeExpr { ))) } - fn is_flatten_cached(&self, _analyzer: &impl GraphBackend) -> bool { + fn is_flatten_cached(&self, analyzer: &impl GraphBackend) -> bool { self.flattened_min.is_some() && self.flattened_max.is_some() + || { + if let Some(idx) = self.arena_idx(analyzer) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Expr(ref arenaized) = *t { + arenaized.flattened_min.is_some() && arenaized.flattened_max.is_some() + } else { + false + } + } else { + false + } + } else { + false + } + } } - fn is_min_max_cached(&self, _analyzer: &impl GraphBackend) -> (bool, bool) { - (self.minimized.is_some(), self.maximized.is_some()) + fn is_min_max_cached(&self, analyzer: &impl GraphBackend) -> (bool, bool) { + let (arena_cached_min, arena_cached_max) = { + if let Some(idx) = self.arena_idx(analyzer) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Expr(ref arenaized) = *t { + (arenaized.minimized.is_some(), arenaized.maximized.is_some()) + } else { + (false, false) + } + } else { + (false, false) + } + } else { + (false, false) + } + }; + (self.minimized.is_some() || arena_cached_min, self.maximized.is_some() || arena_cached_max) } fn range_ord( @@ -261,6 +298,17 @@ impl RangeElem for RangeExpr { Ok(lhs_has_cycle || rhs_has_cycle) } + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result { + let lhs_deps_on = self.lhs.depends_on(var, seen, analyzer)?; + let rhs_deps_on = self.rhs.depends_on(var, seen, analyzer)?; + Ok(lhs_deps_on || rhs_deps_on) + } + #[tracing::instrument(level = "trace", skip_all)] fn filter_recursion( &mut self, @@ -268,6 +316,7 @@ impl RangeElem for RangeExpr { new_idx: NodeIdx, analyzer: &mut impl GraphBackend, ) { + let _ = self.arenaize(analyzer); self.lhs.filter_recursion(node_idx, new_idx, analyzer); self.rhs.filter_recursion(node_idx, new_idx, analyzer); } @@ -314,7 +363,7 @@ impl RangeElem for RangeExpr { let l = self.lhs.simplify_maximize(analyzer)?; let r = self.rhs.simplify_maximize(analyzer)?; let collapsed = collapse(&l, self.op, &r, analyzer); - match collapsed { + let res = match collapsed { MaybeCollapsed::Concretes(..) => RangeExpr::new(l, self.op, r).exec_op(true, analyzer), MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), MaybeCollapsed::Not(..) => { @@ -333,7 +382,9 @@ impl RangeElem for RangeExpr { other => Ok(other), } } - } + }?; + self.set_arenaized_flattened(true, res.clone(), analyzer); + Ok(res) } #[tracing::instrument(level = "trace", skip_all)] @@ -350,9 +401,12 @@ impl RangeElem for RangeExpr { } let l = self.lhs.simplify_minimize(analyzer)?; + self.lhs.set_arenaized_flattened(false, &l, analyzer); let r = self.rhs.simplify_minimize(analyzer)?; + self.rhs.set_arenaized_flattened(false, &r, analyzer); + let collapsed = collapse(&l, self.op, &r, analyzer); - match collapsed { + let res = match collapsed { MaybeCollapsed::Concretes(..) => RangeExpr::new(l, self.op, r).exec_op(false, analyzer), MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), MaybeCollapsed::Not(..) => { @@ -370,34 +424,170 @@ impl RangeElem for RangeExpr { other => Ok(other), } } - } + }?; + + self.set_arenaized_flattened(false, res.clone(), analyzer); + Ok(res) } #[tracing::instrument(level = "trace", skip_all)] fn cache_flatten(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { + self.arenaize(g)?; + + fn simplify_minimize( + mut this: Elem, + analyzer: &mut impl GraphBackend, + ) -> Result, GraphError> { + let Elem::Expr(this) = this else { + this.cache_flatten(analyzer)?; + if let Some(t) = this.arenaized_flattened(false, analyzer) { + return Ok(*t) + } else { + return Ok(this.clone()) + } + }; + + if let Some(simp_min) = &this.flattened_min { + return Ok(*simp_min.clone()); + } + + if let Some(arenaized) = this.arenaized_flat_cache(false, analyzer) { + return Ok(*arenaized) + } + + let l = this.lhs.simplify_minimize(analyzer)?; + let r = this.rhs.simplify_minimize(analyzer)?; + let collapsed = collapse(&l, this.op, &r, analyzer); + let res = match collapsed { + MaybeCollapsed::Concretes(..) => RangeExpr::new(l, this.op, r).exec_op(false, analyzer), + MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), + MaybeCollapsed::Not(..) => { + + let res = + RangeExpr::new(l, this.op, r).simplify_exec_op(false, analyzer)?; + + let idx = analyzer.range_arena_idx_or_upsert(res.clone()); + match res { + Elem::Expr(expr) => { + match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { + MaybeCollapsed::Concretes(..) => { + let exec_res = expr.exec_op(false, analyzer)?; + Elem::Arena(idx).set_arenaized_flattened(false, &exec_res, analyzer); + return Ok(exec_res) + }, + MaybeCollapsed::Collapsed(collapsed) => { + Elem::Arena(idx).set_arenaized_flattened(false, &collapsed, analyzer); + return Ok(collapsed) + }, + _ => {} + } + Ok(Elem::Expr(expr)) + } + other => Ok(other), + } + } + }?; + + this.set_arenaized_flattened(false, res.clone(), analyzer); + Ok(res) + } + + fn simplify_maximize( + mut this: Elem, + analyzer: &mut impl GraphBackend, + ) -> Result, GraphError> { + let Elem::Expr(this) = this else { + this.cache_flatten(analyzer)?; + if let Some(t) = this.arenaized_flattened(true, analyzer) { + return Ok(*t) + } else { + return Ok(this.clone()) + } + }; + + if let Some(simp_min) = &this.flattened_max { + return Ok(*simp_min.clone()); + } + + if let Some(arenaized) = this.arenaized_flat_cache(false, analyzer) { + return Ok(*arenaized) + } + + let l = this.lhs.simplify_maximize(analyzer)?; + let r = this.rhs.simplify_maximize(analyzer)?; + let collapsed = collapse(&l, this.op, &r, analyzer); + let res = match collapsed { + MaybeCollapsed::Concretes(..) => RangeExpr::new(l, this.op, r).exec_op(true, analyzer), + MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), + MaybeCollapsed::Not(..) => { + + let res = + RangeExpr::new(l, this.op, r).simplify_exec_op(true, analyzer)?; + + let idx = analyzer.range_arena_idx_or_upsert(res.clone()); + match res { + Elem::Expr(expr) => { + match collapse(&expr.lhs, expr.op, &expr.rhs, analyzer) { + MaybeCollapsed::Concretes(..) => { + let exec_res = expr.exec_op(true, analyzer)?; + Elem::Arena(idx).set_arenaized_flattened(true, &exec_res, analyzer); + return Ok(exec_res) + }, + MaybeCollapsed::Collapsed(collapsed) => { + Elem::Arena(idx).set_arenaized_flattened(true, &collapsed, analyzer); + return Ok(collapsed) + }, + _ => {} + } + Ok(Elem::Expr(expr)) + } + other => Ok(other), + } + } + }?; + + this.set_arenaized_flattened(false, res.clone(), analyzer); + Ok(res) + } + if self.flattened_max.is_none() { + if let Some(idx) = self.arena_idx(g) { + if let Elem::Expr(ref arenaized) = *g.range_arena().ranges[idx].borrow() { + if arenaized.flattened_max.is_some() { + return Ok(()) + } + }; + } else { + self.arenaize(g)?; + } + self.lhs.cache_flatten(g)?; self.rhs.cache_flatten(g)?; // self.arenaize(g)?; let flat_max = self.flatten(true, g)?; - let simplified_flat_max = flat_max.simplify_maximize(g)?; + let simplified_flat_max = simplify_maximize(flat_max, g)?; simplified_flat_max.clone().arenaize(g)?; self.flattened_max = Some(Box::new(simplified_flat_max)); - self.arenaize(g)?; - // let mut s = Elem::Expr(self.clone()); - // s.arenaize(g)?; } + if self.flattened_min.is_none() { + if let Some(idx) = self.arena_idx(g) { + if let Elem::Expr(ref arenaized) = *g.range_arena().ranges[idx].borrow() { + if arenaized.flattened_min.is_some() { + return Ok(()) + } + }; + } else { + self.arenaize(g)?; + } + self.lhs.cache_flatten(g)?; self.rhs.cache_flatten(g)?; // self.arenaize(g)?; let flat_min = self.flatten(false, g)?; - let simplified_flat_min = flat_min.simplify_minimize(g)?; + let simplified_flat_min = simplify_minimize(flat_min, g)?; simplified_flat_min.clone().arenaize(g)?; self.flattened_min = Some(Box::new(simplified_flat_min)); - self.arenaize(g)?; - // let mut s = Elem::Expr(self.clone()); - // s.arenaize(g)?; } Ok(()) } @@ -405,11 +595,11 @@ impl RangeElem for RangeExpr { #[tracing::instrument(level = "trace", skip_all)] fn cache_maximize(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { tracing::trace!("cache maximizing: {}", Elem::Expr(self.clone())); + self.arenaize(g)?; if self.maximized.is_none() { self.lhs.cache_maximize(g)?; self.rhs.cache_maximize(g)?; self.cache_exec_op(true, g)?; - self.arenaize(g)?; } Ok(()) } @@ -417,11 +607,11 @@ impl RangeElem for RangeExpr { #[tracing::instrument(level = "trace", skip_all)] fn cache_minimize(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { tracing::trace!("cache minimizing: {}", Elem::Expr(self.clone())); + self.arenaize(g)?; if self.minimized.is_none() { self.lhs.cache_minimize(g)?; self.rhs.cache_minimize(g)?; self.cache_exec_op(false, g)?; - self.arenaize(g)?; } Ok(()) } @@ -447,17 +637,25 @@ pub fn collapse<'a, 'b, 'c: 'a + 'b>( let one = Elem::from(Concrete::from(U256::one())); match (l, r) { (Elem::Arena(_), r) => { - match collapse(&l.dearenaize(analyzer).borrow().clone(), op, r, analyzer) { - MaybeCollapsed::Not(..) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(..) => MaybeCollapsed::Concretes(l, r), - MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e) + if let Ok(t) = l.dearenaize(analyzer).try_borrow() { + match collapse(&t, op, r, analyzer) { + MaybeCollapsed::Not(..) => MaybeCollapsed::Not(l, r), + MaybeCollapsed::Concretes(..) => MaybeCollapsed::Concretes(l, r), + MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e) + } + } else { + MaybeCollapsed::Not(l, r) } }, (l, Elem::Arena(_)) => { - match collapse(l, op, &r.dearenaize(analyzer).borrow().clone(), analyzer) { - MaybeCollapsed::Not(..) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(..) => MaybeCollapsed::Concretes(l, r), - MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e) + if let Ok(t) = r.dearenaize(analyzer).try_borrow() { + match collapse(l, op, &t, analyzer) { + MaybeCollapsed::Not(..) => MaybeCollapsed::Not(l, r), + MaybeCollapsed::Concretes(..) => MaybeCollapsed::Concretes(l, r), + MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e) + } + } else { + MaybeCollapsed::Not(l, r) } }, (Elem::Concrete(_), Elem::Concrete(_)) => MaybeCollapsed::Concretes(l, r), diff --git a/crates/graph/src/range/elem/map_or_array.rs b/crates/graph/src/range/elem/map_or_array.rs index 5e66ea8e..bcd350ea 100644 --- a/crates/graph/src/range/elem/map_or_array.rs +++ b/crates/graph/src/range/elem/map_or_array.rs @@ -290,6 +290,21 @@ impl RangeElem for RangeDyn { Ok(has_cycle) } + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result { + let mut deps_on = false; + deps_on |= self.len.depends_on(var, seen, analyzer)?; + self.val.iter().try_for_each(|(_, val)| { + deps_on |= val.0.depends_on(var, seen, analyzer)?; + Ok(()) + })?; + Ok(deps_on) + } + fn flatten( &self, maximize: bool, diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index 87683672..19ac5c37 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -18,6 +18,22 @@ pub enum MinMaxed { Maximized(Box>), } +impl MinMaxed { + pub fn maxed(self) -> Elem { + match self { + Self::Maximized(t) => *t, + _ => panic!("MinMaxed was min not max") + } + } + + pub fn mined(self) -> Elem { + match self { + Self::Minimized(t) => *t, + _ => panic!("MinMaxed was max not min") + } + } +} + /// An operation to be performed on a range element #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RangeOp { diff --git a/crates/graph/src/range/elem/reference.rs b/crates/graph/src/range/elem/reference.rs index 910b1926..2a3940c2 100644 --- a/crates/graph/src/range/elem/reference.rs +++ b/crates/graph/src/range/elem/reference.rs @@ -59,10 +59,11 @@ impl Reference { impl RangeElem for Reference { type GraphError = GraphError; - fn arenaize(&mut self, _analyzer: &mut impl GraphBackend) -> Result<(), GraphError> { - // self.cache_flatten(analyzer)?; - // self.cache_minimize(analyzer)?; - // self.cache_maximize(analyzer)?; + fn arenaize(&mut self, analyzer: &mut impl GraphBackend) -> Result<(), GraphError> { + let smol = Elem::Reference(Reference::new(self.idx)); + if analyzer.range_arena_idx(&smol).is_none() { + let _ = analyzer.range_arena_idx_or_upsert(Elem::Reference(self.clone())); + } Ok(()) } @@ -112,6 +113,30 @@ impl RangeElem for Reference { } } + fn depends_on( + &self, + var: ContextVarNode, + seen: &mut Vec, + analyzer: &impl GraphBackend, + ) -> Result { + let cvar = ContextVarNode::from(self.idx); + if seen.contains(&cvar) { + return Ok(false) + } + + if cvar == var + || cvar.name(analyzer)? == var.name(analyzer)? && self.idx >= var.0.into() { + Ok(true) + } else if let Some(range) = cvar.ref_range(analyzer)? { + seen.push(cvar); + let mut deps_on = range.min.depends_on(var, seen, analyzer)?; + deps_on |= range.max.depends_on(var, seen, analyzer)?; + Ok(deps_on) + } else { + Ok(false) + } + } + fn flatten( &self, maximize: bool, @@ -143,38 +168,81 @@ impl RangeElem for Reference { } } - fn is_flatten_cached(&self, _analyzer: &impl GraphBackend) -> bool { + fn is_flatten_cached(&self, analyzer: &impl GraphBackend) -> bool { self.flattened_min.is_some() && self.flattened_max.is_some() + || { + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + arenaized.flattened_min.is_some() && arenaized.flattened_max.is_some() + } else { + false + } + } else { + false + } + } else { + false + } + } } - fn is_min_max_cached(&self, _analyzer: &impl GraphBackend) -> (bool, bool) { - (self.minimized.is_some(), self.maximized.is_some()) + fn is_min_max_cached(&self, analyzer: &impl GraphBackend) -> (bool, bool) { + let (arena_cached_min, arena_cached_max) = { + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + (arenaized.minimized.is_some(), arenaized.maximized.is_some()) + } else { + (false, false) + } + } else { + (false, false) + } + } else { + (false, false) + } + }; + (self.minimized.is_some() || arena_cached_min, self.maximized.is_some() || arena_cached_max) } fn cache_flatten(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { - if let Some(idx) = g.range_arena_idx(&Elem::Reference(self.clone())) { - if Elem::Arena(idx).is_flatten_cached(g) { - return Ok(()) - } - } + self.arenaize(g)?; if self.flattened_max.is_none() { + if let Some(idx) = g.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = g.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + if arenaized.flattened_max.is_some() { + tracing::trace!("reference cache flatten hit"); + return Ok(()) + } + } + } + } + let cvar = ContextVarNode::from(self.idx); cvar.cache_flattened_range(g)?; let flat_max = self.flatten(true, g)?; let simplified_flat_max = flat_max.simplify_maximize(g)?; self.flattened_max = Some(Box::new(simplified_flat_max)); - let mut s = Elem::Reference(self.clone()); - s.arenaize(g)?; } if self.flattened_min.is_none() { + if let Some(idx) = g.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = g.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + if arenaized.flattened_min.is_some() { + tracing::trace!("reference cache flatten hit"); + return Ok(()) + } + } + } + } let cvar = ContextVarNode::from(self.idx); cvar.cache_flattened_range(g)?; let flat_min = self.flatten(false, g)?; let simplified_flat_min = flat_min.simplify_minimize(g)?; self.flattened_min = Some(Box::new(simplified_flat_min)); - let mut s = Elem::Reference(self.clone()); - s.arenaize(g)?; } Ok(()) } @@ -186,6 +254,17 @@ impl RangeElem for Reference { return Ok(*cached); } + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + tracing::trace!("reference maximize cache hit"); + if let Some(MinMaxed::Maximized(cached)) = arenaized.maximized.clone() { + return Ok(*cached); + } + } + } + } + let cvar = ContextVarNode::from(self.idx).underlying(analyzer)?; match &cvar.ty { VarType::User(TypeNode::Contract(_), maybe_range) @@ -211,6 +290,17 @@ impl RangeElem for Reference { return Ok(*cached); } + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + if let Some(MinMaxed::Minimized(cached)) = arenaized.minimized.clone() { + tracing::trace!("reference minimize cache hit"); + return Ok(*cached); + } + } + } + } + let cvar = ContextVarNode::from(self.idx).underlying(analyzer)?; match &cvar.ty { VarType::User(TypeNode::Contract(_), maybe_range) @@ -239,6 +329,17 @@ impl RangeElem for Reference { return Ok(*simp_max.clone()); } + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + if arenaized.flattened_max.is_some() { + tracing::trace!("reference simplify maximize cache hit"); + return Ok(*arenaized.flattened_max.clone().unwrap()) + } + } + } + } + let cvar = ContextVarNode::from(self.idx); let independent = cvar.is_fundamental(analyzer)?; @@ -260,6 +361,17 @@ impl RangeElem for Reference { return Ok(*simp_min.clone()); } + if let Some(idx) = analyzer.range_arena_idx(&Elem::Reference(Reference::new(self.idx))) { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Reference(ref arenaized) = *t { + if arenaized.flattened_min.is_some() { + tracing::trace!("reference simplify minimize cache hit"); + return Ok(*arenaized.flattened_min.clone().unwrap()) + } + } + } + } + let cvar = ContextVarNode::from(self.idx); if cvar.is_fundamental(analyzer)? { Ok(Elem::Reference(Reference::new( @@ -272,24 +384,27 @@ impl RangeElem for Reference { } fn cache_maximize(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { + self.arenaize(g)?; if self.maximized.is_none() { let cvar = ContextVarNode::from(self.idx); cvar.cache_eval_range(g)?; - self.maximized = Some(MinMaxed::Maximized(Box::new(self.maximize(g)?))); - let mut s = Elem::Reference(self.clone()); - s.arenaize(g)?; + let max = self.maximize(g)?; + Elem::Reference(Reference::new(self.idx)).set_arenaized_cache(true, &max, g); + self.maximized = Some(MinMaxed::Maximized(Box::new(max))); } Ok(()) } fn cache_minimize(&mut self, g: &mut impl GraphBackend) -> Result<(), GraphError> { + self.arenaize(g)?; if self.minimized.is_none() { let cvar = ContextVarNode::from(self.idx); cvar.cache_eval_range(g)?; - self.minimized = Some(MinMaxed::Minimized(Box::new(self.minimize(g)?))); - let mut s = Elem::Reference(self.clone()); - s.arenaize(g)?; + let min = self.minimize(g)?; + Elem::Reference(Reference::new(self.idx)).set_arenaized_cache(false, &min, g); + self.minimized = Some(MinMaxed::Minimized(Box::new(min))); } + Ok(()) } diff --git a/crates/graph/src/range/exec/exec_op.rs b/crates/graph/src/range/exec/exec_op.rs index b8df6666..23911e4c 100644 --- a/crates/graph/src/range/exec/exec_op.rs +++ b/crates/graph/src/range/exec/exec_op.rs @@ -17,8 +17,24 @@ impl ExecOp for RangeExpr { maximize: bool, analyzer: &impl GraphBackend, ) -> Result, Self::GraphError> { + let idx = self.arena_idx(analyzer); + if let Some(idx) = idx { + if let Ok(t) = analyzer.range_arena().ranges[idx].try_borrow() { + if let Elem::Expr(expr) = &*t { + if maximize { + if let Some(MinMaxed::Maximized(max)) = &expr.maximized { + return Ok(*max.clone()) + } + } else if let Some(MinMaxed::Minimized(min)) = &expr.minimized { + return Ok(*min.clone()) + } + } + } + } + let res = self.exec(self.spread(analyzer)?, maximize, analyzer)?; - if let Some(idx) = self.arena_idx(analyzer) { + + if let Some(idx) = idx { if let Ok(mut t) = analyzer.range_arena().ranges[idx].try_borrow_mut() { if let Elem::Expr(expr) = &mut *t { if maximize { @@ -77,15 +93,15 @@ impl ExecOp for RangeExpr { analyzer: &impl GraphBackend, ) -> Result, GraphError> { if maximize { - if let Some(v) = self.arenaized_flat_cache(maximize, analyzer) { - return Ok(*v) + if let Some(v) = &self.flattened_max { + return Ok(*v.clone()); } + } else if let Some(v) = &self.flattened_min { + return Ok(*v.clone()); } - if !maximize { - if let Some(v) = self.arenaized_flat_cache(maximize, analyzer) { - return Ok(*v) - } + if let Some(v) = self.arenaized_flat_cache(maximize, analyzer) { + return Ok(*v) } let (lhs_min, lhs_max, rhs_min, rhs_max) = self.simplify_spread(analyzer)?; @@ -141,7 +157,7 @@ impl ExecOp for RangeExpr { }; finished = true; } - RangeOp::SetLength => { + RangeOp::SetLength => { ret = if maximize { Ok(lhs_max .range_set_length(&rhs_max) @@ -266,9 +282,17 @@ impl ExecOp for RangeExpr { GraphError, > { let lhs_min = self.lhs.minimize(analyzer)?; + self.lhs.set_arenaized_cache(false, &lhs_min, analyzer); + let lhs_max = self.lhs.maximize(analyzer)?; + self.lhs.set_arenaized_cache(true, &lhs_max, analyzer); + let rhs_min = self.rhs.minimize(analyzer)?; + self.rhs.set_arenaized_cache(false, &rhs_min, analyzer); + let rhs_max = self.rhs.maximize(analyzer)?; + self.rhs.set_arenaized_cache(true, &rhs_max, analyzer); + Ok((lhs_min, lhs_max, rhs_min, rhs_max)) } @@ -285,9 +309,17 @@ impl ExecOp for RangeExpr { GraphError, > { let lhs_min = self.lhs.simplify_minimize(analyzer)?; + self.lhs.set_arenaized_flattened(false, &lhs_min, analyzer); + let lhs_max = self.lhs.simplify_maximize(analyzer)?; + self.lhs.set_arenaized_flattened(true, &lhs_max, analyzer); + let rhs_min = self.rhs.simplify_minimize(analyzer)?; + self.rhs.set_arenaized_flattened(false, &rhs_min, analyzer); + let rhs_max = self.rhs.simplify_maximize(analyzer)?; + self.rhs.set_arenaized_flattened(true, &rhs_max, analyzer); + Ok((lhs_min, lhs_max, rhs_min, rhs_max)) } @@ -363,7 +395,7 @@ impl ExecOp for RangeExpr { RangeOp::GetLength => { if maximize { let new = lhs_max.clone(); - let new_max = new.simplify_minimize(analyzer)?; + let new_max = new.simplify_maximize(analyzer)?; let res = new_max.range_get_length(); res.unwrap_or_else(|| fallback(self, lhs_min, rhs_min, consts)) } else { diff --git a/crates/graph/src/range/range_trait.rs b/crates/graph/src/range/range_trait.rs index adbb1520..4a5f0853 100644 --- a/crates/graph/src/range/range_trait.rs +++ b/crates/graph/src/range/range_trait.rs @@ -1,3 +1,4 @@ +use crate::FlattenedRange; use crate::{range::elem::RangeElem, GraphBackend}; use shared::NodeIdx; use std::borrow::Cow; @@ -52,11 +53,11 @@ pub trait Range { /// Set the range maximum fn set_range_max(&mut self, new: Self::ElemTy); /// Set the range exclusions - fn set_range_exclusions(&mut self, new: Vec) + fn set_range_exclusions(&mut self, new: Vec) where Self: std::marker::Sized; /// Add an exclusion value to the range - fn add_range_exclusion(&mut self, new: Self::ElemTy) + fn add_range_exclusion(&mut self, new: usize) where Self: std::marker::Sized; /// Replace a potential recursion causing node index with a new index @@ -79,19 +80,19 @@ pub trait Range { fn flattened_range<'a>( &'a mut self, analyzer: &mut impl GraphBackend, - ) -> Result, Self::GraphError> + ) -> Result, Self::GraphError> where Self: Sized + Clone; fn take_flattened_range( &mut self, analyzer: &mut impl GraphBackend, - ) -> Result + ) -> Result where Self: Sized; } -pub trait RangeEval>: Range { +pub trait RangeEval> { fn sat(&self, analyzer: &impl GraphBackend) -> bool; fn unsat(&self, analyzer: &impl GraphBackend) -> bool { !self.sat(analyzer) diff --git a/crates/graph/src/range/solc_range.rs b/crates/graph/src/range/solc_range.rs index 3e24b0d0..53c3b5a6 100644 --- a/crates/graph/src/range/solc_range.rs +++ b/crates/graph/src/range/solc_range.rs @@ -12,14 +12,28 @@ use tracing::instrument; use std::{borrow::Cow, collections::BTreeMap}; +#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct FlattenedRange { + pub min: usize, + pub max: usize, + pub exclusions: Vec, +} + +impl From for SolcRange { + fn from(range: FlattenedRange) -> Self { + SolcRange::new(Elem::Arena(range.min), Elem::Arena(range.max), range.exclusions) + } +} + + #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct SolcRange { pub min: Elem, - pub min_cached: Option>, + pub min_cached: Option, pub max: Elem, - pub max_cached: Option>, - pub exclusions: Vec>, - pub flattened: Option>, + pub max_cached: Option, + pub exclusions: Vec, + pub flattened: Option, } impl AsDotStr for SolcRange { @@ -36,7 +50,7 @@ impl AsDotStr for SolcRange { .s, self.exclusions .iter() - .map(|excl| excl.to_range_string(false, analyzer).s) + .map(|excl| Elem::Arena(*excl).to_range_string(false, analyzer).s) .collect::>() .join(", ") ) @@ -80,7 +94,7 @@ impl SolcRange { Ok(deps) } - pub fn new(min: Elem, max: Elem, exclusions: Vec>) -> Self { + pub fn new(min: Elem, max: Elem, exclusions: Vec) -> Self { Self { min, min_cached: None, @@ -98,11 +112,9 @@ impl SolcRange { analyzer: &mut impl GraphBackend, ) { if let Some(ref mut flattened) = &mut self.flattened { - flattened - .min + Elem::Arena(flattened.min) .replace_dep(to_replace, replacement.clone(), analyzer); - flattened - .max + Elem::Arena(flattened.max) .replace_dep(to_replace, replacement.clone(), analyzer); } self.min @@ -541,17 +553,26 @@ impl SolcRange { Self::new(min.clone().max(max.clone()), min.max(max), self.exclusions) } - pub fn into_flattened_range(&mut self, analyzer: &mut impl GraphBackend) -> Result { + pub fn into_flattened_range(&mut self, analyzer: &mut impl GraphBackend) -> Result { if let Some(cached) = &self.flattened { - return Ok(*cached.clone()); + return Ok(cached.clone()); } - self.min.cache_flatten(analyzer)?; - self.max.cache_flatten(analyzer)?; - let simp_min = self.min.simplify_minimize(analyzer)?; - let simp_max = self.max.simplify_maximize(analyzer)?; - let flat_range = SolcRange::new(simp_min, simp_max, self.exclusions.clone()); - self.flattened = Some(Box::new(flat_range.clone())); + let mut min = Elem::Arena(analyzer.range_arena_idx_or_upsert(self.min.clone())); + let mut max = Elem::Arena(analyzer.range_arena_idx_or_upsert(self.max.clone())); + min.cache_flatten(analyzer)?; + max.cache_flatten(analyzer)?; + + self.min = min.clone(); + self.max = max.clone(); + + let simp_min = min.simplify_minimize(analyzer)?; + let simp_max = max.simplify_maximize(analyzer)?; + let min = analyzer.range_arena_idx_or_upsert(simp_min); + let max = analyzer.range_arena_idx_or_upsert(simp_max); + + let flat_range = FlattenedRange { min, max, exclusions: self.exclusions.clone() }; + self.flattened = Some(flat_range.clone()); Ok(flat_range) } @@ -574,24 +595,26 @@ impl Range for SolcRange { } fn cache_eval(&mut self, analyzer: &mut impl GraphBackend) -> Result<(), GraphError> { - self.min.arenaize(analyzer)?; - self.max.arenaize(analyzer)?; + let min = std::mem::take(&mut self.min); + let max = std::mem::take(&mut self.max); + self.min = Elem::Arena(analyzer.range_arena_idx_or_upsert(min)); + self.max = Elem::Arena(analyzer.range_arena_idx_or_upsert(max)); if self.max_cached.is_none() { let max = self.range_max_mut(); max.cache_maximize(analyzer)?; - self.max_cached = Some(self.range_max().maximize(analyzer)?); + self.max_cached = Some(analyzer.range_arena_idx_or_upsert(self.range_max().maximize(analyzer)?)); } if self.min_cached.is_none() { let min = self.range_min_mut(); min.cache_minimize(analyzer)?; - self.min_cached = Some(self.range_min().minimize(analyzer)?); + self.min_cached = Some(analyzer.range_arena_idx_or_upsert(self.range_min().minimize(analyzer)?)); } Ok(()) } fn evaled_range_min(&self, analyzer: &impl GraphBackend) -> Result { if let Some(cached) = &self.min_cached { - Ok(cached.clone()) + Ok(Elem::Arena(*cached).dearenaize(analyzer).borrow().clone()) } else { self.range_min().minimize(analyzer) } @@ -599,7 +622,7 @@ impl Range for SolcRange { fn evaled_range_max(&self, analyzer: &impl GraphBackend) -> Result { if let Some(cached) = &self.max_cached { - Ok(cached.clone()) + Ok(Elem::Arena(*cached).dearenaize(analyzer).borrow().clone()) } else { self.range_max().maximize(analyzer) } @@ -623,7 +646,7 @@ impl Range for SolcRange { } fn range_exclusions(&self) -> Vec { - self.exclusions.clone() + self.exclusions.clone().into_iter().map(Elem::Arena).collect() } fn set_range_min(&mut self, new: Self::ElemTy) { self.min_cached = None; @@ -636,12 +659,12 @@ impl Range for SolcRange { self.max = new; } - fn add_range_exclusion(&mut self, new: Self::ElemTy) { + fn add_range_exclusion(&mut self, new: usize) { if !self.exclusions.contains(&new) { self.exclusions.push(new); } } - fn set_range_exclusions(&mut self, new: Vec) { + fn set_range_exclusions(&mut self, new: Vec) { self.exclusions = new; } fn filter_min_recursion( @@ -671,7 +694,7 @@ impl Range for SolcRange { fn flattened_range<'a>( &'a mut self, analyzer: &mut impl GraphBackend, - ) -> Result, Self::GraphError> + ) -> Result, Self::GraphError> where Self: Sized + Clone, { @@ -692,13 +715,13 @@ impl Range for SolcRange { fn take_flattened_range( &mut self, analyzer: &mut impl GraphBackend, - ) -> Result + ) -> Result where Self: Sized, { let taken = std::mem::take(&mut self.flattened); if let Some(flat) = taken { - Ok(*flat) + Ok(flat) } else { self.cache_flatten(analyzer)?; self.take_flattened_range(analyzer) @@ -779,3 +802,23 @@ impl RangeEval> for SolcRange { } } } + +impl RangeEval> for FlattenedRange { + fn sat(&self, analyzer: &impl GraphBackend) -> bool { + >::into(self.clone()).sat(analyzer) + } + fn unsat(&self, analyzer: &impl GraphBackend) -> bool { + !self.sat(analyzer) + } + fn contains(&self, other: &Self, analyzer: &impl GraphBackend) -> bool { + let other = >::into(other.clone()); + >::into(self.clone()).contains(&other, analyzer) + } + fn contains_elem(&self, other: &Elem, analyzer: &impl GraphBackend) -> bool { + >::into(self.clone()).contains_elem(other, analyzer) + } + fn overlaps(&self, other: &Self, analyzer: &impl GraphBackend) -> bool { + let other = >::into(other.clone()); + >::into(self.clone()).overlaps(&other, analyzer) + } +} diff --git a/crates/graph/src/solvers/atoms.rs b/crates/graph/src/solvers/atoms.rs index 76153ee3..8a47b1c1 100644 --- a/crates/graph/src/solvers/atoms.rs +++ b/crates/graph/src/solvers/atoms.rs @@ -238,13 +238,13 @@ pub static LIA_OPS: &[RangeOp] = &[ ]; pub trait Atomize { - fn atoms_or_part(&self, analyzer: &impl GraphBackend) -> AtomOrPart; - fn atomize(&self, analyzer: &impl GraphBackend) -> Option; + fn atoms_or_part(&self, analyzer: &mut impl GraphBackend) -> AtomOrPart; + fn atomize(&self, analyzer: &mut impl GraphBackend) -> Option; } impl Atomize for Elem { #[tracing::instrument(level = "trace", skip_all)] - fn atoms_or_part(&self, analyzer: &impl GraphBackend) -> AtomOrPart { + fn atoms_or_part(&self, analyzer: &mut impl GraphBackend) -> AtomOrPart { match self { Elem::Arena(_) => self.dearenaize(analyzer).borrow().atoms_or_part(analyzer), Elem::Concrete(_) | Elem::Reference(_) => AtomOrPart::Part(self.clone()), @@ -305,16 +305,10 @@ impl Atomize for Elem { todo!("here4"); } (Elem::Concrete(_), Elem::Concrete(_)) => { + expr.clone().arenaize(analyzer); let res = expr .exec_op(true, analyzer) .unwrap(); - let ty = if DL_OPS.contains(&expr.op) { - OpType::DL - } else if CONST_OPS.contains(&expr.op) { - OpType::Const - } else { - OpType::Other - }; if res == Elem::Expr(expr.clone()) { AtomOrPart::Part(res) } else { @@ -343,7 +337,7 @@ impl Atomize for Elem { } #[tracing::instrument(level = "trace", skip_all)] - fn atomize(&self, analyzer: &impl GraphBackend) -> Option { + fn atomize(&self, analyzer: &mut impl GraphBackend) -> Option { use Elem::*; match self { @@ -359,7 +353,18 @@ impl Atomize for Elem { a.update_max_ty(); Some(a) } - Arena(_) => todo!(), + Arena(_) => { + match &*self.dearenaize(analyzer).borrow() { + e @ Expr(_) => { + let AtomOrPart::Atom(mut a) = e.atoms_or_part(analyzer) else { + return None; + }; + a.update_max_ty(); + Some(a) + } + _ => None + } + } } } } diff --git a/crates/graph/src/solvers/brute.rs b/crates/graph/src/solvers/brute.rs index d4969600..fa6ed146 100644 --- a/crates/graph/src/solvers/brute.rs +++ b/crates/graph/src/solvers/brute.rs @@ -97,9 +97,9 @@ impl BruteBinSearchSolver { dep.display_name(analyzer).unwrap() ); } - let r = range.flattened_range(analyzer)?; + let r: SolcRange = range.flattened_range(analyzer)?.into_owned().into(); atomic_idxs.extend(r.dependent_on(analyzer)); - ranges.insert(*dep, r.into_owned()); + ranges.insert(*dep, r); Ok(()) })?; diff --git a/crates/graph/src/solvers/dl.rs b/crates/graph/src/solvers/dl.rs index e9b5a67e..34bda3e2 100644 --- a/crates/graph/src/solvers/dl.rs +++ b/crates/graph/src/solvers/dl.rs @@ -60,7 +60,7 @@ pub struct DLSolveResult { } impl DLSolver { - pub fn new(mut constraints: Vec, analyzer: &impl GraphBackend) -> Self { + pub fn new(mut constraints: Vec, analyzer: &mut impl GraphBackend) -> Self { constraints.iter_mut().for_each(|c| { c.update_max_ty(); }); @@ -97,7 +97,7 @@ impl DLSolver { pub fn add_constraints( &mut self, constraints: Vec, - analyzer: &impl GraphBackend, + analyzer: &mut impl GraphBackend, ) -> BTreeMap>> { let mut dep_to_solve_ty: BTreeMap> = BTreeMap::default(); self.constraints.iter().for_each(|constraint| { @@ -190,13 +190,14 @@ impl DLSolver { .all(|constraint| constraint.ty == OpType::DL) }) }) + .collect::>() + .iter() .map(|constraint| { ( constraint.clone(), - Self::dl_atom_normalize(constraint, analyzer), + Self::dl_atom_normalize(constraint.clone(), analyzer), ) - }) - .collect::>>>() + }).collect::>>>() } pub fn dl_solvable_constraints(&self) -> Vec>> { @@ -515,7 +516,7 @@ impl DLSolver { /// Needed for running negative cycle check. Additionally, if we have an `OR`, we pub fn dl_atom_normalize( constraint: SolverAtom, - analyzer: &impl GraphBackend, + analyzer: &mut impl GraphBackend, ) -> Vec> { // println!("normalizing: {}", constraint.into_expr_elem()); let zero_part = AtomOrPart::Part(Elem::from(Concrete::from(U256::zero()))); diff --git a/crates/pyrometer/src/graph_backend.rs b/crates/pyrometer/src/graph_backend.rs index 0dcdd572..158380cd 100644 --- a/crates/pyrometer/src/graph_backend.rs +++ b/crates/pyrometer/src/graph_backend.rs @@ -42,7 +42,15 @@ impl GraphLike for Analyzer { &mut self.range_arena } - fn range_arena_idx_or_upsert(&mut self, mut elem: Self::RangeElem) -> usize { + fn range_arena_idx(&self, elem: &Self::RangeElem) -> Option { + if let Elem::Arena(idx) = elem { + Some(*idx) + } else { + self.range_arena().map.get(elem).copied() + } + } + + fn range_arena_idx_or_upsert(&mut self, elem: Self::RangeElem) -> usize { // tracing::trace!("arenaizing: {}", elem); if let Elem::Arena(idx) = elem { return idx; @@ -50,7 +58,7 @@ impl GraphLike for Analyzer { if let Some(idx) = self.range_arena_idx(&elem) { let existing = &self.range_arena().ranges[idx]; - let Ok(existing) = existing.try_borrow() else { + let Ok(existing) = existing.try_borrow_mut() else { return idx; }; let (min_cached, max_cached) = existing.is_min_max_cached(self); @@ -664,6 +672,10 @@ impl GraphLike for G<'_> { panic!("Should not call this") } + fn range_arena_idx(&self, elem: &Self::RangeElem) -> Option { + panic!("Should not call this") + } + fn range_arena_idx_or_upsert(&mut self, _elem: Self::RangeElem) -> usize { panic!("Should not call this") } diff --git a/crates/pyrometer/tests/test_data/math.sol b/crates/pyrometer/tests/test_data/math.sol index 1dc0e7cf..963f87c6 100644 --- a/crates/pyrometer/tests/test_data/math.sol +++ b/crates/pyrometer/tests/test_data/math.sol @@ -1,493 +1,493 @@ -// contract Div { -// function div(uint256 x, uint256 y) public pure returns (uint256) { -// return x / y; -// } - -// function int_div(int256 x, int256 y) public pure returns (int256) { -// return x / y; -// } - -// function div_conc() public pure returns (uint256) { -// uint256 a1 = div(100, 1); -// require(a1 == 100); -// uint256 a2 = div(100, 2); -// require(a2 == 50); -// uint256 a3 = div(100, 4); -// require(a3 == 25); -// uint256 a4 = div(100, 8); -// require(a4 == 12); -// uint256 a5 = div(1000000000, 8); -// require(a5 == 125000000); -// uint256 a6 = div(1000000000, 16); -// require(a6 == 62500000); -// uint256 a7 = div(10000000000, 32); -// require(a7 == 312500000); -// uint256 a8 = div(100000000000000000000, 64); -// require(a8 == 1562500000000000000); -// uint256 a9 = div(100000000000000000000000000000000000, 128); -// require(a9 == 781250000000000000000000000000000); -// uint256 a10 = div(1, 255); -// require(a10 == 0); -// } - -// function int_div_conc() public pure { -// int256 a1 = int_div(100, 1); -// require(a1 == 100); -// int256 a2 = int_div(100, 2); -// require(a2 == 50); -// int256 a3 = int_div(100, 4); -// require(a3 == 25); -// int256 a4 = int_div(100, 8); -// require(a4 == 12); -// int256 a5 = int_div(1000000000, 8); -// require(a5 == 125000000); -// int256 a6 = int_div(1000000000, 16); -// require(a6 == 62500000); -// int256 a7 = int_div(10000000000, 32); -// require(a7 == 312500000); -// int256 a8 = int_div(100000000000000000000, 64); -// require(a8 == 1562500000000000000); -// int256 a9 = int_div(100000000000000000000000000000000000, 128); -// require(a9 == 781250000000000000000000000000000); -// int256 a10 = int_div(1, 255); -// require(a10 == 0); - -// int256 a11 = int_div(-100, 1); -// require(a11 == -100); -// int256 a12 = int_div(-100, 2); -// require(a12 == -50); -// int256 a13 = int_div(-100, 4); -// require(a13 == -25); -// int256 a14 = int_div(-100, 8); -// require(a14 == -12); -// int256 a15 = int_div(-1000000000, 8); -// require(a15 == -125000000); -// int256 a16 = int_div(-1000000000, 16); -// require(a16 == -62500000); -// int256 a17 = int_div(-10000000000, 32); -// require(a17 == -312500000); -// int256 a18 = int_div(-100000000000000000000, 64); -// require(a18 == -1562500000000000000); -// int256 a19 = int_div(-100000000000000000000000000000000000, 128); -// require(a19 == -781250000000000000000000000000000); -// int256 a20 = int_div(-1, 255); -// require(a20 == 0); - -// int256 a21 = int_div(-100, -1); -// require(a21 == 100); -// int256 a22 = int_div(-100, -2); -// require(a22 == 50); -// int256 a23 = int_div(-100, -4); -// require(a23 == 25); -// int256 a24 = int_div(-100, -8); -// require(a24 == 12); -// int256 a25 = int_div(-1000000000, -8); -// require(a25 == 125000000); -// int256 a26 = int_div(-1000000000, -16); -// require(a26 == 62500000); -// int256 a27 = int_div(-10000000000, -32); -// require(a27 == 312500000); -// int256 a28 = int_div(-100000000000000000000, -64); -// require(a28 == 1562500000000000000); -// int256 a29 = int_div(-100000000000000000000000000000000000, -128); -// require(a29 == 781250000000000000000000000000000); -// int256 a30 = int_div(-1, -255); -// require(a30 == 0); - -// int256 a31 = int_div(100, -1); -// require(a31 == -100); -// int256 a32 = int_div(100, -2); -// require(a32 == -50); -// int256 a33 = int_div(100, -4); -// require(a33 == -25); -// int256 a34 = int_div(100, -8); -// require(a34 == -12); -// int256 a35 = int_div(1000000000, -8); -// require(a35 == -125000000); -// int256 a36 = int_div(1000000000, -16); -// require(a36 == -62500000); -// int256 a37 = int_div(10000000000, -32); -// require(a37 == -312500000); -// int256 a38 = int_div(100000000000000000000, -64); -// require(a38 == -1562500000000000000); -// int256 a39 = int_div(100000000000000000000000000000000000, -128); -// require(a39 == -781250000000000000000000000000000); -// int256 a40 = int_div(1, -255); -// require(a40 == 0); -// } -// } - -// contract Mul { -// function mul(uint256 x, uint256 y) public pure returns (uint256) { -// return x * y; -// } - -// function int_mul(int256 x, int256 y) public pure returns (int256) { -// return x * y; -// } - -// function mul_conc() public pure returns (uint256) { -// uint256 a1 = mul(100, 1); -// require(a1 == 100); -// uint256 a2 = mul(100, 2); -// require(a2 == 200); -// uint256 a3 = mul(100, 4); -// require(a3 == 400); -// uint256 a4 = mul(100, 8); -// require(a4 == 800); -// uint256 a5 = mul(1000000000, 8); -// require(a5 == 8000000000); -// uint256 a6 = mul(1000000000, 16); -// require(a6 == 16000000000); -// uint256 a7 = mul(10000000000, 32); -// require(a7 == 320000000000); -// uint256 a8 = mul(100000000000000000000, 64); -// require(a8 == 6400000000000000000000); -// uint256 a9 = mul(100000000000000000000000000000000000, 128); -// require(a9 == 12800000000000000000000000000000000000); -// uint256 a10 = mul(1, 255); -// require(a10 == 255); -// } - -// function int_mul_conc() public pure { -// int256 a1 = int_mul(100, 1); -// require(a1 == 100); -// int256 a2 = int_mul(100, 2); -// require(a2 == 200); -// int256 a3 = int_mul(100, 4); -// require(a3 == 400); -// int256 a4 = int_mul(100, 8); -// require(a4 == 800); -// int256 a5 = int_mul(1000000000, 8); -// require(a5 == 8000000000); -// int256 a6 = int_mul(1000000000, 16); -// require(a6 == 16000000000); -// int256 a7 = int_mul(10000000000, 32); -// require(a7 == 320000000000); -// int256 a8 = int_mul(100000000000000000000, 64); -// require(a8 == 6400000000000000000000); -// int256 a9 = int_mul(100000000000000000000000000000000000, 128); -// require(a9 == 12800000000000000000000000000000000000); -// int256 a10 = int_mul(1, 255); -// require(a10 == 255); - -// int256 a11 = int_mul(-100, 1); -// require(a11 == -100); -// int256 a12 = int_mul(-100, 2); -// require(a12 == -200); -// int256 a13 = int_mul(-100, 4); -// require(a13 == -400); -// int256 a14 = int_mul(-100, 8); -// require(a14 == -800); -// int256 a15 = int_mul(-1000000000, 8); -// require(a15 == -8000000000); -// int256 a16 = int_mul(-1000000000, 16); -// require(a16 == -16000000000); -// int256 a17 = int_mul(-10000000000, 32); -// require(a17 == -320000000000); -// int256 a18 = int_mul(-100000000000000000000, 64); -// require(a18 == -6400000000000000000000); -// int256 a19 = int_mul(-100000000000000000000000000000000000, 128); -// require(a19 == -12800000000000000000000000000000000000); -// int256 a20 = int_mul(-1, 255); -// require(a20 == -255); - -// int256 a21 = int_mul(-100, -1); -// require(a21 == 100); -// int256 a22 = int_mul(-100, -2); -// require(a22 == 200); -// int256 a23 = int_mul(-100, -4); -// require(a23 == 400); -// int256 a24 = int_mul(-100, -8); -// require(a24 == 800); -// int256 a25 = int_mul(-1000000000, -8); -// require(a25 == 8000000000); -// int256 a26 = int_mul(-1000000000, -16); -// require(a26 == 16000000000); -// int256 a27 = int_mul(-10000000000, -32); -// require(a27 == 320000000000); -// int256 a28 = int_mul(-100000000000000000000, -64); -// require(a28 == 6400000000000000000000); -// int256 a29 = int_mul(-100000000000000000000000000000000000, -128); -// require(a29 == 12800000000000000000000000000000000000); -// int256 a30 = int_mul(-1, -255); -// require(a30 == 255); - -// int256 a31 = int_mul(100, -1); -// require(a31 == -100); -// int256 a32 = int_mul(100, -2); -// require(a32 == -200); -// int256 a33 = int_mul(100, -4); -// require(a33 == -400); -// int256 a34 = int_mul(100, -8); -// require(a34 == -800); -// int256 a35 = int_mul(1000000000, -8); -// require(a35 == -8000000000); -// int256 a36 = int_mul(1000000000, -16); -// require(a36 == -16000000000); -// int256 a37 = int_mul(10000000000, -32); -// require(a37 == -320000000000); -// int256 a38 = int_mul(100000000000000000000, -64); -// require(a38 == -6400000000000000000000); -// int256 a39 = int_mul(100000000000000000000000000000000000, -128); -// require(a39 == -12800000000000000000000000000000000000); -// int256 a40 = int_mul(1, -255); -// require(a40 == -255); -// } -// } - -// contract Add { -// function add(uint256 x, uint256 y) public pure returns (uint256) { -// return x + y; -// } - -// function int_add(int256 x, int256 y) public pure returns (int256) { -// return x + y; -// } - -// function add_conc() public pure returns (uint256) { -// uint256 a1 = add(100, 1); -// require(a1 == 101); -// uint256 a2 = add(100, 2); -// require(a2 == 102); -// uint256 a3 = add(100, 4); -// require(a3 == 104); -// uint256 a4 = add(100, 8); -// require(a4 == 108); -// uint256 a5 = add(1000000000, 8); -// require(a5 == 1000000008); -// uint256 a6 = add(1000000000, 16); -// require(a6 == 1000000016); -// uint256 a7 = add(10000000000, 32); -// require(a7 == 10000000032); -// uint256 a8 = add(100000000000000000000, 64); -// require(a8 == 100000000000000000064); -// uint256 a9 = add(100000000000000000000000000000000000, 128); -// require(a9 == 100000000000000000000000000000000128); -// uint256 a10 = add(1, 255); -// require(a10 == 256); -// } - -// function int_add_conc() public pure { -// int256 a1 = int_add(100, 1); -// require(a1 == 101); -// int256 a2 = int_add(100, 2); -// require(a2 == 102); -// int256 a3 = int_add(100, 4); -// require(a3 == 104); -// int256 a4 = int_add(100, 8); -// require(a4 == 108); -// int256 a5 = int_add(1000000000, 8); -// require(a5 == 1000000008); -// int256 a6 = int_add(1000000000, 16); -// require(a6 == 1000000016); -// int256 a7 = int_add(10000000000, 32); -// require(a7 == 10000000032); -// int256 a8 = int_add(100000000000000000000, 64); -// require(a8 == 100000000000000000064); -// int256 a9 = int_add(100000000000000000000000000000000000, 128); -// require(a9 == 100000000000000000000000000000000128); -// int256 a10 = int_add(1, 255); -// require(a10 == 256); - -// int256 a11 = int_add(-100, 1); -// require(a11 == -99); -// int256 a12 = int_add(-100, 2); -// require(a12 == -98); -// int256 a13 = int_add(-100, 4); -// require(a13 == -96); -// int256 a14 = int_add(-100, 8); -// require(a14 == -92); -// int256 a15 = int_add(-1000000000, 8); -// require(a15 == -999999992); -// int256 a16 = int_add(-1000000000, 16); -// require(a16 == -999999984); -// int256 a17 = int_add(-10000000000, 32); -// require(a17 == -9999999968); -// int256 a18 = int_add(-100000000000000000000, 64); -// require(a18 == -99999999999999999936); -// int256 a19 = int_add(-100000000000000000000000000000000000, 128); -// require(a19 == -99999999999999999999999999999999872); -// int256 a20 = int_add(-1, 255); -// require(a20 == 254); - -// int256 a21 = int_add(-100, -1); -// require(a21 == -101); -// int256 a22 = int_add(-100, -2); -// require(a22 == -102); -// int256 a23 = int_add(-100, -4); -// require(a23 == -104); -// int256 a24 = int_add(-100, -8); -// require(a24 == -108); -// int256 a25 = int_add(-1000000000, -8); -// require(a25 == -1000000008); -// int256 a26 = int_add(-1000000000, -16); -// require(a26 == -1000000016); -// int256 a27 = int_add(-10000000000, -32); -// require(a27 == -10000000032); -// int256 a28 = int_add(-100000000000000000000, -64); -// require(a28 == -100000000000000000064); -// int256 a29 = int_add(-100000000000000000000000000000000000, -128); -// require(a29 == -100000000000000000000000000000000128); -// int256 a30 = int_add(-1, -255); -// require(a30 == -256); - -// int256 a31 = int_add(100, -1); -// require(a31 == 99); -// int256 a32 = int_add(100, -2); -// require(a32 == 98); -// int256 a33 = int_add(100, -4); -// require(a33 == 96); -// int256 a34 = int_add(100, -8); -// require(a34 == 92); -// int256 a35 = int_add(1000000000, -8); -// require(a35 == 999999992); -// int256 a36 = int_add(1000000000, -16); -// require(a36 == 999999984); -// int256 a37 = int_add(10000000000, -32); -// require(a37 == 9999999968); -// int256 a38 = int_add(100000000000000000000, -64); -// require(a38 == 99999999999999999936); -// int256 a39 = int_add(100000000000000000000000000000000000, -128); -// require(a39 == 99999999999999999999999999999999872); -// int256 a40 = int_add(1, -255); -// require(a40 == -254); -// } -// } - -// contract Sub { -// function sub(uint256 x, uint256 y) public pure returns (uint256) { -// return x - y; -// } - -// function int_sub(int256 x, int256 y) public pure returns (int256) { -// return x - y; -// } - -// function sub_conc() public pure returns (uint256) { -// uint256 a1 = sub(100, 1); -// require(a1 == 99); -// uint256 a2 = sub(100, 2); -// require(a2 == 98); -// uint256 a3 = sub(100, 4); -// require(a3 == 96); -// uint256 a4 = sub(100, 8); -// require(a4 == 92); -// uint256 a5 = sub(1000000000, 8); -// require(a5 == 999999992); -// uint256 a6 = sub(1000000000, 16); -// require(a6 == 999999984); -// uint256 a7 = sub(10000000000, 32); -// require(a7 == 9999999968); -// uint256 a8 = sub(100000000000000000000, 64); -// require(a8 == 99999999999999999936); -// uint256 a9 = sub(100000000000000000000000000000000000, 128); -// require(a9 == 99999999999999999999999999999999872); -// } - -// function int_sub_conc() public pure { -// int256 a1 = int_sub(100, 1); -// require(a1 == 99); -// int256 a2 = int_sub(100, 2); -// require(a2 == 98); -// int256 a3 = int_sub(100, 4); -// require(a3 == 96); -// int256 a4 = int_sub(100, 8); -// require(a4 == 92); -// int256 a5 = int_sub(1000000000, 8); -// require(a5 == 999999992); -// int256 a6 = int_sub(1000000000, 16); -// require(a6 == 999999984); -// int256 a7 = int_sub(10000000000, 32); -// require(a7 == 9999999968); -// int256 a8 = int_sub(100000000000000000000, 64); -// require(a8 == 99999999999999999936); -// int256 a9 = int_sub(100000000000000000000000000000000000, 128); -// require(a9 == 99999999999999999999999999999999872); -// int256 a10 = int_sub(1, 255); -// require(a10 == -254); - -// int256 a11 = int_sub(-100, 1); -// require(a11 == -101); -// int256 a12 = int_sub(-100, 2); -// require(a12 == -102); -// int256 a13 = int_sub(-100, 4); -// require(a13 == -104); -// int256 a14 = int_sub(-100, 8); -// require(a14 == -108); -// int256 a15 = int_sub(-1000000000, 8); -// require(a15 == -1000000008); -// int256 a16 = int_sub(-1000000000, 16); -// require(a16 == -1000000016); -// int256 a17 = int_sub(-10000000000, 32); -// require(a17 == -10000000032); -// int256 a18 = int_sub(-100000000000000000000, 64); -// require(a18 == -100000000000000000064); -// int256 a19 = int_sub(-100000000000000000000000000000000000, 128); -// require(a19 == -100000000000000000000000000000000128); -// int256 a20 = int_sub(-1, 255); -// require(a20 == -256); - -// int256 a21 = int_sub(-100, -1); -// require(a21 == -99); -// int256 a22 = int_sub(-100, -2); -// require(a22 == -98); -// int256 a23 = int_sub(-100, -4); -// require(a23 == -96); -// int256 a24 = int_sub(-100, -8); -// require(a24 == -92); -// int256 a25 = int_sub(-1000000000, -8); -// require(a25 == -999999992); -// int256 a26 = int_sub(-1000000000, -16); -// require(a26 == -999999984); -// int256 a27 = int_sub(-10000000000, -32); -// require(a27 == -9999999968); -// int256 a28 = int_sub(-100000000000000000000, -64); -// require(a28 == -99999999999999999936); -// int256 a29 = int_sub(-100000000000000000000000000000000000, -128); -// require(a29 == -99999999999999999999999999999999872); -// int256 a30 = int_sub(-1, -255); -// require(a30 == 254); - -// int256 a31 = int_sub(100, -1); -// require(a31 == 101); -// int256 a32 = int_sub(100, -2); -// require(a32 == 102); -// int256 a33 = int_sub(100, -4); -// require(a33 == 104); -// int256 a34 = int_sub(100, -8); -// require(a34 == 108); -// int256 a35 = int_sub(1000000000, -8); -// require(a35 == 1000000008); -// int256 a36 = int_sub(1000000000, -16); -// require(a36 == 1000000016); -// int256 a37 = int_sub(10000000000, -32); -// require(a37 == 10000000032); -// int256 a38 = int_sub(100000000000000000000, -64); -// require(a38 == 100000000000000000064); -// int256 a39 = int_sub(100000000000000000000000000000000000, -128); -// require(a39 == 100000000000000000000000000000000128); -// int256 a40 = int_sub(1, -255); -// require(a40 == 256); -// } -// } +contract Div { + function div(uint256 x, uint256 y) public pure returns (uint256) { + return x / y; + } + + function int_div(int256 x, int256 y) public pure returns (int256) { + return x / y; + } + + function div_conc() public pure returns (uint256) { + uint256 a1 = div(100, 1); + require(a1 == 100); + uint256 a2 = div(100, 2); + require(a2 == 50); + uint256 a3 = div(100, 4); + require(a3 == 25); + uint256 a4 = div(100, 8); + require(a4 == 12); + uint256 a5 = div(1000000000, 8); + require(a5 == 125000000); + uint256 a6 = div(1000000000, 16); + require(a6 == 62500000); + uint256 a7 = div(10000000000, 32); + require(a7 == 312500000); + uint256 a8 = div(100000000000000000000, 64); + require(a8 == 1562500000000000000); + uint256 a9 = div(100000000000000000000000000000000000, 128); + require(a9 == 781250000000000000000000000000000); + uint256 a10 = div(1, 255); + require(a10 == 0); + } + + function int_div_conc() public pure { + int256 a1 = int_div(100, 1); + require(a1 == 100); + int256 a2 = int_div(100, 2); + require(a2 == 50); + int256 a3 = int_div(100, 4); + require(a3 == 25); + int256 a4 = int_div(100, 8); + require(a4 == 12); + int256 a5 = int_div(1000000000, 8); + require(a5 == 125000000); + int256 a6 = int_div(1000000000, 16); + require(a6 == 62500000); + int256 a7 = int_div(10000000000, 32); + require(a7 == 312500000); + int256 a8 = int_div(100000000000000000000, 64); + require(a8 == 1562500000000000000); + int256 a9 = int_div(100000000000000000000000000000000000, 128); + require(a9 == 781250000000000000000000000000000); + int256 a10 = int_div(1, 255); + require(a10 == 0); + + int256 a11 = int_div(-100, 1); + require(a11 == -100); + int256 a12 = int_div(-100, 2); + require(a12 == -50); + int256 a13 = int_div(-100, 4); + require(a13 == -25); + int256 a14 = int_div(-100, 8); + require(a14 == -12); + int256 a15 = int_div(-1000000000, 8); + require(a15 == -125000000); + int256 a16 = int_div(-1000000000, 16); + require(a16 == -62500000); + int256 a17 = int_div(-10000000000, 32); + require(a17 == -312500000); + int256 a18 = int_div(-100000000000000000000, 64); + require(a18 == -1562500000000000000); + int256 a19 = int_div(-100000000000000000000000000000000000, 128); + require(a19 == -781250000000000000000000000000000); + int256 a20 = int_div(-1, 255); + require(a20 == 0); + + int256 a21 = int_div(-100, -1); + require(a21 == 100); + int256 a22 = int_div(-100, -2); + require(a22 == 50); + int256 a23 = int_div(-100, -4); + require(a23 == 25); + int256 a24 = int_div(-100, -8); + require(a24 == 12); + int256 a25 = int_div(-1000000000, -8); + require(a25 == 125000000); + int256 a26 = int_div(-1000000000, -16); + require(a26 == 62500000); + int256 a27 = int_div(-10000000000, -32); + require(a27 == 312500000); + int256 a28 = int_div(-100000000000000000000, -64); + require(a28 == 1562500000000000000); + int256 a29 = int_div(-100000000000000000000000000000000000, -128); + require(a29 == 781250000000000000000000000000000); + int256 a30 = int_div(-1, -255); + require(a30 == 0); + + int256 a31 = int_div(100, -1); + require(a31 == -100); + int256 a32 = int_div(100, -2); + require(a32 == -50); + int256 a33 = int_div(100, -4); + require(a33 == -25); + int256 a34 = int_div(100, -8); + require(a34 == -12); + int256 a35 = int_div(1000000000, -8); + require(a35 == -125000000); + int256 a36 = int_div(1000000000, -16); + require(a36 == -62500000); + int256 a37 = int_div(10000000000, -32); + require(a37 == -312500000); + int256 a38 = int_div(100000000000000000000, -64); + require(a38 == -1562500000000000000); + int256 a39 = int_div(100000000000000000000000000000000000, -128); + require(a39 == -781250000000000000000000000000000); + int256 a40 = int_div(1, -255); + require(a40 == 0); + } +} + +contract Mul { + function mul(uint256 x, uint256 y) public pure returns (uint256) { + return x * y; + } + + function int_mul(int256 x, int256 y) public pure returns (int256) { + return x * y; + } + + function mul_conc() public pure returns (uint256) { + uint256 a1 = mul(100, 1); + require(a1 == 100); + uint256 a2 = mul(100, 2); + require(a2 == 200); + uint256 a3 = mul(100, 4); + require(a3 == 400); + uint256 a4 = mul(100, 8); + require(a4 == 800); + uint256 a5 = mul(1000000000, 8); + require(a5 == 8000000000); + uint256 a6 = mul(1000000000, 16); + require(a6 == 16000000000); + uint256 a7 = mul(10000000000, 32); + require(a7 == 320000000000); + uint256 a8 = mul(100000000000000000000, 64); + require(a8 == 6400000000000000000000); + uint256 a9 = mul(100000000000000000000000000000000000, 128); + require(a9 == 12800000000000000000000000000000000000); + uint256 a10 = mul(1, 255); + require(a10 == 255); + } + + function int_mul_conc() public pure { + int256 a1 = int_mul(100, 1); + require(a1 == 100); + int256 a2 = int_mul(100, 2); + require(a2 == 200); + int256 a3 = int_mul(100, 4); + require(a3 == 400); + int256 a4 = int_mul(100, 8); + require(a4 == 800); + int256 a5 = int_mul(1000000000, 8); + require(a5 == 8000000000); + int256 a6 = int_mul(1000000000, 16); + require(a6 == 16000000000); + int256 a7 = int_mul(10000000000, 32); + require(a7 == 320000000000); + int256 a8 = int_mul(100000000000000000000, 64); + require(a8 == 6400000000000000000000); + int256 a9 = int_mul(100000000000000000000000000000000000, 128); + require(a9 == 12800000000000000000000000000000000000); + int256 a10 = int_mul(1, 255); + require(a10 == 255); + + int256 a11 = int_mul(-100, 1); + require(a11 == -100); + int256 a12 = int_mul(-100, 2); + require(a12 == -200); + int256 a13 = int_mul(-100, 4); + require(a13 == -400); + int256 a14 = int_mul(-100, 8); + require(a14 == -800); + int256 a15 = int_mul(-1000000000, 8); + require(a15 == -8000000000); + int256 a16 = int_mul(-1000000000, 16); + require(a16 == -16000000000); + int256 a17 = int_mul(-10000000000, 32); + require(a17 == -320000000000); + int256 a18 = int_mul(-100000000000000000000, 64); + require(a18 == -6400000000000000000000); + int256 a19 = int_mul(-100000000000000000000000000000000000, 128); + require(a19 == -12800000000000000000000000000000000000); + int256 a20 = int_mul(-1, 255); + require(a20 == -255); + + int256 a21 = int_mul(-100, -1); + require(a21 == 100); + int256 a22 = int_mul(-100, -2); + require(a22 == 200); + int256 a23 = int_mul(-100, -4); + require(a23 == 400); + int256 a24 = int_mul(-100, -8); + require(a24 == 800); + int256 a25 = int_mul(-1000000000, -8); + require(a25 == 8000000000); + int256 a26 = int_mul(-1000000000, -16); + require(a26 == 16000000000); + int256 a27 = int_mul(-10000000000, -32); + require(a27 == 320000000000); + int256 a28 = int_mul(-100000000000000000000, -64); + require(a28 == 6400000000000000000000); + int256 a29 = int_mul(-100000000000000000000000000000000000, -128); + require(a29 == 12800000000000000000000000000000000000); + int256 a30 = int_mul(-1, -255); + require(a30 == 255); + + int256 a31 = int_mul(100, -1); + require(a31 == -100); + int256 a32 = int_mul(100, -2); + require(a32 == -200); + int256 a33 = int_mul(100, -4); + require(a33 == -400); + int256 a34 = int_mul(100, -8); + require(a34 == -800); + int256 a35 = int_mul(1000000000, -8); + require(a35 == -8000000000); + int256 a36 = int_mul(1000000000, -16); + require(a36 == -16000000000); + int256 a37 = int_mul(10000000000, -32); + require(a37 == -320000000000); + int256 a38 = int_mul(100000000000000000000, -64); + require(a38 == -6400000000000000000000); + int256 a39 = int_mul(100000000000000000000000000000000000, -128); + require(a39 == -12800000000000000000000000000000000000); + int256 a40 = int_mul(1, -255); + require(a40 == -255); + } +} + +contract Add { + function add(uint256 x, uint256 y) public pure returns (uint256) { + return x + y; + } + + function int_add(int256 x, int256 y) public pure returns (int256) { + return x + y; + } + + function add_conc() public pure returns (uint256) { + uint256 a1 = add(100, 1); + require(a1 == 101); + uint256 a2 = add(100, 2); + require(a2 == 102); + uint256 a3 = add(100, 4); + require(a3 == 104); + uint256 a4 = add(100, 8); + require(a4 == 108); + uint256 a5 = add(1000000000, 8); + require(a5 == 1000000008); + uint256 a6 = add(1000000000, 16); + require(a6 == 1000000016); + uint256 a7 = add(10000000000, 32); + require(a7 == 10000000032); + uint256 a8 = add(100000000000000000000, 64); + require(a8 == 100000000000000000064); + uint256 a9 = add(100000000000000000000000000000000000, 128); + require(a9 == 100000000000000000000000000000000128); + uint256 a10 = add(1, 255); + require(a10 == 256); + } + + function int_add_conc() public pure { + int256 a1 = int_add(100, 1); + require(a1 == 101); + int256 a2 = int_add(100, 2); + require(a2 == 102); + int256 a3 = int_add(100, 4); + require(a3 == 104); + int256 a4 = int_add(100, 8); + require(a4 == 108); + int256 a5 = int_add(1000000000, 8); + require(a5 == 1000000008); + int256 a6 = int_add(1000000000, 16); + require(a6 == 1000000016); + int256 a7 = int_add(10000000000, 32); + require(a7 == 10000000032); + int256 a8 = int_add(100000000000000000000, 64); + require(a8 == 100000000000000000064); + int256 a9 = int_add(100000000000000000000000000000000000, 128); + require(a9 == 100000000000000000000000000000000128); + int256 a10 = int_add(1, 255); + require(a10 == 256); + + int256 a11 = int_add(-100, 1); + require(a11 == -99); + int256 a12 = int_add(-100, 2); + require(a12 == -98); + int256 a13 = int_add(-100, 4); + require(a13 == -96); + int256 a14 = int_add(-100, 8); + require(a14 == -92); + int256 a15 = int_add(-1000000000, 8); + require(a15 == -999999992); + int256 a16 = int_add(-1000000000, 16); + require(a16 == -999999984); + int256 a17 = int_add(-10000000000, 32); + require(a17 == -9999999968); + int256 a18 = int_add(-100000000000000000000, 64); + require(a18 == -99999999999999999936); + int256 a19 = int_add(-100000000000000000000000000000000000, 128); + require(a19 == -99999999999999999999999999999999872); + int256 a20 = int_add(-1, 255); + require(a20 == 254); + + int256 a21 = int_add(-100, -1); + require(a21 == -101); + int256 a22 = int_add(-100, -2); + require(a22 == -102); + int256 a23 = int_add(-100, -4); + require(a23 == -104); + int256 a24 = int_add(-100, -8); + require(a24 == -108); + int256 a25 = int_add(-1000000000, -8); + require(a25 == -1000000008); + int256 a26 = int_add(-1000000000, -16); + require(a26 == -1000000016); + int256 a27 = int_add(-10000000000, -32); + require(a27 == -10000000032); + int256 a28 = int_add(-100000000000000000000, -64); + require(a28 == -100000000000000000064); + int256 a29 = int_add(-100000000000000000000000000000000000, -128); + require(a29 == -100000000000000000000000000000000128); + int256 a30 = int_add(-1, -255); + require(a30 == -256); + + int256 a31 = int_add(100, -1); + require(a31 == 99); + int256 a32 = int_add(100, -2); + require(a32 == 98); + int256 a33 = int_add(100, -4); + require(a33 == 96); + int256 a34 = int_add(100, -8); + require(a34 == 92); + int256 a35 = int_add(1000000000, -8); + require(a35 == 999999992); + int256 a36 = int_add(1000000000, -16); + require(a36 == 999999984); + int256 a37 = int_add(10000000000, -32); + require(a37 == 9999999968); + int256 a38 = int_add(100000000000000000000, -64); + require(a38 == 99999999999999999936); + int256 a39 = int_add(100000000000000000000000000000000000, -128); + require(a39 == 99999999999999999999999999999999872); + int256 a40 = int_add(1, -255); + require(a40 == -254); + } +} + +contract Sub { + function sub(uint256 x, uint256 y) public pure returns (uint256) { + return x - y; + } + + function int_sub(int256 x, int256 y) public pure returns (int256) { + return x - y; + } + + function sub_conc() public pure returns (uint256) { + uint256 a1 = sub(100, 1); + require(a1 == 99); + uint256 a2 = sub(100, 2); + require(a2 == 98); + uint256 a3 = sub(100, 4); + require(a3 == 96); + uint256 a4 = sub(100, 8); + require(a4 == 92); + uint256 a5 = sub(1000000000, 8); + require(a5 == 999999992); + uint256 a6 = sub(1000000000, 16); + require(a6 == 999999984); + uint256 a7 = sub(10000000000, 32); + require(a7 == 9999999968); + uint256 a8 = sub(100000000000000000000, 64); + require(a8 == 99999999999999999936); + uint256 a9 = sub(100000000000000000000000000000000000, 128); + require(a9 == 99999999999999999999999999999999872); + } + + function int_sub_conc() public pure { + int256 a1 = int_sub(100, 1); + require(a1 == 99); + int256 a2 = int_sub(100, 2); + require(a2 == 98); + int256 a3 = int_sub(100, 4); + require(a3 == 96); + int256 a4 = int_sub(100, 8); + require(a4 == 92); + int256 a5 = int_sub(1000000000, 8); + require(a5 == 999999992); + int256 a6 = int_sub(1000000000, 16); + require(a6 == 999999984); + int256 a7 = int_sub(10000000000, 32); + require(a7 == 9999999968); + int256 a8 = int_sub(100000000000000000000, 64); + require(a8 == 99999999999999999936); + int256 a9 = int_sub(100000000000000000000000000000000000, 128); + require(a9 == 99999999999999999999999999999999872); + int256 a10 = int_sub(1, 255); + require(a10 == -254); + + int256 a11 = int_sub(-100, 1); + require(a11 == -101); + int256 a12 = int_sub(-100, 2); + require(a12 == -102); + int256 a13 = int_sub(-100, 4); + require(a13 == -104); + int256 a14 = int_sub(-100, 8); + require(a14 == -108); + int256 a15 = int_sub(-1000000000, 8); + require(a15 == -1000000008); + int256 a16 = int_sub(-1000000000, 16); + require(a16 == -1000000016); + int256 a17 = int_sub(-10000000000, 32); + require(a17 == -10000000032); + int256 a18 = int_sub(-100000000000000000000, 64); + require(a18 == -100000000000000000064); + int256 a19 = int_sub(-100000000000000000000000000000000000, 128); + require(a19 == -100000000000000000000000000000000128); + int256 a20 = int_sub(-1, 255); + require(a20 == -256); + + int256 a21 = int_sub(-100, -1); + require(a21 == -99); + int256 a22 = int_sub(-100, -2); + require(a22 == -98); + int256 a23 = int_sub(-100, -4); + require(a23 == -96); + int256 a24 = int_sub(-100, -8); + require(a24 == -92); + int256 a25 = int_sub(-1000000000, -8); + require(a25 == -999999992); + int256 a26 = int_sub(-1000000000, -16); + require(a26 == -999999984); + int256 a27 = int_sub(-10000000000, -32); + require(a27 == -9999999968); + int256 a28 = int_sub(-100000000000000000000, -64); + require(a28 == -99999999999999999936); + int256 a29 = int_sub(-100000000000000000000000000000000000, -128); + require(a29 == -99999999999999999999999999999999872); + int256 a30 = int_sub(-1, -255); + require(a30 == 254); + + int256 a31 = int_sub(100, -1); + require(a31 == 101); + int256 a32 = int_sub(100, -2); + require(a32 == 102); + int256 a33 = int_sub(100, -4); + require(a33 == 104); + int256 a34 = int_sub(100, -8); + require(a34 == 108); + int256 a35 = int_sub(1000000000, -8); + require(a35 == 1000000008); + int256 a36 = int_sub(1000000000, -16); + require(a36 == 1000000016); + int256 a37 = int_sub(10000000000, -32); + require(a37 == 10000000032); + int256 a38 = int_sub(100000000000000000000, -64); + require(a38 == 100000000000000000064); + int256 a39 = int_sub(100000000000000000000000000000000000, -128); + require(a39 == 100000000000000000000000000000000128); + int256 a40 = int_sub(1, -255); + require(a40 == 256); + } +} contract AssignMath { - // function assignAdd(uint256 x) public pure { - // x += 10; - // } + function assignAdd(uint256 x) public pure { + x += 10; + } - // function assignSub(uint256 x) public pure { - // x -= 10; - // } + function assignSub(uint256 x) public pure { + x -= 10; + } - // function assignDiv(uint256 x) public pure { - // x /= 10; - // } + function assignDiv(uint256 x) public pure { + x /= 10; + } - // function assignMul(uint256 x) public pure { - // x *= 10; - // } + function assignMul(uint256 x) public pure { + x *= 10; + } function preincrement(uint256 x) public pure returns (uint256, uint256) { uint256 y = ++x; @@ -534,111 +534,111 @@ contract AssignMath { } } -// contract Math { -// function rmod(uint256 x, uint256 y) public pure returns (uint256) { -// return x % y; -// } - -// function rexp(uint256 x, uint256 y) public pure returns (uint256) { -// return x ** y; -// } - -// function int_rmod(int256 x, int256 y) public pure returns (int256) { -// return x % y; -// } - -// function int_rexp(int256 x, uint256 y) public pure returns (int256) { -// return x ** y; -// } -// } - -// contract Unchecked { -// function assemblyWrappingSub(uint256 a) public pure { -// assembly { -// a := sub(0, 100) -// } -// require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); - -// int256 y = type(int256).min; -// assembly { -// a := sub(y, 100) -// } -// require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); -// } - -// function uncheckedSub(uint256 a) public pure { -// unchecked { -// a = 0 - 100; -// } -// require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); - -// int256 y = type(int256).min; -// unchecked { -// a = y - 100; -// } -// require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); -// } - -// function uncheckedSymbolicSub(uint256 a, uint256 b) public pure { -// unchecked { -// a -= 100; -// } -// } - -// function assemblyWrappingAdd(uint256 a) public pure { -// uint256 m = type(uint256).max; -// assembly { -// a := add(m, 100) -// } -// require(a == 99); -// a += (type(uint256).max - 99); -// require(a == type(uint256).max); -// } - -// function uncheckedAdd(uint256 a) public pure { -// unchecked { -// a = type(uint256).max + 100; -// } -// require(a == 99); -// a += (type(uint256).max - 99); -// require(a == type(uint256).max); -// } - -// function assemblyWrappingMul(uint256 a) public pure { -// uint256 m = type(uint128).max; -// assembly { -// a := mul(m, m) -// } -// require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); -// a /= 3; -// a *= 3; -// // require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); -// } - -// function uncheckedMul(uint256 a) public pure { -// unchecked { -// a = type(uint256).max + 100; -// } -// require(a == 99); -// a += (type(uint256).max - 99); -// require(a == type(uint256).max); -// } - -// function symbUncheckedMul(int256 a, int b) public pure { -// unchecked { -// a = a * b; -// int c = a * a / a; -// int d = a * c * b; -// } - -// a = a * b; -// int c = a * a / a; -// int d = a * c * b; -// } - -// function asmSymbUncheckedMul(int256 a, int b) public pure { -// assembly { -// a := mul(a, b) -// } -// } -// } \ No newline at end of file +contract Mod { + function rmod(uint256 x, uint256 y) public pure returns (uint256) { + return x % y; + } + + function rexp(uint256 x, uint256 y) public pure returns (uint256) { + return x ** y; + } + + function int_rmod(int256 x, int256 y) public pure returns (int256) { + return x % y; + } + + function int_rexp(int256 x, uint256 y) public pure returns (int256) { + return x ** y; + } +} + +contract Unchecked { + function assemblyWrappingSub(uint256 a) public pure { + assembly { + a := sub(0, 100) + } + require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); + + int256 y = type(int256).min; + assembly { + a := sub(y, 100) + } + require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); + } + + function uncheckedSub(uint256 a) public pure { + unchecked { + a = 0 - 100; + } + require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); + + int256 y = type(int256).min; + unchecked { + a = y - 100; + } + require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); + } + + function uncheckedSymbolicSub(uint256 a, uint256 b) public pure { + unchecked { + a -= 100; + } + } + + function assemblyWrappingAdd(uint256 a) public pure { + uint256 m = type(uint256).max; + assembly { + a := add(m, 100) + } + require(a == 99); + a += (type(uint256).max - 99); + require(a == type(uint256).max); + } + + function uncheckedAdd(uint256 a) public pure { + unchecked { + a = type(uint256).max + 100; + } + require(a == 99); + a += (type(uint256).max - 99); + require(a == type(uint256).max); + } + + function assemblyWrappingMul(uint256 a) public pure { + uint256 m = type(uint128).max; + assembly { + a := mul(m, m) + } + require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); + a /= 3; + a *= 3; + // require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); + } + + function uncheckedMul(uint256 a) public pure { + unchecked { + a = type(uint256).max + 100; + } + require(a == 99); + a += (type(uint256).max - 99); + require(a == type(uint256).max); + } + + function symbUncheckedMul(int256 a, int b) public pure { + unchecked { + a = a * b; + int c = a * a / a; + int d = a * c * b; + } + + a = a * b; + int c = a * a / a; + int d = a * c * b; + } + + function asmSymbUncheckedMul(int256 a, int b) public pure { + assembly { + a := mul(a, b) + } + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/repros/issue69.sol b/crates/pyrometer/tests/test_data/repros/issue69.sol index 232cf4a4..c653635c 100644 --- a/crates/pyrometer/tests/test_data/repros/issue69.sol +++ b/crates/pyrometer/tests/test_data/repros/issue69.sol @@ -1,11 +1,15 @@ -contract SymbolicTest { - // https://github.com/foundry-rs/foundry/issues/2851 - function backdoor(uint256 x) external pure { +contract Test { + function backdoor(uint256 x, uint256 y) external pure { uint256 number = 99; unchecked { uint256 z = x - 1; - if (z == 6912213124124531) { - number = 0; + y = y - 10 + z; + if (y == 69122131241245311234) { + if (z == 6912213124124531) { + number = 0; + } else { + number = 1; + } } else { number = 1; } diff --git a/crates/pyrometer/tests/test_data/repros/overflow2.sol b/crates/pyrometer/tests/test_data/repros/overflow2.sol new file mode 100644 index 00000000..cc284df5 --- /dev/null +++ b/crates/pyrometer/tests/test_data/repros/overflow2.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.0; + +library Math { + function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + uint256 prod0; + uint256 prod1; + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + require(denominator > prod1); + + + uint256 twos = denominator & (~denominator + 1); + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + + return 0; + } + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/viz/func_call.sol b/crates/pyrometer/tests/test_data/viz/func_call.sol index 2e114b10..44e0e697 100644 --- a/crates/pyrometer/tests/test_data/viz/func_call.sol +++ b/crates/pyrometer/tests/test_data/viz/func_call.sol @@ -1,13 +1,32 @@ -contract A { - uint256 storageVariable; - // uint256 public publicStorageVariable; - // uint256 constant const; - - function funcA() public returns (uint256 ret) { - ret = funcB(storageVariable); - } - - function funcB(uint256 innerInput0) internal returns (uint256 ret) { - ret = innerInput0 + 10; - } -} \ No newline at end of file +// contract A { +// uint256 storageVariable; +// // uint256 public publicStorageVariable; +// // uint256 constant const; + +// function funcA() public returns (uint256 ret) { +// ret = funcB(storageVariable); +// } + +// function funcB(uint256 innerInput0) internal returns (uint256 ret) { +// ret = innerInput0 + 10; +// } +// } + +contract InvariantBreaker { + bool public flag0 = true; + bool public flag1 = true; + + function set0(int256 val) public returns (bool) { + if (val % 100 == 0) { + flag0 = false; + } + return flag0; + } + + function set1(int256 val) public returns (bool) { + if (val % 10 == 0 && !flag0) { + flag1 = false; + } + return flag1; + } +} diff --git a/crates/shared/src/graph_like.rs b/crates/shared/src/graph_like.rs index f1ace007..69853082 100644 --- a/crates/shared/src/graph_like.rs +++ b/crates/shared/src/graph_like.rs @@ -35,11 +35,6 @@ pub trait GraphLike { /// Add a node to the graph fn add_node(&mut self, node: impl Into) -> NodeIdx { let res = self.graph_mut().add_node(node.into()); - // if res == 81.into() { - // panic!("here"); - // } else { - // res - // } res } /// Get a reference to a node in the graph @@ -68,9 +63,7 @@ pub trait GraphLike { fn range_arena(&self) -> &RangeArena; fn range_arena_mut(&mut self) -> &mut RangeArena; - fn range_arena_idx(&self, elem: &Self::RangeElem) -> Option { - self.range_arena().map.get(elem).copied() - } + fn range_arena_idx(&self, elem: &Self::RangeElem) -> Option; fn range_arena_idx_or_upsert(&mut self, elem: Self::RangeElem) -> usize; } diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 90dc99cc..85fdd179 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -4,7 +4,7 @@ use crate::{ }; use graph::{ - elem::Elem, + elem::{RangeElem, Elem}, nodes::{ContextNode, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, GraphError, Node, }; @@ -131,7 +131,17 @@ pub trait Assign: AnalyzerBackend + Sized Elem::from(rhs_cvar.latest_version(self)), ); - let new_lhs = self.advance_var_in_ctx(lhs_cvar.latest_version(self), loc, ctx)?; + + + let needs_forcible = new_lower_bound.depends_on(lhs_cvar, &mut vec![], self).into_expr_err(loc)? + || new_upper_bound.depends_on(lhs_cvar, &mut vec![], self).into_expr_err(loc)?; + + let new_lhs = if needs_forcible { + self.advance_var_in_ctx_forcible(lhs_cvar.latest_version(self), loc, ctx, true)? + } else { + self.advance_var_in_ctx(lhs_cvar.latest_version(self), loc, ctx)? + }; + new_lhs.underlying_mut(self).into_expr_err(loc)?.tmp_of = rhs_cvar.tmp_of(self).into_expr_err(loc)?; if lhs_cvar.is_storage(self).into_expr_err(loc)? { diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index 39a719b6..ba084aaf 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -720,6 +720,8 @@ pub trait BinOp: AnalyzerBackend + Sized { .set_range_min(self, expr.clone()) .into_expr_err(loc)?; out_var.set_range_max(self, expr).into_expr_err(loc)?; + + self.advance_var_in_ctx_forcible(lhs_cvar, loc, ctx, true)?; ctx.push_expr(ExprRet::Single(out_var.into()), self) .into_expr_err(loc)?; Ok(()) diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index d3bc7140..9fd53e3b 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -192,7 +192,7 @@ pub trait Cmp: AnalyzerBackend + Sized { .ref_range(self) .into_expr_err(loc)? .expect("No lhs range") - .range_exclusions(); + .exclusions.clone(); SolcRange::new(elem.clone(), elem, exclusions) }; diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 502fb060..08b14019 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -102,7 +102,7 @@ pub trait CallerHelper: AnalyzerBackend + self.add_if_err(res); let res = node .latest_version(self) - .try_set_range_exclusions(self, r.exclusions) + .try_set_range_exclusions(self, r.exclusions.clone()) .into_expr_err(loc); self.add_if_err(res); } @@ -525,7 +525,7 @@ pub trait CallerHelper: AnalyzerBackend + let res = new_input .set_range_exclusions( analyzer, - updated_var_range.range_exclusions(), + updated_var_range.exclusions.clone(), ) .into_expr_err(loc); let _ = analyzer.add_if_err(res); @@ -572,7 +572,7 @@ pub trait CallerHelper: AnalyzerBackend + let _ = new_inheritor_var .set_range_max(analyzer, r.range_max().into_owned()); let _ = new_inheritor_var - .set_range_exclusions(analyzer, r.range_exclusions()); + .set_range_exclusions(analyzer, r.exclusions.clone()); } } else { let new_in_inheritor = diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index 4542c45c..14cd80ec 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -22,28 +22,7 @@ pub trait AddressCaller: AnalyzerBackend + ) -> Result<(), ExprErr> { match &*func_name { "delegatecall" | "staticcall" | "call" => { - // TODO: Check if we have the code for the address - // if we dont, model it as a unrestricted call that can make other calls - ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; - // TODO: try to be smarter based on the address input - let booln = self.builtin_or_add(Builtin::Bool); - let bool_cvar = - ContextVar::new_from_builtin(loc, booln.into(), self).into_expr_err(loc)?; - let bool_node = self.add_node(Node::ContextVar(bool_cvar)); - ctx.add_var(bool_node.into(), self).into_expr_err(loc)?; - self.add_edge(bool_node, ctx, Edge::Context(ContextEdge::Variable)); - - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr( - ExprRet::Multi(vec![ExprRet::Single(bool_node), ExprRet::Single(node)]), - self, - ) - .into_expr_err(loc)?; - Ok(()) + self.external_call(&func_name, loc, ctx) } "code" => { // TODO: try to be smarter based on the address input @@ -76,4 +55,34 @@ pub trait AddressCaller: AnalyzerBackend + )), } } + + fn external_call( + &mut self, + _ty: &str, + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + // TODO: Check if we have the code for the address + // if we dont, model it as a unrestricted call that can make other calls + ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; + // TODO: try to be smarter based on the address input + let booln = self.builtin_or_add(Builtin::Bool); + let bool_cvar = + ContextVar::new_from_builtin(loc, booln.into(), self).into_expr_err(loc)?; + let bool_node = self.add_node(Node::ContextVar(bool_cvar)); + ctx.add_var(bool_node.into(), self).into_expr_err(loc)?; + self.add_edge(bool_node, ctx, Edge::Context(ContextEdge::Variable)); + + let bn = self.builtin_or_add(Builtin::DynamicBytes); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; + let node = self.add_node(Node::ContextVar(cvar)); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr( + ExprRet::Multi(vec![ExprRet::Single(bool_node), ExprRet::Single(node)]), + self, + ) + .into_expr_err(loc)?; + Ok(()) + } } diff --git a/crates/solc-expressions/src/func_call/join.rs b/crates/solc-expressions/src/func_call/join.rs index 96da154f..03fe0b05 100644 --- a/crates/solc-expressions/src/func_call/join.rs +++ b/crates/solc-expressions/src/func_call/join.rs @@ -1,3 +1,4 @@ +use graph::SolcRange; use shared::AnalyzerLike; use graph::nodes::Concrete; use shared::NodeIdx; @@ -66,7 +67,7 @@ pub trait FuncJoiner: new_var.name = new_name.clone(); new_var.display_name = new_name; if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); @@ -88,7 +89,7 @@ pub trait FuncJoiner: new_var.name = new_name.clone(); new_var.display_name = new_name; if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); @@ -116,7 +117,7 @@ pub trait FuncJoiner: .try_for_each(|dep| { let mut new_var = dep.underlying(self)?.clone(); if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); @@ -175,7 +176,7 @@ pub trait FuncJoiner: new_var.name = new_name.clone(); new_var.display_name = new_name; if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); @@ -199,7 +200,7 @@ pub trait FuncJoiner: new_var.name = new_name.clone(); new_var.display_name = new_name; if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); @@ -227,7 +228,7 @@ pub trait FuncJoiner: .try_for_each(|dep| { let mut new_var = dep.underlying(self)?.clone(); if let Some(mut range) = new_var.ty.take_range() { - let mut range = range.take_flattened_range(self).unwrap(); + let mut range: SolcRange = range.take_flattened_range(self).unwrap().into(); replacement_map.iter().for_each(|(replace, replacement)| { range.replace_dep(*replace, replacement.clone(), self); }); diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 04ad8278..d645d4f1 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -1,3 +1,4 @@ +use crate::func_call::intrinsic_call::AddressCaller; use crate::{ExprErr, IntoExprErr, LibraryAccess}; use graph::{ @@ -270,7 +271,7 @@ pub trait BuiltinAccess: let node = self.add_node(Node::Concrete(c)).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; - var.name = format!("int{size}.min"); + var.name = format!("uint{size}.min"); var.display_name = var.name.clone(); var.is_tmp = true; var.is_symbolic = false; @@ -279,6 +280,11 @@ pub trait BuiltinAccess: self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) } + "call" | "delegatecall" | "staticcall" if size == 160 => { + let builtin_name = ident.name.split('(').collect::>()[0]; + let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); + Ok(ExprRet::Single(func_node)) + } e => Err(ExprErr::MemberAccessNotFound( loc, format!( diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 15d2e626..08b4a528 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -873,7 +873,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { return Ok(None); } - ctx.add_ctx_dep(cvar, self).into_expr_err(loc)?; + ctx.add_ctx_dep(conditional_cvar, self).into_expr_err(loc)?; } tracing::trace!( @@ -1014,7 +1014,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { nonconst_var.set_range_max(self, max).into_expr_err(loc)?; } else { // just add as an exclusion - nonconst_range.add_range_exclusion(elem); + let idx = self.range_arena_idx_or_upsert(elem); + nonconst_range.add_range_exclusion(idx); nonconst_var .set_range_exclusions(self, nonconst_range.exclusions) .into_expr_err(loc)?; @@ -1197,14 +1198,16 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { let rhs_elem = Elem::from(new_rhs.latest_version(self)); // just add as an exclusion - lhs_range.add_range_exclusion(rhs_elem); + let idx = self.range_arena_idx_or_upsert(rhs_elem); + lhs_range.add_range_exclusion(idx); new_lhs .set_range_exclusions(self, lhs_range.exclusions) .into_expr_err(loc)?; let lhs_elem = Elem::from(new_lhs.latest_version(self)); // just add as an exclusion - rhs_range.add_range_exclusion(lhs_elem); + let idx = self.range_arena_idx_or_upsert(lhs_elem); + rhs_range.add_range_exclusion(idx); new_rhs .set_range_exclusions(self, rhs_range.exclusions) .into_expr_err(loc)?;