为什么Clang强制结构参数为整数

Jus*_*tin 9 compiler-construction struct abi llvm clang

在函数中使用struct参数时,clang将更改函数签名.签名将是一个相同大小的强制int,而不是使用结构类型.在我的编译器项目中,我使用llvm结构类型作为方法签名(这似乎更合乎逻辑).

这不会是一个问题,除了LLVM在使用struct或coerced类型时产生的汇编是不同的而不是调用兼容的事实.这导致我的编译器不与具有结构的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;
}
Run Code Online (Sandbox Code Playgroud)

从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
}
Run Code Online (Sandbox Code Playgroud)

我期望的是什么(以及我的编译器输出的内容):

%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
}
Run Code Online (Sandbox Code Playgroud)

osg*_*sgx 8

两个月前,有在llvmdev线程:[LLVMdev]"结构参数转换为其他类型的"通过Jaymie勒克,1月14日19点50分04秒CST 2013年,她遇到了类似的问题:" 当一个结构参数的函数或返回类型是用编译的clang -O0 -emit-llvm,生成的bitcode根据struct的类型而变化很大. ",clang将struct转换为指针,vector,将其作为几个双精度传递,或者合并为单个i64类型.Anton Korobeynikov 于2013年1月15日00:41:43 回复:

结构被降低到与平台上的C/C++ ABI相对应的东西,以便以适当的方式传递结构.

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

您可以修改您的编译器项目以使用平台ABI(转换结构类似于它由clang完成),或者您可以定义自己的ABI并调整clang以使用它.

  • 我想真正的问题是为什么在llvm-ir级别而不是装配上完成"类型降低"?有没有理由说前端编译器编写者必须管理ABI而不是LLVM的本机代码生成器? (2认同)
  • @EliBendersky是不是打破了llvm-ir的"中间"概念?我希望它不了解ABI,并且生成的代码与平台无关. (2认同)