Page 25 - 《软件学报》2025年第12期
P. 25
5406 软件学报 2025 年第 36 卷第 12 期
得一提的是, ORCJIT 在兼容 MCJIT (machine code JIT) 功能的同时, 还特别支持了惰性编译, 其详细执行流程可参
见图 3. 操作中使用的伪指令功能如下: MV 用于将源寄存器或地址中的内容移动到目的寄存器, LD 用于从确定
的内存地址中加载数据到目的寄存器, ST 用于从寄存器向确定的内存地址中存储数据, JMP 用于寄存器间接转
移, CALL 用于函数调用, RET 用于函数返回. 操作中使用的寄存器功能如下: $pv 用于存储被调函数入口地址,
$pc 用于存储当前的 PC 值, $ra 用于存储返回地址, $t11 为 Caller-Saved 临时寄存器, $sp 用于存储栈指针, $v0 用
于存储返回值, $a0 和$a1 用于传递函数参数.
Julia hybrid code
MPE code @saca CPE code
MPE compiler SACA.jl
Parser
CPE compiler
High-level SACA wrapper
Julia IR Reused
optimizations SIMD exts
Runtime APIs
Lowering
Low-level
LLVM IR
optimizations LLVM.jl
Support LLVM wrapper
ORCJIT engine
LLVM ToolChains
Executable Runtime CPE DyLink
stream libs objects
SW26010Pro executable image
图 2 swJulia 编译系统组成结构框图
ORCJIT 提供了 LLLazyJITBuilder 类专门用于处理惰性编译, 其在初始化阶段构建 LLLazyJIT 实例并设置惰
性编译的相关属性, 包括是否允许将 JIT 实例保存到缓存文件中、选择编译 module 还是 function 等. 然后,
LLLazyJITBuilder 构建 Stub 管理器和 Trampoline 管理器, 并根据 IR module 实例化与符号入口一一对应的 Stub
及 Trampoline.
Stub 是一段充当占位符的汇编代码, 用于检查被调函数 Callee 是否已被 JIT 编译. 当程序尝试调用 Callee 函
数时, 首先会跳转到 Callee 对应的 Stub 入口, 然后从固定内存地址 TargetAddr 中获取目标地址并执行寄存器间接
转移. 当 Callee 函数首次被调用时, TargetAddr 中存储的是其对应的 Trampoline 入口地址. 而当 Callee 再次被调
用时, 由于其已被 JIT 编译, TargetAddr 中的内容被更新为 Callee 的实际入口地址.
与 Stub 类似, Trampoline 也是一段动态生成的汇编代码, 其长度固定为 32 B, 用于保存 Callee 函数的返回地
址并跳转到 Resolver, 进而实现对 Callee 的 JIT 编译. Trampoline 首先将$ra 中存储的 Callee 函数返回地址保存到
Caller-Saved 寄存器$t11 中, 以确保其不会被后续的函数调用所修改, 然后将更新后的 PC 值存入$ra 并跳转到
Resolver 入口.
Resolver 是一段较为复杂的汇编代码, 其主要功能是调用 Reentry 函数完成对 Callee 的 JIT 编译, 并且管理

