【问题标题】:Why does Clang coerce struct parameters to ints为什么 Clang 将结构参数强制转换为整数
【发布时间】:2014-05-11 16:00:02
【问题描述】:

在函数中使用结构参数时,clang 会更改函数签名。签名不是使用结构类型,而是大小相等的强制 int。在我的编译器项目中,我使用 llvm 结构类型作为方法签名(这看起来更合乎逻辑)。

这不会是一个问题,除了在使用结构或强制类型时由 LLVM 生成的程序集是不同的并且不调用兼容。这导致我的编译器与带有结构的 C 函数的 ABI 不兼容。

clang 为什么要这样做?这是 C ABI 中指定的内容吗?

这是一个简单的示例 C 源文件:

struct TwoInt { int a, b; };

struct EightChar { char a, b, c, d, e, f, g, h; };

void doTwoInt(struct TwoInt a) {}

void doEightChar(struct EightChar a) {}

int main()
{
        struct TwoInt ti;
        struct EightChar fc;

        doTwoInt(ti);
        doEightChar(fc);

        return 0;
}

从 Clang 生成的 LLVM-IR

%struct.TwoInt = type { i32, i32 }
%struct.EightChar = type { i8, i8, i8, i8, i8, i8, i8, i8 }

define void @doTwoInt(i64 %a.coerce) nounwind uwtable {
  %a = alloca %struct.TwoInt, align 8
  %1 = bitcast %struct.TwoInt* %a to i64*
  store i64 %a.coerce, i64* %1, align 1
  ret void
}

define void @doEightChar(i64 %a.coerce) nounwind uwtable {
  %a = alloca %struct.EightChar, align 8
  %1 = bitcast %struct.EightChar* %a to i64*
  store i64 %a.coerce, i64* %1, align 1
  ret void
}

define i32 @main() nounwind uwtable {
  %1 = alloca i32, align 4
  %ti = alloca %struct.TwoInt, align 4
  %fc = alloca %struct.EightChar, align 1
  store i32 0, i32* %1
  %2 = bitcast %struct.TwoInt* %ti to i64*
  %3 = load i64* %2, align 1
  call void @doTwoInt(i64 %3)
  %4 = bitcast %struct.EightChar* %fc to i64*
  %5 = load i64* %4, align 1
  call void @doEightChar(i64 %5)
  ret i32 0
}

我的预期(以及我的编译器输出):

%TwoInt = type { i32, i32 }
%EightChar = type { i8, i8, i8, i8, i8, i8, i8, i8 }

define void @doTwoInt(%TwoInt %a) {
  %1 = alloca i32
  %2 = alloca %TwoInt
  store %TwoInt %a, %TwoInt* %2
  ret void
}

define void @doEightChar(%EightChar %a) {
  %1 = alloca i32
  %2 = alloca %EightChar
  store %EightChar %a, %EightChar* %2
  ret void
}

define i32 @main() {
  %1 = alloca i32
  %ti = alloca %TwoInt
  %fc = alloca %EightChar
  %2 = load %TwoInt* %ti
  call void @doTwoInt(%TwoInt %2)
  %3 = load %EightChar* %fc
  call void @doEightChar(%EightChar %3)
  ret i32 0
}

【问题讨论】:

  • clang 是如何开始的?您的操作系统和 ABI 是什么? ABI 不是在 C/C++ 标准中定义的,而是由操作系统及其库定义的,并且有可能您的编译器不按照 ABI 工作。
  • 很好的概述,带有指向更详细页面的链接:What is an application binary interface (ABI)?

标签: compiler-construction struct clang llvm abi


【解决方案1】:

两个月前,在 llvmdev 中有一个线程:[LLVMdev] "Struct parameters being converted to other types",作者 Jaymie Strecker,2013 年 1 月 14 日 19:50:04 CST。她遇到了类似的问题:“当一个带有结构参数或返回类型的函数是使用clang -O0 -emit-llvm 编译,生成的位码根据结构的类型变化很大。”,clang 将结构转换为指针、向量,将其作为多个双精度值传递,或合并为单个 i64 类型。 Anton Korobeynikovreplied at Jan 15 00:41:43 CST 2013:

结构被降低为与您平台上的 C/C++ ABI 相对应的东西,以便以正确的方式传递结构。

因此,clang 会根据您的操作系统、库和本机编译器使用的方式进行结构传递。这样做是为了允许您构建模块,这些模块将与本地库一起使用。我认为您的编译器项目使用了错误的 ABI。

您可以修复您的编译器项目以使用平台 ABI(转换结构,就像它由 clang 完成的那样),或者您可以定义自己的 ABI 并调整 clang 以使用它。

【讨论】:

  • 我想真正的问题是为什么“类型降低”是在 llvm-ir 级别而不是在程序集中完成的?前端编译器编写者必须管理 ABI 而不是 LLVM 的本机代码生成器是否有原因?
  • @Justin:LLVM IR 无法表示正确降低调用所需的 ABI 规则。因此,任务留给前端来生成精确的 ABI 特定 IR 序列。
  • @EliBendersky 所以Calling Conventions IR 属性不足以指定如何传递参数?文档在这一点上并不完全清楚。
  • @贾斯汀:不。前端像安腾 ABI 一样执行全面的 ABI 降低
  • @EliBendersky 这不是打破了 llvm-ir 的“中间”概念吗?我希望它不了解 ABI,并且生成的代码与平台无关。
猜你喜欢
  • 1970-01-01
  • 2019-05-24
  • 2011-06-06
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
  • 2012-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多