
这次我们不聊模型部署也不聊 Web 框架而是拆一个 Rust 编译器内部比较硬核的话题一份关于“把 MIR 从 phi 节点改成 block arguments”的 LLVM Code Generation RFC 标题及讨论方向。先说明定位这份材料不是给你下载后双击运行的工具而是一个编译器 IR 层面的设计变更。看懂它需要你至少知道 MIR 是什么、SSA 是什么、LLVM IR 里的 phi 节点大概长什么样看不清这些概念后面所有关于 Codegen 的讨论都会飘在空中。这个 RFC 方向最值得关注的几个点如下。第一它动了 rustc 中端表示 MIR 的基础数据结构不再依赖传统 SSA 的 phi 节点而是转向 block arguments 这种在 MLIR、Cranelift 等 IR 里已经验证过的表达方式。第二它会影响 rustc 到 LLVM Code Generation 之间的衔接比如 MIR 优化、DebugInfo、CFG 遍历、以及未来对其它后端的支持。第三验证这个方向不是靠读文档而是要本地构建一个带变更的 rustc 分支用--emitllvm-ir、-Z dump-mir这些命令去观察生成结果。这事门槛不低但一旦跑通你对编译器 IR 设计的理解会上升一个档次。这篇文章会带你做四件事先把这个 RFC 方向涉及的背景概念讲清楚再对比 phi 节点和 block arguments 在表达控制流合并值时的差异然后给出一套可以在本地验证 MIR 与 LLVM IR 输出的环境准备和实验方法最后聊聊如果真要参与这类设计有哪些坑和最佳实践。适合的读者是对 Rust 编译器内部感兴趣的同学、想往 LLVM 后端方向走的编译器工程师、以及在读源码时总被 MIR dump 和 phi 节点绕晕的人。普通业务开发可以先收藏但大概率不是你现在需要的“快速上手”类文章。1. 核心能力速览在开始长篇分析之前先把这份 RFC 材料相关的关键信息整理成一张速览表。这里要特别说明由于当前材料中并没有给出完整 RFC 正文、具体 PR 编号或合并状态下面的表格是基于标题、关键词和编译原理常识做的推断。你需要把“待确认”项当作手动核对的起点而不是结论。项目项说明讨论主题Change MIR to use block arguments instead of phis技术领域Rust 编译器 rustc、MIR、SSA、LLVM Code Generation核心变更将 MIR 中用于表达控制流合并值的 phi 节点替换为 block arguments块参数直接相关方rustc MIR 构建、MIR 优化 pass、LLVM 后端、其它后端如 Cranelift主要动机简化 SSA 处理、降低 CFG 遍历和序列化成本、改善与 LLVM IR 的衔接当前状态讨论阶段具体进度需要看 rust-lang 相关仓库的最新动态验证方式构建带变更的 rustc 分支对比 MIR dump、LLVM IR 输出、编译时间与运行性能推荐平台Linux / macOS 优先Windows 可通过 MSVC/GNU 工具链加独立 LLVM 验证硬件门槛需要构建 rustc内存和磁盘充足具体数值随仓库版本与优化级别变化是否一键启动不适用这是源码级变更不是应用工具是否提供 API不适用可以借用 rustc 命令行接口和 LLVM 工具链做验证适合读者rustc contributor、编译器学习者、LLVM 后端开发、系统语言爱好者这张表的核心结论是这不是一个开箱即用的项目而是一个值得跟踪和验证的编译器演进方向。下面从背景开始展开。2. 适用场景与使用边界这个 RFC 方向适合谁主要是三类人。第一类是 rustc 编译器贡献者尤其是关注 MIR 优化、SSA 构建、控制流图表示的开发者。如果你平时就在看 rustc 源码里Rvalue、Statement、BasicBlock这些结构的定义那这个方向对你来说是真正能写进 code review 或 comment 里的东西。第二类是 LLVM 后端开发者因为 MIR 里怎么表达值合并直接会影响最终生成 LLVM IR 前需要做什么转换以及这些转换是否容易验证。第三类是编译器设计学习者和系统编程爱好者把 phi 换成 block arguments 这个思路本身就是绝佳的 IR 设计案例读懂它你再看 MLIR 的^bb0(%arg0: i32)或者 Cranelift 的 block params会有一种“原来如此”的感觉。但这个方向也有明确的使用边界。它不适合普通业务项目直接使用。只要 RFC 没有合入某个稳定 rustc 版本你就不应该把“依赖这个变更”写进生产项目里。另一个边界是验证成本你现在写一个普通 Rust 项目用的是稳定版 rustcMIR 和 LLVM IR 的变化不会体现出来。除非你真的准备去 fork rustc 并构建一个自定义分支否则这个方向对你的日常开发不会带来立竿见影的效果。合规和安全边界也要说清楚。虽然这个主题不涉及图像、语音、数据隐私等高风险场景但如果你要在公司或受控环境中构建测试编译器建议不要直接把公司私有代码作为大规模 benchmark 输入。编译器实验通常涉及生成 IR、运行性能对比这些动作本身没有风险但如果你把专有代码塞进某份公开 RFC 的实验数据里就可能在授权和保密上出问题。关于使用边界我的建议是当作开源技术研究来对待用公开的测试用例或自写的小函数验证即可。3. 背景理解MIR 为什么需要处理 phi 节点要理解这份 RFC 为什么存在先要理解 rustc 的管线位置。Rust 代码经过解析、HIR 之后会生成 MIRMid-level Intermediate Representation。MIR 是借用检查、部分优化和代码生成的基础。rustc 把 MIR 再做一次下降才能生成 LLVM IR交给 LLVM 优化并生成机器码。所以 MIR 是连接高层语义和底层代码生成之间的一个中间档。SSAStatic Single Assignment是编译器 IR 里最常用的约束形式每个变量只被赋值一次。这个约束对数据流分析特别友好因为当你看到某个值的使用点时它的定义点可以快速被找到。但在有控制流的图里一个变量可能来自不同的前驱块比如 if-else 两个分支都往同一个后续块传值后续块里这个值如果没有 phi 节点就说不清它到底来自哪个分支。于是传统 SSA 引入了 phi 节点在基本块入口处根据控制流来源选择一个值。LLVM IR 里最常见的 phi 写法是这样的; LLVM IR 中的 phi 节点示例 define i32 foo(i1 %cond, i32 %a, i32 %b) { entry: br i1 %cond, label %then, label %else then: br label %merge else: br label %merge merge: %result phi i32 [ %a, %then ], [ %b, %else ] ret i32 %result }这段代码的意思是从then块跳进merge块时%result等于%a从else块跳进merge块时%result等于%b。phi 节点本质上就是在给 SSA 形式“补漏洞”让同一个变量名在不同控制流路径下得到正确值。那么 MIR 里处理 phi 有什么痛点从编译器工程角度看至少有这几个方向会让维护者感到不舒服。第一phi 节点破坏了“值定义点单一”的直觉。虽然 SSA 的规则保证了每个变量名只有一个定义但 phi 节点的语义是“看你是从哪个前驱来的”它不是一个普通运算而是一个与控制流绑定的选择操作。这让许多优化 pass 在处理 phi 时需要额外分类比如把一个 “phi 相关” 的传播逻辑单独写一遍。第二phi 节点在 CFG 遍历和数据流分析中比较复杂。数据流分析要反复计算 phi 的控制依赖甚至要处理“无前驱”的基本块入口值这种边角情况对正确性要求很高。第三如果编译器还需要对 MIR 做序列化比如增量编译缓存或调试信息phi 节点往往要额外记录前驱来源信息内存和解析成本都不小。第四LLVM Code Generation 阶段本身就会生成 phi 节点如果 rustc 的 MIR 层不使用 phi而改用 block arguments理论上可以更直接地把 MIR 的控制流参数信息映射到 LLVM 的 phi 或其他结构减少一层中间转换的认知负担。另外要说一句block arguments 并不是一个新概念。公开资料里MLIR 的基本块是带参数的Cranelift 的 IR 也用 block paramsSwift 的 SIL 和某些研究性 IR 同样采用了类似设计。所以在 LLVM 系之外block arguments 已经被多个编译器项目验证过。这也是这个 RFC 方向能引起讨论的原因之一它并不是凭空发明而是想把一个在其它 IR 里已经证明好用的表达方式迁移到 rustc 的 MIR 里。4. RFC 核心设计从 phi 换成 block arguments既然题目是“Change MIR to use block arguments instead of phis”我们就要理解 block arguments 在语义上是怎么替换 phi 的。我用一个简化示来说明。假设我们要表达一个循环累加传统 SSA 用 phi 的写法和 block arguments 的写法会不一样。传统 phi 风格示意类似 LLVM IRdefine i32 sum_loop(i32 %n) { entry: br label %loop loop: %i phi i32 [ 0, %entry ], [ %i.next, %loop ] %acc phi i32 [ 0, %entry ], [ %acc.next, %loop ] %done icmp eq i32 %i, %n br i1 %done, label %exit, label %body body: %i.next add i32 %i, 1 %acc.next add i32 %acc, %i br label %loop exit: ret i32 %acc }block arguments 风格示意类似 MLIR / Cranelift 表示func.func sum_loop(%n: i32) - i32 { br ^loop(%i: i32 0, %acc: i32 0) ^loop(%i: i32, %acc: i32): %done arith.cmpi eq, %i, %n : i32 cf.cond_br %done, ^exit, ^body ^body: %i.next arith.addi %i, %c1 : i32 %acc.next arith.addi %acc, %i : i32 br ^loop(%i.next, %acc.next) ^exit: return %acc : i32 }注意第二种写法里跳转指令后面直接带实参比如br ^loop(%i.next, %acc.next)。进入^loop时%i和%acc就自动接收这些实参值。你不需要在^loop里再写一个 phi 节点因为块的参数本身就是 “从哪个前驱来就接收哪个前驱传入的值”。这种表达方式的好处之一是CFG 的参数化变得显式了。编译器看到br ^exit时可以一眼看出它传入了哪些值看到^exit(%result: i32)时也可以直接知道这个块需要哪些入口参数。相比之下phi 节点把信息散落在每个块的顶部而且依赖前驱块的跳转来源来推导值分析路径更长。不过这里必须提醒一个关键点如果最终目标还是生成 LLVM IR那么 LLVM IR 本身不支持块的显式参数。LLVM 采用的仍然是我们熟知的 phi 节点。因此这个 RFC 到底是在 MIR 层完全去掉 phi然后在 LLVM Codegen 阶段再把 block arguments 翻译回 phi还是说只在一些优化 pass 内部使用 block args、最终输出仍用 phi需要看最终 RFC 正文的设计细节。材料里目前只有标题和 keyword我不能替它补完具体方案。但一个合理的判断是这个变更大概率发生在 rustc 内部的 MIR 表示层目的是让 MIR 的构建、遍历、优化和序列化更简单而不一定改变 LLVM IR 最终的形态。5. 本地验证环境准备与工具链安装如果你想真正验证这个 RFC 方向而不是只看表层分析那就需要自己在本地搭一套观察环境。这里分两条路线。第一条路线是不修改 rustc先观察目前稳定版或 nightly 版生成的 MIR 和 LLVM IR 是什么样。这能让你建立基线认知。第二条路线是 fork rustc 源码把 RFC 对应的补丁合入本地分支重新构建 rustc然后再跑同样的测试程序对比 MIR 和 LLVM IR 的差异。第二条路线成本高但它才是验证 RFC 效果的正道。先做基线准备。安装 Rust 工具链时最常见的做法是使用 rustup。Windows 上也可以通过 rustup-init 安装不需要先手动装一个完整 Rust 编译器。安装完成后你可以用rustup toolchain install nightly安装 nightly 工具链因为-Z dump-mir这类内部观察 flag 一般在 nightly 才能用。# 安装 rustup 后安装 nightly 工具链 rustup toolchain install nightly rustup default nightly rustc --version现在来看 LLVM 工具链。如果你想在 Windows 上快速获得一套可用的 LLVM 工具链优先考虑 LLVM 官方发布的预编译压缩包。把压缩包解压后把其中的bin目录加入PATH就能使用llvm-dis、llc、opt、llvm-config这些命令。这种方式是免安装的适合做实验不需要走 MSI 安装器的流程。当然具体压缩包版本和下载方式会随 LLVM release 变化建议直接到 LLVM 官方 release 页面找对应平台的版本。# Windows PowerShell 中把 LLVM 解压目录加入当前会话 PATH $env:PATH C:\llvm\bin; $env:PATH llvm-config --version llc --version如果是源码级验证接下来要克隆 rustc 仓库并构建自定义分支。rustc 源码构建不是一个小操作我建议先准备充足的磁盘空间和内存另外把构建目标限制在当前平台的默认 target 上不要一上来就交叉编译。下面的命令是通用模板实际路径和分支名需要按 RFC 对应的仓库和 PR 情况替换# 克隆 rustc 主仓库 git clone https://github.com/rust-lang/rust.git cd rust # 创建实验分支这里的分支名只是示例 git checkout -b block-args-experiment # 构建 stage1 编译器具体构建配置以仓库 README 为准 # 首次构建需要较长的时间请耐心等待 ./x.py build --stage 1构建完成后build/x86_64-unknown-linux-gnu/stage1/bin/rustc就是你的实验用编译器。Windows 下对应的路径会带上 MSVC 的 target 目录具体以实际构建输出为准。关于 x.py 的细节我只给到通用模型因为 rustc 构建系统经常调整参数最稳妥的做法是照着克隆下来的仓库里的 README 和config.toml.example来配置。6. 功能测试与效果验证对比 MIR 和 LLVM IR 输出环境准备好了之后就可以设计实验了。这个 RFC 的核心目标是改变 MIR 对控制流合并值的表示方式所以验证逻辑也非常明确用同一份 Rust 测试程序在未修改的 rustc 和带 RFC 变更的 rustc 上分别生成 MIR dump 和 LLVM IR然后对比差异。先准备一个小测试程序。这个程序要包含分支合流和循环因为 phi 节点最常出现在这两种位置// test_phi.rs fn sum_loop(n: i64) - i64 { let mut i 0; let mut acc 0; while i n { acc i; i 1; } acc } fn choose(flag: bool, a: i64, b: i64) - i64 { if flag { a 1 } else { b - 1 } } fn main() { let n std::env::args() .nth(1) .and_then(|s| s.parse().ok()) .unwrap_or(10); println!({} {}, sum_loop(n), choose(true, 1, 2)); }然后分别观察 MIR 和 LLVM IR。观察 MIR dump 的一种方式是用 nightly rustc 的-Z dump-mir。这个 flag 会把 MIR 中间文件输出到本地文件内容会随编译器版本变化但结构一般很接近。# 生成 MIR dump 文件具体 flag 以 nightly 版本帮助信息为准 rustc -Z dump-mir -Z unprettymir test_phi.rs运行后你会看到当前 rustc 的 MIR 输出。如果这个 RFC 已经合入那么相关块的入口处可能就不再以 phi 形式表达合并值而是出现块参数。当前未合入的版本里你看到的是现有 MIR 的表达方式。这就是最直接的差异证据。观察 LLVM IR 用--emitllvm-ir# 生成 LLVM IR 文件 rustc --emitllvm-ir test_phi.rs # 在生成的 IR 中寻找 phi 节点 grep -n phi test_phi.ll如果当前 LLVM IR 中出现大量 phi 节点这是正常现象因为 LLVM IR 本身就是 SSA 加 phi 的形式。如果 RFC 合入后rustc 在 MIR 层用 block arguments但 LLVM 后端又需要把块参数重新转换回 phi那么最终生成的 LLVM IR 里仍然会有 phi。这种情况下真正的变化要回到 MIR 层去看而不是只盯着 LLVM IR。这一步的关键判断标准是MIR dump 中是否出现了块参数以及这些块参数是否能被正确映射到 LLVM 后端。如果你的实验分支里 LLVM Codegen 直接把 block arguments 翻译成 phi且编译通过输出 IR 语义等价那就可以说这个方向在基础链路上成立。接着可以对比优化行为。使用同一个函数在不同 rustc 版本下观察开启优化前后 LLVM IR 的变化rustc -O --emitllvm-ir test_phi.rs grep -n phi test_phi.ll对比开启-O前后 phi 节点数量能帮你理解 MIR 层改变是否间接影响了优化后 IR 的形态。更进一步的验证可以使用 LLVM 工具链里的opt和llvm-dis对 IR 做分析和阅读比如把 IR 转成人类可读文本llvm-dis test_phi.bc -o test_phi.ll不过要强调任何实验都必须建立在相同基线之上。同一台机器、同一个 rustc 构建配置、同一份源码才能对比出差异。不能拿 release 构建的 rustc 和 debug 构建的 rustc 对比也不能一边开-C opt-level3、另一边不开优化。7. 工具链接口与批量验证这个 RFC 不提供 HTTP API也没有一键启动脚本。但从命令行工具链角度看rustc 和 LLVM 工具链本身提供了足够的“接口”供自动化验证。你可以把rustc --emitllvm-ir看作一个代码生成接口把llvm-dis看作 IR 剖析接口把opt看作优化接口。这些接口可以串联成一条批量验证流水线。比如你写一个简单的脚本对多个 Rust 测试文件批量生成 LLVM IR并把 phi 节点统计出来。以下是通用脚本模板路径和参数要按你的环境调整#!/usr/bin/env bash # 批量生成 LLVM IR 并统计 phi 节点数量的脚本模板 set -euo pipefail RUSTC$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc OUT_DIR./ir_out mkdir -p $OUT_DIR for src in tests/*.rs; do name$(basename $src .rs) $RUSTC --emitllvm-ir -O $src -o $OUT_DIR/$name.ll count$(grep -c phi $OUT_DIR/$name.ll || true) echo $name: $count phi nodes done脚本里使用了grep -c统计phi关键字这不严谨但对于快速筛查足够了。你还可以配合comm或diff对比两个 rustc 分支生成的 IR 差异# 比较两个实验分支生成的 LLVM IR diff -u baseline.ll block_args.llRust 编译器内部还有一些调试输出接口可以用。比如RUSTC_LOG环境变量可以控制编译器内部日志-Z dump-mir可以输出 MIR-Z self-profile可以输出编译流水线中各个 pass 的时间开销。对这类 IR 设计验证来说-Z self-profile特别有价值因为你可以观察 phi 到 block arguments 的转换是否带来了可衡量的 pass 耗时差异。以下是一个通用用法# 生成 self-profile 数据文件名可按需调整 rustc -Z self-profile -Z self-profile-eventsdefault test_phi.rsself-profile 输出的数据可以用工具查看具体工具名和命令我不在这里硬写因为 rustc 团队经常调整推荐工具最好以当前 nightly 工具链的文档为准。总之命令行“接口”是充分可用的关键是你要有明确的实验脚本和对比基线。8. 资源占用与性能观察编译器内部设计变更最终要落到资源占用和性能上。但这里有一个必须遵守的原则不能拍脑袋给数字。构建 rustc 的资源需求、实验分支的编译时间、生成 IR 的差别、运行性能差异都必须在你的实际机器和实际仓库版本上测量。先看构建 rustc 的资源占用。rustc 仓库本身庞大构建过程中要下载依赖、编译各种 crate最后还要链接。初步经验是完整构建需要的磁盘空间可能在几十 GB 量级内存建议充足链接阶段内存占用会明显上升。但这只是一个定性经验具体数值取决于你是否开启 debug 信息、是否只构建 stage1、以及你的 target 平台。建议用一个简单方法观察构建时打开系统资源监视器记录峰值内存和磁盘占用。再看编译测试程序时的性能观察。这里可以分两个维度编译时间和运行时间。编译时间可以用cargo build --timings来观察 crate 构建时序也可以直接用-Z self-profile分析 rustc 内部 pass 耗时。如果 RFC 的目标之一是简化 MIR 处理流程那么你应当去对比MIR_built、codegen_llvm等 pass 在基线分支和实验分支上的耗时差异。运行时间则直接用time命令或 Windows PowerShell 里的Measure-Command。# Linux/macOS 下观察运行耗时 time ./target/release/test_phi 1000# Windows PowerShell 下观察运行耗时 Measure-Command { .\target\release\test_phi.exe 1000 }另外一个值得观察的点是 phi 节点数量与优化时间的关系。你可以在基线分支下统计一个 crate 生成的 LLVM IR 中 phi 节点数量然后尝试在 MIR 层做等价改写如果 RFC 补丁已经可用再统计 phi 数量。如果 phi 数量显著下降说明 LLVM 后端需要处理的 SSA 边界情况会变少。但这不一定是净收益因为把 block arguments 重新翻译回 phi 本身也需要成本。所以最终判断必须以整条编译链路结果为标准。降低资源占用的常见方法使用 stage1 构建不需要完整 stage2只构建当前 host target使用 sccache 缓存编译产物定期清理build目录中不需要的中间产物。如果你发现构建 rustc 的频率很高可以考虑保留一个基线分支和一个实验分支两个分支共享同一个build缓存缩短切换成本。不过 rustc 的构建系统很复杂具体缓存参数需要参考当前仓库的构建文档。9. 常见问题与排查方法我把这个主题下常见的坑和排查思路整理成一张表。由于很多情况是版本相关的我不给死命令而是给排查方向。问题现象可能原因排查方式解决方案x.py build在下载依赖阶段报错网络不稳定或依赖源不可达查看构建日志确认卡在哪一步确保网络稳定必要时配置国内镜像源不要使用不合规网络手段构建 rustc 时缺 C 工具链或 CMake系统环境缺少 LLVM 编译依赖检查 clang/cmake 版本查看系统日志安装对应平台的基础编译工具链确保编译器版本满足要求rustc -Z dump-mir提示 unknown flag使用的工具链不是 nightly执行rustc --version确认版本rustup toolchain install nightly rustup default nightlyllvm-config命令找不到LLVM 工具链未加入 PATH执行where llvm-config或which llvm-config在 PATH 中加入 LLVM 解压目录或使用绝对路径生成的 LLVM IR 里看不到 phi优化级别太高phi 被优化掉先不加-O生成 IR分别跑--emitllvm-ir和-O --emitllvm-ir对照实验分支构建失败本地补丁与当前 rustc 版本冲突查看编译错误信息定位是哪个 crate切换分支基线或更新补丁到当前版本对比结果不稳定两个分支优化级别或配置不一致检查config.toml和命令行参数使用完全相同参数和同一份测试程序运行测试程序时进程内存飙升构建配置或实验数据量过大用资源监视器查看峰值内存减小测试输入规模关闭 debug 信息如果你遇到的是“对比出来的结果没有差异”这也可能是正常现象。特别是当你只在 LLVM IR 层面观察时block arguments 可能已经被 LLVM 后端重新转换成 phi 节点。此时要把重心转向 MIR dump实在不行就去看 rustc 的 codegen 相关源码确认转换逻辑在哪个 pass 中发生。10. 最佳实践与参与 RFC 讨论的建议如果你看完材料后不只是想“知道有这回事”而是想真正参与验证或讨论我有几个建议。第一先建立一套可重复的验证脚本。把基线 rustc、实验 rustc、测试程序、IR 输出目录、结果对比命令全部固化下来。不要每次手动敲命令那样无法保证一致性。第二任何结论都要同时附上实验环境和参数机器型号、rustc commit hash、LLVM 版本、优化级别、测试输入规模。没有这些信息你的数据对别人来说没有参考价值。第三优先使用公开测试用例或你自己写的小函数做验证。不要拿未授权的内部代码用于开源 RFC 的实验。这也是最基本的合规边界。第四如果你想参与 RFC 讨论不要只在标题层面表态。只看标题就说“赞成”或“反对”没有意义。更好的方式是基于本地实验指出 block arguments 在 MIR 层的引入对哪个 pass 产生了什么影响具体哪些场景有收益哪些场景有退化风险。RFC 讨论最有价值的内容是带着数据和复现路径的意见。第五注意补丁提交流程。rustc 项目对代码格式、测试覆盖和 commit message 要求很高。如果你做实验时修改了代码提交之前要跑相关测试确保没有破坏现有 MIR 相关功能。再强调一次这类变更合入主线前一切以维护团队的评审意见为准不要急着自己上线到生产项目。最后是效率建议构建 rustc 很耗时不要频繁从头构建。你可以保留两个源码目录一个作为基线一个作为实验分支两者共享工具链缓存和依赖缓存只在真正需要时才切换构建。实验过程中也要区分“现象”和“结论”。你在本地看到某个函数生成的 LLVM IR 中 phi 减少这是现象只有当实验覆盖足够多样本同时排除了优化级别和代码结构干扰后才能下一句可靠结论。11. 总结与下一步这个 RFC 方向最值得关注的点是它试图把 rustc 的 MIR 从传统 SSA phi 表达迁移到 block arguments 这种在现代编译器 IR 中越来越常见的表达方式。它并不是一个独立的应用工具而是一个会影响 MIR 构建、MIR 优化、序列化和 LLVM Code Generation 的基础设计变更。如果你对编译器 IR 设计感兴趣这个方向是一个很好的研究样本。你最先应该验证的内容不是写一个大 crate而是找一个最简单的分支合流函数分别用基线 rustc 和实验分支 rustc 生成 MIR dump 和 LLVM IR仔细观察 block arguments 是否出现、phi 节点是否减少、最终 LLVM IR 语义是否保持一致。这一步能让你快速理解整个设计的关键链路。最容易被低估的坑是构建成本和对比维度。很多人会花一个下午构建 rustc却忘了记录基线环境最后无法解释变化来自代码变更还是环境差异。记住一个原则每次实验前先固定“什么样的代码在什么样的编译器版本下生成什么样的输出”。接下来可以深入的方向包括研究 block arguments 对增量编译缓存格式的影响、检查 DebugInfo 是否需要额外变换、观察 Cranelift 后端是否因为 MIR 层的变化而更容易对接、以及评估 MLIR 风格的 CFG 表示是否适合 rustc 的优化管线。这些都是在这个 RFC 基础上值得继续啃的硬骨头。建议把这份材料和相关的 LLVM 工具链本地验证方法一起收藏后续 rustc 有任何新进展你可以在已有实验环境上快速跟进。