引言
2026 年 7 月 26 日的文章探讨了在非 RISC - V 机器上,RISC - V 程序性能接近裸机性能的方法,关键是使用提前编译器,通过尾调用连接基本块,采用 Clang 的 `preserve_none` 调用约定保留热点来宾状态。作者参与 [OpenVM](https://github.com/openvm - org/openvm) 开发,这是在 [Axiom](https://axiom.xyz) 构建的 RISC - V 虚拟机,能运行程序并生成加密证明。在 Axiom 工作时,证明生成从单 CPU 扩展到 [GPU 集群](https://ethproofs.org/provers),但执行速度成瓶颈。在 M3 MacBook 上,原生 arm64 程序约 100 毫秒完成,通过解释器运行相同程序需三到四秒。
解释器基准
运行 RISC - V 代码的直接方法是用解释器,执行取指 - 解码 - 执行周期。但来宾指令短小,解释器管理指令时间可能超执行时间。
提前预解码
可对指令预解码,存储操作、操作数和处理程序指针,解码不再是热点循环部分,执行时通过间接调用处理程序指针分发。
通过计算跳转进行分发
预解码后,间接分发成瓶颈,可用“计算跳转”,每个处理程序查找下一个操作码标签并跳转,执行过程通过间接跳转转移控制权。
尾调用线程化
尾调用可连接处理程序,处理程序尾调用下一个处理程序。编译器可将调用转换为普通跳转,Clang 的 `musttail` 属性强制转换。
将状态保留在寄存器中
处理程序通过 `State *` 访问 `pc`,可将其作为函数参数传递,让编译器将频繁使用的值保留在宿主机寄存器中。
实现原生执行
手动编写的汇编后端
简单的提前(AOT)翻译器逐指令翻译 RISC - V 指令,生成汇编代码,代价是可移植性差。
寄存器映射
翻译器可将频繁使用的来宾寄存器映射到宿主机寄存器,减少内存和寄存器间的移动。
让 Clang 作为后端
一个巨型函数
可将整个程序作为一个函数,转换为 C 代码,让编译器优化。但程序规模大时此方法失效。
逐基本块重新编译
基本块是顺序指令序列,重新编译器将每个基本块作为单独 C 函数生成,让 Clang 优化。
识别基本块
重新编译器通过识别基本块和连接成控制流图确定生成函数的起始和结束位置。示例展示了如何应用规则处理对数组求和的函数。