0

I'm trying to mock out a project so that I can test an opt pass that traverses the CG but I'm being blocked right in main by an odd compilation choice by clang that is further exacerbated by llvm-link. I'll give you some code:

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

I compile individually:

; 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

Then link with other files:

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
}

In the initial compilation, if I'm understanding correctly, thinks that playXOs is a var arg function? If I manually edit main.ll to change the call site and forward declaration to not be playXOs(...) then the linked file doesn't have the weird bitcast in it. Does anyone have a solution?

llvm info:

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

Edit: I forgot to mention, this is a problem for me because, though the CG I get contains the call site for playXOs, the CallGraphNode has a null function.

Braaedy
  • 540
  • 1
  • 5
  • 16

2 Answers2

2

A declaration like "int f()" means f is a function with a fixed number of arguments, but we don't know how many arguments. It's NOT a function with a variable number of arguments. It could be a function with 0, 1, 2, 3... arguments, we just don't know which one.

There is also a requirement that the types of all arguments (which we don't know) are types that would be the result of the "usual promotions". So the real function cannot be "int f (float x)" because a call f (3.0f) would promote 3.0f to double, and then the call would be wrong.

When you actually call say f (3.0f, (char) 1), then the compiler will promote 3.0f to double, promote (char) 1 to int, and produce code that is correct if the actual function is "int f (double x, int y)". If that guess is wrong, your application will likely crash.

Of course if the actual function is "int f(void)" and you call f() everything is fine. But you could call f(1) and that would be undefined behaviour.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
0

As per usual, you spend an hour or two feeling completely stumped, give up and post on SO, find an answer seconds later.

I changed the header declaration to void playXOs(void) to explicitly mark it as not having any arguments and it compiles without any of the casting or the VA guessing.

Can someone point me at the c spec where it says the compiler can't assume that f() is f(void) and not f(...)? Will accept that as answer.

Braaedy
  • 540
  • 1
  • 5
  • 16
  • I don't think there's a part of the spec where it says that -- but main is special. In c89 standard `5.1.2.2.1 Program Startup` it says there are exactly two valid prototypes for main `int main(void)` and `int main(int argc, char *argv[])`, and it is undefined behavior otherwise. If you would accept that as an answer, then we should mark this question as a duplicate since that has been written before on SO – Chris Beck Mar 31 '19 at 20:28
  • Main wasn't the issue here. The call was _in_ main, but it was a call to another function that was the issue. – Braaedy Mar 31 '19 at 20:29
  • The code you posted has the same problem with main, are you sure its not related? – Chris Beck Mar 31 '19 at 20:31