Skip to content

Commit

Permalink
analyze: refactor struct and static rewrites (#1005)
Browse files Browse the repository at this point in the history
This branch refactors rewrite generation for structs and statics to make
it easier for `main.rs` to control exactly which items get rewritten.
This is in preparation for adding a filtering option that can disable
rewriting for any `DefId`. In particular, rewriting of structs and
fields has been separated from rewriting of function signatures, and
static rewriting has been broken up, so that all
`rewrite::gen_*_rewrites` functions work on a single item per call
rather than internally iterating over all relevant items.
  • Loading branch information
spernsteiner authored Jul 31, 2023
2 parents 339629c + 8ae7615 commit 9464931
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 221 deletions.
109 changes: 97 additions & 12 deletions c2rust-analyze/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ fn run(tcx: TyCtxt) {
eprintln!(" {:?}", ldid);
}

// ----------------------------------
// Label all global types
// ----------------------------------

// Assign global `PointerId`s for all pointers that appear in function signatures.
for &ldid in &all_fn_ldids {
let sig = tcx.fn_sig(ldid.to_def_id());
Expand Down Expand Up @@ -452,6 +456,10 @@ fn run(tcx: TyCtxt) {
gacx.assign_pointer_to_fields(did);
}

// ----------------------------------
// Compute dataflow constraints
// ----------------------------------

// Initial pass to assign local `PointerId`s and gather equivalence constraints, which state
// that two pointer types must be converted to the same reference type. Some additional data
// computed during this the process is kept around for use in later passes.
Expand Down Expand Up @@ -519,6 +527,10 @@ fn run(tcx: TyCtxt) {
func_info.insert(ldid, info);
}

// ----------------------------------
// Remap `PointerId`s by equivalence class
// ----------------------------------

// Remap pointers based on equivalence classes, so all members of an equivalence class now use
// the same `PointerId`.
let (global_counter, global_equiv_map) = global_equiv.renumber();
Expand All @@ -543,6 +555,10 @@ fn run(tcx: TyCtxt) {
info.local_equiv.clear();
}

// ----------------------------------
// Build initial assignment
// ----------------------------------

// Compute permission and flag assignments.

fn should_make_fixed(info: PointerInfo) -> bool {
Expand Down Expand Up @@ -593,7 +609,7 @@ fn run(tcx: TyCtxt) {
info.lasn.set(lasn);
}

// Process PDG
// Load permission info from PDG
let mut func_def_path_hash_to_ldid = HashMap::new();
for &ldid in &all_fn_ldids {
let def_path_hash: (u64, u64) = tcx.def_path_hash(ldid.to_def_id()).0.as_value();
Expand Down Expand Up @@ -705,11 +721,15 @@ fn run(tcx: TyCtxt) {
}
let lsig = match gacx.fn_sigs.get(&ldid.to_def_id()) {
Some(x) => x,
None => continue,
None => panic!("missing fn_sig for {:?}", ldid),
};
make_sig_fixed(&mut gasn, lsig);
}

// ----------------------------------
// Run dataflow solver and borrowck analysis
// ----------------------------------

// For testing, putting #[c2rust_analyze_test::fail_before_analysis] on a function marks it as
// failed at this point.
for &ldid in &all_fn_ldids {
Expand Down Expand Up @@ -799,6 +819,15 @@ fn run(tcx: TyCtxt) {
}
eprintln!("reached fixpoint in {} iterations", loop_count);

// Check that these perms haven't changed.
for (ptr, perms) in gacx.known_fn_ptr_perms() {
assert_eq!(perms, gasn.perms[ptr]);
}

// ----------------------------------
// Generate rewrites
// ----------------------------------

// For testing, putting #[c2rust_analyze_test::fail_before_rewriting] on a function marks it as
// failed at this point.
for &ldid in &all_fn_ldids {
Expand All @@ -811,11 +840,6 @@ fn run(tcx: TyCtxt) {
);
}

// Check that these perms haven't changed.
for (ptr, perms) in gacx.known_fn_ptr_perms() {
assert_eq!(perms, gasn.perms[ptr]);
}

// Buffer debug output for each function. Grouping together all the different types of info
// for a single function makes FileCheck tests easier to write.
let mut func_reports = HashMap::<LocalDefId, String>::new();
Expand Down Expand Up @@ -926,6 +950,56 @@ fn run(tcx: TyCtxt) {
}
}

// Generate rewrites for statics
let mut static_rewrites = Vec::new();
for (&def_id, &ptr) in gacx.addr_of_static.iter() {
static_rewrites.extend(rewrite::gen_static_rewrites(tcx, &gasn, def_id, ptr));
}
let mut statics_report = String::new();
writeln!(
statics_report,
"generated {} static rewrites:",
static_rewrites.len()
)
.unwrap();
for &(span, ref rw) in &static_rewrites {
writeln!(
statics_report,
" {}: {}",
describe_span(gacx.tcx, span),
rw
)
.unwrap();
}
all_rewrites.extend(static_rewrites);

// Generate rewrites for ADTs
let mut adt_reports = HashMap::<DefId, String>::new();
for &def_id in gacx.adt_metadata.table.keys() {
if gacx.foreign_mentioned_tys.contains(&def_id) {
eprintln!("Avoiding rewrite for foreign-mentioned type: {def_id:?}");
continue;
}

let adt_rewrites = rewrite::gen_adt_ty_rewrites(&gacx, &gasn, def_id);
let report = adt_reports.entry(def_id).or_default();
writeln!(
report,
"generated {} ADT rewrites for {:?}:",
adt_rewrites.len(),
def_id
)
.unwrap();
for &(span, ref rw) in &adt_rewrites {
writeln!(report, " {}: {}", describe_span(gacx.tcx, span), rw).unwrap();
}
all_rewrites.extend(adt_rewrites);
}

// ----------------------------------
// Print reports for tests and debugging
// ----------------------------------

// Print analysis results for each function in `all_fn_ldids`, going in declaration order.
// Concretely, we iterate over `body_owners()`, which is a superset of `all_fn_ldids`, and
// filter based on membership in `func_info`, which contains an entry for each ID in
Expand Down Expand Up @@ -993,7 +1067,9 @@ fn run(tcx: TyCtxt) {
&gasn.flags,
);
}
eprintln!("\n{statics_report}");

// Print results for ADTs and fields
eprintln!("\nfinal labeling for fields:");
let mut field_dids = gacx.field_ltys.keys().cloned().collect::<Vec<_>>();
field_dids.sort();
Expand All @@ -1008,16 +1084,25 @@ fn run(tcx: TyCtxt) {
}
}

let static_rewrites = rewrite::gen_static_rewrites(&gacx, &gasn);
eprintln!("generated {} static rewrites:", static_rewrites.len());
for &(span, ref rw) in &static_rewrites {
eprintln!(" {}: {}", describe_span(gacx.tcx, span), rw);
let mut adt_dids = gacx.adt_metadata.table.keys().cloned().collect::<Vec<_>>();
adt_dids.sort();
for did in adt_dids {
if let Some(report) = adt_reports.remove(&did) {
eprintln!("\n{}", report);
}
}
all_rewrites.extend(static_rewrites);

// ----------------------------------
// Apply rewrites
// ----------------------------------

// Apply rewrite to all functions at once.
rewrite::apply_rewrites(tcx, all_rewrites);

// ----------------------------------
// Report caught panics
// ----------------------------------

// Report errors that were caught previously
eprintln!("\nerror details:");
for ldid in tcx.hir().body_owners() {
Expand Down
2 changes: 1 addition & 1 deletion c2rust-analyze/src/rewrite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use self::shim::{gen_shim_call_rewrites, gen_shim_definition_rewrite};
use self::span_index::SpanIndex;
pub use self::statics::gen_static_rewrites;
pub use self::ty::dump_rewritten_local_tys;
pub use self::ty::gen_ty_rewrites;
pub use self::ty::{gen_adt_ty_rewrites, gen_ty_rewrites};

#[derive(Clone, PartialEq, Eq, Debug)]
pub enum LifetimeName {
Expand Down
80 changes: 40 additions & 40 deletions c2rust-analyze/src/rewrite/statics.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
use crate::context::PermissionSet;
use crate::pointer_id::PointerId;
use crate::rewrite::Rewrite;
use crate::{GlobalAnalysisCtxt, GlobalAssignment};
use crate::GlobalAssignment;
use rustc_hir::def_id::DefId;
use rustc_hir::{ItemKind, Mutability, Node};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;

/// For every static, if its write permission does not match its declared mutability, emit a rewrite
/// changing the declaration to match observed/analyzed usage.
pub fn gen_static_rewrites<'tcx>(
gacx: &GlobalAnalysisCtxt<'tcx>,
tcx: TyCtxt<'tcx>,
gasn: &GlobalAssignment,
) -> Vec<(Span, Rewrite)> {
let mut hir_rewrites = Vec::new();
for (did, &ptr) in gacx.addr_of_static.iter() {
// The map of statics and their ty + permissions tracks statics by did; map this to an Item
// node to look at the static's spans and declared mutability.
let item = if let Some(Node::Item(item)) = gacx.tcx.hir().get_if_local(*did) {
item
} else {
panic!("def id {:?} not found", did);
};
if let ItemKind::Static(_ty, mutbl, _body_id) = item.kind {
let perms = gasn.perms[ptr];
let written_to = perms.contains(PermissionSet::WRITE);
let is_mutable = mutbl == Mutability::Mut;
if written_to != is_mutable {
let ident = gacx
.tcx
.opt_item_ident(*did)
.expect("did not find ident when trying to generate rewrite for static item");
// Generate a span from beginning of ident to end of body.
let span = ident.span.with_hi(item.span.hi());
hir_rewrites.push((
item.span,
Rewrite::StaticMut(
if written_to {
Mutability::Mut
} else {
Mutability::Not
},
span,
),
))
}
} else {
panic!("expected item {:?} to be a `static`", item);
}
def_id: DefId,
ptr: PointerId,
) -> Option<(Span, Rewrite)> {
// The map of statics and their ty + permissions tracks statics by DefId; map this to an Item
// node to look at the static's spans and declared mutability.
let item = if let Some(Node::Item(item)) = tcx.hir().get_if_local(def_id) {
item
} else {
panic!("def id {:?} not found", def_id);
};
let is_mutable = match item.kind {
ItemKind::Static(_ty, mutbl, _body_id) => mutbl == Mutability::Mut,
_ => panic!("expected item {:?} to be a `static`", item),
};
let perms = gasn.perms[ptr];
let written_to = perms.contains(PermissionSet::WRITE);
if written_to != is_mutable {
let ident = tcx
.opt_item_ident(def_id)
.expect("def_id has no ident when trying to generate rewrite for static item");
// Generate a span from beginning of ident to end of body.
let span = ident.span.with_hi(item.span.hi());
Some((
item.span,
Rewrite::StaticMut(
if written_to {
Mutability::Mut
} else {
Mutability::Not
},
span,
),
))
} else {
None
}

hir_rewrites
}
Loading

0 comments on commit 9464931

Please sign in to comment.