Skip to content

Commit 818934b

Browse files
Model generator resumption in dataflow
We now have a way to apply an effect only *after* a `yield` resumes, similar to calls (which can either return or unwind).
1 parent 2070ea2 commit 818934b

File tree

7 files changed

+88
-31
lines changed

7 files changed

+88
-31
lines changed

src/librustc_mir/dataflow/generic/cursor.rs

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::borrow::Borrow;
44

5-
use rustc::mir::{self, BasicBlock, Location};
5+
use rustc::mir::{self, BasicBlock, Location, TerminatorKind};
66
use rustc_index::bit_set::BitSet;
77

88
use super::{Analysis, Results};
@@ -29,14 +29,14 @@ where
2929

3030
pos: CursorPosition,
3131

32-
/// When this flag is set, the cursor is pointing at a `Call` terminator whose call return
33-
/// effect has been applied to `state`.
32+
/// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call
33+
/// return or resume effect has been applied to `state`.
3434
///
35-
/// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the
35+
/// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the
3636
/// same target will result in exactly one invocation of `apply_call_return_effect`. It is
3737
/// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
3838
/// terminator will always require a cursor reset.
39-
call_return_effect_applied: bool,
39+
success_effect_applied: bool,
4040
}
4141

4242
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
@@ -50,7 +50,7 @@ where
5050
body,
5151
pos: CursorPosition::BlockStart(mir::START_BLOCK),
5252
state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
53-
call_return_effect_applied: false,
53+
success_effect_applied: false,
5454
results,
5555
}
5656
}
@@ -76,14 +76,14 @@ where
7676
pub fn seek_to_block_start(&mut self, block: BasicBlock) {
7777
self.state.overwrite(&self.results.borrow().entry_sets[block]);
7878
self.pos = CursorPosition::BlockStart(block);
79-
self.call_return_effect_applied = false;
79+
self.success_effect_applied = false;
8080
}
8181

8282
/// Advances the cursor to hold all effects up to and including to the "before" effect of the
8383
/// statement (or terminator) at the given location.
8484
///
8585
/// If you wish to observe the full effect of a statement or terminator, not just the "before"
86-
/// effect, use `seek_after` or `seek_after_assume_call_returns`.
86+
/// effect, use `seek_after` or `seek_after_assume_success`.
8787
pub fn seek_before(&mut self, target: Location) {
8888
assert!(target <= self.body.terminator_loc(target.block));
8989
self.seek_(target, false);
@@ -93,15 +93,15 @@ where
9393
/// terminators) up to and including the `target`.
9494
///
9595
/// If the `target` is a `Call` terminator, any call return effect for that terminator will
96-
/// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call
96+
/// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call
9797
/// return effect.
9898
pub fn seek_after(&mut self, target: Location) {
9999
assert!(target <= self.body.terminator_loc(target.block));
100100

101101
// If we have already applied the call return effect, we are currently pointing at a `Call`
102102
// terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
103103
// the call return effect.
104-
if self.call_return_effect_applied {
104+
if self.success_effect_applied {
105105
self.seek_to_block_start(target.block);
106106
}
107107

@@ -111,25 +111,25 @@ where
111111
/// Advances the cursor to hold all effects up to and including of the statement (or
112112
/// terminator) at the given location.
113113
///
114-
/// If the `target` is a `Call` terminator, any call return effect for that terminator will
115-
/// be observed. Use `seek_after` if you do **not** wish to observe the call return effect.
116-
pub fn seek_after_assume_call_returns(&mut self, target: Location) {
114+
/// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that
115+
/// terminator will be observed. Use `seek_after` if you do **not** wish to observe the
116+
/// "success" effect.
117+
pub fn seek_after_assume_success(&mut self, target: Location) {
117118
let terminator_loc = self.body.terminator_loc(target.block);
118119
assert!(target.statement_index <= terminator_loc.statement_index);
119120

120121
self.seek_(target, true);
121122

122-
if target != terminator_loc {
123+
if target != terminator_loc || self.success_effect_applied {
123124
return;
124125
}
125126

127+
// Apply the effect of the "success" path of the terminator.
128+
129+
self.success_effect_applied = true;
126130
let terminator = self.body.basic_blocks()[target.block].terminator();
127-
if let mir::TerminatorKind::Call {
128-
destination: Some((return_place, _)), func, args, ..
129-
} = &terminator.kind
130-
{
131-
if !self.call_return_effect_applied {
132-
self.call_return_effect_applied = true;
131+
match &terminator.kind {
132+
TerminatorKind::Call { destination: Some((return_place, _)), func, args, .. } => {
133133
self.results.borrow().analysis.apply_call_return_effect(
134134
&mut self.state,
135135
target.block,
@@ -138,6 +138,14 @@ where
138138
return_place,
139139
);
140140
}
141+
TerminatorKind::Yield { resume, resume_arg, .. } => {
142+
self.results.borrow().analysis.apply_yield_resume_effect(
143+
&mut self.state,
144+
*resume,
145+
resume_arg,
146+
);
147+
}
148+
_ => {}
141149
}
142150
}
143151

@@ -172,7 +180,7 @@ where
172180
self.seek_to_block_start(target.block)
173181
}
174182

175-
// N.B., `call_return_effect_applied` is checked in `seek_after`, not here.
183+
// N.B., `success_effect_applied` is checked in `seek_after`, not here.
176184
_ => (),
177185
}
178186

src/librustc_mir/dataflow/generic/engine.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,18 @@ where
218218

219219
Goto { target }
220220
| Assert { target, cleanup: None, .. }
221-
| Yield { resume: target, drop: None, .. }
222221
| Drop { target, location: _, unwind: None }
223222
| DropAndReplace { target, value: _, location: _, unwind: None } => {
224223
self.propagate_bits_into_entry_set_for(in_out, target, dirty_list)
225224
}
226225

227-
Yield { resume: target, drop: Some(drop), .. } => {
226+
Yield { resume: target, drop, resume_arg, .. } => {
227+
if let Some(drop) = drop {
228+
self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
229+
}
230+
231+
self.analysis.apply_yield_resume_effect(in_out, target, &resume_arg);
228232
self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
229-
self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
230233
}
231234

232235
Assert { target, cleanup: Some(unwind), .. }

src/librustc_mir/dataflow/generic/graphviz.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ where
241241
)?;
242242

243243
let state_on_unwind = this.results.get().clone();
244-
this.results.seek_after_assume_call_returns(terminator_loc);
244+
this.results.seek_after_assume_success(terminator_loc);
245245
write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
246246

247247
write!(w, "</td>")

src/librustc_mir/dataflow/generic/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
191191
return_place: &mir::Place<'tcx>,
192192
);
193193

194+
/// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
195+
///
196+
/// This is similar to `apply_call_return_effect` in that it only takes place after the
197+
/// generator is resumed, not when it is dropped.
198+
///
199+
/// By default, no effects happen.
200+
fn apply_yield_resume_effect(
201+
&self,
202+
_state: &mut BitSet<Self::Idx>,
203+
_resume_block: BasicBlock,
204+
_resume_place: &mir::Place<'tcx>,
205+
) {
206+
}
207+
194208
/// Updates the current dataflow state with the effect of taking a particular branch in a
195209
/// `SwitchInt` terminator.
196210
///
@@ -284,6 +298,15 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
284298
return_place: &mir::Place<'tcx>,
285299
);
286300

301+
/// See `Analysis::apply_yield_resume_effect`.
302+
fn yield_resume_effect(
303+
&self,
304+
_trans: &mut BitSet<Self::Idx>,
305+
_resume_block: BasicBlock,
306+
_resume_place: &mir::Place<'tcx>,
307+
) {
308+
}
309+
287310
/// See `Analysis::apply_discriminant_switch_effect`.
288311
fn discriminant_switch_effect(
289312
&self,
@@ -347,6 +370,15 @@ where
347370
self.call_return_effect(state, block, func, args, return_place);
348371
}
349372

373+
fn apply_yield_resume_effect(
374+
&self,
375+
state: &mut BitSet<Self::Idx>,
376+
resume_block: BasicBlock,
377+
resume_place: &mir::Place<'tcx>,
378+
) {
379+
self.yield_resume_effect(state, resume_block, resume_place);
380+
}
381+
350382
fn apply_discriminant_switch_effect(
351383
&self,
352384
state: &mut BitSet<Self::Idx>,

src/librustc_mir/dataflow/generic/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ fn cursor_seek() {
294294

295295
cursor.seek_after(call_terminator_loc);
296296
assert!(!cursor.get().contains(call_return_effect));
297-
cursor.seek_after_assume_call_returns(call_terminator_loc);
297+
cursor.seek_after_assume_success(call_terminator_loc);
298298
assert!(cursor.get().contains(call_return_effect));
299299

300300
let every_target = || {
@@ -310,7 +310,7 @@ fn cursor_seek() {
310310
BlockStart(block) => cursor.seek_to_block_start(block),
311311
Before(loc) => cursor.seek_before(loc),
312312
After(loc) => cursor.seek_after(loc),
313-
AfterAssumeCallReturns(loc) => cursor.seek_after_assume_call_returns(loc),
313+
AfterAssumeCallReturns(loc) => cursor.seek_after_assume_success(loc),
314314
}
315315

316316
assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ));

src/librustc_mir/dataflow/impls/storage_liveness.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,16 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir,
161161
self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
162162

163163
match &terminator.kind {
164-
TerminatorKind::Call { destination: Some((place, _)), .. }
165-
| TerminatorKind::Yield { resume_arg: place, .. } => {
164+
TerminatorKind::Call { destination: Some((place, _)), .. } => {
166165
trans.gen(place.local);
167166
}
168167

168+
// Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
169+
// that is that a `yield` will return from the function, and `resume_arg` is written
170+
// only when the generator is later resumed. Unlike `Call`, this doesn't require the
171+
// place to have storage *before* the yield, only after.
172+
TerminatorKind::Yield { .. } => {}
173+
169174
// Nothing to do for these. Match exhaustively so this fails to compile when new
170175
// variants are added.
171176
TerminatorKind::Call { destination: None, .. }
@@ -230,6 +235,15 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir,
230235
) {
231236
trans.gen(return_place.local);
232237
}
238+
239+
fn yield_resume_effect(
240+
&self,
241+
trans: &mut BitSet<Self::Idx>,
242+
_resume_block: BasicBlock,
243+
resume_place: &mir::Place<'tcx>,
244+
) {
245+
trans.gen(resume_place.local);
246+
}
233247
}
234248

235249
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {

src/librustc_mir/transform/generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ fn locals_live_across_suspend_points(
506506

507507
for (block, data) in body.basic_blocks().iter_enumerated() {
508508
if let TerminatorKind::Yield { .. } = data.terminator().kind {
509-
let loc = Location { block: block, statement_index: data.statements.len() };
509+
let loc = Location { block, statement_index: data.statements.len() };
510510

511511
if !movable {
512512
// The `liveness` variable contains the liveness of MIR locals ignoring borrows.
@@ -539,7 +539,7 @@ fn locals_live_across_suspend_points(
539539
let mut live_locals_here = storage_required;
540540
live_locals_here.intersect(&liveness.outs[block]);
541541

542-
// The generator argument is ignored
542+
// The generator argument is ignored.
543543
live_locals_here.remove(self_arg());
544544

545545
debug!("loc = {:?}, live_locals_here = {:?}", loc, live_locals_here);

0 commit comments

Comments
 (0)