bigywater.tech
Rust · 124

MIR 是什么

MIR 是 Rust 编译器里最重要的中间层:借用检查、优化、单态化都围绕它。这篇让 rustc 把 MIR 打印出来,逐行读基本块、溢出断言、drop 与 unwind 路径,再看常量折叠怎样把它缩小。

6 个
pick 函数的基本块数
42 → 16 行
folded() 在 mir-opt-level 0 与 2 下的 MIR 行数
3 个
make 函数里 panic 清理(cleanup)块的数量

基本块与终结符

实测 · 本机执行输出
  源码:  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 的文本格式随版本变化。

drop 与 unwind

实测 · 本机执行输出
  源码:  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 优化

实测 · 本机执行输出
  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`.

借用规则见不可变借用与可变借用。

分步演示

MIR
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。MIR 来自 rustc --emit=mir(去掉 debuginfo);优化级别用 -Z mir-opt-level 切换
    • 没有验证:borrowck 使用的 MIR 版本(优化前)与 -Z dump-mir 各阶段的差别、MIR 解释器 Miri(需要 nightly)、泛型函数的 MIR 在单态化前后的区别、async fn 的 coroutine MIR
    京ICP备2026067502号-1京公网安备11010502063487号