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 8d2152a

Browse files
committedFeb 27, 2025
Probably a bad idea but I'm curious
1 parent aa21fc2 commit 8d2152a

10 files changed

+315
-65
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use rustc_index::IndexVec;
2+
use rustc_middle::bug;
3+
use rustc_middle::mir::*;
4+
use rustc_middle::ty::TyCtxt;
5+
use tracing::{debug, instrument, trace};
6+
7+
use std::mem;
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 && body.basic_blocks[targets.otherwise()].is_empty_unreachable())
30+
};
31+
32+
let mut candidates = Vec::new();
33+
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
34+
if let TerminatorKind::SwitchInt { targets, .. } = &bbdata.terminator().kind
35+
&& is_branch(targets)
36+
&& let Ok(preds) = <[BasicBlock; 2]>::try_from(body.basic_blocks.predecessors()[bb].as_slice())
37+
&& preds.iter().copied().all(|p| matches!(body.basic_blocks[p].terminator().kind, TerminatorKind::Goto { .. }))
38+
&& bbdata.statements.iter().all(|x| is_negligible(&x.kind))
39+
{
40+
candidates.push((bb, preds));
41+
}
42+
}
43+
44+
if candidates.is_empty() {
45+
return;
46+
}
47+
48+
let basic_blocks = body.basic_blocks.as_mut();
49+
for (bb, [p0, p1]) in candidates {
50+
let bbdata = &mut basic_blocks[bb];
51+
let statements = mem::take(&mut bbdata.statements);
52+
let unreachable = Terminator {
53+
source_info: bbdata.terminator().source_info,
54+
kind: TerminatorKind::Unreachable,
55+
};
56+
let terminator = mem::replace(bbdata.terminator_mut(), unreachable);
57+
58+
let pred0data = &mut basic_blocks[p0];
59+
pred0data.statements.extend(statements.iter().cloned());
60+
*pred0data.terminator_mut() = terminator.clone();
61+
62+
let pred1data = &mut basic_blocks[p1];
63+
pred1data.statements.extend(statements);
64+
*pred1data.terminator_mut() = terminator;
65+
}
66+
}
67+
68+
fn is_required(&self) -> bool {
69+
false
70+
}
71+
}
72+
73+
fn is_negligible<'tcx>(stmt: &StatementKind<'tcx>) -> bool {
74+
use StatementKind::*;
75+
use Rvalue::*;
76+
match stmt {
77+
StorageLive(..) | StorageDead(..) => true,
78+
Assign(place_and_rvalue) => match &place_and_rvalue.1 {
79+
Ref(..) | RawPtr(..) | Discriminant(..) | NullaryOp(..) => true,
80+
_ => false,
81+
}
82+
_ => false,
83+
}
84+
}

‎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

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub fn checked_shl(x: u32, rhs: u32) -> Option<u32> {
2222
}
2323

2424
// EMIT_MIR checked_ops.use_checked_sub.PreCodegen.after.mir
25+
// EMIT_MIR checked_ops.use_checked_sub.BranchDuplicator.diff
2526
// EMIT_MIR checked_ops.use_checked_sub.GVN.diff
2627
pub fn use_checked_sub(x: u32, rhs: u32) {
2728
// We want this to be equivalent to open-coding it, leaving no `Option`s around.
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+

‎tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.GVN.panic-abort.diff

+9-5
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@
5656

5757
bb4: {
5858
_3 = const Option::<u32>::None;
59-
goto -> bb6;
59+
StorageDead(_10);
60+
StorageDead(_5);
61+
StorageDead(_4);
62+
_6 = discriminant(_3);
63+
switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
6064
}
6165

6266
bb5: {
@@ -65,17 +69,17 @@
6569
+ _11 = SubUnchecked(copy _1, copy _2);
6670
_3 = Option::<u32>::Some(move _11);
6771
StorageDead(_11);
68-
goto -> bb6;
69-
}
70-
71-
bb6: {
7272
StorageDead(_10);
7373
StorageDead(_5);
7474
StorageDead(_4);
7575
_6 = discriminant(_3);
7676
switchInt(move _6) -> [1: bb1, 0: bb3, otherwise: bb7];
7777
}
7878

79+
bb6: {
80+
unreachable;
81+
}
82+
7983
bb7: {
8084
unreachable;
8185
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.