Skip to content

Commit e1a7a9b

Browse files
committed
1% local win: single pass dataflow
1 parent 914e23a commit e1a7a9b

File tree

2 files changed

+217
-26
lines changed

2 files changed

+217
-26
lines changed

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
3939
}
4040
}
4141

42-
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
43-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
44-
unreachable!();
42+
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
43+
self.borrows.initialize_start_block(body, &mut state.borrows);
44+
self.uninits.initialize_start_block(body, &mut state.uninits);
45+
self.ever_inits.initialize_start_block(body, &mut state.ever_inits);
4546
}
4647

4748
fn apply_early_statement_effect(
@@ -83,30 +84,36 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
8384
term: &'mir mir::Terminator<'tcx>,
8485
loc: Location,
8586
) -> TerminatorEdges<'mir, 'tcx> {
86-
self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
87-
self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
88-
self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
87+
let _edges1 = self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
88+
let _edges2 = self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
89+
let edges3 =
90+
self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
8991

90-
// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
91-
// analysis doesn't use.
92-
TerminatorEdges::None
92+
// assert_eq!(_edges1, _edges2);
93+
// assert_eq!(_edges2, edges3);
94+
95+
edges3
9396
}
9497

9598
fn apply_call_return_effect(
9699
&mut self,
97-
_state: &mut Self::Domain,
98-
_block: BasicBlock,
99-
_return_places: CallReturnPlaces<'_, 'tcx>,
100+
state: &mut Self::Domain,
101+
block: BasicBlock,
102+
return_places: CallReturnPlaces<'_, 'tcx>,
100103
) {
101-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
102-
unreachable!();
104+
self.borrows.apply_call_return_effect(&mut state.borrows, block, return_places);
105+
self.uninits.apply_call_return_effect(&mut state.uninits, block, return_places);
106+
self.ever_inits.apply_call_return_effect(&mut state.ever_inits, block, return_places);
103107
}
104108
}
105109

106110
impl JoinSemiLattice for BorrowckDomain {
107-
fn join(&mut self, _other: &Self) -> bool {
108-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
109-
unreachable!();
111+
fn join(&mut self, other: &Self) -> bool {
112+
let mut changed = false;
113+
changed |= self.borrows.join(&other.borrows);
114+
changed |= self.uninits.join(&other.uninits);
115+
changed |= self.ever_inits.join(&other.ever_inits);
116+
changed
110117
}
111118
}
112119

compiler/rustc_borrowck/src/lib.rs

Lines changed: 193 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -436,15 +436,19 @@ fn do_mir_borrowck<'tcx>(
436436
// Compute and report region errors, if any.
437437
mbcx.report_region_errors(nll_errors);
438438

439-
let (mut flow_analysis, flow_entry_states) =
440-
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
441-
visit_results(
442-
body,
443-
traversal::reverse_postorder(body).map(|(bb, _)| bb),
444-
&mut flow_analysis,
445-
&flow_entry_states,
446-
&mut mbcx,
447-
);
439+
if body.basic_blocks.is_cfg_cyclic() {
440+
let (mut flow_analysis, flow_entry_states) =
441+
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
442+
visit_results(
443+
body,
444+
traversal::reverse_postorder(body).map(|(bb, _)| bb),
445+
&mut flow_analysis,
446+
&flow_entry_states,
447+
&mut mbcx,
448+
);
449+
} else {
450+
compute_dataflow(tcx, body, &move_data, &borrow_set, &regioncx, &mut mbcx);
451+
}
448452

449453
mbcx.report_move_errors();
450454

@@ -497,6 +501,186 @@ fn do_mir_borrowck<'tcx>(
497501
result
498502
}
499503

504+
fn compute_dataflow<'a, 'tcx>(
505+
tcx: TyCtxt<'tcx>,
506+
body: &'a Body<'tcx>,
507+
508+
move_data: &'a MoveData<'tcx>,
509+
borrow_set: &'a BorrowSet<'tcx>,
510+
regioncx: &RegionInferenceContext<'tcx>,
511+
512+
vis: &mut MirBorrowckCtxt<'a, '_, 'tcx>,
513+
) {
514+
let borrows = Borrows::new(tcx, body, regioncx, borrow_set);
515+
let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data);
516+
let ever_inits = EverInitializedPlaces::new(body, move_data);
517+
518+
let mut analysis = Borrowck { borrows, uninits, ever_inits };
519+
520+
// Set up lazy state for the CFG
521+
use rustc_middle::mir;
522+
use rustc_mir_dataflow::JoinSemiLattice;
523+
524+
let mut results: IndexVec<BasicBlock, Option<BorrowckDomain>> =
525+
IndexVec::from_elem_n(None, body.basic_blocks.len());
526+
527+
// Ensure the start block has some state in it;
528+
results[mir::START_BLOCK] = Some(analysis.bottom_value(body));
529+
analysis.initialize_start_block(body, results[mir::START_BLOCK].as_mut().unwrap());
530+
531+
for (_idx, (block, block_data)) in traversal::reverse_postorder(body).enumerate() {
532+
// Apply effects in block
533+
let mut block_state = results[block].take().unwrap_or_else(|| analysis.bottom_value(body));
534+
535+
vis.visit_block_start(&mut block_state);
536+
537+
for (statement_index, statement) in block_data.statements.iter().enumerate() {
538+
let location = Location { block, statement_index };
539+
analysis.apply_early_statement_effect(&mut block_state, statement, location);
540+
vis.visit_after_early_statement_effect(
541+
&mut analysis,
542+
&block_state,
543+
statement,
544+
location,
545+
);
546+
547+
analysis.apply_primary_statement_effect(&mut block_state, statement, location);
548+
vis.visit_after_primary_statement_effect(
549+
&mut analysis,
550+
&block_state,
551+
statement,
552+
location,
553+
);
554+
}
555+
let terminator = block_data.terminator();
556+
let location = Location { block, statement_index: block_data.statements.len() };
557+
analysis.apply_early_terminator_effect(&mut block_state, terminator, location);
558+
vis.visit_after_early_terminator_effect(&mut analysis, &block_state, terminator, location);
559+
560+
let edges =
561+
analysis.apply_primary_terminator_effect(&mut block_state, terminator, location);
562+
vis.visit_after_primary_terminator_effect(
563+
&mut analysis,
564+
&block_state,
565+
terminator,
566+
location,
567+
);
568+
569+
// notify visitor the block is ready
570+
vis.visit_block_end(&mut block_state);
571+
572+
match edges {
573+
TerminatorEdges::None => {}
574+
TerminatorEdges::Single(target) => match results[target].as_mut() {
575+
None => {
576+
results[target] = Some(block_state);
577+
}
578+
Some(existing_state) => {
579+
existing_state.join(&block_state);
580+
}
581+
},
582+
TerminatorEdges::Double(target, unwind) if target == unwind => {
583+
// wtf
584+
match results[target].as_mut() {
585+
None => {
586+
results[target] = Some(block_state);
587+
}
588+
Some(existing_state) => {
589+
existing_state.join(&block_state);
590+
}
591+
}
592+
}
593+
TerminatorEdges::Double(target, unwind) => match results.pick2_mut(target, unwind) {
594+
(None, None) => {
595+
results[target] = Some(block_state.clone());
596+
results[unwind] = Some(block_state);
597+
}
598+
(None, Some(unwind_state)) => {
599+
unwind_state.join(&block_state);
600+
results[target] = Some(block_state);
601+
}
602+
(Some(target_state), None) => {
603+
target_state.join(&block_state);
604+
results[unwind] = Some(block_state);
605+
}
606+
(Some(target_state), Some(unwind_state)) => {
607+
target_state.join(&block_state);
608+
unwind_state.join(&block_state);
609+
}
610+
},
611+
TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
612+
// This must be done *first*, otherwise the unwind path will see the assignments.
613+
if let Some(cleanup) = cleanup {
614+
match results[cleanup].as_mut() {
615+
None => {
616+
results[cleanup] = Some(block_state.clone());
617+
}
618+
Some(existing_state) => {
619+
existing_state.join(&block_state);
620+
}
621+
}
622+
}
623+
624+
if !return_.is_empty() {
625+
analysis.apply_call_return_effect(&mut block_state, block, place);
626+
627+
// fixme: optimize, if we've merged the previous target states instead
628+
// of moving, we don't need to clone it.
629+
630+
let target_count = return_.len();
631+
for &target in return_.iter().take(target_count - 1) {
632+
match results[target].as_mut() {
633+
None => {
634+
results[target] = Some(block_state.clone());
635+
}
636+
Some(existing_state) => {
637+
existing_state.join(&block_state);
638+
}
639+
}
640+
}
641+
642+
let target = *return_.last().unwrap();
643+
match results[target].as_mut() {
644+
None => {
645+
results[target] = Some(block_state.clone());
646+
}
647+
Some(existing_state) => {
648+
existing_state.join(&block_state);
649+
}
650+
}
651+
}
652+
}
653+
TerminatorEdges::SwitchInt { targets, discr } => {
654+
if let Some(_data) = analysis.get_switch_int_data(block, discr) {
655+
todo!("wat. this is unused in tests");
656+
} else {
657+
let target_count = targets.all_targets().len();
658+
for &target in targets.all_targets().iter().take(target_count - 1) {
659+
match results[target].as_mut() {
660+
None => {
661+
results[target] = Some(block_state.clone());
662+
}
663+
Some(existing_state) => {
664+
existing_state.join(&block_state);
665+
}
666+
}
667+
}
668+
669+
let target = *targets.all_targets().last().unwrap();
670+
match results[target].as_mut() {
671+
None => {
672+
results[target] = Some(block_state.clone());
673+
}
674+
Some(existing_state) => {
675+
existing_state.join(&block_state);
676+
}
677+
}
678+
}
679+
}
680+
}
681+
}
682+
}
683+
500684
fn get_flow_results<'a, 'tcx>(
501685
tcx: TyCtxt<'tcx>,
502686
body: &'a Body<'tcx>,

0 commit comments

Comments
 (0)