在遵循LLVM IR中的C调用约定的同时传递按值传递结构

时间:2018-12-14 22:36:48

标签: c++ llvm-ir llvm-c++-api

我想在C ++和JIT的LLVM程序之间传递结构按值。我已经看到了很多关于此的讨论,甚至还有关于SO的几个问题。我已经读到,如果我想让我的程序真正传递值,我需要做一个称为“参数强制”的事情。使用byvalsret看起来像是简单的跨平台解决方案。仍然有些痛苦,C ++代码必须记住传递指针而不是值(尽管调用代码是C ++,所以我可以做一些模板魔术)。

我对这个问题了解的越多,对它的理解就越少。调用约定是特定于平台的问题,应由代码生成器处理,对吗?我不明白为什么特定于平台的代码生成器不仅仅处理特定于平台的结构处理方式(同时符合平台的C ABI)。前端应该与平台无关!

有没有通行证可以对我进行论证胁迫?访问每个函数声明和每个函数调用并转换所有结构,以便它们与平台的C ABI兼容的传递?我觉得这是所有前端都可以使用的东西(如果存在),而Clang不使用它,因此也许是不可能的。为什么这不是可行的解决方案?如果通行证可以解决这个问题,那么我希望它会成为LLVM的一部分。

我不明白为什么每个前端都必须进行论证强制。我什至不知道该如何做强制性论证。我见过一些人使用Clang代码生成代码并排除了执行参数强制的部分的实例。不幸的是,如果我想要真正的C ABI兼容性,这似乎是最好的解决方案。甚至有可能重用另一种前端的一部分来使用完全不同的语言,这一事实使我继续怀疑为什么必须在前端进行此操作?

必须对此做些事情!我们不能只在每个前端中编写相同的C ABI兼容性代码。太荒谬了!也许我根本听不懂。

有人可以帮我解决这个问题吗?我在考虑使用byvalsret只是因为它比修改clang代码生成器容易。有没有更简单的方法?

1 个答案:

答案 0 :(得分:0)

在LLVM IR中按值传递结构时,您必须制定自己的规则。我选择了最简单的规则集。

假设我有一个像这样的程序:

struct MyStruct {
  int a;
  char b, c, d, e;
};

MyStruct identityImpl(MyStruct s) {
  return s;
}

MyStruct identity(MyStruct s) {
  return identityImpl(s);
}

此程序的LLVM IR与此等效:

void identityImpl(MyStruct *ret, const MyStruct *s) {
  MyStruct localS = *s;
  *ret = localS;
}

void identity(MyStruct *ret, const MyStruct *s) {
  MyStruct localS = *s;
  MyStruct localRet;
  identityImpl(&localRet, &localS);
  *ret = localRet;
}

这不是传递结构的最有效方法,因为MyStruct可以放入64位寄存器中。但是,如果可以证明从未写入过localS,则优化器可以删除s并直接使用localS。这两个函数都优化为对memcpy的单个调用。

这只花了半天。走Clang路线可能要花至少一周的时间。我仍然认为我不得不这样做是很不幸的,但是我现在明白了这个问题。平台的C ABI没有指定结构的传递。