Skip to content

Commit 4ff53bf

Browse files
committed
Create place and value indices on-demand.
1 parent 264e06e commit 4ff53bf

File tree

3 files changed

+210
-72
lines changed

3 files changed

+210
-72
lines changed

compiler/rustc_mir_dataflow/src/value_analysis.rs

Lines changed: 151 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::assert_matches::debug_assert_matches;
12
use std::fmt::{Debug, Formatter};
23
use std::ops::Range;
34

@@ -352,32 +353,47 @@ pub struct Map<'tcx> {
352353
projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
353354
places: IndexVec<PlaceIndex, PlaceInfo<'tcx>>,
354355
value_count: usize,
356+
mode: PlaceCollectionMode,
355357
// The Range corresponds to a slice into `inner_values_buffer`.
356358
inner_values: IndexVec<PlaceIndex, Range<usize>>,
357359
inner_values_buffer: Vec<ValueIndex>,
358360
}
359361

362+
#[derive(Copy, Clone, Debug)]
363+
pub enum PlaceCollectionMode {
364+
Full { value_limit: Option<usize> },
365+
OnDemand,
366+
}
367+
360368
impl<'tcx> Map<'tcx> {
361369
/// Returns a map that only tracks places whose type has scalar layout.
362370
///
363371
/// This is currently the only way to create a [`Map`]. The way in which the tracked places are
364372
/// chosen is an implementation detail and may not be relied upon (other than that their type
365373
/// are scalars).
366-
pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
367-
let capacity = 4 * body.local_decls.len() + value_limit.unwrap_or(body.local_decls.len());
374+
#[tracing::instrument(level = "trace", skip(tcx, body))]
375+
pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, mode: PlaceCollectionMode) -> Self {
376+
tracing::trace!(def_id=?body.source.def_id());
377+
let capacity = 4 * body.local_decls.len();
368378
let mut map = Self {
369379
locals: IndexVec::from_elem(None, &body.local_decls),
370380
projections: FxHashMap::default(),
371381
places: IndexVec::with_capacity(capacity),
372382
value_count: 0,
373-
inner_values: IndexVec::with_capacity(capacity),
383+
mode,
384+
inner_values: IndexVec::new(),
374385
inner_values_buffer: Vec::new(),
375386
};
376387
map.register_locals(tcx, body);
377-
map.collect_places(tcx, body);
378-
map.propagate_assignments(tcx, body);
379-
map.create_values(tcx, body, value_limit);
380-
map.trim_useless_places();
388+
match mode {
389+
PlaceCollectionMode::Full { value_limit } => {
390+
map.collect_places(tcx, body);
391+
map.propagate_assignments(tcx, body);
392+
map.create_values(tcx, body, value_limit);
393+
map.trim_useless_places();
394+
}
395+
PlaceCollectionMode::OnDemand => {}
396+
}
381397
debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
382398
map
383399
}
@@ -429,12 +445,18 @@ impl<'tcx> Map<'tcx> {
429445
match rhs {
430446
Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs))
431447
| Rvalue::CopyForDeref(rhs) => {
432-
let Some(lhs) = self.register_place(tcx, body, *lhs) else { continue };
433-
let Some(rhs) = self.register_place(tcx, body, *rhs) else { continue };
448+
let Some(lhs) = self.register_place_and_discr(tcx, body, *lhs) else {
449+
continue;
450+
};
451+
let Some(rhs) = self.register_place_and_discr(tcx, body, *rhs) else {
452+
continue;
453+
};
434454
assignments.insert((lhs, rhs));
435455
}
436456
Rvalue::Aggregate(kind, fields) => {
437-
let Some(mut lhs) = self.register_place(tcx, body, *lhs) else { continue };
457+
let Some(mut lhs) = self.register_place_and_discr(tcx, body, *lhs) else {
458+
continue;
459+
};
438460
match **kind {
439461
// Do not propagate unions.
440462
AggregateKind::Adt(_, _, _, _, Some(_)) => continue,
@@ -457,7 +479,7 @@ impl<'tcx> Map<'tcx> {
457479
}
458480
for (index, field) in fields.iter_enumerated() {
459481
if let Some(rhs) = field.place()
460-
&& let Some(rhs) = self.register_place(tcx, body, rhs)
482+
&& let Some(rhs) = self.register_place_and_discr(tcx, body, rhs)
461483
{
462484
let lhs = self.register_place_index(
463485
self.places[rhs].ty,
@@ -514,6 +536,7 @@ impl<'tcx> Map<'tcx> {
514536
/// Create values for places whose type have scalar layout.
515537
#[tracing::instrument(level = "trace", skip(self, tcx, body))]
516538
fn create_values(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) {
539+
debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
517540
let typing_env = body.typing_env(tcx);
518541
for place_info in self.places.iter_mut() {
519542
// The user requires a bound on the number of created values.
@@ -552,6 +575,7 @@ impl<'tcx> Map<'tcx> {
552575
/// Trim useless places.
553576
#[tracing::instrument(level = "trace", skip(self))]
554577
fn trim_useless_places(&mut self) {
578+
debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
555579
for opt_place in self.locals.iter_mut() {
556580
if let Some(place) = *opt_place
557581
&& self.inner_values[place].is_empty()
@@ -564,7 +588,7 @@ impl<'tcx> Map<'tcx> {
564588
}
565589

566590
#[tracing::instrument(level = "trace", skip(self), ret)]
567-
fn register_place_index(
591+
pub fn register_place_index(
568592
&mut self,
569593
ty: Ty<'tcx>,
570594
base: PlaceIndex,
@@ -578,49 +602,124 @@ impl<'tcx> Map<'tcx> {
578602
})
579603
}
580604

581-
#[tracing::instrument(level = "trace", skip(self, tcx, body))]
582-
fn register_place(
605+
#[tracing::instrument(level = "trace", skip(self, tcx, body), ret)]
606+
pub fn register_place(
583607
&mut self,
584608
tcx: TyCtxt<'tcx>,
585609
body: &Body<'tcx>,
586610
place: Place<'tcx>,
611+
tail: Option<TrackElem>,
587612
) -> Option<PlaceIndex> {
588613
// Create a place for this projection.
589614
let mut place_index = self.locals[place.local]?;
590615
let mut ty = PlaceTy::from_ty(body.local_decls[place.local].ty);
591616
tracing::trace!(?place_index, ?ty);
592617

593-
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
594-
&& let ty::Slice(..) = ref_ty.kind()
595-
{
596-
self.register_place_index(tcx.types.usize, place_index, TrackElem::DerefLen);
597-
} else if ty.ty.is_enum() {
598-
let discriminant_ty = ty.ty.discriminant_ty(tcx);
599-
self.register_place_index(discriminant_ty, place_index, TrackElem::Discriminant);
600-
}
601-
602618
for proj in place.projection {
603619
let track_elem = proj.try_into().ok()?;
604620
ty = ty.projection_ty(tcx, proj);
605621
place_index = self.register_place_index(ty.ty, place_index, track_elem);
606622
tracing::trace!(?proj, ?place_index, ?ty);
623+
}
607624

608-
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
609-
&& let ty::Slice(..) = ref_ty.kind()
610-
{
611-
self.register_place_index(tcx.types.usize, place_index, TrackElem::DerefLen);
612-
} else if ty.ty.is_enum() {
613-
let discriminant_ty = ty.ty.discriminant_ty(tcx);
614-
self.register_place_index(discriminant_ty, place_index, TrackElem::Discriminant);
615-
}
625+
if let Some(tail) = tail {
626+
let ty = match tail {
627+
TrackElem::Discriminant => ty.ty.discriminant_ty(tcx),
628+
TrackElem::Variant(..) | TrackElem::Field(..) => todo!(),
629+
TrackElem::DerefLen => tcx.types.usize,
630+
};
631+
place_index = self.register_place_index(ty, place_index, tail);
616632
}
617633

618634
Some(place_index)
619635
}
620636

637+
#[tracing::instrument(level = "trace", skip(self, tcx, body), ret)]
638+
fn register_place_and_discr(
639+
&mut self,
640+
tcx: TyCtxt<'tcx>,
641+
body: &Body<'tcx>,
642+
place: Place<'tcx>,
643+
) -> Option<PlaceIndex> {
644+
let place = self.register_place(tcx, body, place, None)?;
645+
let ty = self.places[place].ty;
646+
647+
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind()
648+
&& let ty::Slice(..) = ref_ty.kind()
649+
{
650+
self.register_place_index(tcx.types.usize, place, TrackElem::DerefLen);
651+
} else if ty.is_enum() {
652+
let discriminant_ty = ty.discriminant_ty(tcx);
653+
self.register_place_index(discriminant_ty, place, TrackElem::Discriminant);
654+
}
655+
656+
Some(place)
657+
}
658+
659+
#[tracing::instrument(level = "trace", skip(self, tcx, typing_env), ret)]
660+
pub fn register_value(
661+
&mut self,
662+
tcx: TyCtxt<'tcx>,
663+
typing_env: ty::TypingEnv<'tcx>,
664+
place: PlaceIndex,
665+
) -> Option<ValueIndex> {
666+
let place_info = &mut self.places[place];
667+
if let Some(value) = place_info.value_index {
668+
return Some(value);
669+
}
670+
671+
if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, place_info.ty) {
672+
place_info.ty = ty;
673+
}
674+
675+
// Allocate a value slot if it doesn't have one, and the user requested one.
676+
if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(place_info.ty))
677+
&& layout.backend_repr.is_scalar()
678+
{
679+
place_info.value_index = Some(self.value_count.into());
680+
self.value_count += 1;
681+
}
682+
683+
place_info.value_index
684+
}
685+
686+
#[tracing::instrument(level = "trace", skip(self, f))]
687+
pub fn register_copy_tree(
688+
&mut self,
689+
// Tree to copy.
690+
source: PlaceIndex,
691+
// Tree to build.
692+
target: PlaceIndex,
693+
f: &mut impl FnMut(ValueIndex, ValueIndex),
694+
) {
695+
if let Some(source_value) = self.places[source].value_index {
696+
let target_value = *self.places[target].value_index.get_or_insert_with(|| {
697+
let value_index = self.value_count.into();
698+
self.value_count += 1;
699+
value_index
700+
});
701+
f(source_value, target_value)
702+
}
703+
704+
// Iterate over `source` children and recurse.
705+
let mut source_child_iter = self.places[source].first_child;
706+
while let Some(source_child) = source_child_iter {
707+
source_child_iter = self.places[source_child].next_sibling;
708+
709+
// Try to find corresponding child and recurse. Reasoning is similar as above.
710+
let source_info = &self.places[source_child];
711+
let source_ty = source_info.ty;
712+
let source_elem = source_info.proj_elem.unwrap();
713+
let target_child = self.register_place_index(source_ty, target, source_elem);
714+
self.register_copy_tree(source_child, target_child, f);
715+
}
716+
}
717+
621718
/// Precompute the list of values inside `root` and store it inside
622719
/// as a slice within `inner_values_buffer`.
720+
#[tracing::instrument(level = "trace", skip(self))]
623721
fn cache_preorder_invoke(&mut self, root: PlaceIndex) {
722+
debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. });
624723
let start = self.inner_values_buffer.len();
625724
if let Some(vi) = self.places[root].value_index {
626725
self.inner_values_buffer.push(vi);
@@ -651,7 +750,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
651750
return;
652751
}
653752

654-
self.map.register_place(self.tcx, self.body, *place);
753+
self.map.register_place_and_discr(self.tcx, self.body, *place);
655754
}
656755
}
657756

@@ -726,6 +825,7 @@ impl<'tcx> Map<'tcx> {
726825
///
727826
/// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
728827
/// as such.
828+
#[tracing::instrument(level = "trace", skip(self, f))]
729829
pub fn for_each_aliasing_place(
730830
&self,
731831
place: PlaceRef<'_>,
@@ -763,6 +863,7 @@ impl<'tcx> Map<'tcx> {
763863
}
764864

765865
/// Invoke the given function on all the descendants of the given place, except one branch.
866+
#[tracing::instrument(level = "trace", skip(self, f))]
766867
fn for_each_variant_sibling(
767868
&self,
768869
parent: PlaceIndex,
@@ -783,11 +884,22 @@ impl<'tcx> Map<'tcx> {
783884
}
784885

785886
/// Invoke a function on each value in the given place and all descendants.
887+
#[tracing::instrument(level = "trace", skip(self, f))]
786888
fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) {
787-
let range = self.inner_values[root].clone();
788-
let values = &self.inner_values_buffer[range];
789-
for &v in values {
790-
f(v)
889+
if let Some(range) = self.inner_values.get(root) {
890+
// Optimized path: we have cached the inner values.
891+
let values = &self.inner_values_buffer[range.clone()];
892+
for &v in values {
893+
f(v)
894+
}
895+
} else {
896+
if let Some(root) = self.places[root].value_index {
897+
f(root)
898+
}
899+
900+
for child in self.children(root) {
901+
self.for_each_value_inside(child, f);
902+
}
791903
}
792904
}
793905

@@ -800,7 +912,9 @@ impl<'tcx> Map<'tcx> {
800912
f: &mut impl FnMut(PlaceIndex, &O),
801913
) {
802914
// Fast path is there is nothing to do.
803-
if self.inner_values[root].is_empty() {
915+
if let Some(value_range) = self.inner_values.get(root)
916+
&& value_range.is_empty()
917+
{
804918
return;
805919
}
806920

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
2121
use rustc_mir_dataflow::fmt::DebugWithContext;
2222
use rustc_mir_dataflow::lattice::{FlatSet, HasBottom};
2323
use rustc_mir_dataflow::value_analysis::{
24-
Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context,
24+
Map, PlaceCollectionMode, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context,
2525
};
2626
use rustc_mir_dataflow::{Analysis, ResultsVisitor, visit_reachable_results};
2727
use rustc_span::DUMMY_SP;
@@ -55,10 +55,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp {
5555
// `O(num_nodes * tracked_places * n)` in terms of time complexity. Since the number of
5656
// map nodes is strongly correlated to the number of tracked places, this becomes more or
5757
// less `O(n)` if we place a constant limit on the number of tracked places.
58-
let place_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None };
58+
let value_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None };
5959

6060
// Decide which places to track during the analysis.
61-
let map = Map::new(tcx, body, place_limit);
61+
let map = Map::new(tcx, body, PlaceCollectionMode::Full { value_limit });
6262

6363
// Perform the actual dataflow analysis.
6464
let mut const_ = debug_span!("analyze")

0 commit comments

Comments
 (0)