Rust 下使用 LLVM 构建 JIT 开发
使用 rust + inkwell 开发了一个 JIT 代码示例。碰到了不少问题。罗列如下,以作记录:
LLVM 版本依赖
LLVM 引擎,需要指定版本。编译 release 版本时,需要指定 RUST_FLAGS 指定 LLVM 引擎的版本。
RUSTFLAGS='-lLLVM-19' cargo build --release |
千万别装多套 LLVM 引擎。我就在某次装了多个版本后,完全无法编译运行了。直到我重装了系统,才恢复正常。
if-then-else 结构
需要构建出 BasicBlock,
then-block,匹配if条件下的逻辑,else-block,匹配if条件外的逻辑,merge-block,合并then-block和else-block的逻辑。
其值,需要通过构建 Phi 节点,将 then-block 和 else-block 的值关联起来。
特别重要的一点,需要严格关注当前代码的 position,需要确保当前的代码在正确的位置。比如,
then-block的代码,必须执行position_at_end(then-block),然后再编写else-block的代码,必须执行position_at_end(else-block),然后再编写merge-block的代码,必须执行position_at_end(merge-block),然后再编写
Phi 节点,需要将 then-block 和 else-block 的值捏合到一起,所以,要求两侧返回的类型必须一致。
另外,理论上支持嵌套,但是我没有成功。
bool -> i64
某个场景下需要将 bool 转换成 i64。一开始使用了 builder.build_int_cast。但是 builder.build_int_cast,无法正确转换。因为 bool 的值,是 1 和 0,且无符号。必须使用 builder.build_int_cast_sign_flag 才可以,且必须设置 is_signed 为 false。
函数调用
普通函数可注册进 LLVM Module 中,被 JIT 代码调用。非常方便且简单,只要声明好函数的参数和返回值类型,就可以调用。
特别地,说明一下函数的返回值类型或参数类型是自定义类型时,必须使用 LLVM 提供的 StructType/StructValue 类型。
一开始我是当作普通的 StructValue 使用的,想当然使用下标进行访问。结果,数据完全不是正确的值。需要通过下面代码才能正确访问。
let val1 = builder.build_extract_value(struct_value, 0, "val1").unwrap(); |
Debug
没找到什么好用的 LLVM Debug 工具。API 也没找到。不得以,用上面的函数调用转着圈实现了通过日志输出来查看数据。
封装了一个打印数据的函数,在需要打印数据的地方调用。然后封装的 rust 函数调用 log::info! 进行日志输出。间接解决了问题。
总结
总的来说,通过 inkwell 构建 LLVM JIT 引擎,非常简单。但是遇到很多问题,需要自己解决。