Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft of how I think fake edges should work #88

Draft
wants to merge 4 commits into
base: pdg
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 48 additions & 29 deletions crates/flowistry/src/pdg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use log::{debug, trace};
use petgraph::graph::DiGraph;
use rustc_abi::FieldIdx;
use rustc_ast::Mutability;

Check warning on line 10 in crates/flowistry/src/pdg/construct.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `rustc_ast::Mutability`

Check warning on line 10 in crates/flowistry/src/pdg/construct.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `rustc_ast::Mutability`
use rustc_borrowck::consumers::{
places_conflict, BodyWithBorrowckFacts, PlaceConflictBias,
};
Expand All @@ -18,7 +18,7 @@
use rustc_middle::{
mir::{
visit::Visitor, AggregateKind, BasicBlock, Body, Location, Operand, Place, PlaceElem,
Rvalue, Statement, StatementKind, Terminator, TerminatorKind, RETURN_PLACE,
Rvalue, Statement, StatementKind, Terminator, TerminatorKind, RETURN_PLACE
},
ty::{GenericArg, GenericArgsRef, List, ParamEnv, TyCtxt, TyKind},
};
Expand Down Expand Up @@ -682,42 +682,31 @@
// Find every reference to a parent-able node in the child's graph.
let is_arg = |node: &DepNode<'tcx>| {
node.at.leaf().function == child_constructor.def_id
&& (node.place.local == RETURN_PLACE
&& (node.place.local == RETURN_PLACE
|| node.place.is_arg(&child_constructor.body))
};
let parentable_srcs = child_graph
.edges
.iter()
.map(|(src, _, _)| *src)
.filter(is_arg)
.filter(|node| node.at.leaf().location.is_start());
// An attempt at getting immutable arguments to connect
let parentable_srcs = if self.params.false_call_edges {
let dummy_state = PartialGraph::default();
let constructor_ref = &child_constructor;
Either::Right(child_constructor.body.args_iter()
.map(|local| Place::from(local))
.flat_map(move |place| constructor_ref.find_data_inputs(&dummy_state, place)))
} else {
Either::Left(child_graph
.edges
.iter()
.map(|(src, _, _)| *src)
.filter(is_arg)
.filter(|node| node.at.leaf().location.is_start()))
};
let parentable_dsts = child_graph
.edges
.iter()
.map(|(_, dst, _)| *dst)
.filter(is_arg)
.filter(|node| node.at.leaf().location.is_end());

if self.params.false_call_edges {
for arg in args {
if let Some(place) = arg.place() {
for mut_ref in self.place_info.reachable_values(place, Mutability::Mut) {
if mut_ref.ty(&parent_body.local_decls, tcx).ty.is_ref() {
debug!("false mutation");
let mutated = tcx.mk_place_deref(*mut_ref);
self.apply_mutation(
state,
location,
Either::Left(mutated),
Either::Left(vec![mutated]),
MutationStatus::Possibly,
);
}
}
}
}
}

// For each source node CHILD that is parentable to PLACE,
// add an edge from PLACE -> CHILD.
for child_src in parentable_srcs {
Expand Down Expand Up @@ -874,6 +863,29 @@

let empty = PartialGraph::default();
let mut domains = IndexVec::from_elem_n(empty.clone(), bb_graph.len());

if self.params.false_call_edges {
let start_domain = &mut domains[0_u32.into()];
for arg in self.body.args_iter() {
let place = Place::from(arg);
for mutation in self.find_data_inputs(start_domain, place) {
start_domain.last_mutation
.entry(mutation.place)
.or_default()
.insert(RichLocation::Start);
}
// for child in self.place_info.children(place).iter().copied() {
// let ty = child.ty(self.body.as_ref(), self.tcx);
// if !ty.ty.is_mutable_ptr() {
// continue;
// }
// let target = child.project_deeper(&[PlaceElem::Deref], self.tcx);
// let initial = start_domain.last_mutation.entry(target).or_default();
// initial.insert(RichLocation::Start);
// }
}
}

for block in blocks {
for parent in bb_graph.predecessors()[block].iter() {
let (child, parent) = domains.pick2_mut(block, *parent);
Expand All @@ -900,7 +912,7 @@
for block in all_returns {
let return_state = &domains[block];
for (place, locations) in &return_state.last_mutation {
if place.local == RETURN_PLACE || place.is_arg(&self.body) {
if place.local == RETURN_PLACE || self.is_ptr_argument_mutation(*place) {
for location in locations {
let src = self.make_dep_node(*place, *location);
let dst = self.make_dep_node(*place, RichLocation::End);
Expand All @@ -916,6 +928,13 @@
final_state
}

fn is_ptr_argument_mutation(&self, place: Place<'tcx>) -> bool {
place.is_arg(&self.body)
// && place.iter_projections().any(|(place_ref, projection)|
// projection == PlaceElem::Deref && place_ref.ty(self.body.as_ref(), self.tcx).ty.is_mutable_ptr()
// )
}

fn domain_to_petgraph(self, domain: PartialGraph<'tcx>) -> DepGraph<'tcx> {
let mut graph: DiGraph<DepNode<'tcx>, DepEdge> = DiGraph::new();
let mut nodes = FxHashMap::default();
Expand Down
127 changes: 117 additions & 10 deletions crates/flowistry/tests/pdg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,48 @@ fn main() {
})
}

#[test]
fn test_fields_passing_through() {

let input = r#"
use std::num::Wrapping;
fn pass(x2: &Wrapping<u32>) {
println!("{}", x2);
}

fn main() {
let ref mut i = Wrapping(0);
let y = 10;
i.0 += y;
pass(i);
}
"#;
let _ = env_logger::try_init();
flowistry::test_utils::compile(input, move |tcx| {
let def_id = get_main(tcx);
let params = PdgParams::new(tcx, def_id);

let with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges());

with_edges.generate_graphviz(
"graph.pdf"
).unwrap();


assert!(connects(
tcx,
&with_edges,
"y",
"*x2",
None
));
})
}

#[test]
fn false_call_edges() {
let input = r#"
fn does_not_mutate(x: &mut i32) {}
fn does_not_mutate(x2: &mut i32) {}

fn main() {
let mut x = 0;
Expand All @@ -521,22 +559,91 @@ fn main() {
let def_id = get_main(tcx);
let params = PdgParams::new(tcx, def_id);

let without_edges = flowistry::pdg::compute_pdg(params.clone());
assert!(!connects(
tcx,
&without_edges,
"x",
"y",
Some("does_not_mutate")
));
// let without_edges = flowistry::pdg::compute_pdg(params.clone());
// assert!(!connects(
// tcx,
// &without_edges,
// "x",
// "y",
// None
// ));

let with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges());

with_edges.generate_graphviz(
"graph.pdf"
).unwrap();
assert!(connects(
tcx,
&with_edges,
"x",
"*x2",
None
));
assert!(connects(
tcx,
&with_edges,
"*x2",
"y",
Some("does_not_mutate")
None
));
})
}

#[test]
fn false_call_edges_2() {
let input = r#"
struct UserData {
pub data: Vec<i64>,
}
fn get_user_data() -> UserData {
return UserData {
data: vec![1, 2, 3],
};
}
fn send_user_data(user_data: &UserData) {}
fn send2(user_data2: &UserData) {}
fn modify_vec(v: &mut [i64]) {}
fn main() {
let ref mut p = get_user_data();
modify_vec(&mut p.data);
send_user_data(p);
}
"#;

let _ = env_logger::try_init();
flowistry::test_utils::compile(input, move |tcx| {
let def_id = get_main(tcx);
let params = PdgParams::new(tcx, def_id);

let ref with_edges = flowistry::pdg::compute_pdg(params.with_false_call_edges());

with_edges.generate_graphviz(
"graph.pdf"
).unwrap();

println!("Running in {}", std::env::current_dir().unwrap().display());

assert!(connects(
tcx,
with_edges,
"RETURN.data",
"*v",
None,
));
assert!(connects(
tcx,
with_edges,
"*v",
"user_data",
None,
));
assert!(!connects(
tcx,
with_edges,
"user_data",
"user_data2",
None,
));
});
}
Loading