Skip to content

Commit 65de226

Browse files
committed
add edges between operands, use MaybeBorrowedLocals as upper bound
1 parent 2f7aa5d commit 65de226

File tree

3 files changed

+151
-15
lines changed

3 files changed

+151
-15
lines changed

compiler/rustc_mir_dataflow/src/impls/live_borrows.rs

Lines changed: 143 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
use super::*;
135135

136136
use crate::framework::{Analysis, Results, ResultsCursor};
137+
use crate::impls::MaybeBorrowedLocals;
137138
use crate::{
138139
AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsRefCursor,
139140
};
@@ -145,6 +146,7 @@ use rustc_middle::mir::*;
145146
use rustc_middle::ty;
146147

147148
use either::Either;
149+
use std::cell::RefCell;
148150

149151
#[derive(Copy, Clone, Debug)]
150152
enum NodeKind {
@@ -234,6 +236,68 @@ impl<'a, 'tcx> BorrowDependencies<'a, 'tcx> {
234236
node_idx
235237
}
236238
}
239+
240+
/// Panics if `local` doesn't have a `Node` in `self.dep_graph`.
241+
fn get_node_idx_for_local(&self, local: Local) -> NodeIndex {
242+
let node_idx = self
243+
.locals_to_node_indexes
244+
.get(&local)
245+
.unwrap_or_else(|| bug!("expected {:?} to have a Node", local));
246+
247+
*node_idx
248+
}
249+
250+
#[instrument(skip(self), level = "debug")]
251+
fn local_is_ref_ptr_or_localwithrefs(&self, place: &Place<'tcx>) -> bool {
252+
let place_ty = place.ty(self.local_decls, self.tcx).ty;
253+
let is_ref_or_ptr = matches!(place_ty.kind(), ty::Ref(..) | ty::RawPtr(..));
254+
255+
// Also account for `LocalWithRef`s
256+
let is_local_with_refs =
257+
if let Some(node_idx) = self.locals_to_node_indexes.get(&place.local) {
258+
let node_for_place = self.dep_graph.node(*node_idx);
259+
debug!(?node_for_place.data);
260+
matches!(node_for_place.data, NodeKind::LocalWithRefs(_))
261+
} else {
262+
false
263+
};
264+
265+
debug!(?is_ref_or_ptr, ?is_local_with_refs);
266+
is_ref_or_ptr || is_local_with_refs
267+
}
268+
269+
#[instrument(skip(self), level = "debug")]
270+
fn maybe_create_edges_for_operands(&mut self, args: &Vec<Operand<'tcx>>) {
271+
for i in 0..args.len() {
272+
let outer_operand = &args[i];
273+
debug!(?outer_operand);
274+
match outer_operand {
275+
Operand::Copy(outer_place) | Operand::Move(outer_place) => {
276+
if self.local_is_ref_ptr_or_localwithrefs(outer_place) {
277+
for j in i + 1..args.len() {
278+
let inner_operand = &args[j];
279+
debug!(?inner_operand);
280+
match inner_operand {
281+
Operand::Copy(inner_place) | Operand::Move(inner_place) => {
282+
if self.local_is_ref_ptr_or_localwithrefs(inner_place) {
283+
let node_idx_outer =
284+
self.get_node_idx_for_local(outer_place.local);
285+
let node_idx_inner =
286+
self.get_node_idx_for_local(inner_place.local);
287+
288+
self.dep_graph.add_edge(node_idx_outer, node_idx_inner, ());
289+
self.dep_graph.add_edge(node_idx_inner, node_idx_outer, ());
290+
}
291+
}
292+
Operand::Constant(_) => {}
293+
}
294+
}
295+
}
296+
}
297+
Operand::Constant(_) => {}
298+
}
299+
}
300+
}
237301
}
238302

239303
impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
@@ -427,6 +491,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
427491
self.visit_operand(arg, location);
428492
}
429493

494+
// Additionally we have to introduce edges between borrowed operands, since we could
495+
// mutate those in the call (either through mutable references or interior mutability)
496+
self.maybe_create_edges_for_operands(args);
497+
430498
self.current_local = None;
431499
}
432500
TerminatorKind::Yield { resume_arg, value, .. } => {
@@ -444,7 +512,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
444512
}
445513
}
446514

447-
pub struct BorrowedLocalsResults<'mir, 'tcx> {
515+
pub struct BorrowedLocalsResults<'a, 'mir, 'tcx> {
448516
// the results of the liveness analysis of `LiveBorrows`
449517
borrows_analysis_results: Results<'tcx, LiveBorrows<'mir, 'tcx>>,
450518

@@ -453,28 +521,49 @@ pub struct BorrowedLocalsResults<'mir, 'tcx> {
453521
// exposed pointers or a composite value that might include refs, pointers or exposed pointers)
454522
// to the set of `Local`s that are borrowed through those references, pointers or composite values.
455523
borrowed_local_to_locals_to_keep_alive: FxHashMap<Local, Vec<Local>>,
524+
525+
maybe_borrowed_locals_results_cursor: RefCell<
526+
ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals, &'a Results<'tcx, MaybeBorrowedLocals>>,
527+
>,
456528
}
457529

458-
impl<'mir, 'tcx> BorrowedLocalsResults<'mir, 'tcx>
530+
impl<'a, 'mir, 'tcx> BorrowedLocalsResults<'a, 'mir, 'tcx>
459531
where
460532
'tcx: 'mir,
533+
'tcx: 'a,
461534
{
462-
fn new(borrows_analysis_results: Results<'tcx, LiveBorrows<'mir, 'tcx>>) -> Self {
535+
fn new(
536+
borrows_analysis_results: Results<'tcx, LiveBorrows<'mir, 'tcx>>,
537+
maybe_borrowed_locals_results_cursor: ResultsCursor<
538+
'mir,
539+
'tcx,
540+
MaybeBorrowedLocals,
541+
&'a Results<'tcx, MaybeBorrowedLocals>,
542+
>,
543+
) -> Self {
463544
let dep_graph = &borrows_analysis_results.analysis.borrow_deps.dep_graph;
464545
let borrowed_local_to_locals_to_keep_alive = Self::get_locals_to_keep_alive_map(dep_graph);
465-
Self { borrows_analysis_results, borrowed_local_to_locals_to_keep_alive }
546+
Self {
547+
borrows_analysis_results,
548+
borrowed_local_to_locals_to_keep_alive,
549+
maybe_borrowed_locals_results_cursor: RefCell::new(
550+
maybe_borrowed_locals_results_cursor,
551+
),
552+
}
466553
}
467554

468555
/// Uses the dependency graph to find all locals that we need to keep live for a given
469556
/// `Node` (or more specically the `Local` corresponding to that `Node`).
470-
fn get_locals_to_keep_alive_map<'a>(
471-
dep_graph: &'a Graph<NodeKind, ()>,
557+
#[instrument(skip(dep_graph), level = "debug")]
558+
fn get_locals_to_keep_alive_map<'b>(
559+
dep_graph: &'b Graph<NodeKind, ()>,
472560
) -> FxHashMap<Local, Vec<Local>> {
473561
let mut borrows_to_locals: FxHashMap<Local, Vec<Local>> = Default::default();
474562
let mut locals_visited = vec![];
475563

476564
for (node_idx, node) in dep_graph.enumerated_nodes() {
477565
let current_local = node.data.get_local();
566+
debug!(?current_local);
478567
if borrows_to_locals.get(&current_local).is_none() {
479568
Self::dfs_for_node(
480569
node_idx,
@@ -489,6 +578,7 @@ where
489578
borrows_to_locals
490579
}
491580

581+
#[instrument(skip(dep_graph), level = "debug")]
492582
fn dfs_for_node(
493583
node_idx: NodeIndex,
494584
borrows_to_locals: &mut FxHashMap<Local, Vec<Local>>,
@@ -499,6 +589,7 @@ where
499589
let current_local = src_node.data.get_local();
500590
locals_visited.push(current_local);
501591
if let Some(locals_to_keep_alive) = borrows_to_locals.get(&current_local) {
592+
debug!("already prev. calculated: {:?}", locals_to_keep_alive);
502593
// already traversed this node
503594
return (*locals_to_keep_alive).clone();
504595
}
@@ -510,7 +601,17 @@ where
510601
let target_node_idx = edge.target();
511602
let target_node = dep_graph.node(target_node_idx);
512603
let target_local = target_node.data.get_local();
604+
605+
// necessary to prevent loops
513606
if locals_visited.contains(&target_local) {
607+
if let Some(locals_to_keep_alive) = borrows_to_locals.get(&target_local) {
608+
debug!(
609+
"prev. calculated locals to keep alive for {:?}: {:?}",
610+
target_local, locals_to_keep_alive
611+
);
612+
locals_for_node.append(&mut locals_to_keep_alive.clone());
613+
}
614+
514615
continue;
515616
}
516617

@@ -537,18 +638,25 @@ where
537638
NodeKind::Borrow(_) => {}
538639
}
539640

641+
debug!("locals for {:?}: {:?}", current_local, locals_for_node);
540642
borrows_to_locals.insert(current_local, locals_for_node.clone());
541643
locals_for_node
542644
}
543645
}
544646

545647
/// The function gets the results of the borrowed locals analysis in this module. See the module
546648
/// doc-comment for information on what exactly this analysis does.
547-
#[instrument(skip(tcx), level = "debug")]
548-
pub fn get_borrowed_locals_results<'mir, 'tcx>(
649+
#[instrument(skip(tcx, maybe_borrowed_locals_cursor, body), level = "debug")]
650+
pub fn get_borrowed_locals_results<'a, 'mir, 'tcx>(
549651
body: &'mir Body<'tcx>,
550652
tcx: TyCtxt<'tcx>,
551-
) -> BorrowedLocalsResults<'mir, 'tcx> {
653+
maybe_borrowed_locals_cursor: ResultsCursor<
654+
'mir,
655+
'tcx,
656+
MaybeBorrowedLocals,
657+
&'a Results<'tcx, MaybeBorrowedLocals>,
658+
>,
659+
) -> BorrowedLocalsResults<'a, 'mir, 'tcx> {
552660
debug!("body: {:#?}", body);
553661

554662
let mut borrow_deps = BorrowDependencies::new(body.local_decls(), tcx);
@@ -585,7 +693,7 @@ pub fn get_borrowed_locals_results<'mir, 'tcx>(
585693
let results =
586694
live_borrows.into_engine(tcx, body).pass_name("borrowed_locals").iterate_to_fixpoint();
587695

588-
BorrowedLocalsResults::new(results)
696+
BorrowedLocalsResults::new(results, maybe_borrowed_locals_cursor)
589697
}
590698

591699
/// The `ResultsCursor` equivalent for the borrowed locals analysis. Since this analysis doesn't
@@ -602,10 +710,15 @@ pub struct BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
602710
// corresponding to `NodeKind::LocalWithRefs` since they might contain refs, ptrs or
603711
// exposed pointers and need to be treated equivalently to refs/ptrs
604712
borrowed_local_to_locals_to_keep_alive: &'a FxHashMap<Local, Vec<Local>>,
713+
714+
// the cursor of the conservative borrowed locals analysis
715+
maybe_borrowed_locals_results_cursor: &'a RefCell<
716+
ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals, &'a Results<'tcx, MaybeBorrowedLocals>>,
717+
>,
605718
}
606719

607720
impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
608-
pub fn new(body: &'mir Body<'tcx>, results: &'a BorrowedLocalsResults<'mir, 'tcx>) -> Self {
721+
pub fn new(body: &'mir Body<'tcx>, results: &'a BorrowedLocalsResults<'a, 'mir, 'tcx>) -> Self {
609722
let mut cursor = ResultsCursor::new(body, &results.borrows_analysis_results);
610723

611724
// We don't care about the order of the blocks, only about the result at a given location.
@@ -617,6 +730,7 @@ impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
617730
body,
618731
borrows_analysis_cursor: cursor,
619732
borrowed_local_to_locals_to_keep_alive: &results.borrowed_local_to_locals_to_keep_alive,
733+
maybe_borrowed_locals_results_cursor: &results.maybe_borrowed_locals_results_cursor,
620734
}
621735
}
622736

@@ -662,6 +776,16 @@ impl<'a, 'mir, 'tcx> BorrowedLocalsResultsCursor<'a, 'mir, 'tcx> {
662776
Either::Left(_) => {}
663777
}
664778

779+
// use results of conservative analysis as an "upper bound" on the borrowed locals. This
780+
// is necessary since to guarantee soundness for this analysis requires us to be more conservative
781+
// in some cases than the analysis performed by `MaybeBorrowedLocals`.
782+
let mut maybe_borrowed_locals_cursor =
783+
self.maybe_borrowed_locals_results_cursor.borrow_mut();
784+
maybe_borrowed_locals_cursor.allow_unreachable();
785+
maybe_borrowed_locals_cursor.seek_before_primary_effect(loc);
786+
let upper_bound_borrowed_locals = maybe_borrowed_locals_cursor.get();
787+
borrowed_locals.intersect(upper_bound_borrowed_locals);
788+
665789
debug!(?borrowed_locals);
666790
borrowed_locals
667791
}
@@ -821,7 +945,14 @@ where
821945
debug!("gen {:?}", local);
822946
self._trans.gen(local);
823947
}
824-
_ => {}
948+
_ => {
949+
if let Some(node_idx) = self.borrow_deps.locals_to_node_indexes.get(&local) {
950+
let node = self.borrow_deps.dep_graph.node(*node_idx);
951+
if matches!(node.data, NodeKind::LocalWithRefs(_)) {
952+
self._trans.gen(local);
953+
}
954+
}
955+
}
825956
}
826957

827958
self.super_place(place, context, location);

compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ pub struct MaybeRequiresStorage<'a, 'mir, 'tcx> {
157157
impl<'a, 'mir, 'tcx> MaybeRequiresStorage<'a, 'mir, 'tcx> {
158158
pub fn new(
159159
body: &'mir Body<'tcx>,
160-
borrowed_locals: &'a BorrowedLocalsResults<'mir, 'tcx>,
160+
borrowed_locals: &'a BorrowedLocalsResults<'a, 'mir, 'tcx>,
161161
) -> Self {
162162
MaybeRequiresStorage {
163163
body,

compiler/rustc_mir_transform/src/generator.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use rustc_middle::mir::*;
6767
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
6868
use rustc_middle::ty::{GeneratorSubsts, SubstsRef};
6969
use rustc_mir_dataflow::impls::{
70-
get_borrowed_locals_results, BorrowedLocalsResultsCursor, MaybeLiveLocals,
70+
get_borrowed_locals_results, BorrowedLocalsResultsCursor, MaybeBorrowedLocals, MaybeLiveLocals,
7171
MaybeRequiresStorage, MaybeStorageLive,
7272
};
7373
use rustc_mir_dataflow::storage::always_storage_live_locals;
@@ -593,8 +593,13 @@ fn locals_live_across_suspend_points<'tcx>(
593593
.iterate_to_fixpoint()
594594
.into_results_cursor(body_ref);
595595

596+
let borrowed_locals_results =
597+
MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint();
598+
let borrowed_locals_cursor =
599+
rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results);
600+
596601
// Calculate the locals that are live due to outstanding references or pointers.
597-
let live_borrows_results = get_borrowed_locals_results(body_ref, tcx);
602+
let live_borrows_results = get_borrowed_locals_results(body_ref, tcx, borrowed_locals_cursor);
598603
let mut live_borrows_cursor = BorrowedLocalsResultsCursor::new(body_ref, &live_borrows_results);
599604

600605
// Calculate the MIR locals that we actually need to keep storage around

0 commit comments

Comments
 (0)