Skip to content

优化器

RedScript 会在写出最终 datapack 之前执行多轮编译期优化。目标很直接:减少命令数量、删掉不可达 helper、并在不改变语义的前提下压缩热路径。

优化等级

可以用下面这些 CLI 参数控制优化器强度:

参数含义
--no-dce只关闭死代码消除。其他仍然启用的优化照常运行。
-O0关闭优化。最适合调试生成结果。
-O1开启标准、安全的优化集合。开发期的默认推荐。
-O2开启完整优化流水线,包括更激进的内联与循环清理。

典型用法:

bash
redscript compile main.mcrs -O0
redscript compile main.mcrs -O1 --stats
redscript compile main.mcrs -O2 --no-dce

DCE

死代码消除会移除从任何公共入口都无法到达的函数。

高层规则:

  • 名称不以下划线 _ 开头的函数会被视为 public,并生成到输出中。
  • 以下划线开头的函数会被视为 private helper;如果不可达就可能被删除。
  • @load@tick@on(...)@coroutine 等装饰过的函数会自动保留。
  • @keep 可以覆盖 DCE,强制保留函数。
rs
fn start() {
    _helper()
}

fn _helper() {
    say("reachable")
}

fn _dead() {
    say("removed by DCE")
}

@keep
fn _manual_entry() {
    say("kept for manual /function use")
}

当你想检查所有 helper 是否被输出,或在调试可达性问题时,可以使用 --no-dce

常量折叠

常量折叠会在所有输入都已知时,于编译期直接计算表达式。

典型例子:

rs
let ticks_per_minute: int = 20 * 60
let doubled: int = 8 + 8
let ready: bool = 3 > 1

折叠后,生成代码会直接使用计算好的常量,而不是在运行时重复计算。

它最适合:

  • 字面量算术
  • 简单布尔条件
  • 固定大小的循环边界
  • 编译期可确定的配置值

@inline

@inline 是给热路径上极小 helper 的性能提示。

rs
@inline
fn clamp_zero(x: int) -> int {
    if (x < 0) {
        return 0
    }
    return x
}

当优化器接受这个提示时,它会在后续清理阶段之前把函数体直接替换到调用点。这样通常能进一步触发常量折叠、复制传播和死分支删除。

使用建议:

  • 只给很小的 helper 用
  • 主要用于热循环
  • 不要拿它替代清晰的结构

复制传播

复制传播会在安全时,用原值替换临时别名。

rs
fn award(points: int) {
    let p = points
    let value = p
    scoreboard_add(@s, "score", value)
}

传播完成后,优化器通常能把这串别名折叠掉,让生成代码直接使用 points

这很重要,因为 RedScript 把高层表达式 lowering 成命令时,经常会产生短生命周期临时值。先把这些副本传播掉,后续优化会更容易。

循环展开

循环展开会把小而固定次数的循环复制成直线代码,让运行时不必每轮都付出分支成本。

rs
let i: int = 0
while (i < 4) {
    say("tick")
    i = i + 1
}

-O2 下,如果循环次数是很小的编译期常量,优化器可能会直接展开循环体。它最适合:

  • 循环上界是很小的常量
  • 循环体本身很短
  • 去掉循环控制开销后,后续优化会更简单

很大或依赖运行时数据的循环一般不会展开。

基本块合并

基本块合并会在控制流是线性的情况下,把相邻基本块拼接起来,不再保留多余的跳转边界。

实际效果通常是:

  • 生成 helper 之间的跳转更少
  • 内联后的控制流链更短
  • 常量条件折叠后,输出更干净

这个 pass 一般会在常量折叠、复制传播之后收益最大,因为前面的简化很容易制造出只有单一路径的 trivial block。

这些 Pass 如何配合

从高层看,优化器流水线大致会这样工作:

  1. 先简化常量和复制
  2. 再按条件内联热路径小函数
  3. 清理循环,并合并直线型 block
  4. 最后用 DCE 删除不可达代码

你通常不需要记住精确顺序。真正重要的是:写小而清晰的 helper、谨慎使用装饰器,并为当前任务选对优化等级。

实用建议

  • 对照源码和生成结果时,用 -O0
  • 日常开发用 -O1
  • 发布大型 datapack 或热路径密集逻辑前,用 -O2
  • 需要保留所有 helper 以便检查时,用 --no-dce
  • 想快速评估优化器效果时,用 --stats

Released under the MIT License.