第34章 编译器原理与高级工具链 34.1 Rust 编译流程34.1.1 编译阶段概览源代码 (.rs) ↓ 词法分析 (Lexer) ↓ 语法分析 (Parser) → AST (抽象语法树) ↓ 宏展开 (Macro Expansion) ↓ 名称解析 (Name Resolution) ↓ HIR (High-level IR) - 高级中间表示 ↓ 类型检查 (Type Checking) ↓ 借用检查 (Borrow Checking) ↓ MIR (Mid-level IR) - 中级中间表示 ↓ 优化 (MIR Optimizations) ↓ 代码生成 (Codegen) ↓ LLVM IR ↓ LLVM 优化 ↓ 机器码 (.o / .obj) ↓ 链接 (Linking) ↓ 可执行文件34.1.2 查看编译过程# 查看宏展开cargoexpand# 查看 HIRrustc src/main.rs-Zunprettyhir# 查看 MIRrustc src/main.rs--emitmir# 查看 LLVM IRrustc src/main.rs--emitllvm-ir# 查看汇编rustc src/main.rs--emitasm# 详细编译信息cargobuild-vv34.2 HIRHigh-level IR34.2.1 HIR 的作用HIR 是去糖化desugared后的 AST保留了高级语言特性但更规范化。示例代码fnmain(){letxiftrue{1}else{2};println!({},x);}查看 HIRrustc src/main.rs-Zunprettyhir-treeHIR 输出简化fn main() - () { let x: i32 match true { true 1, _ 2, }; { ::std::io::_print( ::core::fmt::Arguments::new_v1( [, \n], [::core::fmt::ArgumentV1::new_display(x)], ), ); }; }34.2.2 HIR 的特点if表达式转换为match宏已展开类型推断信息保留生命周期标注显式化34.3 MIRMid-level IR34.3.1 MIR 的作用MIR 是控制流图CFG表示用于借用检查优化常量求值代码生成示例代码fnadd(a:i32,b:i32)-i32{ab}fnmain(){letresultadd(2,3);println!({},result);}查看 MIRrustc src/main.rs--emitmir-ooutput.mirMIR 输出简化fn add(_1: i32, _2: i32) - i32 { let mut _0: i32; bb0: { _0 Add(move _1, move _2); return; } } fn main() - () { let mut _0: (); let _1: i32; let mut _2: i32; let mut _3: i32; bb0: { _2 const 2_i32; _3 const 3_i32; _1 add(move _2, move _3) - bb1; } bb1: { // println! 调用 return; } }34.3.2 MIR 的基本块fnexample(x:i32)-i32{ifx0{x*2}else{x*3}}MIR 表示fn example(_1: i32) - i32 { let mut _0: i32; let mut _2: bool; bb0: { _2 Gt(_1, const 0_i32); switchInt(move _2) - [false: bb2, otherwise: bb1]; } bb1: { _0 Mul(_1, const 2_i32); goto - bb3; } bb2: { _0 Mul(_1, const 3_i32); goto - bb3; } bb3: { return; } }34.4 LLVM IR34.4.1 LLVM IR 特点LLVM IR 是低级的、平台无关的中间表示。查看 LLVM IRrustc src/main.rs--emitllvm-ir-ooutput.ll示例代码fnadd(a:i32,b:i32)-i32{ab}LLVM IR 输出简化define i32 add(i32 %a, i32 %b) { start: %0 add i32 %a, %b ret i32 %0 }34.4.2 优化级别# 无优化rustc-Copt-level0src/main.rs# 基本优化rustc-Copt-level1src/main.rs# 标准优化rustc-Copt-level2src/main.rs# 激进优化rustc-Copt-level3src/main.rs# 大小优化rustc-Copt-levels src/main.rs# 极致大小优化rustc-Copt-levelz src/main.rs34.5 编译器诊断信息深度解读34.5.1 错误代码解析fnmain(){letsString::from(hello);lets2s;println!({},s);// 错误}编译器输出error[E0382]: borrow of moved value: s -- src/main.rs:4:20 | 2 | let s String::from(hello); | - move occurs because s has type String, which does not implement the Copy trait 3 | let s2 s; | - value moved here 4 | println!({}, s); | ^ value borrowed here after move | note: this error originates in the macro $crate::format_args_nl which comes from the expansion of the macro println (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | 3 | let s2 s.clone(); | 解读错误代码E0382- 使用已移动的值位置精确到行和列原因String不实现Copy建议使用clone()或重新设计34.5.2 查看错误详情# 查看错误代码详细说明rustc--explainE0382# 输出 JSON 格式的诊断信息cargobuild --message-formatjson34.5.3 常见错误代码错误代码含义常见场景E0382使用已移动的值所有权转移后使用E0499多个可变引用借用规则冲突E0502同时存在可变和不可变引用借用规则冲突E0597借用的生命周期不够长悬垂引用E0308类型不匹配类型推断失败E0277Trait 未实现Trait bound 不满足34.6 Miri检测未定义行为34.6.1 安装和使用 Miri# 安装 Mirirustup nightly componentaddmiri# 运行 Miricargonightly miri run# 测试cargonightly miritest34.6.2 检测未定义行为示例 1越界访问fnmain(){letarr[1,2,3];unsafe{letptrarr.as_ptr();letvalue*ptr.add(10);// 越界println!({},value);}}Miri 输出error: Undefined Behavior: pointer arithmetic failed: alloc123 has size 12, so pointer to 40 bytes starting at offset 0 is out-of-bounds示例 2使用未初始化内存fnmain(){letx:i32;println!({},x);// 未初始化}Miri 输出error: Undefined Behavior: using uninitialized data示例 3数据竞争usestd::thread;usestd::sync::Arc;fnmain(){letdataArc::new(0);letdata1data.clone();letdata2data.clone();lett1thread::spawn(move||{unsafe{letptrArc::as_ptr(data1)as*muti32;*ptr1;// 数据竞争}});lett2thread::spawn(move||{unsafe{letptrArc::as_ptr(data2)as*muti32;*ptr2;// 数据竞争}});t1.join().unwrap();t2.join().unwrap();}34.6.3 Miri 的限制不支持所有系统调用不支持内联汇编不支持 FFI执行速度较慢34.7 安全审计工具34.7.1 cargo-deny安装cargoinstallcargo-deny配置文件deny.toml[advisories] vulnerability deny unmaintained warn yanked deny [licenses] unlicensed deny allow [ MIT, Apache-2.0, BSD-3-Clause, ] [bans] multiple-versions warn wildcards deny [sources] unknown-registry deny unknown-git deny使用# 检查所有规则cargodeny check# 只检查安全漏洞cargodeny check advisories# 只检查许可证cargodeny check licenses34.7.2 cargo-audit安装cargoinstallcargo-audit使用# 检查已知漏洞cargoaudit# 修复可修复的漏洞cargoaudit fix# 生成报告cargoaudit--jsonaudit-report.json示例输出Fetching advisory database from https://github.com/RustSec/advisory-db.git Loaded 500 security advisories (from rustsec-advisory-db) Scanning Cargo.lock for vulnerabilities (123 crate dependencies) Crate: time Version: 0.1.43 Warning: potential segfault in time crate ID: RUSTSEC-2020-0071 Solution: Upgrade to 0.2.2334.7.3 cargo-geiger检测 unsafe 代码使用情况。安装cargoinstallcargo-geiger使用cargogeiger输出示例Metric output format: x/y x unsafe code used by the build y total unsafe code found in the crate Functions Expressions Impls Traits Methods Dependency 0/0 0/0 0/0 0/0 0/0 my_crate 2/5 10/20 0/0 0/0 1/2 ├── dependency_a 0/0 0/0 0/0 0/0 0/0 └── dependency_b34.8 自定义 Lint 规则34.8.1 使用 Clippy Lints配置clippy.toml# 设置复杂度阈值 cognitive-complexity-threshold 30 # 设置类型复杂度阈值 type-complexity-threshold 250 # 允许的宏 allowed-macros [vec, println]在代码中配置// 允许特定 lint#![allow(clippy::needless_return)]// 警告特定 lint#![warn(clippy::all)]// 禁止特定 lint#![deny(clippy::unwrap_used)]fnmain(){letxSome(5);// 这会触发 clippy::unwrap_used// let y x.unwrap();// 正确做法letyx.expect(x 应该有值);println!({},y);}34.8.2 编写自定义 Lint创建 Clippy 插件高级// 需要使用 nightly 和 clippy 内部 API#![feature(rustc_private)]externcraterustc_ast;externcraterustc_lint;externcraterustc_session;userustc_lint::{LateContext,LateLintPass,LintContext};userustc_session::{declare_lint,declare_lint_pass};declare_lint!{pubCUSTOM_LINT,Warn,自定义 lint 说明}declare_lint_pass!(CustomLint[CUSTOM_LINT]);impltcxLateLintPasstcxforCustomLint{fncheck_fn(mutself,cx:LateContexttcx,_:rustc_ast::ast::FnKindtcx,_:tcxrustc_ast::ast::FnDecltcx,_:tcxrustc_ast::ast::Bodytcx,_:rustc_span::Span,_:rustc_ast::ast::NodeId,){cx.lint(CUSTOM_LINT,检测到函数,|lint|{lint.build(这是一个自定义 lint).emit();});}}34.9 编译器标志与优化34.9.1 常用编译器标志Cargo.toml[profile.release] opt-level 3 # 优化级别 lto true # 链接时优化 codegen-units 1 # 代码生成单元减少以提高优化 panic abort # panic 时直接终止 strip true # 移除符号信息 overflow-checks false # 禁用溢出检查 [profile.dev] opt-level 0 # 开发时不优化 debug true # 包含调试信息34.9.2 目标特定优化[profile.release] # 针对当前 CPU 优化 [target.cfg(target_arch x86_64)] rustflags [-C, target-cpunative] # 启用特定 CPU 特性 [target.cfg(target_arch x86_64)] rustflags [-C, target-featureavx2,fma]34.9.3 查看编译器版本和配置# 查看 rustc 版本rustc--version--verbose# 查看默认目标rustc--printtarget-list# 查看目标特性rustc--printtarget-features# 查看配置选项rustc-Chelp常见误区与陷阱误区 1过度依赖优化级别// ❌ 不好依赖编译器优化掉明显的错误fnbad(){letx1/0;// 即使优化也会 panic}// ✅ 好编写正确的代码fngood(divisor:i32)-Optioni32{ifdivisor0{None}else{Some(1/divisor)}}误区 2忽略编译器警告// ❌ 不好忽略未使用的变量警告fnbad(){letx5;// 警告未使用的变量println!(Hello);}// ✅ 好处理警告fngood(){let_x5;// 使用 _ 前缀表示有意不使用println!(Hello);}误区 3不理解 unsafe 的边界// ❌ 不好unsafe 块过大unsafe{letptrget_ptr();// 100 行代码*ptr42;}// ✅ 好最小化 unsafe 范围letptrget_ptr();// 安全代码unsafe{*ptr42;}实战练习练习 34.1分析编译产物要求编写一个简单函数查看其 MIR 和 LLVM IR对比不同优化级别的输出参考答案// src/main.rsfnfactorial(n:u32)-u32{ifn0{1}else{n*factorial(n-1)}}fnmain(){println!({},factorial(5));}分析步骤# 查看 MIRrustc src/main.rs--emitmir-ofactorial.mir# 查看 LLVM IR无优化rustc src/main.rs--emitllvm-ir-Copt-level0-ofactorial-O0.ll# 查看 LLVM IR优化rustc src/main.rs--emitllvm-ir-Copt-level3-ofactorial-O3.ll# 对比difffactorial-O0.ll factorial-O3.ll练习 34.2使用 Miri 检测问题要求编写包含潜在未定义行为的代码使用 Miri 检测问题修复问题参考答案// 有问题的代码fnbuggy(){letmutvvec![1,2,3];letptrv.as_mut_ptr();unsafe{v.push(4);// 可能导致重新分配*ptr10;// ptr 可能已失效}}// 修复后的代码fnfixed(){letmutvvec![1,2,3];v.push(4);v[0]10;// 安全访问}fnmain(){fixed();}运行 Miricargonightly miri run本章小结编译流程源码 → AST → HIR → MIR → LLVM IR → 机器码HIR去糖化的高级表示用于类型检查MIR控制流图表示用于借用检查和优化LLVM IR低级平台无关表示用于代码生成诊断信息理解错误代码和编译器建议Miri检测未定义行为的强大工具安全审计cargo-deny、cargo-audit、cargo-geiger自定义 Lint使用 Clippy 或编写自定义规则编译优化理解优化级别和编译器标志最佳实践编写正确代码不依赖优化掩盖错误