Dol*_*000 5 llvm pass-by-value calling-convention
我正在为JIT目的生成LLVM IR,并且我注意到当涉及到聚合值时,LLVM的调用约定似乎与C调用约定不匹配。例如,当我声明一个函数采用一个{i32, i32}(即struct {int a, b;}C语言中的a)参数时,即使x86-64 ABI 指定了该参数,它似乎也会将其自身的x86-64 GPR中的每个struct元素传递给该函数。(第3.2.3节),此类结构应打包在单个64位GPR中。
尽管LLVM的文档声称默认情况下与C调用约定匹配:
“
ccc”-C调用约定此调用约定(如果未指定其他调用约定,则为默认设置)与目标C调用约定匹配。此调用约定支持varargs函数调用,并且可以在声明的原型和函数的已实现声明中允许某些不匹配(与普通C一样)。
那么,我的问题是:我做错了什么导致LLVM与C调用约定不匹配,或者这是已知行为吗?(至少,文档似乎是错误的,不是吗?)
我在网络上几乎找不到该问题的参考,例如2007年的此错误报告,该报告声称已修复。它还声称“首先,LLVM无法将聚合作为奇异的Value *'s处理”,我不知道2007年是否为真,但鉴于extractvalue/insertvalue,现在看来似乎并非如此。说明。我还发现了这样的问题,其第二个(未接受的)答案似乎只是隐含地接受了论点强制必须手动完成。
我目前正在构建用于在IR生成器中执行参数强制的代码,但这使我的设计相当复杂(更不用说使其成为特定于体系结构的了),因此,如果我只是做错了什么,我宁愿知道这一点。 。:)
恐怕 LLVM 对 C 语言兼容调用约定的支持极其有限。有些人希望在 LLVM(或相关库)中提供更直接的调用约定支持,但到目前为止这还没有出现。该逻辑目前在 C 语言前端(例如 Clang)中编码。
LLVM 提供的是从特定 LLVM IR 类型到特定 CPU 后端的特定 C ABI 降低的映射。您可以通过使用 Clang 发出 LLVM IR 来查看给定 C 函数使用哪些 IR 类型,就像上面的评论所建议的那样: https ://c.compiler-explorer.com/z/8jWExWPYq
struct S { int x, y; };
void f(struct S s);
void test(int x, int y) {
struct S s = {x, y};
f(s);
}
Run Code Online (Sandbox Code Playgroud)
变成:
define dso_local void @test(i32 noundef %0, i32 noundef %1) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca %struct.S, align 4
store i32 %0, ptr %3, align 4
store i32 %1, ptr %4, align 4
%6 = getelementptr inbounds %struct.S, ptr %5, i32 0, i32 0
%7 = load i32, ptr %3, align 4
store i32 %7, ptr %6, align 4
%8 = getelementptr inbounds %struct.S, ptr %5, i32 0, i32 1
%9 = load i32, ptr %4, align 4
store i32 %9, ptr %8, align 4
%10 = load i64, ptr %5, align 4
call void @f(i64 %10)
ret void
}
declare void @f(i64) #1
Run Code Online (Sandbox Code Playgroud)
遗憾的是,有一些不平凡的逻辑将特定的 C 类型映射到 LLVM IR,当针对平台降低时,该逻辑将与 ABI 相匹配。除了极其简单的类型(基本 C 整数类型、指针、、、float可能double还有其他一些类型)之外,这些类型甚至无法在不同体系结构 ABI/调用约定之间移植。
FWIW,对于 C++ 来说情况更糟,恐怕它的复杂性要高得多。
所以你的选择是: