You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86_64 Linux下LLVM 12调用C函数出现调用约定不匹配问题

问题根源

你遇到的是LLVM对匿名结构体和命名结构体的ABI处理规则不同,导致的参数传递不匹配问题。
你在IR中使用匿名结构体{ i32*, i32, i32, i32 }对应C语言的StringDictionary结构体时,LLVM会默认将该匿名结构体拆分为独立的标量值,尝试用寄存器传递,不会将其识别为需要遵循SysV ABI规则的整体MEMORY类参数。同时你定义的函数直接返回该结构体,也不符合x86_64下超过16字节结构体的返回规则:C ABI中这类大结构体返回时,由调用方分配返回值内存,将内存指针作为隐式第一个参数传递给被调用方,而非直接通过寄存器返回结构体。


解决步骤

  1. 首先在IR中显式定义命名结构体类型,不要使用匿名结构体:
%StringDictionary = type { i32*, i32, i32, i32 }
  1. 修正函数声明,添加sret和byval属性标记返回值和传值参数:
declare void @growStringDictionary(%StringDictionary* sret(%StringDictionary), %StringDictionary* byval(%StringDictionary), i32)

参数说明:

  • 第一个参数标记sret,表示这是返回值的写入地址,对应ABI中隐式的返回值指针
  • 第二个参数标记byval,表示这是按值传递的结构体,LLVM会自动按照ABI规则将其放在栈上传递
  • 第三个参数为原函数的neededWordsCapacity入参
  1. 修正调用逻辑:
; 分配返回值和入参结构体的栈内存
%ret_dict = alloca %StringDictionary
%arg_dict = alloca %StringDictionary

; 将你之前构造的结构体值%70写入入参内存
%arg_base = getelementptr inbounds %StringDictionary, %StringDictionary* %arg_dict, i32 0, i32 0
store i32* %70.0, i32** %arg_base
%arg_elementSize = getelementptr inbounds %StringDictionary, %StringDictionary* %arg_dict, i32 0, i32 1
store i32 %70.1, i32* %arg_elementSize
%arg_rowCount = getelementptr inbounds %StringDictionary, %StringDictionary* %arg_dict, i32 0, i32 2
store i32 %70.2, i32* %arg_rowCount
%arg_wordsCapacity = getelementptr inbounds %StringDictionary, %StringDictionary* %arg_dict, i32 0, i32 3
store i32 %70.3, i32* %arg_wordsCapacity

; 调用函数
call void @growStringDictionary(%StringDictionary* sret(%StringDictionary) %ret_dict, %StringDictionary* byval(%StringDictionary) %arg_dict, i32 %73)

; 后续使用返回值直接从%ret_dict中读取对应字段即可

如果不确定正确的IR写法,可以将C代码用clang -S -emit-llvm -O0编译生成参考IR,对照clang的输出编写就能避免ABI不匹配问题。


内容的提问来源于stack exchange,提问作者Tudor Bosman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 01:24:04