llvm jit函数调用时参数通过调用约定传递,默认为c abi:x86-64下前6个整型/指针参数依次入%rdi、%rsi、%rdx等寄存器,超量入栈;关键在于ir函数签名与c函数指针类型必须严格匹配——参数个数、顺序、类型及返回值需一一对应,否则参数错位或截断导致崩溃。

LLVM JIT函数调用时参数怎么传进去
LLVM JIT生成的函数,本质上是内存中可执行的机器码,调用方式和普通C函数完全一致——靠调用约定(calling convention)决定参数怎么放、放哪、谁清理栈。默认用的是LLVMCallConvC,也就是标准C ABI:整数/指针参数从左到右依次进寄存器(x86-64下是%rdi、%rsi、%rdx…),超过6个才压栈。
关键点不是“怎么传”,而是“你得确保IR里定义的函数类型和实际调用时的C函数指针类型严格匹配”。比如你在IR里定义:
define i32 @add1(i32 %x) { ... }
那对应的C函数指针必须是:
typedef int (*add1_func_t)(int);
否则强制转换后调用,参数会被错位读取——%rdi里塞了int,但函数体却当成double*去解引用,直接崩溃。
- IR函数签名和C函数指针类型必须一一对应:参数个数、顺序、类型(含const/volatile)、返回值类型
- 不要用
void*通用指针接返回值,尤其当返回值是struct或__m128这类ABI特殊处理的类型 - 如果函数有
PG_FUNCTION_ARGS这类宏包装的参数(如PostgreSQL JIT场景),本质仍是C风格变参,但需按FunctionCallInfoData*结构体布局来构造入参内存
怎么安全读取JIT函数的返回值
返回值读取失败,90%是因为类型不匹配或未对齐。LLVM IR里ret i64和C里int看似能兼容,但在x86-64 ABI中,i64返回值走%rax,而int只读低32位%eax——若高位非零,结果就被截断了。
实操建议:
- 用
LLVMGetReturnType查IR函数真实返回类型,再映射为C端精确等价类型(比如i32→int32_t,double→double) - 避免跨平台类型别名:不用
int/long,改用int32_t/int64_t - 若返回结构体(如
{i32, i32}),LLVM默认按值返回(通过隐藏指针参数),此时C端必须声明为void func(struct ret_s *out, ...),不能直接struct ret_s func(...)
PostgreSQL JIT场景下PG_FUNCTION_ARGS怎么处理
PostgreSQL的JIT函数(如表达式计算)不接收原始C参数,而是统一用PG_FUNCTION_ARGS宏展开为FunctionCallInfoData *指针。这个指针指向一个固定结构体,里面arg数组存参数值,argnull数组标记是否为NULL。
你在LLVM IR里写的函数,比如:
define void @eval_expr(%struct.FunctionCallInfoData* %fcinfo) { ... }
对应C端调用就是:
FunctionCallInfoData fcinfo_data; // 手动填好arg[0], arg[1], argnull[0]...<code>eval_expr_func(&fcinfo_data);</code>
重点:
-
%struct.FunctionCallInfoData类型必须和PostgreSQL头文件里定义的struct FunctionCallInfoData内存布局完全一致(字段顺序、padding) - PostgreSQL源码里的
llvmjit_types.c就是干这事的:它用C代码把关键结构体重新声明一遍,再导出给LLVM用,避免手写IR结构体偏移出错 - 别试图在IR里直接访问
fcinfo->arg[0]——得用gep指令按偏移计算地址,且偏移量必须来自C端offsetof宏
容易被忽略的ABI陷阱
最隐蔽的问题往往不在IR生成,而在链接和调用边界。比如:
- Windows上默认用
__cdecl,Linux用System V ABI,寄存器分配规则不同;跨平台JIT必须显式指定setCallingConv - 函数有
noalias或nonnull属性时,LLVM优化可能删掉空指针检查,但C端万一真传了NULL,就会段错误 - JIT函数若调用外部C函数(如
malloc),必须用LLVMAddFunction提前声明,且函数类型要带externally_initialized标记,否则运行时找不到符号
类型和调用约定一旦错位,行为不可预测——可能偶尔正常,换编译器版本或优化等级就崩。盯住LLVMGetElementType和LLVMGetTypeKind输出,比猜强得多。











