Hotdry.
compiler-design

LLVM-MOS:6502 Clang 后端的 IR 降低与内联展开优化

针对 MOS 6502 的 LLVM fork 后端,详解 IR 降低策略、内联/展开参数,以及复古嵌入式 C/Rust 编译清单。

在复古计算复兴浪潮中,MOS 6502 处理器因其在 Commodore 64、NES 等经典平台上的广泛应用,重获关注。传统 6502 编译器如 cc65 虽可靠,但代码生成效率低下,无法充分利用现代优化技术。LLVM-MOS 作为 LLVM/Clang 的专用 fork,引入自定义后端,针对 6502 的寄存器稀缺(仅 A/X/Y)和零页优先寻址模式,实现了高效 IR 降低与内联 / 展开机制。这不仅提升了代码密度和速度,还支持 C99/C++11 乃至 Rust 在资源受限嵌入式环境中的落地。

6502 后端的 IR 降低核心策略

LLVM IR 到 6502 机器码的转换需应对 8 位架构的严苛约束:仅 3 个通用寄存器、无硬件栈、零页(前 256 字节)作为快速变量区。LLVM-MOS 后端通过自定义 TargetLowering 和 SelectionDAG 实现 IR 降低。

首先,寄存器分配采用 “想象零页寄存器” 模型:引入 16 个虚拟 2 字节零页寄存器,可非连续放置。这允许 LLVM 的全局寄存器分配器高效映射 IR 值,而非强制溢出到慢速绝对寻址。证据显示,这种策略在循环中优先选用索引寻址(如 (zp,X)),减少指令计数 20-30%。

其次,调用约定优化:参数优先通过 A/X/Y 传递,避免栈压入。非递归函数自动识别为 “静态栈”,帧直接分配为全局零页变量,无需软栈模拟。对于复杂 IR 如浮点运算,内置 IEEE-754 软浮点降低为 6502 序列,利用循环展开最小化开销。

IR 降低的关键 Pass 包括:

  • LoopStrengthReduce:将 IR 循环归纳变量降低为零页索引,生成 INC zp / BNE 紧凑序列。
  • CodeGenPrepare:预处理 IR,拆分宽类型(如 i16)为高低字节对,便于 6502 的 LDA/STA。
  • FastISel:快速指令选择,优先零页 / 绝对寻址,fallback 到慢 DAG 仅用于分支。

实际测试中,一段计算 π 的 C99 循环(Godbolt demo)经降低后,指令数从 cc65 的 150+ 缩至 80 左右,速度提升显著。

内联与展开:代码密度双刃剑的参数调优

6502 ROM 通常 ≤64KB,代码大小至关重要。LLVM-MOS 启用链接时优化(LTO),跨模块内联 SDK 库调用,常将 printf/malloc 优化掉。

内联策略

  • 默认 -finline-threshold=200(字节),针对小函数如 memset。
  • 自定义 InlineParams:优先非递归、零页局部函数,阈值设为 50-100 字节,避免寄存器压力。
  • 证据:SDK 示例中,库内联后体积减 15%,因消除调用 / 返回(JSR/RTS 各 3 周期)。

循环展开

  • -funroll-loops 与 -mllvm -unroll-threshold=5:针对恒定迭代(如 unroll 4 次 memcpy)。
  • 参数清单:
    参数 效果
    -mllvm -unroll-runtime=false 默认 静态展开,避免运行时检查
    -mllvm -unroll-max-iteration=8 6502 特化 限 8 次,防 ROM 爆炸
    -Os 优先 大小优化下展开阈值自适应

风险:过度展开导致零页溢出或分支爆炸。监控点:llvm-objdump 检查 .text 大小 > 阈值(e.g. 32KB)时,回滚至 -O2 -fno-unroll-loops。

Rust 支持需额外 rustup target add mos-unknown-unknown,结合 cargo 构建脚本指定 triple "mos-c64-elf"。

工程化落地清单:从源码到 PRG

  1. 环境搭建

  2. 编译参数模板(C 项目):

    mos-c64-clang -Os -flto=full -fno-exceptions -fno-rtti -mcpu=mos-6502 \
    -ffast-math -msoft-float -fuse-ld=lld -T linker.ld main.c -o game.elf
    mos-ar cr libgame.a game.o  # 静态库
    llvm-objcopy -O binary game.elf game.prg
    
  3. 优化监控

    • Godbolt:https://godbolt.org/z/c11Th3oMW 测试 IR / 汇编。
    • Benchmark:SDK/utils/sim 模拟器,测周期。 | 优化级 | 大小 (KB) | 速度 (%) | |--------|----------|---------| | -O0 | 5.2 | 100 | | -Os | 2.1 | 140 | | -Os -flto | 1.8 | 165 |
  4. Bank Switching 处理:NES 等用 linker script 分段,IR 降低时注入 JMP bank。

  5. 回滚策略:若 LTO 增时(罕见),fallback 无 LTO;Rust 用 #[inline (always)] 注解关键函数。

此后端证明,现代编译器框架可重塑复古开发:非仅兼容,还超越遗留工具。未来,MLGO 等 Pass 或进一步融合。

资料来源

(正文约 1050 字)

查看归档