Skip to content

Commit 139b66c

Browse files
committed
Use regular MaybeLiveLocals.
1 parent b051186 commit 139b66c

File tree

4 files changed

+57
-138
lines changed

4 files changed

+57
-138
lines changed

compiler/rustc_index/src/interval.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,11 @@ impl<I: Idx> IntervalSet<I> {
146146
let point = point.index() as u32;
147147

148148
if let Some((first_start, _)) = self.map.first_mut() {
149-
assert!(point < *first_start);
150-
if point + 1 == *first_start {
149+
assert!(point <= *first_start);
150+
if point == *first_start {
151+
// The point is already present in the set.
152+
} else if point + 1 == *first_start {
153+
// Just extend the first range.
151154
*first_start = point;
152155
} else {
153156
self.map.insert(0, (point, point));

compiler/rustc_mir_transform/src/dest_prop.rs

Lines changed: 48 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@
140140
use rustc_data_structures::fx::FxIndexMap;
141141
use rustc_index::bit_set::DenseBitSet;
142142
use rustc_index::interval::SparseIntervalMatrix;
143-
use rustc_index::{IndexSlice, IndexVec, newtype_index};
143+
use rustc_index::{IndexVec, newtype_index};
144144
use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor};
145145
use rustc_middle::mir::*;
146146
use rustc_middle::ty::TyCtxt;
147-
use rustc_mir_dataflow::impls::DefUse;
147+
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
148148
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
149-
use rustc_mir_dataflow::{Analysis, Backward, Results};
149+
use rustc_mir_dataflow::{Analysis, Results};
150150
use tracing::{debug, trace};
151151

152152
pub(super) struct DestinationPropagation;
@@ -169,12 +169,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
169169
return;
170170
}
171171

172-
let live =
173-
MaybeTwoStepLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
172+
let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
174173

175174
let points = DenseLocationMap::new(body);
176175
let mut relevant = RelevantLocals::compute(&candidates, body.local_decls.len());
177-
let mut live = save_as_intervals(&points, body, &relevant.original, live.results);
176+
let mut live = save_as_intervals(&points, body, &relevant, live.results);
178177

179178
dest_prop_mir_dump(tcx, body, &points, &live, &relevant);
180179

@@ -515,8 +514,6 @@ fn dest_prop_mir_dump<'tcx>(
515514
});
516515
}
517516

518-
struct MaybeTwoStepLiveLocals;
519-
520517
#[derive(Copy, Clone, Debug)]
521518
enum Effect {
522519
Before,
@@ -567,137 +564,29 @@ where
567564
}
568565
}
569566

570-
impl<'tcx> Analysis<'tcx> for MaybeTwoStepLiveLocals {
571-
type Domain = DenseBitSet<Local>;
572-
type Direction = Backward;
573-
574-
const NAME: &'static str = "transitive liveness";
575-
576-
fn bottom_value(&self, body: &Body<'tcx>) -> DenseBitSet<Local> {
577-
// bottom = not live
578-
DenseBitSet::new_empty(body.local_decls.len())
579-
}
580-
581-
fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut DenseBitSet<Local>) {
582-
// No variables are live until we observe a use
583-
}
584-
585-
// This happens between the previous statement and this one.
586-
#[tracing::instrument(level = "trace", skip(self, statement))]
587-
fn apply_primary_statement_effect(
588-
&mut self,
589-
state: &mut DenseBitSet<Local>,
590-
statement: &Statement<'tcx>,
591-
location: Location,
592-
) {
593-
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
594-
DefUse::Def => {
595-
state.remove(place.local);
596-
}
597-
DefUse::Use => {
598-
state.insert(place.local);
599-
}
600-
DefUse::PartialWrite | DefUse::NonUse => {}
601-
})
602-
.visit_statement(statement, location);
603-
}
604-
605-
// This happens between this statement and the next one.
606-
#[tracing::instrument(level = "trace", skip(self, statement))]
607-
fn apply_early_statement_effect(
608-
&mut self,
609-
state: &mut DenseBitSet<Local>,
610-
statement: &Statement<'tcx>,
611-
location: Location,
612-
) {
613-
// We need to ensure we have a non-zero live range even for dead stores. This is done by
614-
// marking all the writes locals as live in the second half of the statement.
615-
VisitPlacesWith(|place: Place<'tcx>, ctxt| match DefUse::for_place(place, ctxt) {
616-
DefUse::Def | DefUse::PartialWrite => {
617-
state.insert(place.local);
618-
}
619-
// We already perform the reads in the first part of the statement. As statements are
620-
// not splittable, we do not need to re-read the same values.
621-
DefUse::Use | DefUse::NonUse => {}
622-
})
623-
.visit_statement(statement, location);
624-
}
625-
626-
// We model terminator as a special case in this two-step analysis. Consider the terminator
627-
// `destination = func(arg0...)`.
628-
//
629-
// -- state at (location, Effect::Before)
630-
// read(arg0)...
631-
// write(destination)
632-
// -- state at (location, Effect::After)
633-
// read(arg0)...
634-
635-
// This happens between the last statement and the terminator.
636-
#[tracing::instrument(level = "trace", skip(self, terminator))]
637-
fn apply_primary_terminator_effect<'mir>(
638-
&mut self,
639-
state: &mut DenseBitSet<Local>,
640-
terminator: &'mir Terminator<'tcx>,
641-
location: Location,
642-
) -> TerminatorEdges<'mir, 'tcx> {
643-
// Consider that all writes in this terminator happen at the start of the execution of the
644-
// terminator. For instance if we pass a return-pointer to a `Call` terminator.
645-
VisitPlacesWith(|place: Place<'tcx>, ctxt| match DefUse::for_place(place, ctxt) {
646-
DefUse::Def => {
647-
state.remove(place.local);
648-
}
649-
DefUse::Use => {
650-
state.insert(place.local);
651-
}
652-
DefUse::PartialWrite | DefUse::NonUse => {}
653-
})
654-
.visit_terminator(terminator, location);
655-
terminator.edges()
656-
}
657-
658-
// This happens between the terminator and the end of the block.
659-
#[tracing::instrument(level = "trace", skip(self, terminator))]
660-
fn apply_early_terminator_effect<'mir>(
661-
&mut self,
662-
state: &mut DenseBitSet<Local>,
663-
terminator: &'mir Terminator<'tcx>,
664-
location: Location,
665-
) {
666-
// Consider that all reads in this terminator happen at the end of the execution of the
667-
// terminator, even after it may have written to the destination local. For instance if we
668-
// pass arguments as pointers to a `Call` terminator.
669-
VisitPlacesWith(|place: Place<'tcx>, ctxt| match DefUse::for_place(place, ctxt) {
670-
DefUse::Def | DefUse::Use | DefUse::PartialWrite => {
671-
state.insert(place.local);
672-
}
673-
DefUse::NonUse => {}
674-
})
675-
.visit_terminator(terminator, location);
676-
}
677-
}
678-
679567
/// Add points depending on the result of the given dataflow analysis.
680568
fn save_as_intervals<'tcx>(
681569
elements: &DenseLocationMap,
682570
body: &Body<'tcx>,
683-
relevant: &IndexSlice<RelevantLocal, Local>,
571+
relevant: &RelevantLocals,
684572
results: Results<DenseBitSet<Local>>,
685573
) -> SparseIntervalMatrix<RelevantLocal, TwoStepIndex> {
686574
let mut values = SparseIntervalMatrix::new(2 * elements.num_points());
687-
let mut state = MaybeTwoStepLiveLocals.bottom_value(body);
575+
let mut state = MaybeLiveLocals.bottom_value(body);
688576
let reachable_blocks = traversal::reachable_as_bitset(body);
689577

690578
let two_step_loc = |location, effect| {
691579
let point = elements.point_from_location(location);
692580
TwoStepIndex::new(point, effect)
693581
};
694-
let mut prepend_at = |state: &mut DenseBitSet<Local>, twostep| {
695-
for (relevant, &original) in relevant.iter_enumerated() {
696-
if state.contains(original) {
697-
values.prepend(relevant, twostep);
582+
let prepend_at =
583+
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
584+
for (relevant, &original) in relevant.original.iter_enumerated() {
585+
if state.contains(original) {
586+
values.prepend(relevant, twostep);
587+
}
698588
}
699-
}
700-
};
589+
};
701590

702591
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
703592
// allows to use the more efficient `prepend` method to interval sets.
@@ -713,25 +602,51 @@ fn save_as_intervals<'tcx>(
713602

714603
let term = block_data.terminator();
715604
let mut twostep = two_step_loc(loc, Effect::After);
716-
MaybeTwoStepLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
717-
prepend_at(&mut state, twostep);
605+
prepend_at(&mut values, &state, twostep);
606+
// Ensure we have a non-zero live range even for dead stores. This is done by marking all
607+
// the writes locals as live in the second half of the statement.
608+
// We also ensure that operands read by terminators conflict with reads by that terminator.
609+
// For instance a function call may read args after having written to the destination.
610+
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
611+
DefUse::Def | DefUse::Use | DefUse::PartialWrite => {
612+
if let Some(relevant) = relevant.shrink[place.local] {
613+
values.insert(relevant, twostep);
614+
}
615+
}
616+
DefUse::NonUse => {}
617+
})
618+
.visit_terminator(term, loc);
718619

719620
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
720621
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
721-
MaybeTwoStepLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
722-
prepend_at(&mut state, twostep);
622+
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
623+
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
624+
prepend_at(&mut values, &state, twostep);
723625

724626
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
725627
let loc = Location { block, statement_index };
726628
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
727629
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
728-
MaybeTwoStepLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
729-
prepend_at(&mut state, twostep);
630+
prepend_at(&mut values, &state, twostep);
631+
// Ensure we have a non-zero live range even for dead stores. This is done by marking
632+
// all the writes locals as live in the second half of the statement.
633+
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
634+
DefUse::Def | DefUse::PartialWrite => {
635+
if let Some(relevant) = relevant.shrink[place.local] {
636+
values.insert(relevant, twostep);
637+
}
638+
}
639+
DefUse::Use | DefUse::NonUse => {}
640+
})
641+
.visit_statement(stmt, loc);
730642

731643
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
732644
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
733-
MaybeTwoStepLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
734-
prepend_at(&mut state, twostep);
645+
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
646+
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
647+
// ... but reads from operands are marked as live here so they do not conflict with
648+
// the all the writes locals as live in the second half of the statement.
649+
prepend_at(&mut values, &state, twostep);
735650
}
736651
}
737652

tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// skip-filecheck
21
// This is a miscompilation, #111005 to track
32

43
//@ test-mir-pass: DestinationPropagation
@@ -10,6 +9,8 @@ use core::intrinsics::mir::*;
109
// EMIT_MIR nrvo_miscompile_111005.wrong.DestinationPropagation.diff
1110
#[custom_mir(dialect = "runtime", phase = "initial")]
1211
pub fn wrong(arg: char) -> char {
12+
// CHECK-LABEL: fn wrong(
13+
// CHECK-NOT: _0 = const 'b';
1314
mir! {
1415
{
1516
let temp = arg;

tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
- _2 = copy _1;
1010
- _0 = copy _2;
1111
- _2 = const 'b';
12-
+ _0 = copy _1;
1312
+ nop;
14-
+ _0 = const 'b';
13+
+ _0 = copy _1;
14+
+ _1 = const 'b';
1515
return;
1616
}
1717
}

0 commit comments

Comments
 (0)