Skip to content

Commit

Permalink
analyze: use a common namespace for all local and global PointerIds (#…
Browse files Browse the repository at this point in the history
…1164)

Currently, each function has its own namespace for local `PointerId`s:
`PointerId::local(3)` in function `f` is distinct from
`PointerId::local(3)` in function `g`. This creates some problems for
pointee type inference*. This branch puts all functions' local
`PointerId`s into a common namespace, with each function occupying a
different range in that space. This means each `PointerId` is now
globally unique, and code that is analyzing one function can mention
`PointerId`s from a different function without ambiguity.

Most analyses still only look at a single function; these use a sparse
`PointerTable` that has entries only for global `PointerId`s (that is,
`PointerId`s in the range allocated for globals) and for the local
`PointerId`s of the current function. Any analyses that need to consider
cross-function local `PointerId`s can use a single large
`GlobalPointerTable` instead.

---

\* The specific issue is this: the allocation in lighttpd's
`buffer_init` is not initialized inside that function, so correctly
inferring the type requires unifying type variables interprocedurally.
The type variables for the pointee analysis are tracked in a `VarTable`,
which contains `LTy`s. We'd like to track variables from all functions
in one big `VarTable`, so that we can unify variables that originated in
different functions. However, `LTy`s may contain local `PointerId`s, so
a shared `VarTable` could mix up local `PointerId`s from different
functions/namespaces, producing nonsensical results. The fix being
applied here is to put all `PointerId`s into a single namespace, so all
type variables can be tracked in a common `VarTable`, and there is no
longer an obstacle to unifying type variables from different functions.
  • Loading branch information
spernsteiner authored Dec 2, 2024
2 parents 5830b4f + 2149fa6 commit ee4b3cc
Show file tree
Hide file tree
Showing 13 changed files with 647 additions and 481 deletions.
563 changes: 303 additions & 260 deletions c2rust-analyze/src/analyze.rs

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions c2rust-analyze/src/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::context::AdtMetadataTable;
use crate::context::{AnalysisCtxt, PermissionSet};
use crate::dataflow::DataflowConstraints;
use crate::labeled_ty::{LabeledTy, LabeledTyCtxt};
use crate::pointer_id::{PointerTable, PointerTableMut};
use crate::pointer_id::GlobalPointerTable;
use crate::util::{describe_rvalue, RvalueDesc};
use indexmap::{IndexMap, IndexSet};
use log::{debug, info, warn};
Expand Down Expand Up @@ -121,8 +121,8 @@ impl std::fmt::Debug for OriginParam {
pub fn borrowck_mir<'tcx>(
acx: &AnalysisCtxt<'_, 'tcx>,
dataflow: &DataflowConstraints,
hypothesis: &mut PointerTableMut<PermissionSet>,
updates_forbidden: &PointerTable<PermissionSet>,
hypothesis: &mut GlobalPointerTable<PermissionSet>,
updates_forbidden: &GlobalPointerTable<PermissionSet>,
name: &str,
mir: &Body<'tcx>,
field_ltys: HashMap<DefId, context::LTy<'tcx>>,
Expand Down Expand Up @@ -201,7 +201,7 @@ pub fn borrowck_mir<'tcx>(

fn run_polonius<'tcx>(
acx: &AnalysisCtxt<'_, 'tcx>,
hypothesis: &PointerTableMut<PermissionSet>,
hypothesis: &GlobalPointerTable<PermissionSet>,
name: &str,
mir: &Body<'tcx>,
field_ltys: &HashMap<DefId, context::LTy<'tcx>>,
Expand Down Expand Up @@ -617,7 +617,7 @@ fn construct_adt_origins<'tcx>(

fn assign_origins<'tcx>(
ltcx: LTyCtxt<'tcx>,
hypothesis: &PointerTableMut<PermissionSet>,
hypothesis: &GlobalPointerTable<PermissionSet>,
_facts: &mut AllFacts,
maps: &mut AtomMaps<'tcx>,
adt_metadata: &AdtMetadataTable<'tcx>,
Expand Down
6 changes: 3 additions & 3 deletions c2rust-analyze/src/borrowck/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::borrowck::{assign_origins, construct_adt_origins, LTy, LTyCtxt, Label
use crate::context::{const_alloc_id, find_static_for_alloc};
use crate::context::{AnalysisCtxt, PermissionSet};
use crate::panic_detail;
use crate::pointer_id::PointerTableMut;
use crate::pointer_id::GlobalPointerTable;
use crate::util::{self, ty_callee, Callee};
use assert_matches::assert_matches;
use indexmap::IndexMap;
Expand All @@ -29,7 +29,7 @@ struct TypeChecker<'tcx, 'a> {
local_ltys: &'a [LTy<'tcx>],
rvalue_ltys: &'a HashMap<Location, LTy<'tcx>>,
field_permissions: &'a HashMap<DefId, PermissionSet>,
hypothesis: &'a PointerTableMut<'a, PermissionSet>,
hypothesis: &'a GlobalPointerTable<PermissionSet>,
local_decls: &'a IndexVec<Local, LocalDecl<'tcx>>,
current_location: Location,
static_origin: Origin,
Expand Down Expand Up @@ -628,7 +628,7 @@ pub fn visit_body<'tcx>(
local_ltys: &[LTy<'tcx>],
rvalue_ltys: &HashMap<Location, LTy<'tcx>>,
field_permissions: &HashMap<DefId, PermissionSet>,
hypothesis: &PointerTableMut<PermissionSet>,
hypothesis: &GlobalPointerTable<PermissionSet>,
mir: &Body<'tcx>,
static_origin: Origin,
) {
Expand Down
141 changes: 64 additions & 77 deletions c2rust-analyze/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use crate::borrowck::{AdtMetadata, FieldMetadata, OriginArg, OriginParam};
use crate::known_fn::{all_known_fns, KnownFn};
use crate::labeled_ty::{LabeledTy, LabeledTyCtxt};
use crate::panic_detail::PanicDetail;
use crate::pointer_id::{
GlobalPointerTable, LocalPointerTable, NextGlobalPointerId, NextLocalPointerId, PointerTable,
PointerTableMut,
};
use crate::pointer_id::{GlobalPointerTable, LocalPointerTable, PointerTable, PointerTableMut};
use crate::util::{self, describe_rvalue, PhantomLifetime, RvalueDesc};
use assert_matches::assert_matches;
use bitflags::bitflags;
Expand Down Expand Up @@ -409,6 +406,8 @@ pub struct GlobalAnalysisCtxt<'tcx> {
pub lcx: LTyCtxt<'tcx>,

ptr_info: GlobalPointerTable<PointerInfo>,
/// Total number of global and local pointers across all functions.
num_total_pointers: usize,

pub fn_sigs: HashMap<DefId, LFnSig<'tcx>>,
pub fn_fields_used: MultiMap<LocalDefId, LocalDefId>,
Expand Down Expand Up @@ -801,6 +800,7 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
tcx,
lcx: LabeledTyCtxt::new(tcx),
ptr_info: GlobalPointerTable::empty(),
num_total_pointers: 0,
fn_sigs: HashMap::new(),
fn_fields_used: MultiMap::new(),
known_fns: all_known_fns()
Expand Down Expand Up @@ -834,8 +834,12 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
self.fn_origins = fn_origin_args_params(self.tcx, &self.adt_metadata);
}

pub fn function_context<'a>(&'a mut self, mir: &'a Body<'tcx>) -> AnalysisCtxt<'a, 'tcx> {
AnalysisCtxt::new(self, mir)
pub fn function_context<'a>(
&'a mut self,
mir: &'a Body<'tcx>,
local_ptr_base: u32,
) -> AnalysisCtxt<'a, 'tcx> {
AnalysisCtxt::new(self, mir, local_ptr_base)
}

pub fn function_context_with_data<'a>(
Expand All @@ -850,26 +854,35 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
self.ptr_info.push(info)
}

pub fn num_pointers(&self) -> usize {
pub fn num_global_pointers(&self) -> usize {
self.ptr_info.len()
}

pub fn num_total_pointers(&self) -> usize {
self.num_total_pointers
}

pub fn set_num_total_pointers(&mut self, n: usize) {
assert_eq!(
self.num_total_pointers, 0,
"num_total_pointers has already been set"
);
self.num_total_pointers = n;
}

pub fn ptr_info(&self) -> &GlobalPointerTable<PointerInfo> {
&self.ptr_info
}

/// Update all [`PointerId`]s in `self`, replacing each `p` with `map[p]`. Also sets the "next
/// [`PointerId`]" counter to `counter`. `map` and `counter` are usually computed together via
/// [`GlobalEquivSet::renumber`][crate::equiv::GlobalEquivSet::renumber].
pub fn remap_pointers(
&mut self,
map: &GlobalPointerTable<PointerId>,
counter: NextGlobalPointerId,
) {
pub fn remap_pointers(&mut self, map: &GlobalPointerTable<PointerId>, count: usize) {
let GlobalAnalysisCtxt {
tcx: _,
lcx,
ref mut ptr_info,
num_total_pointers: _,
ref mut fn_sigs,
fn_fields_used: _,
known_fns: _,
Expand All @@ -886,7 +899,7 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
foreign_mentioned_tys: _,
} = *self;

*ptr_info = remap_global_ptr_info(ptr_info, map, counter.num_pointers());
*ptr_info = remap_global_ptr_info(ptr_info, map, count);

for sig in fn_sigs.values_mut() {
sig.inputs = lcx.mk_slice(
Expand Down Expand Up @@ -981,16 +994,21 @@ impl<'tcx> GlobalAnalysisCtxt<'tcx> {
.get(def_id)
.intersects(DontRewriteFnReason::ANALYSIS_INVALID_MASK)
}

pub fn ptr_is_global(&self, ptr: PointerId) -> bool {
self.ptr_info.contains(ptr)
}
}

impl<'a, 'tcx> AnalysisCtxt<'a, 'tcx> {
pub fn new(
gacx: &'a mut GlobalAnalysisCtxt<'tcx>,
mir: &'a Body<'tcx>,
local_ptr_base: u32,
) -> AnalysisCtxt<'a, 'tcx> {
AnalysisCtxt {
gacx,
ptr_info: LocalPointerTable::empty(),
ptr_info: LocalPointerTable::empty(local_ptr_base),
local_decls: &mir.local_decls,
local_tys: IndexVec::new(),
addr_of_local: IndexVec::new(),
Expand Down Expand Up @@ -1056,10 +1074,18 @@ impl<'a, 'tcx> AnalysisCtxt<'a, 'tcx> {
self.gacx.ptr_info.and_mut(&mut self.ptr_info)
}

pub fn _local_ptr_info(&self) -> &LocalPointerTable<PointerInfo> {
pub fn local_ptr_info(&self) -> &LocalPointerTable<PointerInfo> {
&self.ptr_info
}

pub fn local_ptr_base(&self) -> u32 {
self.ptr_info.base()
}

pub fn ptr_is_global(&self, ptr: PointerId) -> bool {
self.gacx.ptr_is_global(ptr)
}

pub fn type_of<T: TypeOf<'tcx>>(&self, x: T) -> LTy<'tcx> {
x.type_of(self)
}
Expand Down Expand Up @@ -1221,7 +1247,8 @@ impl<'tcx> AnalysisCtxtData<'tcx> {
&mut self,
gacx: &mut GlobalAnalysisCtxt<'tcx>,
map: PointerTable<PointerId>,
counter: NextLocalPointerId,
local_base: u32,
local_count: usize,
) {
let lcx = gacx.lcx;

Expand All @@ -1234,7 +1261,7 @@ impl<'tcx> AnalysisCtxtData<'tcx> {
} = self;

*ptr_info =
remap_local_ptr_info(ptr_info, &mut gacx.ptr_info, &map, counter.num_pointers());
remap_local_ptr_info(ptr_info, &mut gacx.ptr_info, &map, local_base, local_count);

for lty in local_tys {
*lty = remap_lty_pointers(lcx, &map, lty);
Expand All @@ -1258,6 +1285,10 @@ impl<'tcx> AnalysisCtxtData<'tcx> {
pub fn num_pointers(&self) -> usize {
self.ptr_info.len()
}

pub fn local_ptr_base(&self) -> u32 {
self.ptr_info.base()
}
}

/// For every [`PointerId`] `p` that appears in `lty`, replace `p` with `map[p]` (except that
Expand Down Expand Up @@ -1296,12 +1327,13 @@ fn remap_local_ptr_info(
old_local_ptr_info: &LocalPointerTable<PointerInfo>,
new_global_ptr_info: &mut GlobalPointerTable<PointerInfo>,
map: &PointerTable<PointerId>,
num_pointers: usize,
base: u32,
count: usize,
) -> LocalPointerTable<PointerInfo> {
let mut new_local_ptr_info = LocalPointerTable::<PointerInfo>::new(num_pointers);
let mut new_local_ptr_info = LocalPointerTable::<PointerInfo>::new(base, count);
let mut new_ptr_info = new_global_ptr_info.and_mut(&mut new_local_ptr_info);
for (old, &new) in map.iter() {
if old.is_global() {
if !old_local_ptr_info.contains(old) {
// If `old` is global then `new` is also global, and this remapping was handled already
// by `remap_global_ptr_info`.
continue;
Expand Down Expand Up @@ -1427,78 +1459,33 @@ pub fn label_no_pointers<'tcx>(acx: &AnalysisCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> LT
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct GlobalAssignment {
pub struct Assignment {
pub perms: GlobalPointerTable<PermissionSet>,
pub flags: GlobalPointerTable<FlagSet>,
}

impl GlobalAssignment {
pub fn new(
len: usize,
default_perms: PermissionSet,
default_flags: FlagSet,
) -> GlobalAssignment {
GlobalAssignment {
impl Assignment {
pub fn new(len: usize, default_perms: PermissionSet, default_flags: FlagSet) -> Assignment {
Assignment {
perms: GlobalPointerTable::from_raw(vec![default_perms; len]),
flags: GlobalPointerTable::from_raw(vec![default_flags; len]),
}
}

pub fn and<'a>(&'a mut self, local: &'a mut LocalAssignment) -> Assignment<'a> {
Assignment {
global: self,
local,
}
}
}

#[derive(Clone, Debug)]
pub struct LocalAssignment {
pub perms: LocalPointerTable<PermissionSet>,
pub flags: LocalPointerTable<FlagSet>,
}

impl LocalAssignment {
pub fn new(
len: usize,
default_perms: PermissionSet,
default_flags: FlagSet,
) -> LocalAssignment {
LocalAssignment {
perms: LocalPointerTable::from_raw(vec![default_perms; len]),
flags: LocalPointerTable::from_raw(vec![default_flags; len]),
}
}
}

pub struct Assignment<'a> {
pub global: &'a mut GlobalAssignment,
local: &'a mut LocalAssignment,
}

impl Assignment<'_> {
pub fn perms(&self) -> PointerTable<PermissionSet> {
self.global.perms.and(&self.local.perms)
}

pub fn perms_mut(&mut self) -> PointerTableMut<PermissionSet> {
self.global.perms.and_mut(&mut self.local.perms)
pub fn perms(&self) -> &GlobalPointerTable<PermissionSet> {
&self.perms
}

pub fn flags(&self) -> PointerTable<FlagSet> {
self.global.flags.and(&self.local.flags)
pub fn perms_mut(&mut self) -> &mut GlobalPointerTable<PermissionSet> {
&mut self.perms
}

#[allow(dead_code)]
pub fn _flags_mut(&mut self) -> PointerTableMut<FlagSet> {
self.global.flags.and_mut(&mut self.local.flags)
pub fn flags(&self) -> &GlobalPointerTable<FlagSet> {
&self.flags
}

pub fn all_mut(&mut self) -> (PointerTableMut<PermissionSet>, PointerTableMut<FlagSet>) {
(
self.global.perms.and_mut(&mut self.local.perms),
self.global.flags.and_mut(&mut self.local.flags),
)
pub fn _flags_mut(&mut self) -> &mut GlobalPointerTable<FlagSet> {
&mut self.flags
}
}

Expand Down
Loading

0 comments on commit ee4b3cc

Please sign in to comment.