MIR优化

MIR优化是指在代码生成之前,为了产生更好的MIR指令而执行的优化。 这些优化十分重要,体现在两个方面: 首先,它使得最终生成的可执行代码的质量更好;其次,这意味着LLVM需要的工作量更少,编译速度更快。 请注意,由于MIR是通用的(不是[monomorphized] monomorph)所以这些优化特别有效,我们可以优化通用代码,使得所有代码的特化版本同样受益!

MIR的优化执行在借用检查之后。通过执行一系列的pass不断优化MIR。 一些pass需要执行在全量的代码上,一些pass则不执行实际的优化操作只进行代码检查,还有些pass只在release模式下适用。

调用optimized_mir查询为给定的DefId生成优化的MIR,该查询确保借用检查器已运行并且已经进行了一些校验。 然后,窃取MIR,执行优化后,返回被优化后的MIR。

定义优化Passes

优化pass的声明和执行顺序由run_optimization_passes函数定义。 它包含了一组待执行的pass,其中的每个pass都是一个实现了MirPass trait的结构体。通过一个元素类型为&dyn MirPass的数组实现。 这些pass通常在rustc_mir::transform模块下完成自己的实现。

下面有一些pass的示例:

  • CleanupNonCodegenStatements: 清理那些只用于分析而不用于代码生成的信息;
  • ConstProp: 常量传播

您可以查看关于MirPass实现的相关章节中的更多示例。

MIR优化 levels

MIR优化有不同程度的行为。 实验性的优化可能会导致错误编译或增加编译时间。 这样的pass包含在nightly版本中,以收集反馈并修改。要启用这些缓慢的或实验性的优化,可以指定-Z mir-opt-level调试标志。 您可以在compiler MCP中找到这些级别的定义。 如果您正在开发MIR优化pass,并且想查询您的优化是否应该运行,可以使用tcx.sess.opts.debugging_opts.mir_opt_level检查输入的级别。

优化参数fuel

fuel是一个编译器选项 (-Z fuel=<crate>=<value>),可以精细地控制在编译过程中的优化情况:每次优化将fuel减少1,当fuel达到0时不再进行任何优化。 fuel的主要用途是调试那些可能不正确或使用不当的优化。通过更改选项,您可以通过二分法定位到发生错误的优化。

一般来讲,MIR优化执行过程中会通过调用tcx.consider_optimizing来检查fuel,如果fuel为空则跳过优化。 有如下注意事项:

  1. 如果认为一个优化行为是有保证的(即,为了结果的正确性每次编译都要执行),那么fuel是可以跳过的,比如PromoteTemps
  2. 在某些情况下,需要执行一个初始pass来收集候选,然后对他们迭代执行以达到优化的目的。在这种情况下,我们应该让初始pass对fuel的值尽可能地突变。 这可以获得最佳的调试体验,因为可以确定候选列表中的某个优化pass可能未正确调用。 例如InstCombineConstantPropagation