在LLVM IR中传递结构值

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生成器中执行参数强制的代码,但这使我的设计相当复杂(更不用说使其成为特定于体系结构的了),因此,如果我只是做错了什么,我宁愿知道这一点。 。:)

Cha*_*uth 3

恐怕 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++ 来说情况更糟,恐怕它的复杂性要高得多。

所以你的选择是:

  1. 在有限范围的签名中使用非常小的类型集,您构建自定义逻辑以正确地降低到 LLVM IR,检查它是否与 Clang(或另一个 C 前端)在每种情况下生成的内容相匹配。
  2. 直接使用 Clang 或其他 C 前端来发出 LLVM IR。
  3. 承担将ABI/调用约定逻辑从 Clang 提取到可重用库的主要项目。过去,LLVM/Clang 社区对此一直有兴趣,但据我了解,这是一项非常庞大且复杂的任务。您也许能够找到并重用一些部分成果(特别是 C 和 JIT),但我不太记得所有这些成果在哪里。