24

There is a curious difference between assemblies of a small program, when compiled as a C-program or as a C++-program (for Linux x86-64).

The code in question:

int fun();
int main(){
    return fun();
}

Compiling it as a C-program (with gcc -O2) yields:

main:
    xorl    %eax, %eax
    jmp fun

But compiling it as a C++-program (with g++ -02) yields:

main:
    jmp _Z3funv

I find it puzzling, that the C-version initializes the return value of the main-function with 0 (xorl %eax, %eax).

Which feature of the C-language is responsible for this necessity?

Edit: It is true that, for int fun(void); the is no initialization of the eax-register.

If there is no prototype of fun at all, i.e.:

int main(){
    return fun();
}

then the C-compiler zeros the eax-register once again.

ead
  • 32,758
  • 6
  • 90
  • 153

2 Answers2

44

In C int fun(); can take any number of arguments, so it may even be a varargs function. In C++ however it means it takes no arguments.

The x86-64 sysv abi convention demands that the register AL must contain the number of SSE registers used when invoking a varargs function. You of course pass no argument, so it is zeroed. For convenience the compiler decided to zero the whole eax. Declare your prototype as int fun(void); and the xor shall disappear.

Jester
  • 56,577
  • 4
  • 81
  • 125
  • Now I stumbled upon this: In ISO C the variadic function has to have a fixed/named argument (see for example http://stackoverflow.com/questions/11811828/is-it-possible-to-create-a-c-varargs-function-with-no-arguments), so actually compiler should know that `fun` is not a variadic function because there are no fixed parameters. Do you know, why gcc zeroes `al` anyway? – ead Apr 09 '17 at 18:41
  • Because `int fun()` does not provide that information. It could be anything, including `int fun(x, ...)` which is variadic. – Jester Apr 09 '17 at 19:07
  • Maybe I don't understand something. I think that the compiler deduces the function signature (if there is no parameter specification) from its usage. So if `fun` would be used as `fun(5)` that could mean two things either `fun(int x)` or `fun(int x, ...)`. However `fun()` could mean only `fun(void)`, because `fun(...)` is not valid in ISO C. So from this zeroing `al` should not be mandatory. Did I get it wrong? – ead Apr 09 '17 at 19:40
  • Ah yeah, true. I missed you actuall invoked it as `fun()`. Looks like a slight missed optimization then. – Jester Apr 09 '17 at 20:14
  • Somehow it is hard to believe, that gcc, clang and icc all missed this optimization.. – ead Apr 10 '17 at 09:11
  • This does not explain anything from the point of view of standard C. In C language calling a variadic function that has no previously declared full-blown *prototype* causes undefined behavor. It has been like this since C89/90. For this reason the compiler does not have to assume that a function declared only as `int fun()` has varargs. If it does so, it is a peculiarity of a specific ABI/implementation. A defensive measure, maybe, but a missed optimization as well. – AnT stands with Russia Jun 20 '17 at 16:24
  • In fact, a function declaration with an empty parameter type list *never* declares a variadic function. The compiler does not have to consider calls to the function to make this evaluation. This follows from the compatibility rules for function types in [paragraph 6.7.6.3/15 of the standard](http://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p15). As a practical matter, this makes sense because the calling convention for variadic functions may be different from the convention for non-variadic ones. – John Bollinger Jun 20 '17 at 16:27
  • This is probably related to [a Gnu C extension](https://gcc.gnu.org/onlinedocs/gcc/Function-Prototypes.html) that allows better interoperability with pre-ANSI code. It can't be dropped now without breaking backwards compatibility (but I guess the Gnu folks care less about that than the Microsoft camp whose terms I'm more accustomed to thinking on). – Cody Gray - on strike Jun 21 '17 at 01:56
5

Apparently it is a defensive measure, designed for situations when prototype-less fun function happens to actually be a variadic function, as explained by @Jester's answer.

Note though that this explanation does not hold any water from the point of view of standard C language.

Since the beginning of standardized times (C89/90) C language explicitly required all variadic functions to be declared with prototype before the point of the call. Calling a non-prototyped variadic function triggers undefined behavior in standard C. So, formally, compilers do not have to accommodate the possibility of fun being variadic - if it is, the behavior would be undefined anyway.

Moreover, as @John Bollinger noted in the comments, according to the C standard, a non-prototype int fun() declaration actually precludes further variadic prototype declarations of fun. I.e. a variadic function cannot be legally pre-declared as a () function. That would be another reason why the above non-prototype declaration is sufficient for the compiler to assume that fun cannot possibly be variadic.

This could actually be a legacy feature, designed to support pre-standard C code, where pre-declaring variadic functions with prototype was not required.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • I think [Cody's comment](https://stackoverflow.com/questions/41945951/differences-in-the-initialization-of-the-eax-register-when-calling-a-function-in#comment76316514_41946081) in reply to John's is the answer: As an extension to the ISO C rules, the GNU dialect of C doesn't out defining a variadic function after an `int fun();` declaration. – Peter Cordes Jul 03 '17 at 16:23