Clang使用var args编译对void函数的调用

时间:2019-03-31 20:02:40

标签: c llvm llvm-clang llvm-ir

我正在尝试模拟一个项目,以便可以测试遍历CG的opt pass,但是我主要受到clang奇怪的编译选择的阻碍,而llvm-link进一步加剧了这种选择。我给你一些代码:

main.c:

#include "xos/xos.h"

int main() {

  playXOs();

  return 0;
}

xos.h:

#ifndef SLICEREXAMPLEPROJECT_XOS_H
#define SLICEREXAMPLEPROJECT_XOS_H

void playXOs();

#endif // SLICEREXAMPLEPROJECT_XOS_H

我单独编译:

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  call void(...) @playXOs()
  ret i32 0
}

declare dso_local void @playXOs(...) #1

然后链接其他文件:

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  call void (...) bitcast (void ()* @playXOs to void (...)*)()
  ret i32 0
}

在最初的编译中,如果我理解正确,认为playXOs是一个var arg函数?如果我手动编辑main.ll来更改呼叫站点并转发声明不为playXOs(...),则链接的文件中没有奇怪的位广播。有人有解决方案吗?

llvm信息:

clang version 8.0.0 (git@github.com:llvm-mirror/clang.git 8ca7a0dcb7e9a0cd7bf71ff4b70e12462c16f205) (git@github.com:llvm-mirror/llvm.git e9eedd7fa6f4f861afbc7a2862f3f5504e6d340f)
Target: x86_64-unknown-linux-gnu
Thread model: posix

编辑:我忘了提及,这对我来说是个问题,因为尽管我得到的CG包含playXOs的呼叫站点,但CallGraphNode具有null函数。

2 个答案:

答案 0 :(得分:2)

像“ int f()”这样的声明意味着f是一个具有固定数量参数的函数,但是我们不知道有多少个参数。它不是具有可变数量参数的函数。它可能是一个带有0、1、2、3 ...自变量的函数,我们只是不知道哪个。

还要求所有参数的类型(我们不知道)是“通常的提升”结果的类型。因此,实际函数不能为“ int f(float x)”,因为调用f(3.0f)会将3.0f提升为两倍,然后调用将是错误的。

当您实际调用说f(3.0f,(char)1)时,编译器会将3.0f提升为double,将(char)1提升为int,如果实际函数为“ int”,则会生成正确的代码f(double x,int y)”。如果这种猜测是错误的,则您的应用程序可能会崩溃。

当然,如果实际函数是“ int f(void)”,而您调用f()则一切都很好。但是您可以调用f(1),这将是不确定的行为。

答案 1 :(得分:0)

与往常一样,您花了一个或两个小时就完全感到沮丧,放弃并发表在SO上,几秒钟后找到答案。

我将标头声明更改为void playXOs(void),以明确地将其声明为没有任何参数,并且可以在不进行任何强制转换或VA猜测的情况下进行编译。

有人能指出我的c规范吗?它说编译器不能假设f()f(void)而不是f(...)?会接受它作为答案。