Skip to content

Commit 3628637

Browse files
committed
TB: Tree structure
1 parent 8c7104f commit 3628637

File tree

1 file changed

+215
-0
lines changed
  • src/tools/miri/src/borrow_tracker/tree_borrows

1 file changed

+215
-0
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
//! In this file we handle the "Tree" part of Tree Borrows, i.e. all tree
2+
//! traversal functions, optimizations to trim branches, and keeping track of
3+
//! the relative position of the access to each node being updated. This of course
4+
//! also includes the definition of the tree structure.
5+
//!
6+
//! Functions here manipulate permissions but are oblivious to them: as
7+
//! the internals of `Permission` are private, the update process is a black
8+
//! box. All we need to know here are
9+
//! - the fact that updates depend only on the old state, the status of protectors,
10+
//! and the relative position of the access;
11+
//! - idempotency properties asserted in `perms.rs` (for optimizations)
12+
13+
use smallvec::SmallVec;
14+
15+
use rustc_const_eval::interpret::InterpResult;
16+
use rustc_data_structures::fx::FxHashSet;
17+
use rustc_target::abi::Size;
18+
19+
use crate::borrow_tracker::tree_borrows::{
20+
diagnostics::{NodeDebugInfo, TbError, TransitionError},
21+
unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap},
22+
Permission,
23+
};
24+
use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};
25+
use crate::*;
26+
27+
/// Data for a single *location*.
28+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29+
pub(super) struct LocationState {
30+
/// This pointer's current permission
31+
permission: Permission,
32+
/// A location is initialized when it is child accessed for the first time,
33+
/// and it then stays initialized forever.
34+
/// Before initialization we still apply some preemptive transitions on
35+
/// `permission` to know what to do in case it ever gets initialized,
36+
/// but these can never cause any immediate UB. There can however be UB
37+
/// the moment we attempt to initalize (i.e. child-access) because some
38+
/// foreign access done between the creation and the initialization is
39+
/// incompatible with child accesses.
40+
initialized: bool,
41+
/// Strongest foreign access whose effects have already been applied to
42+
/// this node and all its children since the last child access.
43+
/// This is `None` if the most recent access is a child access,
44+
/// `Some(Write)` if at least one foreign write access has been applied
45+
/// since the previous child access, and `Some(Read)` if at least one
46+
/// foreign read and no foreign write have occurred since the last child access.
47+
latest_foreign_access: Option<AccessKind>,
48+
}
49+
50+
impl LocationState {
51+
/// Default initial state has never been accessed and has been subjected to no
52+
/// foreign access.
53+
fn new(permission: Permission) -> Self {
54+
Self { permission, initialized: false, latest_foreign_access: None }
55+
}
56+
57+
/// Record that this location was accessed through a child pointer by
58+
/// marking it as initialized
59+
fn with_access(mut self) -> Self {
60+
self.initialized = true;
61+
self
62+
}
63+
64+
pub fn is_initialized(&self) -> bool {
65+
self.initialized
66+
}
67+
68+
pub fn permission(&self) -> Permission {
69+
self.permission
70+
}
71+
}
72+
73+
/// Tree structure with both parents and children since we want to be
74+
/// able to traverse the tree efficiently in both directions.
75+
#[derive(Clone, Debug)]
76+
pub struct Tree {
77+
/// Mapping from tags to keys. The key obtained can then be used in
78+
/// any of the `UniValMap` relative to this allocation, i.e. both the
79+
/// `nodes` and `rperms` of the same `Tree`.
80+
/// The parent-child relationship in `Node` is encoded in terms of these same
81+
/// keys, so traversing the entire tree needs exactly one access to
82+
/// `tag_mapping`.
83+
pub(super) tag_mapping: UniKeyMap<BorTag>,
84+
/// All nodes of this tree.
85+
pub(super) nodes: UniValMap<Node>,
86+
/// Maps a tag and a location to a perm, with possible lazy
87+
/// initialization.
88+
///
89+
/// NOTE: not all tags registered in `nodes` are necessarily in all
90+
/// ranges of `rperms`, because `rperms` is in part lazily initialized.
91+
/// Just because `nodes.get(key)` is `Some(_)` does not mean you can safely
92+
/// `unwrap` any `perm.get(key)`.
93+
///
94+
/// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
95+
pub(super) rperms: RangeMap<UniValMap<LocationState>>,
96+
/// The index of the root node.
97+
pub(super) root: UniIndex,
98+
}
99+
100+
/// A node in the borrow tree. Each node is uniquely identified by a tag via
101+
/// the `nodes` map of `Tree`.
102+
#[derive(Clone, Debug)]
103+
pub(super) struct Node {
104+
/// The tag of this node.
105+
pub tag: BorTag,
106+
/// All tags except the root have a parent tag.
107+
pub parent: Option<UniIndex>,
108+
/// If the pointer was reborrowed, it has children.
109+
// FIXME: bench to compare this to FxHashSet and to other SmallVec sizes
110+
pub children: SmallVec<[UniIndex; 4]>,
111+
/// Either `Reserved` or `Frozen`, the permission this tag will be lazily initialized
112+
/// to on the first access.
113+
default_initial_perm: Permission,
114+
/// Some extra information useful only for debugging purposes
115+
pub debug_info: NodeDebugInfo,
116+
}
117+
118+
impl Tree {
119+
/// Create a new tree, with only a root pointer.
120+
pub fn new(root_tag: BorTag, size: Size) -> Self {
121+
let root_perm = Permission::new_root();
122+
let mut tag_mapping = UniKeyMap::default();
123+
let root_idx = tag_mapping.insert(root_tag);
124+
let nodes = {
125+
let mut nodes = UniValMap::<Node>::default();
126+
nodes.insert(
127+
root_idx,
128+
Node {
129+
tag: root_tag,
130+
parent: None,
131+
children: SmallVec::default(),
132+
default_initial_perm: root_perm,
133+
debug_info: NodeDebugInfo::new(root_tag),
134+
},
135+
);
136+
nodes
137+
};
138+
let rperms = {
139+
let mut perms = UniValMap::default();
140+
perms.insert(root_idx, LocationState::new(root_perm).with_access());
141+
RangeMap::new(size, perms)
142+
};
143+
Self { root: root_idx, nodes, rperms, tag_mapping }
144+
}
145+
}
146+
147+
impl<'tcx> Tree {
148+
/// Insert a new tag in the tree
149+
pub fn new_child(
150+
&mut self,
151+
parent_tag: BorTag,
152+
new_tag: BorTag,
153+
default_initial_perm: Permission,
154+
range: AllocRange,
155+
) -> InterpResult<'tcx> {
156+
assert!(!self.tag_mapping.contains_key(&new_tag));
157+
let idx = self.tag_mapping.insert(new_tag);
158+
let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
159+
// Create the node
160+
self.nodes.insert(
161+
idx,
162+
Node {
163+
tag: new_tag,
164+
parent: Some(parent_idx),
165+
children: SmallVec::default(),
166+
default_initial_perm,
167+
debug_info: NodeDebugInfo::new(new_tag),
168+
},
169+
);
170+
// Register new_tag as a child of parent_tag
171+
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
172+
// Initialize perms
173+
let perm = LocationState::new(default_initial_perm).with_access();
174+
for (_range, perms) in self.rperms.iter_mut(range.start, range.size) {
175+
perms.insert(idx, perm);
176+
}
177+
Ok(())
178+
}
179+
180+
/// Relative position of the access
181+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
182+
pub enum AccessRelatedness {
183+
/// The accessed pointer is the current one
184+
This,
185+
/// The accessed pointer is a (transitive) child of the current one.
186+
// Current pointer is excluded (unlike in some other places of this module
187+
// where "child" is inclusive).
188+
StrictChildAccess,
189+
/// The accessed pointer is a (transitive) parent of the current one.
190+
// Current pointer is excluded.
191+
AncestorAccess,
192+
/// The accessed pointer is neither of the above.
193+
// It's a cousin/uncle/etc., something in a side branch.
194+
// FIXME: find a better name ?
195+
DistantAccess,
196+
}
197+
198+
impl AccessRelatedness {
199+
/// Check that access is either Ancestor or Distant, i.e. not
200+
/// a transitive child (initial pointer included).
201+
pub fn is_foreign(self) -> bool {
202+
matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::DistantAccess)
203+
}
204+
205+
/// Given the AccessRelatedness for the parent node, compute the AccessRelatedness
206+
/// for the child node. This function assumes that we propagate away from the initial
207+
/// access.
208+
pub fn for_child(self) -> Self {
209+
use AccessRelatedness::*;
210+
match self {
211+
AncestorAccess | This => AncestorAccess,
212+
StrictChildAccess | DistantAccess => DistantAccess,
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)