Illegal Optimization(?): Combining empty type instances

I think I may have found an illegal optimization in LLVM. The problem is that if you have an empty type LLVM optimizes it away so it produces weird results. The assembler output from llvm-gcc clearly outputs the "alloca" instructions, so some other pass is removing them. I doubt if any real program relies on this behaviour, and there is a trivial work around. However, my program ran across this problem, so maybe it should be corrected.

Example program:

#include <stdio.h>

struct Empty {};

struct Empty globalA;
struct Empty globalB;

int main( int argc, char* argv )
{
  struct Empty a;
  struct Empty b;
  
  printf( "%p %p\n", &a, &b );
  printf( "%p %p\n", &globalA, &globalB );
  return 0;
}

The expected output is that all four pointers will be unique and valid. With LLVM the output is:

(nil) (nil)
0x4133aff9 0x4133aff9

Assembling and executing llvm-gcc's output directly is slightly better but the globals are still wrong:

0xbfffe0ef 0xbfffe0ee
0x4133aff9 0x4133aff9

Running llvm-gcc's output directly using 'lli -force-interpreter' is correct:

0x86b8748 0x86b8758
0x86a4058 0x86b8658

This caused a problem in my program because I was testing if the two addresses were equal, and the test was always returning true. The workaround is to put something in the structure so that LLVM doesn't optimize it away. The question is what does the C standard require? I'm assuming that GCC is doing the right thing, but I could be wrong.

Evan Jones

I think I may have found an illegal optimization in LLVM. The problem is that if you have an empty type LLVM optimizes it away so it produces weird results. The assembler output from llvm-gcc clearly outputs the "alloca" instructions, so some other pass is removing them. I doubt if any real program relies on this behaviour, and there is a trivial work around. However, my program ran across this problem, so maybe it should be corrected.

The expected output is that all four pointers will be unique and valid. With

Uniqueness is something guaranteed by C++, but not by C. In C, empty structures have size = 0, which gives you funny cases like this. Consider the following:

struct Empty {};
struct Empty Foo[10];

The size of both 'Empty' and 'Foo' are zero, which means that each element of Foo is at the same address.

In the C++ world, this sort of thing is explicitly banned: the smallest structure you can get with standard C++ is one byte.

This caused a problem in my program because I was testing if the two addresses were equal, and the test was always returning true. The workaround is to put something in the structure so that LLVM doesn't optimize it away. The question is what does the C standard require?

I believe that LLVM's behavior is correct in this case.

I'm assuming that GCC is doing the right thing, but I could be wrong.

GCC may well be doing what you expect in this case, but that may just be a side effect of it not optimizing as aggressively.

-Chris

Uniqueness is something guaranteed by C++, but not by C.

Ah! I knew that someone out there would have memorized the standards on this list, and llvm-g++ does the right thing. Never mind!

GCC may well be doing what you expect in this case, but that may just be a side effect of it not optimizing as aggressively.

That must be it.

Evan Jones