Skip to content

Commit f6307c2

Browse files
Auto merge of #147858 - yotamofek:pr/mir/coroutine-layout-opt, r=<try>
Micro-optimization attempt in coroutine layout computation
2 parents 04ff05c + a67c615 commit f6307c2

File tree

1 file changed

+62
-60
lines changed

1 file changed

+62
-60
lines changed

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@
5252
5353
mod by_move_body;
5454
mod drop;
55-
use std::{iter, ops};
55+
use std::ops;
5656

5757
pub(super) use by_move_body::coroutine_by_move_body_def_id;
5858
use drop::{
5959
cleanup_async_drops, create_coroutine_drop_shim, create_coroutine_drop_shim_async,
6060
create_coroutine_drop_shim_proxy_async, elaborate_coroutine_drops, expand_async_drops,
6161
has_expandable_async_drops, insert_clean_drop,
6262
};
63+
use itertools::izip;
6364
use rustc_abi::{FieldIdx, VariantIdx};
6465
use rustc_data_structures::fx::FxHashSet;
6566
use rustc_errors::pluralize;
@@ -730,53 +731,53 @@ fn locals_live_across_suspend_points<'tcx>(
730731
let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
731732

732733
for (block, data) in body.basic_blocks.iter_enumerated() {
733-
if let TerminatorKind::Yield { .. } = data.terminator().kind {
734-
let loc = Location { block, statement_index: data.statements.len() };
735-
736-
liveness.seek_to_block_end(block);
737-
let mut live_locals = liveness.get().clone();
738-
739-
if !movable {
740-
// The `liveness` variable contains the liveness of MIR locals ignoring borrows.
741-
// This is correct for movable coroutines since borrows cannot live across
742-
// suspension points. However for immovable coroutines we need to account for
743-
// borrows, so we conservatively assume that all borrowed locals are live until
744-
// we find a StorageDead statement referencing the locals.
745-
// To do this we just union our `liveness` result with `borrowed_locals`, which
746-
// contains all the locals which has been borrowed before this suspension point.
747-
// If a borrow is converted to a raw reference, we must also assume that it lives
748-
// forever. Note that the final liveness is still bounded by the storage liveness
749-
// of the local, which happens using the `intersect` operation below.
750-
borrowed_locals_cursor2.seek_before_primary_effect(loc);
751-
live_locals.union(borrowed_locals_cursor2.get());
752-
}
734+
let TerminatorKind::Yield { .. } = data.terminator().kind else { continue };
735+
736+
let loc = Location { block, statement_index: data.statements.len() };
737+
738+
liveness.seek_to_block_end(block);
739+
let mut live_locals = liveness.get().clone();
740+
741+
if !movable {
742+
// The `liveness` variable contains the liveness of MIR locals ignoring borrows.
743+
// This is correct for movable coroutines since borrows cannot live across
744+
// suspension points. However for immovable coroutines we need to account for
745+
// borrows, so we conservatively assume that all borrowed locals are live until
746+
// we find a StorageDead statement referencing the locals.
747+
// To do this we just union our `liveness` result with `borrowed_locals`, which
748+
// contains all the locals which has been borrowed before this suspension point.
749+
// If a borrow is converted to a raw reference, we must also assume that it lives
750+
// forever. Note that the final liveness is still bounded by the storage liveness
751+
// of the local, which happens using the `intersect` operation below.
752+
borrowed_locals_cursor2.seek_before_primary_effect(loc);
753+
live_locals.union(borrowed_locals_cursor2.get());
754+
}
753755

754-
// Store the storage liveness for later use so we can restore the state
755-
// after a suspension point
756-
storage_live.seek_before_primary_effect(loc);
757-
storage_liveness_map[block] = Some(storage_live.get().clone());
756+
// Store the storage liveness for later use so we can restore the state
757+
// after a suspension point
758+
storage_live.seek_before_primary_effect(loc);
759+
storage_liveness_map[block] = Some(storage_live.get().clone());
758760

759-
// Locals live are live at this point only if they are used across
760-
// suspension points (the `liveness` variable)
761-
// and their storage is required (the `storage_required` variable)
762-
requires_storage_cursor.seek_before_primary_effect(loc);
763-
live_locals.intersect(requires_storage_cursor.get());
761+
// Locals live are live at this point only if they are used across
762+
// suspension points (the `liveness` variable)
763+
// and their storage is required (the `storage_required` variable)
764+
requires_storage_cursor.seek_before_primary_effect(loc);
765+
live_locals.intersect(requires_storage_cursor.get());
764766

765-
// The coroutine argument is ignored.
766-
live_locals.remove(SELF_ARG);
767+
// The coroutine argument is ignored.
768+
live_locals.remove(SELF_ARG);
767769

768-
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
770+
debug!(?loc, ?live_locals);
769771

770-
// Add the locals live at this suspension point to the set of locals which live across
771-
// any suspension points
772-
live_locals_at_any_suspension_point.union(&live_locals);
772+
// Add the locals live at this suspension point to the set of locals which live across
773+
// any suspension points
774+
live_locals_at_any_suspension_point.union(&live_locals);
773775

774-
live_locals_at_suspension_points.push(live_locals);
775-
source_info_at_suspension_points.push(data.terminator().source_info);
776-
}
776+
live_locals_at_suspension_points.push(live_locals);
777+
source_info_at_suspension_points.push(data.terminator().source_info);
777778
}
778779

779-
debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point);
780+
debug!(?live_locals_at_any_suspension_point);
780781
let saved_locals = CoroutineSavedLocals(live_locals_at_any_suspension_point);
781782

782783
// Renumber our liveness_map bitsets to include only the locals we are
@@ -977,8 +978,8 @@ fn compute_layout<'tcx>(
977978
} = liveness;
978979

979980
// Gather live local types and their indices.
980-
let mut locals = IndexVec::<CoroutineSavedLocal, _>::new();
981-
let mut tys = IndexVec::<CoroutineSavedLocal, _>::new();
981+
let mut locals = IndexVec::<CoroutineSavedLocal, _>::with_capacity(saved_locals.domain_size());
982+
let mut tys = IndexVec::<CoroutineSavedLocal, _>::with_capacity(saved_locals.domain_size());
982983
for (saved_local, local) in saved_locals.iter_enumerated() {
983984
debug!("coroutine saved local {:?} => {:?}", saved_local, local);
984985

@@ -1012,38 +1013,39 @@ fn compute_layout<'tcx>(
10121013
// In debuginfo, these will correspond to the beginning (UNRESUMED) or end
10131014
// (RETURNED, POISONED) of the function.
10141015
let body_span = body.source_scopes[OUTERMOST_SOURCE_SCOPE].span;
1015-
let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = [
1016+
let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = IndexVec::with_capacity(
1017+
CoroutineArgs::RESERVED_VARIANTS + live_locals_at_suspension_points.len(),
1018+
);
1019+
variant_source_info.extend([
10161020
SourceInfo::outermost(body_span.shrink_to_lo()),
10171021
SourceInfo::outermost(body_span.shrink_to_hi()),
10181022
SourceInfo::outermost(body_span.shrink_to_hi()),
1019-
]
1020-
.iter()
1021-
.copied()
1022-
.collect();
1023+
]);
10231024

10241025
// Build the coroutine variant field list.
10251026
// Create a map from local indices to coroutine struct indices.
1026-
let mut variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, CoroutineSavedLocal>> =
1027-
iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect();
1027+
let mut variant_fields: IndexVec<VariantIdx, _> = IndexVec::from_elem_n(
1028+
IndexVec::new(),
1029+
CoroutineArgs::RESERVED_VARIANTS + live_locals_at_suspension_points.len(),
1030+
);
10281031
let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size());
1029-
for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
1030-
let variant_index =
1031-
VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx);
1032-
let mut fields = IndexVec::new();
1033-
for (idx, saved_local) in live_locals.iter().enumerate() {
1034-
fields.push(saved_local);
1032+
for (live_locals, &source_info_at_suspension_point, (variant_index, fields)) in izip!(
1033+
&live_locals_at_suspension_points,
1034+
&source_info_at_suspension_points,
1035+
variant_fields.iter_enumerated_mut().skip(CoroutineArgs::RESERVED_VARIANTS)
1036+
) {
1037+
*fields = live_locals.iter().collect();
1038+
for (idx, &saved_local) in fields.iter_enumerated() {
10351039
// Note that if a field is included in multiple variants, we will
10361040
// just use the first one here. That's fine; fields do not move
10371041
// around inside coroutines, so it doesn't matter which variant
10381042
// index we access them by.
1039-
let idx = FieldIdx::from_usize(idx);
10401043
remap[locals[saved_local]] = Some((tys[saved_local].ty, variant_index, idx));
10411044
}
1042-
variant_fields.push(fields);
1043-
variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]);
1045+
variant_source_info.push(source_info_at_suspension_point);
10441046
}
1045-
debug!("coroutine variant_fields = {:?}", variant_fields);
1046-
debug!("coroutine storage_conflicts = {:#?}", storage_conflicts);
1047+
debug!(?variant_fields);
1048+
debug!(?storage_conflicts);
10471049

10481050
let mut field_names = IndexVec::from_elem(None, &tys);
10491051
for var in &body.var_debug_info {

0 commit comments

Comments
 (0)