Skip to content

Commit 9d74efe

Browse files
committed
Auto merge of #76844 - simonvandel:fix-76803, r=wesleywiser
Fix #76803 miscompilation Fixes #76803 Seems like it was an oversight that the discriminant value being set was not compared to the target value from the SwitchInt, as a comment says this is a requirement for the optimization to be sound. r? `@wesleywiser` since you are probably familiar with the optimization and made #76837 to workaround the bug
2 parents 5bfeee5 + a875c7a commit 9d74efe

File tree

4 files changed

+96
-35
lines changed

4 files changed

+96
-35
lines changed

compiler/rustc_mir/src/transform/simplify_try.rs

+34-23
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
1616
use rustc_middle::mir::*;
1717
use rustc_middle::ty::{self, List, Ty, TyCtxt};
1818
use rustc_target::abi::VariantIdx;
19-
use std::iter::{Enumerate, Peekable};
19+
use std::iter::{once, Enumerate, Peekable};
2020
use std::slice::Iter;
2121

2222
/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
@@ -551,6 +551,12 @@ struct SimplifyBranchSameOptimization {
551551
bb_to_opt_terminator: BasicBlock,
552552
}
553553

554+
struct SwitchTargetAndValue {
555+
target: BasicBlock,
556+
// None in case of the `otherwise` case
557+
value: Option<u128>,
558+
}
559+
554560
struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
555561
body: &'a Body<'tcx>,
556562
tcx: TyCtxt<'tcx>,
@@ -562,8 +568,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
562568
.basic_blocks()
563569
.iter_enumerated()
564570
.filter_map(|(bb_idx, bb)| {
565-
let (discr_switched_on, targets) = match &bb.terminator().kind {
566-
TerminatorKind::SwitchInt { targets, discr, .. } => (discr, targets),
571+
let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
572+
TerminatorKind::SwitchInt { targets, discr, values, .. } => {
573+
// if values.len() == targets.len() - 1, we need to include None where no value is present
574+
// such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None
575+
let values_extended = values.iter().map(|x|Some(*x)).chain(once(None));
576+
let targets_and_values:Vec<_> = targets.iter().zip(values_extended)
577+
.map(|(target, value)| SwitchTargetAndValue{target:*target, value})
578+
.collect();
579+
assert_eq!(targets.len(), targets_and_values.len());
580+
(discr, targets_and_values)},
567581
_ => return None,
568582
};
569583

@@ -587,9 +601,9 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
587601
},
588602
};
589603

590-
let mut iter_bbs_reachable = targets
604+
let mut iter_bbs_reachable = targets_and_values
591605
.iter()
592-
.map(|idx| (*idx, &self.body.basic_blocks()[*idx]))
606+
.map(|target_and_value| (target_and_value, &self.body.basic_blocks()[target_and_value.target]))
593607
.filter(|(_, bb)| {
594608
// Reaching `unreachable` is UB so assume it doesn't happen.
595609
bb.terminator().kind != TerminatorKind::Unreachable
@@ -603,16 +617,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
603617
})
604618
.peekable();
605619

606-
let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]);
620+
let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(&targets_and_values[0]);
607621
let mut all_successors_equivalent = StatementEquality::TrivialEqual;
608622

609623
// All successor basic blocks must be equal or contain statements that are pairwise considered equal.
610-
for ((bb_l_idx,bb_l), (bb_r_idx,bb_r)) in iter_bbs_reachable.tuple_windows() {
624+
for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() {
611625
let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup
612626
&& bb_l.terminator().kind == bb_r.terminator().kind;
613627
let statement_check = || {
614628
bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| {
615-
let stmt_equality = self.statement_equality(*adt_matched_on, &l, bb_l_idx, &r, bb_r_idx, self.tcx.sess.opts.debugging_opts.mir_opt_level);
629+
let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r);
616630
if matches!(stmt_equality, StatementEquality::NotEqual) {
617631
// short circuit
618632
None
@@ -634,7 +648,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
634648
// statements are trivially equal, so just take first
635649
trace!("Statements are trivially equal");
636650
Some(SimplifyBranchSameOptimization {
637-
bb_to_goto: bb_first,
651+
bb_to_goto: bb_first.target,
638652
bb_to_opt_terminator: bb_idx,
639653
})
640654
}
@@ -669,10 +683,9 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
669683
&self,
670684
adt_matched_on: Place<'tcx>,
671685
x: &Statement<'tcx>,
672-
x_bb_idx: BasicBlock,
686+
x_target_and_value: &SwitchTargetAndValue,
673687
y: &Statement<'tcx>,
674-
y_bb_idx: BasicBlock,
675-
mir_opt_level: usize,
688+
y_target_and_value: &SwitchTargetAndValue,
676689
) -> StatementEquality {
677690
let helper = |rhs: &Rvalue<'tcx>,
678691
place: &Place<'tcx>,
@@ -691,13 +704,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
691704

692705
match rhs {
693706
Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => {
694-
// FIXME(76803): This logic is currently broken because it does not take into
695-
// account the current discriminant value.
696-
if mir_opt_level > 2 {
697-
StatementEquality::ConsideredEqual(side_to_choose)
698-
} else {
699-
StatementEquality::NotEqual
700-
}
707+
StatementEquality::ConsideredEqual(side_to_choose)
701708
}
702709
_ => {
703710
trace!(
@@ -717,16 +724,20 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
717724
(
718725
StatementKind::Assign(box (_, rhs)),
719726
StatementKind::SetDiscriminant { place, variant_index },
720-
) => {
727+
)
728+
// we need to make sure that the switch value that targets the bb with SetDiscriminant (y), is the same as the variant index
729+
if Some(variant_index.index() as u128) == y_target_and_value.value => {
721730
// choose basic block of x, as that has the assign
722-
helper(rhs, place, variant_index, x_bb_idx)
731+
helper(rhs, place, variant_index, x_target_and_value.target)
723732
}
724733
(
725734
StatementKind::SetDiscriminant { place, variant_index },
726735
StatementKind::Assign(box (_, rhs)),
727-
) => {
736+
)
737+
// we need to make sure that the switch value that targets the bb with SetDiscriminant (x), is the same as the variant index
738+
if Some(variant_index.index() as u128) == x_target_and_value.value => {
728739
// choose basic block of y, as that has the assign
729-
helper(rhs, place, variant_index, y_bb_idx)
740+
helper(rhs, place, variant_index, y_target_and_value.target)
730741
}
731742
_ => {
732743
trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
- // MIR for `encode` before SimplifyBranchSame
2+
+ // MIR for `encode` after SimplifyBranchSame
3+
4+
fn encode(_1: Type) -> Type {
5+
debug v => _1; // in scope 0 at $DIR/76803_regression.rs:10:15: 10:16
6+
let mut _0: Type; // return place in scope 0 at $DIR/76803_regression.rs:10:27: 10:31
7+
let mut _2: isize; // in scope 0 at $DIR/76803_regression.rs:12:9: 12:16
8+
9+
bb0: {
10+
_2 = discriminant(_1); // scope 0 at $DIR/76803_regression.rs:12:9: 12:16
11+
switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:12:9: 12:16
12+
}
13+
14+
bb1: {
15+
_0 = move _1; // scope 0 at $DIR/76803_regression.rs:13:14: 13:15
16+
goto -> bb3; // scope 0 at $DIR/76803_regression.rs:11:5: 14:6
17+
}
18+
19+
bb2: {
20+
discriminant(_0) = 1; // scope 0 at $DIR/76803_regression.rs:12:20: 12:27
21+
goto -> bb3; // scope 0 at $DIR/76803_regression.rs:11:5: 14:6
22+
}
23+
24+
bb3: {
25+
return; // scope 0 at $DIR/76803_regression.rs:15:2: 15:2
26+
}
27+
}
28+

src/test/mir-opt/76803_regression.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags: -Z mir-opt-level=1
2+
// EMIT_MIR 76803_regression.encode.SimplifyBranchSame.diff
3+
4+
#[derive(Debug, Eq, PartialEq)]
5+
pub enum Type {
6+
A,
7+
B,
8+
}
9+
10+
pub fn encode(v: Type) -> Type {
11+
match v {
12+
Type::A => Type::B,
13+
_ => v,
14+
}
15+
}
16+
17+
fn main() {
18+
assert_eq!(Type::B, encode(Type::A));
19+
}

src/test/mir-opt/simplify_arm.id.SimplifyBranchSame.diff

+15-12
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,27 @@
1313

1414
bb0: {
1515
_2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16
16-
switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16
16+
- switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16
17+
+ goto -> bb1; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16
1718
}
1819

1920
bb1: {
20-
discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21
21-
goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6
22-
}
23-
24-
bb2: {
25-
unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12
26-
}
27-
28-
bb3: {
21+
- discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21
22+
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6
23+
- }
24+
-
25+
- bb2: {
26+
- unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12
27+
- }
28+
-
29+
- bb3: {
2930
_0 = move _1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27
30-
goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6
31+
- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6
32+
+ goto -> bb2; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6
3133
}
3234

33-
bb4: {
35+
- bb4: {
36+
+ bb2: {
3437
return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2
3538
}
3639
}

0 commit comments

Comments
 (0)