1

Consider the following code:

__attribute__((noinline)) int foo1(int x, int y)
{
    return x;
}

int bar1(int* a)
{
    int b = foo1(a[5], a[10]);
    return b * b;
}

Even though foo1 is not inlined, a compiler can easily tell one of its parameters is unused; so it's not actually necessary to bother initializing it. And indeed, both GCC 8.2 and clang 7.0 compile bar1() to only read the first argument's value from memory.

Now suppose we put those two int parameters into a struct:

struct two_ints { int x, y; };

__attribute__((noinline)) int foo2(struct two_ints s)
{
    return s.x;
}

int bar2(int* a)
{
    struct two_ints ti = { a[5], a[10] };
    int b = foo2(ti);
    return b * b;
}

There shouldn't be any difference, right? The parameters (certainly as far as the assembly is concerned) are two integers; and the second one is not used. So I would expect to see the same assembly code. Yet... I don't. Both clang and gcc initialize both fields of ti before calling foo2().

Both code fragments with gcc & clang (GodBolt.org)

So, is there anything obligating compilers to initialize both fields, or is this a missed optimization opportunity?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    The compiler can analyze `foo1` and decide to suppress the second parameter everywhere that the function is called. But in the second case, the compiler needs to analyze the entire program before it can safely suppress the second initializer. The fact that `foo2` doesn't use member `y` tells the compiler nothing about whether `ti.y` is needed or not. – user3386109 Mar 03 '19 at 01:21
  • @user3386109: That's not true. The compiler doesn't need to suppress the second initializer _globally_. It just needs to suppress it in `foo2()`. – einpoklum Mar 03 '19 at 07:08
  • I was misled by the statement, *"Both clang and gcc initialize both fields of `ti` before calling `foo2()`"*. It seems that you're actually concerned about the initialization of `s`, which is the copy of `ti` passed to `foo2`. – user3386109 Mar 03 '19 at 08:40
  • @user3386109: `ti` is elided with/into the input argument of `foo2()`. There's just one structure. – einpoklum Mar 03 '19 at 08:48

0 Answers0