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 a8189ff

Browse files
committedMar 13, 2020
Modify SimplifyArmIdentity so it can trigger on mir-opt-level=1
I also added test cases to make sure the optimization can fire on all of these cases: ```rust fn case_1(o: Option<u8>) -> Option<u8> { match o { Some(u) => Some(u), None => None, } } fn case2(r: Result<u8, i32>) -> Result<u8, i32> { match r { Ok(u) => Ok(u), Err(i) => Err(i), } } fn case3(r: Result<u8, i32>) -> Result<u8, i32> { let u = r?; Ok(u) } ``` Without MIR inlining, this still does not completely optimize away the `?` operator because the `Try::into_result()`, `From::from()` and `Try::from_error()` calls still exist. This does move us a bit closer to that goal though because: - We can now run the pass on mir-opt-level=1 - We no longer depend on the copy propagation pass running which is unlikely to stabilize anytime soon.
1 parent 1581278 commit a8189ff

File tree

6 files changed

+977
-81
lines changed

6 files changed

+977
-81
lines changed
 

‎src/librustc_mir/transform/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,11 @@ fn run_optimization_passes<'tcx>(
311311
&const_prop::ConstProp,
312312
&simplify_branches::SimplifyBranches::new("after-const-prop"),
313313
&deaggregator::Deaggregator,
314+
&simplify_try::SimplifyArmIdentity,
315+
&simplify_try::SimplifyBranchSame,
314316
&copy_prop::CopyPropagation,
315317
&simplify_branches::SimplifyBranches::new("after-copy-prop"),
316318
&remove_noop_landing_pads::RemoveNoopLandingPads,
317-
&simplify::SimplifyCfg::new("after-remove-noop-landing-pads"),
318-
&simplify_try::SimplifyArmIdentity,
319-
&simplify_try::SimplifyBranchSame,
320319
&simplify::SimplifyCfg::new("final"),
321320
&simplify::SimplifyLocals,
322321
&add_call_guards::CriticalCallEdges,

‎src/librustc_mir/transform/simplify_try.rs

Lines changed: 299 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@ use crate::transform::{simplify, MirPass, MirSource};
1313
use itertools::Itertools as _;
1414
use rustc::mir::*;
1515
use rustc::ty::{Ty, TyCtxt};
16+
use rustc_index::vec::IndexVec;
1617
use rustc_target::abi::VariantIdx;
18+
use std::iter::{Enumerate, Peekable};
19+
use std::slice::Iter;
1720

1821
/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
1922
///
2023
/// This is done by transforming basic blocks where the statements match:
2124
///
2225
/// ```rust
2326
/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
24-
/// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP;
27+
/// _TMP_2 = _LOCAL_TMP;
28+
/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2;
2529
/// discriminant(_LOCAL_0) = VAR_IDX;
2630
/// ```
2731
///
@@ -32,50 +36,306 @@ use rustc_target::abi::VariantIdx;
3236
/// ```
3337
pub struct SimplifyArmIdentity;
3438

39+
#[derive(Debug)]
40+
struct ArmIdentityInfo<'tcx> {
41+
/// Storage location for the variant's field
42+
local_temp_0: Local,
43+
/// Storage location holding the variant being read from
44+
local_1: Local,
45+
/// The variant field being read from
46+
vf_s0: VarField<'tcx>,
47+
/// Index of the statement which loads the variant being read
48+
get_variant_field_stmt: usize,
49+
50+
/// Tracks each assignment to a temporary of the variant's field
51+
field_tmp_assignments: Vec<(Local, Local)>,
52+
53+
/// Storage location holding the variant's field that was read from
54+
local_tmp_s1: Local,
55+
/// Storage location holding the enum that we are writing to
56+
local_0: Local,
57+
/// The variant field being written to
58+
vf_s1: VarField<'tcx>,
59+
60+
/// Storage location that the discriminant is being written to
61+
set_discr_local: Local,
62+
/// The variant being written
63+
set_discr_var_idx: VariantIdx,
64+
65+
/// Index of the statement that should be overwritten as a move
66+
stmt_to_overwrite: usize,
67+
/// SourceInfo for the new move
68+
source_info: SourceInfo,
69+
70+
/// Indices of matching Storage{Live,Dead} statements encountered.
71+
/// (StorageLive index,, StorageDead index, Local)
72+
storage_stmts: Vec<(usize, usize, Local)>,
73+
74+
/// The statements that should be removed (turned into nops)
75+
stmts_to_remove: Vec<usize>,
76+
}
77+
78+
fn get_arm_identity_info<'a, 'tcx>(stmts: &'a [Statement<'tcx>]) -> Option<ArmIdentityInfo<'tcx>> {
79+
// This can't possibly match unless there are at least 3 statements in the block
80+
// so fail fast on tiny blocks.
81+
if stmts.len() < 3 {
82+
return None;
83+
}
84+
85+
let mut tmp_assigns = Vec::new();
86+
let mut nop_stmts = Vec::new();
87+
let mut storage_stmts = Vec::new();
88+
let mut storage_live_stmts = Vec::new();
89+
let mut storage_dead_stmts = Vec::new();
90+
91+
type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>;
92+
93+
fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
94+
matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_))
95+
}
96+
97+
fn try_eat_storage_stmts<'a, 'tcx>(
98+
stmt_iter: &mut StmtIter<'a, 'tcx>,
99+
storage_live_stmts: &mut Vec<(usize, Local)>,
100+
storage_dead_stmts: &mut Vec<(usize, Local)>,
101+
) {
102+
while stmt_iter.peek().map(|(_, stmt)| is_storage_stmt(stmt)).unwrap_or(false) {
103+
let (idx, stmt) = stmt_iter.next().unwrap();
104+
105+
if let StatementKind::StorageLive(l) = stmt.kind {
106+
storage_live_stmts.push((idx, l));
107+
} else if let StatementKind::StorageDead(l) = stmt.kind {
108+
storage_dead_stmts.push((idx, l));
109+
}
110+
}
111+
}
112+
113+
fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
114+
if let StatementKind::Assign(box (place, Rvalue::Use(op))) = &stmt.kind {
115+
if let Operand::Copy(p) | Operand::Move(p) = op {
116+
return place.as_local().is_some() && p.as_local().is_some();
117+
}
118+
}
119+
120+
false
121+
}
122+
123+
fn try_eat_assign_tmp_stmts<'a, 'tcx>(
124+
stmt_iter: &mut StmtIter<'a, 'tcx>,
125+
tmp_assigns: &mut Vec<(Local, Local)>,
126+
nop_stmts: &mut Vec<usize>,
127+
) {
128+
while stmt_iter.peek().map(|(_, stmt)| is_tmp_storage_stmt(stmt)).unwrap_or(false) {
129+
let (idx, stmt) = stmt_iter.next().unwrap();
130+
131+
if let StatementKind::Assign(box (place, Rvalue::Use(op))) = &stmt.kind {
132+
if let Operand::Copy(p) | Operand::Move(p) = op {
133+
tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap()));
134+
nop_stmts.push(idx);
135+
}
136+
}
137+
}
138+
}
139+
140+
fn find_storage_live_dead_stmts_for_local<'tcx>(
141+
l: Local,
142+
stmts: &[Statement<'tcx>],
143+
) -> Option<(usize, usize)> {
144+
trace!("looking for {:?}", l);
145+
let mut storage_live_stmt = None;
146+
let mut storage_dead_stmt = None;
147+
for (idx, stmt) in stmts.iter().enumerate() {
148+
if stmt.kind == StatementKind::StorageLive(l) {
149+
storage_live_stmt = Some(idx);
150+
} else if stmt.kind == StatementKind::StorageDead(l) {
151+
storage_dead_stmt = Some(idx);
152+
}
153+
}
154+
155+
Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX)))
156+
}
157+
158+
// Try to match the expected MIR structure with the basic block we're processing.
159+
// We want to see something that looks like:
160+
// ```
161+
// (StorageLive(_) | StorageDead(_));*
162+
// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
163+
// (StorageLive(_) | StorageDead(_));*
164+
// (tmp_n+1 = tmp_n);*
165+
// (StorageLive(_) | StorageDead(_));*
166+
// (tmp_n+1 = tmp_n);*
167+
// ((LOCAL_FROM as Variant).FIELD: TY) = move tmp;
168+
// discriminant(LOCAL_FROM) = VariantIdx;
169+
// (StorageLive(_) | StorageDead(_));*
170+
// ```
171+
let mut stmt_iter = stmts.iter().enumerate().peekable();
172+
173+
try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
174+
175+
let (get_variant_field_stmt, stmt) = stmt_iter.next()?;
176+
let (local_tmp_s0, local_1, vf_s0) = match_get_variant_field(stmt)?;
177+
178+
try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
179+
180+
try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
181+
182+
try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
183+
184+
try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
185+
186+
let (idx, stmt) = stmt_iter.next()?;
187+
let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?;
188+
nop_stmts.push(idx);
189+
190+
let (idx, stmt) = stmt_iter.next()?;
191+
let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?;
192+
let discr_stmt_source_info = stmt.source_info;
193+
nop_stmts.push(idx);
194+
195+
try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
196+
197+
for (live_idx, live_local) in storage_live_stmts {
198+
if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) {
199+
let (dead_idx, _) = storage_dead_stmts.swap_remove(i);
200+
storage_stmts.push((live_idx, dead_idx, live_local));
201+
202+
if live_local == local_tmp_s0 {
203+
nop_stmts.push(get_variant_field_stmt);
204+
}
205+
}
206+
}
207+
208+
nop_stmts.sort();
209+
210+
// Use one of the statements we're going to discard between the point
211+
// where the storage location for the variant field becomes live and
212+
// is killed.
213+
let (live_idx, daed_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?;
214+
let stmt_to_overwrite =
215+
nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < daed_idx);
216+
217+
Some(ArmIdentityInfo {
218+
local_temp_0: local_tmp_s0,
219+
local_1,
220+
vf_s0,
221+
get_variant_field_stmt,
222+
field_tmp_assignments: tmp_assigns,
223+
local_tmp_s1,
224+
local_0,
225+
vf_s1,
226+
set_discr_local,
227+
set_discr_var_idx,
228+
stmt_to_overwrite: *stmt_to_overwrite?,
229+
source_info: discr_stmt_source_info,
230+
storage_stmts,
231+
stmts_to_remove: nop_stmts,
232+
})
233+
}
234+
235+
fn optimization_applies<'tcx>(
236+
opt_info: &ArmIdentityInfo<'tcx>,
237+
local_decls: &IndexVec<Local, LocalDecl<'tcx>>,
238+
) -> bool {
239+
trace!("testing if optimization applies...");
240+
241+
// FIXME(wesleywiser): possible relax this restriction?
242+
if opt_info.local_0 == opt_info.local_1 {
243+
trace!("NO: moving into ourselves");
244+
return false;
245+
} else if opt_info.vf_s0 != opt_info.vf_s1 {
246+
trace!("NO: the field-and-variant information do not match");
247+
return false;
248+
} else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty {
249+
// FIXME(Centril,oli-obk): possibly relax to same layout?
250+
trace!("NO: source and target locals have different types");
251+
return false;
252+
} else if (opt_info.local_0, opt_info.vf_s0.var_idx)
253+
!= (opt_info.set_discr_local, opt_info.set_discr_var_idx)
254+
{
255+
trace!("NO: the discriminants do not match");
256+
return false;
257+
}
258+
259+
// Verify the assigment chain consists of the form b = a; c = b; d = c; etc...
260+
if opt_info.field_tmp_assignments.len() == 0 {
261+
trace!("NO: no assignments found");
262+
}
263+
let mut last_assigned_to = opt_info.field_tmp_assignments[0].1;
264+
let source_local = last_assigned_to;
265+
for (l, r) in &opt_info.field_tmp_assignments {
266+
if *r != last_assigned_to {
267+
trace!("NO: found unexpected assignment {:?} = {:?}", l, r);
268+
return false;
269+
}
270+
271+
last_assigned_to = *l;
272+
}
273+
274+
if source_local != opt_info.local_temp_0 {
275+
trace!(
276+
"NO: start of assignment chain does not match enum variant temp: {:?} != {:?}",
277+
source_local,
278+
opt_info.local_temp_0
279+
);
280+
return false;
281+
} else if last_assigned_to != opt_info.local_tmp_s1 {
282+
trace!(
283+
"NO: end of assignemnt chain does not match written enum temp: {:?} != {:?}",
284+
last_assigned_to,
285+
opt_info.local_tmp_s1
286+
);
287+
return false;
288+
}
289+
290+
trace!("SUCCESS: optimization applies!");
291+
return true;
292+
}
293+
35294
impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
36-
fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
295+
fn run_pass(&self, _: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
296+
trace!("running SimplifyArmIdentity on {:?}", source);
37297
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
38298
for bb in basic_blocks {
39-
// Need 3 statements:
40-
let (s0, s1, s2) = match &mut *bb.statements {
41-
[s0, s1, s2] => (s0, s1, s2),
42-
_ => continue,
43-
};
299+
if let Some(opt_info) = get_arm_identity_info(&bb.statements) {
300+
trace!("got opt_info = {:#?}", opt_info);
301+
if !optimization_applies(&opt_info, local_decls) {
302+
debug!("optimization skipped for {:?}", source);
303+
continue;
304+
}
44305

45-
// Pattern match on the form we want:
46-
let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) {
47-
None => continue,
48-
Some(x) => x,
49-
};
50-
let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) {
51-
None => continue,
52-
Some(x) => x,
53-
};
54-
if local_tmp_s0 != local_tmp_s1
55-
// Avoid moving into ourselves.
56-
|| local_0 == local_1
57-
// The field-and-variant information match up.
58-
|| vf_s0 != vf_s1
59-
// Source and target locals have the same type.
60-
// FIXME(Centril | oli-obk): possibly relax to same layout?
61-
|| local_decls[local_0].ty != local_decls[local_1].ty
62-
// We're setting the discriminant of `local_0` to this variant.
63-
|| Some((local_0, vf_s0.var_idx)) != match_set_discr(s2)
64-
{
65-
continue;
66-
}
306+
// Also remove unused Storage{Live,Dead} statements which correspond
307+
// to temps used previously.
308+
for (live_idx, dead_idx, local) in &opt_info.storage_stmts {
309+
// The temporary that we've read the variant field into is scoped to this block,
310+
// so we can remove the assignment.
311+
if *local == opt_info.local_temp_0 {
312+
bb.statements[opt_info.get_variant_field_stmt].make_nop();
313+
}
67314

68-
// Right shape; transform!
69-
s0.source_info = s2.source_info;
70-
match &mut s0.kind {
71-
StatementKind::Assign(box (place, rvalue)) => {
72-
*place = local_0.into();
73-
*rvalue = Rvalue::Use(Operand::Move(local_1.into()));
315+
for (left, right) in &opt_info.field_tmp_assignments {
316+
if local == left || local == right {
317+
bb.statements[*live_idx].make_nop();
318+
bb.statements[*dead_idx].make_nop();
319+
}
320+
}
74321
}
75-
_ => unreachable!(),
322+
323+
// Right shape; transform
324+
for stmt_idx in opt_info.stmts_to_remove {
325+
bb.statements[stmt_idx].make_nop();
326+
}
327+
328+
let stmt = &mut bb.statements[opt_info.stmt_to_overwrite];
329+
stmt.source_info = opt_info.source_info;
330+
stmt.kind = StatementKind::Assign(box (
331+
opt_info.local_0.into(),
332+
Rvalue::Use(Operand::Move(opt_info.local_1.into())),
333+
));
334+
335+
bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop);
336+
337+
trace!("block is now {:?}", bb.statements);
76338
}
77-
s1.make_nop();
78-
s2.make_nop();
79339
}
80340
}
81341
}
@@ -129,7 +389,7 @@ fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)>
129389
}
130390
}
131391

132-
#[derive(PartialEq)]
392+
#[derive(PartialEq, Debug)]
133393
struct VarField<'tcx> {
134394
field: Field,
135395
field_ty: Ty<'tcx>,

‎src/test/mir-opt/simplify-arm-identity.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,14 @@ fn main() {
3939
// }
4040
// ...
4141
// bb3: {
42+
// StorageLive(_4);
4243
// _4 = ((_1 as Foo).0: u8);
43-
// ((_2 as Foo).0: u8) = move _4;
44+
// StorageLive(_5);
45+
// _5 = _4;
46+
// ((_2 as Foo).0: u8) = move _5;
4447
// discriminant(_2) = 0;
48+
// StorageDead(_5);
49+
// StorageDead(_4);
4550
// goto -> bb4;
4651
// }
4752
// ...
@@ -65,9 +70,14 @@ fn main() {
6570
// }
6671
// ...
6772
// bb3: {
73+
// StorageLive(_4);
6874
// _4 = ((_1 as Foo).0: u8);
69-
// ((_2 as Foo).0: u8) = move _4;
75+
// StorageLive(_5);
76+
// _5 = _4;
77+
// ((_2 as Foo).0: u8) = move _5;
7078
// discriminant(_2) = 0;
79+
// StorageDead(_5);
80+
// StorageDead(_4);
7181
// goto -> bb4;
7282
// }
7383
// ...

‎src/test/mir-opt/simplify-arm.rs

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
// compile-flags: -Z mir-opt-level=1
2+
3+
fn id(o: Option<u8>) -> Option<u8> {
4+
match o {
5+
Some(v) => Some(v),
6+
None => None,
7+
}
8+
}
9+
10+
fn id_result(r: Result<u8, i32>) -> Result<u8, i32> {
11+
match r {
12+
Ok(x) => Ok(x),
13+
Err(y) => Err(y),
14+
}
15+
}
16+
17+
fn id_try(r: Result<u8, i32>) -> Result<u8, i32> {
18+
let x = r?;
19+
Ok(x)
20+
}
21+
22+
fn main() {
23+
id(None);
24+
id_result(Ok(4));
25+
id_try(Ok(4));
26+
}
27+
28+
// END RUST SOURCE
29+
// START rustc.id.SimplifyArmIdentity.before.mir
30+
// debug o => _1;
31+
// let mut _0: std::option::Option<u8>;
32+
// let mut _2: isize;
33+
// let _3: u8;
34+
// let mut _4: u8;
35+
// scope 1 {
36+
// debug v => _3;
37+
// }
38+
// bb0: {
39+
// _2 = discriminant(_1);
40+
// switchInt(move _2) -> [0isize: bb1, 1isize: bb3, otherwise: bb2];
41+
// }
42+
// bb1: {
43+
// discriminant(_0) = 0;
44+
// goto -> bb4;
45+
// }
46+
// bb2: {
47+
// unreachable;
48+
// }
49+
// bb3: {
50+
// StorageLive(_3);
51+
// _3 = ((_1 as Some).0: u8);
52+
// StorageLive(_4);
53+
// _4 = _3;
54+
// ((_0 as Some).0: u8) = move _4;
55+
// discriminant(_0) = 1;
56+
// StorageDead(_4);
57+
// StorageDead(_3);
58+
// goto -> bb4;
59+
// }
60+
// bb4: {
61+
// return;
62+
// }
63+
// END rustc.id.SimplifyArmIdentity.before.mir
64+
// START rustc.id.SimplifyArmIdentity.after.mir
65+
// debug o => _1;
66+
// let mut _0: std::option::Option<u8>;
67+
// let mut _2: isize;
68+
// let _3: u8;
69+
// let mut _4: u8;
70+
// scope 1 {
71+
// debug v => _3;
72+
// }
73+
// bb0: {
74+
// _2 = discriminant(_1);
75+
// switchInt(move _2) -> [0isize: bb1, 1isize: bb3, otherwise: bb2];
76+
// }
77+
// bb1: {
78+
// discriminant(_0) = 0;
79+
// goto -> bb4;
80+
// }
81+
// bb2: {
82+
// unreachable;
83+
// }
84+
// bb3: {
85+
// _0 = move _1;
86+
// goto -> bb4;
87+
// }
88+
// bb4: {
89+
// return;
90+
// }
91+
// END rustc.id.SimplifyArmIdentity.after.mir
92+
93+
// START rustc.id_result.SimplifyArmIdentity.before.mir
94+
// debug r => _1;
95+
// let mut _0: std::result::Result<u8, i32>;
96+
// let mut _2: isize;
97+
// let _3: u8;
98+
// let mut _4: u8;
99+
// let _5: i32;
100+
// let mut _6: i32;
101+
// scope 1 {
102+
// debug x => _3;
103+
// }
104+
// scope 2 {
105+
// debug y => _5;
106+
// }
107+
// bb0: {
108+
// _2 = discriminant(_1);
109+
// switchInt(move _2) -> [0isize: bb3, 1isize: bb1, otherwise: bb2];
110+
// }
111+
// bb1: {
112+
// StorageLive(_5);
113+
// _5 = ((_1 as Err).0: i32);
114+
// StorageLive(_6);
115+
// _6 = _5;
116+
// ((_0 as Err).0: i32) = move _6;
117+
// discriminant(_0) = 1;
118+
// StorageDead(_6);
119+
// StorageDead(_5);
120+
// goto -> bb4;
121+
// }
122+
// bb2: {
123+
// unreachable;
124+
// }
125+
// bb3: {
126+
// StorageLive(_3);
127+
// _3 = ((_1 as Ok).0: u8);
128+
// StorageLive(_4);
129+
// _4 = _3;
130+
// ((_0 as Ok).0: u8) = move _4;
131+
// discriminant(_0) = 0;
132+
// StorageDead(_4);
133+
// StorageDead(_3);
134+
// goto -> bb4;
135+
// }
136+
// bb4: {
137+
// return;
138+
// }
139+
// END rustc.id_result.SimplifyArmIdentity.before.mir
140+
// START rustc.id_result.SimplifyArmIdentity.after.mir
141+
// debug r => _1;
142+
// let mut _0: std::result::Result<u8, i32>;
143+
// let mut _2: isize;
144+
// let _3: u8;
145+
// let mut _4: u8;
146+
// let _5: i32;
147+
// let mut _6: i32;
148+
// scope 1 {
149+
// debug x => _3;
150+
// }
151+
// scope 2 {
152+
// debug y => _5;
153+
// }
154+
// bb0: {
155+
// _2 = discriminant(_1);
156+
// switchInt(move _2) -> [0isize: bb3, 1isize: bb1, otherwise: bb2];
157+
// }
158+
// bb1: {
159+
// _0 = move _1;
160+
// goto -> bb4;
161+
// }
162+
// bb2: {
163+
// unreachable;
164+
// }
165+
// bb3: {
166+
// _0 = move _1;
167+
// goto -> bb4;
168+
// }
169+
// bb4: {
170+
// return;
171+
// }
172+
// END rustc.id_result.SimplifyArmIdentity.after.mir
173+
// START rustc.id_result.SimplifyBranchSame.before.mir
174+
// debug r => _1;
175+
// let mut _0: std::result::Result<u8, i32>;
176+
// let mut _2: isize;
177+
// let _3: u8;
178+
// let mut _4: u8;
179+
// let _5: i32;
180+
// let mut _6: i32;
181+
// scope 1 {
182+
// debug x => _3;
183+
// }
184+
// scope 2 {
185+
// debug y => _5;
186+
// }
187+
// bb0: {
188+
// _2 = discriminant(_1);
189+
// switchInt(move _2) -> [0isize: bb3, 1isize: bb1, otherwise: bb2];
190+
// }
191+
// bb1: {
192+
// _0 = move _1;
193+
// goto -> bb4;
194+
// }
195+
// bb2: {
196+
// unreachable;
197+
// }
198+
// bb3: {
199+
// _0 = move _1;
200+
// goto -> bb4;
201+
// }
202+
// bb4: {
203+
// return;
204+
// }
205+
// END rustc.id_result.SimplifyBranchSame.before.mir
206+
// START rustc.id_result.SimplifyBranchSame.after.mir
207+
// debug r => _1;
208+
// let mut _0: std::result::Result<u8, i32>;
209+
// let mut _2: isize;
210+
// let _3: u8;
211+
// let mut _4: u8;
212+
// let _5: i32;
213+
// let mut _6: i32;
214+
// scope 1 {
215+
// debug x => _3;
216+
// }
217+
// scope 2 {
218+
// debug y => _5;
219+
// }
220+
// bb0: {
221+
// _2 = discriminant(_1);
222+
// goto -> bb1;
223+
// }
224+
// bb1: {
225+
// _0 = move _1;
226+
// goto -> bb2;
227+
// }
228+
// bb2: {
229+
// return;
230+
// }
231+
// END rustc.id_result.SimplifyBranchSame.after.mir
232+
233+
// START rustc.id_try.SimplifyArmIdentity.before.mir
234+
// debug r => _1;
235+
// let mut _0: std::result::Result<u8, i32>;
236+
// let _2: u8;
237+
// let mut _3: std::result::Result<u8, i32>;
238+
// let mut _4: std::result::Result<u8, i32>;
239+
// let mut _5: isize;
240+
// let _6: i32;
241+
// let mut _7: !;
242+
// let mut _8: i32;
243+
// let mut _9: i32;
244+
// let _10: u8;
245+
// let mut _11: u8;
246+
// scope 1 {
247+
// debug x => _2;
248+
// }
249+
// scope 2 {
250+
// debug err => _6;
251+
// scope 3 {
252+
// }
253+
// }
254+
// scope 4 {
255+
// debug val => _10;
256+
// scope 5 {
257+
// }
258+
// }
259+
// bb0: {
260+
// StorageLive(_2);
261+
// StorageLive(_3);
262+
// StorageLive(_4);
263+
// _4 = _1;
264+
// _3 = const <std::result::Result<u8, i32> as std::ops::Try>::into_result(move _4) -> bb1;
265+
// }
266+
// bb1: {
267+
// StorageDead(_4);
268+
// _5 = discriminant(_3);
269+
// switchInt(move _5) -> [0isize: bb2, 1isize: bb4, otherwise: bb3];
270+
// }
271+
// bb2: {
272+
// StorageLive(_10);
273+
// _10 = ((_3 as Ok).0: u8);
274+
// _2 = _10;
275+
// StorageDead(_10);
276+
// StorageDead(_3);
277+
// StorageLive(_11);
278+
// _11 = _2;
279+
// ((_0 as Ok).0: u8) = move_ 11;
280+
// discriminant(_0) = 0;
281+
// StorageDead(_11);
282+
// StorageDead(_2);
283+
// goto -> bb5;
284+
// }
285+
// bb3: {
286+
// unreachable;
287+
// }
288+
// bb4: {
289+
// StorageLive(_6);
290+
// _6 = ((_3 as Err).0: i32);
291+
// StorageLive(_8);
292+
// StorageLive(_9);
293+
// _9 = _6;
294+
// _8 = const <i32 as std::convert::From<i32>>::from(move _9) -> bb6;
295+
// }
296+
// bb5: {
297+
// return;
298+
// }
299+
// bb6: {
300+
// StorageDead(_9);
301+
// _0 = const <std::result::Result<u8, i32> as std::ops::Try>::from_error(move _8) -> bb7;
302+
// }
303+
// bb7: {
304+
// StorageDead(_8);
305+
// StorageDead(_6);
306+
// StorageDead(_3);
307+
// StorageDead(_2);
308+
// goto -> bb5;
309+
// }
310+
// END rustc.id_try.SimplifyArmIdentity.before.mir
311+
// START rustc.id_try.SimplifyArmIdentity.after.mir
312+
// debug r => _1;
313+
// let mut _0: std::result::Result<u8, i32>;
314+
// let _2: u8;
315+
// let mut _3: std::result::Result<u8, i32>;
316+
// let mut _4: std::result::Result<u8, i32>;
317+
// let mut _5: isize;
318+
// let _6: i32;
319+
// let mut _7: !;
320+
// let mut _8: i32;
321+
// let mut _9: i32;
322+
// let _10: u8;
323+
// let mut _11: u8;
324+
// scope 1 {
325+
// debug x => _2;
326+
// }
327+
// scope 2 {
328+
// debug err => _6;
329+
// scope 3 {
330+
// }
331+
// }
332+
// scope 4 {
333+
// debug val => _10;
334+
// scope 5 {
335+
// }
336+
// }
337+
// bb0: {
338+
// StorageLive(_2);
339+
// StorageLive(_3);
340+
// StorageLive(_4);
341+
// _4 = _1;
342+
// _3 = const <std::result::Result<u8, i32> as std::ops::Try>::into_result(move _4) -> bb1;
343+
// }
344+
// bb1: {
345+
// StorageDead(_4);
346+
// _5 = discriminant(_3);
347+
// switchInt(move _5) -> [0isize: bb2, 1isize: bb4, otherwise: bb3];
348+
// }
349+
// bb2: {
350+
// _0 = move _3;
351+
// StorageDead(_3);
352+
// StorageDead(_2);
353+
// goto -> bb5;
354+
// }
355+
// bb3: {
356+
// unreachable;
357+
// }
358+
// bb4: {
359+
// StorageLive(_6);
360+
// _6 = ((_3 as Err).0: i32);
361+
// StorageLive(_8);
362+
// StorageLive(_9);
363+
// _9 = _6;
364+
// _8 = const <i32 as std::convert::From<i32>>::from(move _9) -> bb6;
365+
// }
366+
// bb5: {
367+
// return;
368+
// }
369+
// bb6: {
370+
// StorageDead(_9);
371+
// _0 = const <std::result::Result<u8, i32> as std::ops::Try>::from_error(move _8) -> bb7;
372+
// }
373+
// bb7: {
374+
// StorageDead(_8);
375+
// StorageDead(_6);
376+
// StorageDead(_3);
377+
// StorageDead(_2);
378+
// goto -> bb5;
379+
// }
380+
// END rustc.id_try.SimplifyArmIdentity.after.mir

‎src/test/mir-opt/simplify_try.rs

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ fn main() {
2323
// let _10: u32;
2424
// let mut _11: u32;
2525
// scope 1 {
26-
// debug y => _10;
26+
// debug y => _2;
2727
// }
2828
// scope 2 {
2929
// debug err => _6;
3030
// scope 3 {
3131
// scope 7 {
32-
// debug t => _6;
32+
// debug t => _9;
3333
// }
3434
// scope 8 {
35-
// debug v => _6;
35+
// debug v => _8;
3636
// let mut _12: i32;
3737
// }
3838
// }
@@ -43,22 +43,49 @@ fn main() {
4343
// }
4444
// }
4545
// scope 6 {
46-
// debug self => _1;
46+
// debug self => _4;
4747
// }
4848
// bb0: {
49-
// _5 = discriminant(_1);
49+
// StorageLive(_2);
50+
// StorageLive(_3);
51+
// StorageLive(_4);
52+
// _4 = _1;
53+
// _3 = move _4;
54+
// StorageDead(_4);
55+
// _5 = discriminant(_3);
5056
// switchInt(move _5) -> [0isize: bb1, otherwise: bb2];
5157
// }
5258
// bb1: {
53-
// _10 = ((_1 as Ok).0: u32);
54-
// ((_0 as Ok).0: u32) = move _10;
59+
// StorageLive(_10);
60+
// _10 = ((_3 as Ok).0: u32);
61+
// _2 = _10;
62+
// StorageDead(_10);
63+
// StorageDead(_3);
64+
// StorageLive(_11);
65+
// _11 = _2;
66+
// ((_0 as Ok).0: u32) = move _11;
5567
// discriminant(_0) = 0;
68+
// StorageDead(_11);
69+
// StorageDead(_2);
5670
// goto -> bb3;
5771
// }
5872
// bb2: {
59-
// _6 = ((_1 as Err).0: i32);
60-
// ((_0 as Err).0: i32) = move _6;
73+
// StorageLive(_6);
74+
// _6 = ((_3 as Err).0: i32);
75+
// StorageLive(_8);
76+
// StorageLive(_9);
77+
// _9 = _6;
78+
// _8 = move _9;
79+
// StorageDead(_9);
80+
// StorageLive(_12);
81+
// _12 = move _8;
82+
// ((_0 as Err).0: i32) = move _12;
6183
// discriminant(_0) = 1;
84+
// StorageDead(_12);
85+
// StorageDead(_8);
86+
// StorageDead(_6);
87+
// StorageDead(_3);
88+
// StorageDead(_2);
6289
// goto -> bb3;
6390
// }
6491
// bb3: {
@@ -82,16 +109,16 @@ fn main() {
82109
// let _10: u32;
83110
// let mut _11: u32;
84111
// scope 1 {
85-
// debug y => _10;
112+
// debug y => _2;
86113
// }
87114
// scope 2 {
88115
// debug err => _6;
89116
// scope 3 {
90117
// scope 7 {
91-
// debug t => _6;
118+
// debug t => _9;
92119
// }
93120
// scope 8 {
94-
// debug v => _6;
121+
// debug v => _8;
95122
// let mut _12: i32;
96123
// }
97124
// }
@@ -102,22 +129,28 @@ fn main() {
102129
// }
103130
// }
104131
// scope 6 {
105-
// debug self => _1;
132+
// debug self => _4;
106133
// }
107134
// bb0: {
108-
// _5 = discriminant(_1);
135+
// StorageLive(_2);
136+
// StorageLive(_3);
137+
// StorageLive(_4);
138+
// _4 = _1;
139+
// _3 = move _4;
140+
// StorageDead(_4);
141+
// _5 = discriminant(_3);
109142
// switchInt(move _5) -> [0isize: bb1, otherwise: bb2];
110143
// }
111144
// bb1: {
112-
// _0 = move _1;
113-
// nop;
114-
// nop;
145+
// _0 = move _3;
146+
// StorageDead(_3);
147+
// StorageDead(_2);
115148
// goto -> bb3;
116149
// }
117150
// bb2: {
118-
// _0 = move _1;
119-
// nop;
120-
// nop;
151+
// _0 = move _3;
152+
// StorageDead(_3);
153+
// StorageDead(_2);
121154
// goto -> bb3;
122155
// }
123156
// bb3: {
@@ -141,16 +174,16 @@ fn main() {
141174
// let _10: u32;
142175
// let mut _11: u32;
143176
// scope 1 {
144-
// debug y => _10;
177+
// debug y => _2;
145178
// }
146179
// scope 2 {
147180
// debug err => _6;
148181
// scope 3 {
149182
// scope 7 {
150-
// debug t => _6;
183+
// debug t => _9;
151184
// }
152185
// scope 8 {
153-
// debug v => _6;
186+
// debug v => _8;
154187
// let mut _12: i32;
155188
// }
156189
// }
@@ -161,16 +194,22 @@ fn main() {
161194
// }
162195
// }
163196
// scope 6 {
164-
// debug self => _1;
197+
// debug self => _4;
165198
// }
166199
// bb0: {
167-
// _5 = discriminant(_1);
200+
// StorageLive(_2);
201+
// StorageLive(_3);
202+
// StorageLive(_4);
203+
// _4 = _1;
204+
// _3 = move _4;
205+
// StorageDead(_4);
206+
// _5 = discriminant(_3);
168207
// goto -> bb1;
169208
// }
170209
// bb1: {
171-
// _0 = move _1;
172-
// nop;
173-
// nop;
210+
// _0 = move _3;
211+
// StorageDead(_3);
212+
// StorageDead(_2);
174213
// goto -> bb2;
175214
// }
176215
// bb2: {
@@ -183,34 +222,39 @@ fn main() {
183222
// fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
184223
// debug x => _1;
185224
// let mut _0: std::result::Result<u32, i32>;
186-
// let mut _2: isize;
187-
// let _3: i32;
188-
// let _4: u32;
225+
// let _2: u32;
226+
// let mut _3: isize;
227+
// let _4: i32;
228+
// let mut _5: i32;
229+
// let mut _6: i32;
230+
// let _7: u32;
189231
// scope 1 {
190-
// debug y => _4;
232+
// debug y => _2;
191233
// }
192234
// scope 2 {
193-
// debug err => _3;
235+
// debug err => _4;
194236
// scope 3 {
195237
// scope 7 {
196-
// debug t => _3;
238+
// debug t => _6;
197239
// }
198240
// scope 8 {
199-
// debug v => _3;
241+
// debug v => _5;
200242
// }
201243
// }
202244
// }
203245
// scope 4 {
204-
// debug val => _4;
246+
// debug val => _7;
205247
// scope 5 {
206248
// }
207249
// }
208250
// scope 6 {
209251
// debug self => _1;
210252
// }
211253
// bb0: {
212-
// _2 = discriminant(_1);
254+
// StorageLive(_2);
255+
// _3 = discriminant(_1);
213256
// _0 = move _1;
257+
// StorageDead(_2);
214258
// return;
215259
// }
216260
// }
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// compile-flags: -Zmir-opt-level=1
2+
// ignore-tidy-linelength
3+
4+
use std::ptr::NonNull;
5+
6+
pub struct LinkedList {
7+
head: Option<NonNull<Node>>,
8+
tail: Option<NonNull<Node>>,
9+
}
10+
11+
pub struct Node {
12+
next: Option<NonNull<Node>>,
13+
}
14+
15+
impl LinkedList {
16+
pub fn new() -> Self {
17+
Self { head: None, tail: None }
18+
}
19+
20+
pub fn append(&mut self, other: &mut Self) {
21+
match self.tail {
22+
None => { },
23+
Some(mut tail) => {
24+
// `as_mut` is okay here because we have exclusive access to the entirety
25+
// of both lists.
26+
if let Some(other_head) = other.head.take() {
27+
unsafe {
28+
tail.as_mut().next = Some(other_head);
29+
}
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
fn main() {
37+
let mut one = LinkedList::new();
38+
let mut two = LinkedList::new();
39+
one.append(&mut two);
40+
}
41+
42+
// END RUST SOURCE
43+
// START rustc.{{impl}}-append.SimplifyArmIdentity.before.mir
44+
// debug self => _1;
45+
// debug other => _2;
46+
// let mut _0: ();
47+
// let mut _3: isize;
48+
// let mut _4: std::ptr::NonNull<Node>;
49+
// let mut _5: std::option::Option<std::ptr::NonNull<Node>>;
50+
// let mut _6: &mut std::option::Option<std::ptr::NonNull<Node>>;
51+
// let mut _7: isize;
52+
// let mut _9: std::option::Option<std::ptr::NonNull<Node>>;
53+
// let mut _10: std::ptr::NonNull<Node>;
54+
// let mut _11: &mut Node;
55+
// let mut _12: &mut std::ptr::NonNull<Node>;
56+
// scope 1 {
57+
// debug tail => _4;
58+
// let _8: std::ptr::NonNull<Node>;
59+
// scope 2 {
60+
// debug other_head => _8;
61+
// scope 3 {
62+
// }
63+
// }
64+
// }
65+
// bb0: {
66+
// _3 = discriminant(((*_1).1: std::option::Option<std::ptr::NonNull<Node>>));
67+
// switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2];
68+
// }
69+
// bb1: {
70+
// StorageLive(_4);
71+
// _4 = ((((*_1).1: std::option::Option<std::ptr::NonNull<Node>>) as Some).0: std::ptr::NonNull<Node>);
72+
// StorageLive(_5);
73+
// StorageLive(_6);
74+
// _6 = &mut ((*_2).0: std::option::Option<std::ptr::NonNull<Node>>);
75+
// _5 = const std::option::Option::<std::ptr::NonNull<Node>>::take(move _6) -> bb4;
76+
// }
77+
// bb2: {
78+
// unreachable;
79+
// }
80+
// bb3: {
81+
// nop;
82+
// goto -> bb9;
83+
// }
84+
// bb4: {
85+
// StorageDead(_6);
86+
// _7 = discriminant(_5);
87+
// switchInt(move _7) -> [1isize: bb6, otherwise: bb5];
88+
// }
89+
// bb5: {
90+
// nop;
91+
// goto -> bb8;
92+
// }
93+
// bb6: {
94+
// StorageLive(_8);
95+
// _8 = ((_5 as Some).0: std::ptr::NonNull<Node>);
96+
// StorageLive(_9);
97+
// StorageLive(_10);
98+
// _10 = _8;
99+
// ((_9 as Some).0: std::ptr::NonNull<Node>) = move _10;
100+
// discriminant(_9) = 1;
101+
// StorageDead(_10);
102+
// StorageLive(_11);
103+
// StorageLive(_12);
104+
// _12 = &mut _4;
105+
// _11 = const std::ptr::NonNull::<Node>::as_mut(move _12) -> bb7;
106+
// }
107+
// bb7: {
108+
// StorageDead(_12);
109+
// ((*_11).0: std::option::Option<std::ptr::NonNull<Node>>) = move _9;
110+
// StorageDead(_9);
111+
// StorageDead(_11);
112+
// nop;
113+
// StorageDead(_8);
114+
// goto -> bb8;
115+
// }
116+
// bb8: {
117+
// StorageDead(_5);
118+
// StorageDead(_4);
119+
// goto -> bb9;
120+
// }
121+
// bb9: {
122+
// return;
123+
// }
124+
// END rustc.{{impl}}-append.SimplifyArmIdentity.before.mir
125+
126+
// START rustc.{{impl}}-append.SimplifyArmIdentity.after.mir
127+
// debug self => _1;
128+
// debug other => _2;
129+
// let mut _0: ();
130+
// let mut _3: isize;
131+
// let mut _4: std::ptr::NonNull<Node>;
132+
// let mut _5: std::option::Option<std::ptr::NonNull<Node>>;
133+
// let mut _6: &mut std::option::Option<std::ptr::NonNull<Node>>;
134+
// let mut _7: isize;
135+
// let mut _9: std::option::Option<std::ptr::NonNull<Node>>;
136+
// let mut _10: std::ptr::NonNull<Node>;
137+
// let mut _11: &mut Node;
138+
// let mut _12: &mut std::ptr::NonNull<Node>;
139+
// scope 1 {
140+
// debug tail => _4;
141+
// let _8: std::ptr::NonNull<Node>;
142+
// scope 2 {
143+
// debug other_head => _8;
144+
// scope 3 {
145+
// }
146+
// }
147+
// }
148+
// bb0: {
149+
// _3 = discriminant(((*_1).1: std::option::Option<std::ptr::NonNull<Node>>));
150+
// switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2];
151+
// }
152+
// bb1: {
153+
// StorageLive(_4);
154+
// _4 = ((((*_1).1: std::option::Option<std::ptr::NonNull<Node>>) as Some).0: std::ptr::NonNull<Node>);
155+
// StorageLive(_5);
156+
// StorageLive(_6);
157+
// _6 = &mut ((*_2).0: std::option::Option<std::ptr::NonNull<Node>>);
158+
// _5 = const std::option::Option::<std::ptr::NonNull<Node>>::take(move _6) -> bb4;
159+
// }
160+
// bb2: {
161+
// unreachable;
162+
// }
163+
// bb3: {
164+
// nop;
165+
// goto -> bb9;
166+
// }
167+
// bb4: {
168+
// StorageDead(_6);
169+
// _7 = discriminant(_5);
170+
// switchInt(move _7) -> [1isize: bb6, otherwise: bb5];
171+
// }
172+
// bb5: {
173+
// nop;
174+
// goto -> bb8;
175+
// }
176+
// bb6: {
177+
// StorageLive(_8);
178+
// _8 = ((_5 as Some).0: std::ptr::NonNull<Node>);
179+
// StorageLive(_9);
180+
// _9 = move _5;
181+
// StorageLive(_11);
182+
// StorageLive(_12);
183+
// _12 = &mut _4;
184+
// _11 = const std::ptr::NonNull::<Node>::as_mut(move _12) -> bb7;
185+
// }
186+
// bb7: {
187+
// StorageDead(_12);
188+
// ((*_11).0: std::option::Option<std::ptr::NonNull<Node>>) = move _9;
189+
// StorageDead(_9);
190+
// StorageDead(_11);
191+
// nop;
192+
// StorageDead(_8);
193+
// goto -> bb8;
194+
// }
195+
// bb8: {
196+
// StorageDead(_5);
197+
// StorageDead(_4);
198+
// goto -> bb9;
199+
// }
200+
// bb9: {
201+
// return;
202+
// }
203+
// END rustc.{{impl}}-append.SimplifyArmIdentity.after.mir

0 commit comments

Comments
 (0)
Please sign in to comment.