【问题标题】:Clang compiles call to void function with var args [duplicate]Clang使用var args编译对void函数的调用[重复]
【发布时间】:2019-08-22 00:27:48
【问题描述】:

我正在尝试模拟一个项目,以便我可以测试一个遍历 CG 的 opt pass,但是我在 main 中被 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 有一个空函数。

【问题讨论】:

  • @ChrisBeck clang 版本在那里。
  • @MarkBenningfield 请作为答案发布,将接受。
  • 问题与现有问题重复的事实不是答案。
  • 那么请标记为重复。
  • @ChrisBeck 不,这不是重复的。有问题的是playXOs 的签名。 @MarkBenningfield 的重复建议是正确的。

标签: c llvm llvm-clang llvm-ir


【解决方案1】:

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

还有一个要求,即所有参数的类型(我们不知道)是“通常的提升”结果的类型。所以真正的函数不能是“int f (float x)”,因为调用 f (3.0f) 会使 3.0f 加倍,然后调用就会出错。

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

当然,如果实际函数是“int f(void)”并且你调用 f() 一切都很好。但是您可以调用 f(1) ,那将是未定义的行为。

【讨论】:

    【解决方案2】:

    像往常一样,你会花一两个小时感到完全困惑,放弃并在 SO 上发帖,几秒钟后找到答案。

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

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

    【讨论】:

    • 我不认为规范中有一部分是这么说的——但 main 是特别的。在 c89 标准 5.1.2.2.1 Program Startup 中,它表示主 int main(void)int main(int argc, char *argv[]) 恰好有两个有效原型,否则它是未定义的行为。如果您接受这个作为答案,那么我们应该将此问题标记为重复,因为之前已经在 SO
    • 主要不是这里的问题。该调用是 in main,但它是对另一个函数的调用,这是问题所在。
    • 你贴的代码和main有同样的问题,确定不相关吗?
    猜你喜欢
    • 2014-10-24
    • 2012-12-06
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多