Page 26 - 《软件学报》2025年第12期
P. 26
沈莉 等: swJulia: 面向新一代神威超级计算机的 Julia 语言编译系统 5407
Prologue 和 Epilogue 以实现 Caller-Saved 寄存器的保留及恢复. Resolver 的执行过程主要包括 3 个阶段: 首先, 其
开辟一块栈空间用于保存当前上下文的 Caller-Saved 寄存器, 并且为 Reentry 函数准备参数. 在 ORCJIT 具体实现
中, Reentry 根据 Trampoline 来匹配被 JIT 编译的目标函数, 因此需要通过$ra-32 计算出 Callee 的 Trampoline 入口
地址. 然后, 其调用 Reentry 实施 Callee 函数的 JIT 编译, 并使用 Callee 函数的实际加载地址更新 TargetAddr 中的
内容. 最后, 其从栈上恢复被保留的 Caller-Saved 寄存器, 从$t11 中恢复被保留的 Callee 函数返回地址, 并且通过
寄存器间接转移跳转到 Callee 函数的实际入口地址开始执行.
如图 3 所示, 当 Callee 函数被首次调用时, ORCJIT 的执行路径依次经过 1-2-3-4-5-6-7-8. 而在后续调用
Callee 函数时, 无需再重复复杂的 Stub 解析与函数编译流程, 执行路径简化为 1-2'. 通过提供面向 SW26010Pro 的
ORCJIT 以及惰性编译等功能, 本文有效支撑了 Julia 在 MPE 上的动态运行, 并且为充分利用硬件特性开展针对性
的编译优化奠定了基础.
LLLazyJITBuilder … Stub manager … Trampoline manager
Add module Materialize Materialize
IR module StubsBlockWorkingMem TrampolineBlockWorkingMem
… …
JIT Entry:
1 Trampoline:
Caller: Stub: MV $ra, $t11
… MV TargetAddr, $pv 2
MV Stub, $pv LD $pv, 0($pv) MV Resolver, $pv 32 B
…
CALL ($pc→$ra), $pv 2′ JMP $pv CALL($pc→$ra), $pv
… … …
…
Update
Callee: 3
…
Emit //JIT Compile Callee ResolverWorkingMem
RET $ra Reentry:
… …
8
//Update TargetAddr
… Resolver:
//Restore regs MV Callee, $v0 //Preserve regs
LD $v0,0($sp) RET $ra MV $sp-456, $sp
LD $a0,8($sp) ST $v0, 0($sp)
… 6 5 ST $a0, 8($sp)
MV $sp+456, $sp …
// Call Reentry
//Jump to Callee //Prepare Reentry args
MV $t11, $ra 7 MV Reentry, $pv 4 MV ctx, $a0
MV $v0, $pv CALL ($pc→$ra), $pv MV $ra-32, $a1
JMP $pv … …
图 3 SW26010Pro 后端中 ORCJIT 惰性编译流程
2.2 LLVM.jl
[12]
LLVM.jl 是一个在 Julia 环境中为集成 LLVM 工具而设计的第三方库. 该库通过为 LLVM API 提供高级封
装, 充分利用 Julia 强大的外部函数接口 (foreign function interface, FFI), 实现与底层运行时和加速计算库的高效交
互. 借助 LLVM.jl, Julia 开发者能够更加便捷地利用 LLVM 的先进功能, 例如目标代码优化和平台代码生成, 进而
提升 Julia 代码的性能及其跨平台兼容性.
在 SW26010Pro 平台上, 我们基于 LLVM.jl 提供了面向 SW26010Pro 的优化编译器 swLLVM 及二进制工具
链的交互接口封装, 同时还针对 Julia 的动态特性增加了相应的编译优化支持. 如第 1.2 节所述, 由于 SW26010Pro
的 CPE 全面支持 GPC 方案, 使得动态库间的从核函数跳转成为可能. 动态程序访存行为复杂性的增加对片上存
储空间 LDM 的有效管理提出了更高的要求.

