Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bbc4837

Browse files
committedFeb 27, 2025
Auto merge of rust-lang#137702 - scottmcm:deconstruct-options, r=<try>
[nothing to see here] this is probably a bad idea but I'm curious r? ghost
2 parents 96cfc75 + 905cd3d commit bbc4837

12 files changed

+596
-56
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use std::mem;
2+
3+
use rustc_index::IndexVec;
4+
use rustc_middle::bug;
5+
use rustc_middle::mir::*;
6+
use rustc_middle::ty::TyCtxt;
7+
use tracing::{debug, instrument, trace};
8+
9+
pub(super) struct BranchDuplicator;
10+
11+
impl<'tcx> crate::MirPass<'tcx> for BranchDuplicator {
12+
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
13+
sess.mir_opt_level() >= 2
14+
}
15+
16+
#[instrument(skip_all level = "debug")]
17+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
18+
let def_id = body.source.def_id();
19+
debug!(?def_id);
20+
21+
// Optimizing coroutines creates query cycles.
22+
if tcx.is_coroutine(def_id) {
23+
trace!("Skipped for coroutine {:?}", def_id);
24+
return;
25+
}
26+
27+
let is_branch = |targets: &SwitchTargets| {
28+
targets.all_targets().len() == 2
29+
|| (targets.all_values().len() == 2
30+
&& body.basic_blocks[targets.otherwise()].is_empty_unreachable())
31+
};
32+
33+
let mut candidates = Vec::new();
34+
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
35+
if let TerminatorKind::SwitchInt { targets, .. } = &bbdata.terminator().kind
36+
&& is_branch(targets)
37+
&& let Ok(preds) =
38+
<[BasicBlock; 2]>::try_from(body.basic_blocks.predecessors()[bb].as_slice())
39+
&& preds.iter().copied().all(|p| {
40+
matches!(body.basic_blocks[p].terminator().kind, TerminatorKind::Goto { .. })
41+
})
42+
&& bbdata.statements.iter().all(|x| is_negligible(&x.kind))
43+
{
44+
candidates.push((bb, preds));
45+
}
46+
}
47+
48+
if candidates.is_empty() {
49+
return;
50+
}
51+
52+
let basic_blocks = body.basic_blocks.as_mut();
53+
for (bb, [p0, p1]) in candidates {
54+
let bbdata = &mut basic_blocks[bb];
55+
let statements = mem::take(&mut bbdata.statements);
56+
let unreachable = Terminator {
57+
source_info: bbdata.terminator().source_info,
58+
kind: TerminatorKind::Unreachable,
59+
};
60+
let terminator = mem::replace(bbdata.terminator_mut(), unreachable);
61+
62+
let pred0data = &mut basic_blocks[p0];
63+
pred0data.statements.extend(statements.iter().cloned());
64+
*pred0data.terminator_mut() = terminator.clone();
65+
66+
let pred1data = &mut basic_blocks[p1];
67+
pred1data.statements.extend(statements);
68+
*pred1data.terminator_mut() = terminator;
69+
}
70+
}
71+
72+
fn is_required(&self) -> bool {
73+
false
74+
}
75+
}
76+
77+
fn is_negligible<'tcx>(stmt: &StatementKind<'tcx>) -> bool {
78+
use Rvalue::*;
79+
use StatementKind::*;
80+
match stmt {
81+
StorageLive(..) | StorageDead(..) => true,
82+
Assign(place_and_rvalue) => match &place_and_rvalue.1 {
83+
Ref(..) | RawPtr(..) | Discriminant(..) | NullaryOp(..) => true,
84+
_ => false,
85+
},
86+
_ => false,
87+
}
88+
}

‎compiler/rustc_mir_transform/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ declare_passes! {
118118
mod add_moves_for_packed_drops : AddMovesForPackedDrops;
119119
mod add_retag : AddRetag;
120120
mod add_subtyping_projections : Subtyper;
121+
mod branch_duplicator: BranchDuplicator;
121122
mod check_inline : CheckForceInline;
122123
mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
123124
mod check_alignment : CheckAlignment;
@@ -670,6 +671,9 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
670671
&unreachable_enum_branching::UnreachableEnumBranching,
671672
&unreachable_prop::UnreachablePropagation,
672673
&o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching),
674+
// If we inlined something returning `Option` or `Result`,
675+
// duplicating the discriminant branches on those helps later passes.
676+
&branch_duplicator::BranchDuplicator,
673677
// Inlining may have introduced a lot of redundant code and a large move pattern.
674678
// Now, we need to shrink the generated MIR.
675679
&ref_prop::ReferencePropagation,

‎tests/mir-opt/const_goto_const_eval_fail.f.JumpThreading.diff

+6-15
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,27 @@
1212

1313
bb1: {
1414
_1 = const true;
15-
- goto -> bb3;
16-
+ goto -> bb7;
15+
goto -> bb3;
1716
}
1817

1918
bb2: {
2019
_1 = const B;
21-
goto -> bb3;
20+
switchInt(copy _1) -> [0: bb4, otherwise: bb3];
2221
}
2322

2423
bb3: {
25-
switchInt(copy _1) -> [0: bb5, otherwise: bb4];
26-
}
27-
28-
bb4: {
2924
_0 = const 2_u64;
30-
goto -> bb6;
25+
goto -> bb5;
3126
}
3227

33-
bb5: {
28+
bb4: {
3429
_0 = const 1_u64;
35-
goto -> bb6;
30+
goto -> bb5;
3631
}
3732

38-
bb6: {
33+
bb5: {
3934
StorageDead(_1);
4035
return;
41-
+ }
42-
+
43-
+ bb7: {
44-
+ goto -> bb4;
4536
}
4637
}
4738

‎tests/mir-opt/pre-codegen/checked_ops.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// skip-filecheck
21
//@ compile-flags: -O -Zmir-opt-level=2
32
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
43

@@ -8,10 +7,36 @@
87
// EMIT_MIR checked_ops.step_forward.PreCodegen.after.mir
98
pub fn step_forward(x: u16, n: usize) -> u16 {
109
// This uses `u16` so that the conversion to usize is always widening.
10+
11+
// CHECK-LABEL: fn step_forward
12+
// CHECK: inlined{{.+}}forward
1113
std::iter::Step::forward(x, n)
1214
}
1315

1416
// EMIT_MIR checked_ops.checked_shl.PreCodegen.after.mir
1517
pub fn checked_shl(x: u32, rhs: u32) -> Option<u32> {
18+
// CHECK-LABEL: fn checked_shl
19+
// CHECK: [[TEMP:_[0-9]+]] = ShlUnchecked(copy _1, copy _2)
20+
// CHECK: _0 = Option::<u32>::Some({{move|copy}} [[TEMP]])
1621
x.checked_shl(rhs)
1722
}
23+
24+
// EMIT_MIR checked_ops.use_checked_sub.PreCodegen.after.mir
25+
// EMIT_MIR checked_ops.use_checked_sub.BranchDuplicator.diff
26+
// EMIT_MIR checked_ops.use_checked_sub.GVN.diff
27+
pub fn use_checked_sub(x: u32, rhs: u32) {
28+
// We want this to be equivalent to open-coding it, leaving no `Option`s around.
29+
30+
// CHECK-LABEL: fn use_checked_sub
31+
// FIXME-CHECK-NOT: let{{.+}}Option
32+
// CHECK: inlined{{.+}}u32{{.+}}checked_sub
33+
// CHECK: [[DELTA:_[0-9]+]] = SubUnchecked(copy _1, copy _2)
34+
// FIXME-CHECK: do_something({{move|copy}} [[DELTA]])
35+
if let Some(delta) = x.checked_sub(rhs) {
36+
do_something(delta);
37+
}
38+
}
39+
40+
unsafe extern "Rust" {
41+
safe fn do_something(_: u32);
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
- // MIR for `use_checked_sub` before BranchDuplicator
2+
+ // MIR for `use_checked_sub` after BranchDuplicator
3+
4+
fn use_checked_sub(_1: u32, _2: u32) -> () {
5+
debug x => _1;
6+
debug rhs => _2;
7+
let mut _0: ();
8+
let mut _3: std::option::Option<u32>;
9+
let mut _4: u32;
10+
let mut _5: u32;
11+
let mut _6: isize;
12+
let _8: ();
13+
let mut _9: u32;
14+
scope 1 {
15+
debug delta => _7;
16+
let _7: u32;
17+
scope 2 (inlined core::num::<impl u32>::checked_sub) {
18+
let mut _10: bool;
19+
let mut _11: u32;
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_3);
25+
StorageLive(_4);
26+
_4 = copy _1;
27+
StorageLive(_5);
28+
_5 = copy _2;
29+
StorageLive(_10);
30+
_10 = Lt(copy _4, copy _5);
31+
switchInt(move _10) -> [0: bb5, otherwise: bb4];
32+
}
33+
34+
bb1: {
35+
StorageLive(_7);
36+
_7 = copy ((_3 as Some).0: u32);
37+
StorageLive(_9);
38+
_9 = copy _7;
39+
_8 = do_something(move _9) -> [return: bb2, unwind unreachable];
40+
}
41+
42+
bb2: {
43+
StorageDead(_9);
44+
StorageDead(_7);
45+
goto -> bb3;
46+
}
47+
48+
bb3: {
49+
StorageDead(_3);
50+
return;
51+
}
52+
53+
bb4: {
54+
_3 = const Option::<u32>::None;
55+
- goto -> bb6;
56+
+ StorageDead(_10);
57+
+ StorageDead(_5);
58+
+ StorageDead(_4);
59+
+ _6 = discriminant(_3);
60+
+ switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
61+
}
62+
63+
bb5: {
64+
StorageLive(_11);
65+
_11 = SubUnchecked(copy _4, copy _5);
66+
_3 = Option::<u32>::Some(move _11);
67+
StorageDead(_11);
68+
- goto -> bb6;
69+
- }
70+
-
71+
- bb6: {
72+
StorageDead(_10);
73+
StorageDead(_5);
74+
StorageDead(_4);
75+
_6 = discriminant(_3);
76+
switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
77+
+ }
78+
+
79+
+ bb6: {
80+
+ unreachable;
81+
}
82+
83+
bb7: {
84+
unreachable;
85+
}
86+
}
87+
88+
ALLOC0 (size: 8, align: 4) {
89+
00 00 00 00 __ __ __ __ │ ....░░░░
90+
}
91+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
- // MIR for `use_checked_sub` before BranchDuplicator
2+
+ // MIR for `use_checked_sub` after BranchDuplicator
3+
4+
fn use_checked_sub(_1: u32, _2: u32) -> () {
5+
debug x => _1;
6+
debug rhs => _2;
7+
let mut _0: ();
8+
let mut _3: std::option::Option<u32>;
9+
let mut _4: u32;
10+
let mut _5: u32;
11+
let mut _6: isize;
12+
let _8: ();
13+
let mut _9: u32;
14+
scope 1 {
15+
debug delta => _7;
16+
let _7: u32;
17+
scope 2 (inlined core::num::<impl u32>::checked_sub) {
18+
let mut _10: bool;
19+
let mut _11: u32;
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_3);
25+
StorageLive(_4);
26+
_4 = copy _1;
27+
StorageLive(_5);
28+
_5 = copy _2;
29+
StorageLive(_10);
30+
_10 = Lt(copy _4, copy _5);
31+
switchInt(move _10) -> [0: bb5, otherwise: bb4];
32+
}
33+
34+
bb1: {
35+
StorageLive(_7);
36+
_7 = copy ((_3 as Some).0: u32);
37+
StorageLive(_9);
38+
_9 = copy _7;
39+
_8 = do_something(move _9) -> [return: bb2, unwind continue];
40+
}
41+
42+
bb2: {
43+
StorageDead(_9);
44+
StorageDead(_7);
45+
goto -> bb3;
46+
}
47+
48+
bb3: {
49+
StorageDead(_3);
50+
return;
51+
}
52+
53+
bb4: {
54+
_3 = const Option::<u32>::None;
55+
- goto -> bb6;
56+
+ StorageDead(_10);
57+
+ StorageDead(_5);
58+
+ StorageDead(_4);
59+
+ _6 = discriminant(_3);
60+
+ switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
61+
}
62+
63+
bb5: {
64+
StorageLive(_11);
65+
_11 = SubUnchecked(copy _4, copy _5);
66+
_3 = Option::<u32>::Some(move _11);
67+
StorageDead(_11);
68+
- goto -> bb6;
69+
- }
70+
-
71+
- bb6: {
72+
StorageDead(_10);
73+
StorageDead(_5);
74+
StorageDead(_4);
75+
_6 = discriminant(_3);
76+
switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
77+
+ }
78+
+
79+
+ bb6: {
80+
+ unreachable;
81+
}
82+
83+
bb7: {
84+
unreachable;
85+
}
86+
}
87+
88+
ALLOC0 (size: 8, align: 4) {
89+
00 00 00 00 __ __ __ __ │ ....░░░░
90+
}
91+
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.