Closed
Description
The following MIR can be improved by converting a copy to a move: https://godbolt.org/z/jnKK4f .
Observed
fn opt_char(_1: char) -> u32 {
debug x => _1; // in scope 0 at ./example.rs:1:17: 1:18
let mut _0: u32; // return place in scope 0 at ./example.rs:1:29: 1:32
let mut _2: bool; // in scope 0 at ./example.rs:2:8: 2:28
let mut _3: char; // in scope 0 at ./example.rs:2:8: 2:9
let mut _4: bool; // in scope 0 at ./example.rs:2:20: 2:28
let mut _5: char; // in scope 0 at ./example.rs:2:20: 2:21
bb0: {
StorageLive(_2); // scope 0 at ./example.rs:2:8: 2:28
StorageLive(_3); // scope 0 at ./example.rs:2:8: 2:9
_3 = _1; // scope 0 at ./example.rs:2:8: 2:9
switchInt(move _3) -> ['b': bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:8: 2:28
}
bb1: {
StorageDead(_3); // scope 0 at ./example.rs:2:8: 2:28
_2 = const true; // scope 0 at ./example.rs:2:8: 2:28
goto -> bb3; // scope 0 at ./example.rs:2:8: 2:28
}
bb2: {
StorageDead(_3); // scope 0 at ./example.rs:2:8: 2:28
StorageLive(_4); // scope 0 at ./example.rs:2:20: 2:28
StorageLive(_5); // scope 0 at ./example.rs:2:20: 2:21
_5 = _1; // scope 0 at ./example.rs:2:20: 2:21
_4 = Eq(move _5, const 'a'); // scope 0 at ./example.rs:2:20: 2:28
StorageDead(_5); // scope 0 at ./example.rs:2:27: 2:28
_2 = Ne(_4, const false); // scope 0 at ./example.rs:2:8: 2:28
goto -> bb3; // scope 0 at ./example.rs:2:8: 2:28
}
bb3: {
StorageDead(_4); // scope 0 at ./example.rs:2:27: 2:28
switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at ./example.rs:2:5: 2:45
}
bb4: {
_0 = const 1_u32; // scope 0 at ./example.rs:2:42: 2:43
goto -> bb6; // scope 0 at ./example.rs:2:5: 2:45
}
bb5: {
_0 = const 0_u32; // scope 0 at ./example.rs:2:31: 2:32
goto -> bb6; // scope 0 at ./example.rs:2:5: 2:45
}
bb6: {
StorageDead(_2); // scope 0 at ./example.rs:3:1: 3:2
return; // scope 0 at ./example.rs:3:2: 3:2
}
}
Expected: _2 is moved into the switchInt as it has no uses later. _4 could similarly be moved into the Ne comparison
fn opt_char(_1: char) -> u32 {
debug x => _1; // in scope 0 at ./example.rs:1:17: 1:18
let mut _0: u32; // return place in scope 0 at ./example.rs:1:29: 1:32
let mut _2: bool; // in scope 0 at ./example.rs:2:8: 2:28
let mut _3: char; // in scope 0 at ./example.rs:2:8: 2:9
let mut _4: bool; // in scope 0 at ./example.rs:2:20: 2:28
let mut _5: char; // in scope 0 at ./example.rs:2:20: 2:21
bb0: {
StorageLive(_2); // scope 0 at ./example.rs:2:8: 2:28
StorageLive(_3); // scope 0 at ./example.rs:2:8: 2:9
_3 = _1; // scope 0 at ./example.rs:2:8: 2:9
switchInt(move _3) -> ['b': bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:8: 2:28
}
bb1: {
StorageDead(_3); // scope 0 at ./example.rs:2:8: 2:28
_2 = const true; // scope 0 at ./example.rs:2:8: 2:28
goto -> bb3; // scope 0 at ./example.rs:2:8: 2:28
}
bb2: {
StorageDead(_3); // scope 0 at ./example.rs:2:8: 2:28
StorageLive(_4); // scope 0 at ./example.rs:2:20: 2:28
StorageLive(_5); // scope 0 at ./example.rs:2:20: 2:21
_5 = _1; // scope 0 at ./example.rs:2:20: 2:21
_4 = Eq(move _5, const 'a'); // scope 0 at ./example.rs:2:20: 2:28
StorageDead(_5); // scope 0 at ./example.rs:2:27: 2:28
_2 = Ne(move _4, const false); // scope 0 at ./example.rs:2:8: 2:28
goto -> bb3; // scope 0 at ./example.rs:2:8: 2:28
}
bb3: {
StorageDead(_4); // scope 0 at ./example.rs:2:27: 2:28
switchInt(move _2) -> [false: bb4, otherwise: bb5]; // scope 0 at ./example.rs:2:5: 2:45
}
bb4: {
_0 = const 1_u32; // scope 0 at ./example.rs:2:42: 2:43
goto -> bb6; // scope 0 at ./example.rs:2:5: 2:45
}
bb5: {
_0 = const 0_u32; // scope 0 at ./example.rs:2:31: 2:32
goto -> bb6; // scope 0 at ./example.rs:2:5: 2:45
}
bb6: {
StorageDead(_2); // scope 0 at ./example.rs:3:1: 3:2
return; // scope 0 at ./example.rs:3:2: 3:2
}
}
Original issue - no longer relevant
In the following snippet https://godbolt.org/z/6f3xs3 , I would have expected _2
to be move
d into the switchInt since it is not used afterwards.
This was noticed in #75370 (comment) where the proposed MIR-optimization currently fails to remove a comparison..
Observed:
fn f(_1: i8) -> i8 {
debug x => _1; // in scope 0 at ./example.rs:1:6: 1:7
let mut _0: i8; // return place in scope 0 at ./example.rs:1:16: 1:18
let mut _2: bool; // in scope 0 at ./example.rs:2:8: 2:15
bb0: {
StorageLive(_2); // scope 0 at ./example.rs:2:8: 2:15
_2 = Eq(move _1, const 42_i8); // scope 0 at ./example.rs:2:8: 2:15
switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:5: 2:32
}
bb1: {
_0 = const 1_i8; // scope 0 at ./example.rs:2:29: 2:30
goto -> bb3; // scope 0 at ./example.rs:2:5: 2:32
}
bb2: {
_0 = const 0_i8; // scope 0 at ./example.rs:2:18: 2:19
goto -> bb3; // scope 0 at ./example.rs:2:5: 2:32
}
bb3: {
StorageDead(_2); // scope 0 at ./example.rs:3:1: 3:2
return; // scope 0 at ./example.rs:3:2: 3:2
}
}
Expected:
fn f(_1: i8) -> i8 {
debug x => _1; // in scope 0 at ./example.rs:1:6: 1:7
let mut _0: i8; // return place in scope 0 at ./example.rs:1:16: 1:18
let mut _2: bool; // in scope 0 at ./example.rs:2:8: 2:15
bb0: {
StorageLive(_2); // scope 0 at ./example.rs:2:8: 2:15
_2 = Eq(move _1, const 42_i8); // scope 0 at ./example.rs:2:8: 2:15
switchInt(move _2) -> [false: bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:5: 2:32
}
bb1: {
_0 = const 1_i8; // scope 0 at ./example.rs:2:29: 2:30
goto -> bb3; // scope 0 at ./example.rs:2:5: 2:32
}
bb2: {
_0 = const 0_i8; // scope 0 at ./example.rs:2:18: 2:19
goto -> bb3; // scope 0 at ./example.rs:2:5: 2:32
}
bb3: {
StorageDead(_2); // scope 0 at ./example.rs:3:1: 3:2
return; // scope 0 at ./example.rs:3:2: 3:2
}
}