Unverified Commit 7b6c6f4a by Enkelmann Committed by GitHub

Fix handling of default values for fixpoint computations (#273)

parent 9981d746
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
//! //!
//! In the current implementation edge transition functions are also allowed to return `None` //! In the current implementation edge transition functions are also allowed to return `None`
//! to indicate that no information flows through the edge. //! to indicate that no information flows through the edge.
//! In such a case the value at the target node of the edge will not get updated.
//! For example, an analysis can use this to indicate edges that are never taken //! For example, an analysis can use this to indicate edges that are never taken
//! and thus prevent dead code to affect the analysis. //! and thus prevent dead code to affect the analysis.
//! //!
...@@ -49,7 +50,7 @@ pub trait Context { ...@@ -49,7 +50,7 @@ pub trait Context {
type NodeLabel; type NodeLabel;
/// The type of the value that gets assigned to each node. /// The type of the value that gets assigned to each node.
/// The values should form a partially ordered set. /// The values should form a partially ordered set.
type NodeValue: PartialEq + Eq; type NodeValue: PartialEq + Eq + Clone;
/// Get the graph on which the fixpoint computation operates. /// Get the graph on which the fixpoint computation operates.
fn get_graph(&self) -> &DiGraph<Self::NodeLabel, Self::EdgeLabel>; fn get_graph(&self) -> &DiGraph<Self::NodeLabel, Self::EdgeLabel>;
...@@ -91,8 +92,6 @@ pub struct Computation<T: Context> { ...@@ -91,8 +92,6 @@ pub struct Computation<T: Context> {
priority_to_node_list: Vec<NodeIndex>, priority_to_node_list: Vec<NodeIndex>,
/// The worklist contains the priority numbers (not the node indices!) of nodes marked as not yet stabilized. /// The worklist contains the priority numbers (not the node indices!) of nodes marked as not yet stabilized.
worklist: BTreeSet<usize>, worklist: BTreeSet<usize>,
/// The (optional) default value assigned to nodes without an explicit value.
default_value: Option<T::NodeValue>,
/// The internal map containing all known node values. /// The internal map containing all known node values.
node_values: FnvHashMap<NodeIndex, T::NodeValue>, node_values: FnvHashMap<NodeIndex, T::NodeValue>,
} }
...@@ -126,9 +125,11 @@ impl<T: Context> Computation<T> { ...@@ -126,9 +125,11 @@ impl<T: Context> Computation<T> {
let node_priority_list: Vec<usize> = node_to_index.values().copied().collect(); let node_priority_list: Vec<usize> = node_to_index.values().copied().collect();
let mut worklist = BTreeSet::new(); let mut worklist = BTreeSet::new();
// If a default value exists, all nodes are added to the worklist. If not, the worklist is empty // If a default value exists, all nodes are added to the worklist. If not, the worklist is empty
if default_value.is_some() { let mut node_values: FnvHashMap<NodeIndex, T::NodeValue> = FnvHashMap::default();
if let Some(default) = default_value {
for i in 0..priority_sorted_nodes.len() { for i in 0..priority_sorted_nodes.len() {
worklist.insert(i); worklist.insert(i);
node_values.insert(NodeIndex::new(i), default.clone());
} }
} }
Computation { Computation {
...@@ -136,8 +137,7 @@ impl<T: Context> Computation<T> { ...@@ -136,8 +137,7 @@ impl<T: Context> Computation<T> {
node_priority_list, node_priority_list,
priority_to_node_list: priority_sorted_nodes, priority_to_node_list: priority_sorted_nodes,
worklist, worklist,
default_value, node_values,
node_values: FnvHashMap::default(),
} }
} }
...@@ -169,11 +169,7 @@ impl<T: Context> Computation<T> { ...@@ -169,11 +169,7 @@ impl<T: Context> Computation<T> {
/// Get the value of a node. /// Get the value of a node.
pub fn get_node_value(&self, node: NodeIndex) -> Option<&T::NodeValue> { pub fn get_node_value(&self, node: NodeIndex) -> Option<&T::NodeValue> {
if let Some(value) = self.node_values.get(&node) { self.node_values.get(&node)
Some(value)
} else {
self.default_value.as_ref()
}
} }
/// Set the value of a node and mark the node as not yet stabilized. /// Set the value of a node and mark the node as not yet stabilized.
...@@ -337,6 +333,27 @@ mod tests { ...@@ -337,6 +333,27 @@ mod tests {
} }
#[test] #[test]
fn fixpoint_with_default_value() {
let mut graph: DiGraph<(), u64> = DiGraph::new();
for _i in 0..101 {
graph.add_node(());
}
for i in 0..100 {
graph.add_edge(NodeIndex::new(i), NodeIndex::new(i + 1), i as u64 % 10 + 1);
}
for i in 0..10 {
graph.add_edge(NodeIndex::new(i * 10), NodeIndex::new(i * 10 + 5), 0);
}
let mut solution = Computation::new(FPContext { graph }, Some(100));
solution.set_node_value(NodeIndex::new(10), 0);
solution.compute_with_max_steps(20);
assert_eq!(100, *solution.get_node_value(NodeIndex::new(0)).unwrap());
assert_eq!(3, *solution.get_node_value(NodeIndex::new(12)).unwrap());
}
#[test]
fn worklist_node_order() { fn worklist_node_order() {
let mut graph: DiGraph<(), u64> = DiGraph::new(); let mut graph: DiGraph<(), u64> = DiGraph::new();
for _i in 0..21 { for _i in 0..21 {
......
...@@ -14,8 +14,8 @@ use crate::prelude::*; ...@@ -14,8 +14,8 @@ use crate::prelude::*;
/// The interprocedural_flow value will either be transferred from the end of the called subroutine /// The interprocedural_flow value will either be transferred from the end of the called subroutine
/// to the return site in case of a forward analysis or from the beginning of the called subroutine /// to the return site in case of a forward analysis or from the beginning of the called subroutine
/// to the callsite in a backward analysis. /// to the callsite in a backward analysis.
#[derive(PartialEq, Eq, Serialize, Deserialize)] #[derive(PartialEq, Eq, Serialize, Deserialize, Clone)]
pub enum NodeValue<T: PartialEq + Eq> { pub enum NodeValue<T: PartialEq + Eq + Clone> {
/// A single abstract value /// A single abstract value
Value(T), Value(T),
/// The value saved at artificial combinator nodes. /// The value saved at artificial combinator nodes.
...@@ -29,7 +29,7 @@ pub enum NodeValue<T: PartialEq + Eq> { ...@@ -29,7 +29,7 @@ pub enum NodeValue<T: PartialEq + Eq> {
}, },
} }
impl<T: PartialEq + Eq> NodeValue<T> { impl<T: PartialEq + Eq + Clone> NodeValue<T> {
/// Unwraps the contained value for non-combinator nodes. /// Unwraps the contained value for non-combinator nodes.
/// Panics if given a combinator value of an artificial node. /// Panics if given a combinator value of an artificial node.
pub fn unwrap_value(&self) -> &T { pub fn unwrap_value(&self) -> &T {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment