MIR 是 Rust 编译器里最重要的中间层:借用检查、优化、单态化都围绕它。这篇让 rustc 把 MIR 打印出来,逐行读基本块、溢出断言、drop 与 unwind 路径,再看常量折叠怎样把它缩小。
源码: pub fn pick(x: i32) -> i32 { if x > 10 { x * 2 } else { x + 1 } }
MIR:
fn pick(_1: i32) -> i32 {
debug x => _1;
let mut _0: i32;
let mut _2: bool;
let mut _3: (i32, bool);
let mut _4: (i32, bool);
bb0: {
_2 = Gt(copy _1, const 10_i32);
switchInt(move _2) -> [0: bb3, otherwise: bb1];
}
bb1: {
_3 = MulWithOverflow(copy _1, const 2_i32);
assert(!move (_3.1: bool), "attempt to compute `{} * {}`, which would overflow", copy _1, const 2_i32) -> [success: bb2, unwind continue];
}
bb2: {
_0 = move (_3.0: i32);
goto -> bb5;
}
bb3: {
_4 = AddWithOverflow(copy _1, const 1_i32);
assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _1, const 1_i32) -> [success: bb4, unwind continue];
}
bb4: {
_0 = move (_4.0: i32);
goto -> bb5;
}
bb5: {
return;
}
}
基本块数 6;switchInt 出现 1 次;checked 溢出断言 assert 出现 2 次(debug 构建的溢出检查是 MIR 里的 assert)MIR 在编译流水线中的位置见Rust 编译阶段;MIR 之后是LLVM IR 与优化。输出用 RUSTC_BOOTSTRAP=1,MIR 的文本格式随版本变化。
源码: pub fn make(n: usize) -> usize { let s = String::from("hello"); let t = s.len() + n; t }
fn make(_1: usize) -> usize {
debug n => _1;
let mut _0: usize;
let _2: std::string::String;
let mut _3: usize;
let mut _4: &std::string::String;
let mut _5: (usize, bool);
scope 1 {
debug s => _2;
scope 2 {
debug t => _0;
}
}
bb0: {
_2 = <String as From<&str>>::from(const "hello") -> [return: bb1, unwind continue];
}
bb1: {
_4 = &_2;
_3 = String::len(move _4) -> [return: bb2, unwind: bb5];
}
bb2: {
_5 = AddWithOverflow(copy _3, copy _1);
assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", move _3, copy _1) -> [success: bb3, unwind: bb5];
}
bb3: {
_0 = move (_5.0: usize);
drop(_2) -> [return: bb4, unwind continue];
}
bb4: {
return;
}
bb5 (cleanup): {
drop(_2) -> [return: bb6, unwind terminate(cleanup)];
}
bb6 (cleanup): {
resume;
}
}
drop 终结符 2 次;unwind 路径("unwind" 出现) 5 次;cleanup 块 3 个Drop 的语义见Drop Trait 与资源释放。
mir-opt-level=0:42 行
fn folded() -> i32 {
let mut _0: i32;
let _1: i32;
let mut _2: (i32, bool);
let mut _4: i32;
let mut _5: (i32, bool);
scope 1 {
debug a => _1;
let _3: i32;
scope 2 {
debug b => _3;
let _6: i32;
scope 3 {
debug unused => _6;
}
}
}
bb0: {
StorageLive(_1);
_2 = AddWithOverflow(const 2_i32, const 3_i32);
assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 3_i32) -> [success: bb1, unwind continue];
}
bb1: {
_1 = move (_2.0: i32);
StorageLive(_3);
StorageLive(_4);
_4 = copy _1;
_5 = MulWithOverflow(copy _4, const 4_i32);
assert(!move (_5.1: bool), "attempt to compute `{} * {}`, which would overflow", move _4, const 4_i32) -> [success: bb2, unwind continue];
}
bb2: {
_3 = move (_5.0: i32);
StorageDead(_4);
StorageLive(_6);
_6 = const 99_i32;
_0 = copy _3;
StorageDead(_6);
StorageDead(_3);
StorageDead(_1);
return;
}
}
mir-opt-level=2:16 行
fn folded() -> i32 {
let mut _0: i32;
scope 1 {
debug a => const 5_i32;
scope 2 {
debug b => const 20_i32;
scope 3 {
debug unused => const 99_i32;
}
}
}
bb0: {
_0 = const 20_i32;
return;
}
} 源码:
1 | fn main() { let mut v = vec![1, 2, 3]; let first = &v[0]; v.push(4); println!("{}", first); }
编译:失败
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> <实验目录>/src/u124d.rs:1:59
|
1 | fn main() { let mut v = vec![1, 2, 3]; let first = &v[0]; v.push(4); println!("{}", first); }
| - ^^^^^^^^^ ----- immutable borrow later used here
| | |
| | mutable borrow occurs here
| immutable borrow occurs here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0502`.借用规则见不可变借用与可变借用。