Skip to content

Commit b9262bc

Browse files
committed
Use regular MaybeLiveLocals.
1 parent 9b8a719 commit b9262bc

File tree

3 files changed

+58
-136
lines changed

3 files changed

+58
-136
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;
@@ -177,12 +177,11 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
177177
return;
178178
}
179179

180-
let live =
181-
MaybeTwoStepLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
180+
let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
182181

183182
let points = DenseLocationMap::new(body);
184183
let mut relevant = RelevantLocals::compute(&candidates, body.local_decls.len());
185-
let mut live = save_as_intervals(&points, body, &relevant.original, live.results);
184+
let mut live = save_as_intervals(&points, body, &relevant, live.results);
186185

187186
dest_prop_mir_dump(tcx, body, &points, &live, &relevant);
188187

@@ -527,8 +526,6 @@ fn dest_prop_mir_dump<'tcx>(
527526
}
528527
}
529528

530-
struct MaybeTwoStepLiveLocals;
531-
532529
#[derive(Copy, Clone, Debug)]
533530
enum Effect {
534531
Before,
@@ -579,137 +576,29 @@ where
579576
}
580577
}
581578

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

702590
let two_step_loc = |location, effect| {
703591
let point = elements.point_from_location(location);
704592
TwoStepIndex::new(point, effect)
705593
};
706-
let mut prepend_at = |state: &mut DenseBitSet<Local>, twostep| {
707-
for (relevant, &original) in relevant.iter_enumerated() {
708-
if state.contains(original) {
709-
values.prepend(relevant, twostep);
594+
let prepend_at =
595+
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
596+
for (relevant, &original) in relevant.original.iter_enumerated() {
597+
if state.contains(original) {
598+
values.prepend(relevant, twostep);
599+
}
710600
}
711-
}
712-
};
601+
};
713602

714603
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
715604
// allows to use the more efficient `prepend` method to interval sets.
@@ -725,25 +614,51 @@ fn save_as_intervals<'tcx>(
725614

726615
let term = block_data.terminator();
727616
let mut twostep = two_step_loc(loc, Effect::After);
728-
MaybeTwoStepLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
729-
prepend_at(&mut state, twostep);
617+
prepend_at(&mut values, &state, twostep);
618+
// Ensure we have a non-zero live range even for dead stores. This is done by marking all
619+
// the written-to locals as live in the second half of the statement.
620+
// We also ensure that operands read by terminators conflict with writes by that terminator.
621+
// For instance a function call may read args after having written to the destination.
622+
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
623+
DefUse::Def | DefUse::Use | DefUse::PartialWrite => {
624+
if let Some(relevant) = relevant.shrink[place.local] {
625+
values.insert(relevant, twostep);
626+
}
627+
}
628+
DefUse::NonUse => {}
629+
})
630+
.visit_terminator(term, loc);
730631

731632
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
732633
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
733-
MaybeTwoStepLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
734-
prepend_at(&mut state, twostep);
634+
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
635+
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
636+
prepend_at(&mut values, &state, twostep);
735637

736638
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
737639
let loc = Location { block, statement_index };
738640
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
739641
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
740-
MaybeTwoStepLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
741-
prepend_at(&mut state, twostep);
642+
prepend_at(&mut values, &state, twostep);
643+
// Ensure we have a non-zero live range even for dead stores. This is done by marking
644+
// all the written-to locals as live in the second half of the statement.
645+
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
646+
DefUse::Def | DefUse::PartialWrite => {
647+
if let Some(relevant) = relevant.shrink[place.local] {
648+
values.insert(relevant, twostep);
649+
}
650+
}
651+
DefUse::Use | DefUse::NonUse => {}
652+
})
653+
.visit_statement(stmt, loc);
742654

743655
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
744656
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
745-
MaybeTwoStepLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
746-
prepend_at(&mut state, twostep);
657+
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
658+
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
659+
// ... but reads from operands are marked as live here so they do not conflict with
660+
// the all the writes we manually marked as live in the second half of the statement.
661+
prepend_at(&mut values, &state, twostep);
747662
}
748663
}
749664

tests/mir-opt/nrvo_miscompile_111005.rs

Lines changed: 5 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: RenameReturnPlace
@@ -10,6 +9,11 @@ use core::intrinsics::mir::*;
109
// EMIT_MIR nrvo_miscompile_111005.wrong.RenameReturnPlace.diff
1110
#[custom_mir(dialect = "runtime", phase = "initial")]
1211
pub fn wrong(arg: char) -> char {
12+
// CHECK-LABEL: fn wrong(
13+
// CHECK: _0 = copy _1;
14+
// FIXME: This is wrong:
15+
// CHECK-NEXT: _0 = const 'b';
16+
// CHECK-NEXT: return;
1317
mir! {
1418
{
1519
let temp = arg;

0 commit comments

Comments
 (0)