Skip to content

Commit 32cc823

Browse files
Auto merge of #147858 - yotamofek:pr/mir/coroutine-layout-opt, r=<try>
Micro-optimization attempt in coroutine layout computation
2 parents 6380899 + 6f682c2 commit 32cc823

File tree

1 file changed

+88
-89
lines changed

1 file changed

+88
-89
lines changed

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 88 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -735,53 +735,53 @@ fn locals_live_across_suspend_points<'tcx>(
735735
let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
736736

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

759-
// Store the storage liveness for later use so we can restore the state
760-
// after a suspension point
761-
storage_live.seek_before_primary_effect(loc);
762-
storage_liveness_map[block] = Some(storage_live.get().clone());
760+
// Store the storage liveness for later use so we can restore the state
761+
// after a suspension point
762+
storage_live.seek_before_primary_effect(loc);
763+
storage_liveness_map[block] = Some(storage_live.get().clone());
763764

764-
// Locals live are live at this point only if they are used across
765-
// suspension points (the `liveness` variable)
766-
// and their storage is required (the `storage_required` variable)
767-
requires_storage_cursor.seek_before_primary_effect(loc);
768-
live_locals.intersect(requires_storage_cursor.get());
765+
// Locals live are live at this point only if they are used across
766+
// suspension points (the `liveness` variable)
767+
// and their storage is required (the `storage_required` variable)
768+
requires_storage_cursor.seek_before_primary_effect(loc);
769+
live_locals.intersect(requires_storage_cursor.get());
769770

770-
// The coroutine argument is ignored.
771-
live_locals.remove(SELF_ARG);
771+
// The coroutine argument is ignored.
772+
live_locals.remove(SELF_ARG);
772773

773-
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
774+
debug!(?loc, ?live_locals);
774775

775-
// Add the locals live at this suspension point to the set of locals which live across
776-
// any suspension points
777-
live_locals_at_any_suspension_point.union(&live_locals);
776+
// Add the locals live at this suspension point to the set of locals which live across
777+
// any suspension points
778+
live_locals_at_any_suspension_point.union(&live_locals);
778779

779-
live_locals_at_suspension_points.push(live_locals);
780-
source_info_at_suspension_points.push(data.terminator().source_info);
781-
}
780+
live_locals_at_suspension_points.push(live_locals);
781+
source_info_at_suspension_points.push(data.terminator().source_info);
782782
}
783783

784-
debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point);
784+
debug!(?live_locals_at_any_suspension_point);
785785
let saved_locals = CoroutineSavedLocals(live_locals_at_any_suspension_point);
786786

787787
// Renumber our liveness_map bitsets to include only the locals we are
@@ -982,71 +982,70 @@ fn compute_layout<'tcx>(
982982
} = liveness;
983983

984984
// Gather live local types and their indices.
985-
let mut locals = IndexVec::<CoroutineSavedLocal, _>::new();
986-
let mut tys = IndexVec::<CoroutineSavedLocal, _>::new();
987-
for (saved_local, local) in saved_locals.iter_enumerated() {
988-
debug!("coroutine saved local {:?} => {:?}", saved_local, local);
989-
990-
locals.push(local);
991-
let decl = &body.local_decls[local];
992-
debug!(?decl);
993-
994-
// Do not `unwrap_crate_local` here, as post-borrowck cleanup may have already cleared
995-
// the information. This is alright, since `ignore_for_traits` is only relevant when
996-
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
997-
// default.
998-
let ignore_for_traits = match decl.local_info {
999-
// Do not include raw pointers created from accessing `static` items, as those could
1000-
// well be re-created by another access to the same static.
1001-
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
1002-
!is_thread_local
1003-
}
1004-
// Fake borrows are only read by fake reads, so do not have any reality in
1005-
// post-analysis MIR.
1006-
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
1007-
_ => false,
1008-
};
1009-
let decl =
1010-
CoroutineSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
1011-
debug!(?decl);
985+
let (locals, tys): (IndexVec<_, _>, IndexVec<_, _>) = saved_locals
986+
.iter_enumerated()
987+
.map(|(saved_local, local)| {
988+
debug!("coroutine saved local {saved_local:?} => {local:?}");
1012989

1013-
tys.push(decl);
1014-
}
990+
let decl = &body.local_decls[local];
991+
debug!(?decl);
992+
993+
// Do not `unwrap_crate_local` here, as post-borrowck cleanup may have already cleared
994+
// the information. This is alright, since `ignore_for_traits` is only relevant when
995+
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
996+
// default.
997+
let ignore_for_traits = match decl.local_info {
998+
// Do not include raw pointers created from accessing `static` items, as those could
999+
// well be re-created by another access to the same static.
1000+
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
1001+
!is_thread_local
1002+
}
1003+
// Fake borrows are only read by fake reads, so do not have any reality in
1004+
// post-analysis MIR.
1005+
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
1006+
_ => false,
1007+
};
1008+
let decl =
1009+
CoroutineSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
1010+
debug!(?decl);
1011+
1012+
(local, decl)
1013+
})
1014+
.unzip();
10151015

10161016
// Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
10171017
// In debuginfo, these will correspond to the beginning (UNRESUMED) or end
10181018
// (RETURNED, POISONED) of the function.
10191019
let body_span = body.source_scopes[OUTERMOST_SOURCE_SCOPE].span;
1020-
let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = [
1020+
let variant_source_info: IndexVec<VariantIdx, SourceInfo> = [
10211021
SourceInfo::outermost(body_span.shrink_to_lo()),
10221022
SourceInfo::outermost(body_span.shrink_to_hi()),
10231023
SourceInfo::outermost(body_span.shrink_to_hi()),
10241024
]
1025-
.iter()
1026-
.copied()
1025+
.into_iter()
1026+
.chain(source_info_at_suspension_points)
10271027
.collect();
10281028

10291029
// Build the coroutine variant field list.
10301030
// Create a map from local indices to coroutine struct indices.
1031-
let mut variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, CoroutineSavedLocal>> =
1032-
iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect();
10331031
let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size());
1034-
for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
1035-
let variant_index =
1036-
VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx);
1037-
let mut fields = IndexVec::new();
1038-
for (idx, saved_local) in live_locals.iter().enumerate() {
1039-
fields.push(saved_local);
1040-
// Note that if a field is included in multiple variants, we will
1041-
// just use the first one here. That's fine; fields do not move
1042-
// around inside coroutines, so it doesn't matter which variant
1043-
// index we access them by.
1044-
let idx = FieldIdx::from_usize(idx);
1045-
remap[locals[saved_local]] = Some((tys[saved_local].ty, variant_index, idx));
1046-
}
1047-
variant_fields.push(fields);
1048-
variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]);
1049-
}
1032+
let variant_fields = iter::repeat_n(IndexVec::new(), CoroutineArgs::RESERVED_VARIANTS)
1033+
.chain(live_locals_at_suspension_points.into_iter().enumerate().map(
1034+
|(suspension_point_idx, live_locals)| {
1035+
let variant_index =
1036+
VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx);
1037+
let fields = live_locals.iter().collect::<IndexVec<FieldIdx, _>>();
1038+
for (idx, &saved_local) in fields.iter_enumerated() {
1039+
// Note that if a field is included in multiple variants, we will
1040+
// just use the first one here. That's fine; fields do not move
1041+
// around inside coroutines, so it doesn't matter which variant
1042+
// index we access them by.
1043+
remap[locals[saved_local]] = Some((tys[saved_local].ty, variant_index, idx));
1044+
}
1045+
fields
1046+
},
1047+
))
1048+
.collect::<IndexVec<VariantIdx, _>>();
10501049
debug!("coroutine variant_fields = {:?}", variant_fields);
10511050
debug!("coroutine storage_conflicts = {:#?}", storage_conflicts);
10521051

0 commit comments

Comments
 (0)