2

I compile this simple C code with GCC:

int foo(int *a, int *b)
{
    *a += 1;
    *b += 1;
    *a += 1;
}

int foo2(int *a, int *b)
{
    *a += 1;
    *b += 1;
    *a += 1;
}

and it produces the following assembly code:

foo:
    add     DWORD PTR [rdi], 1
    add     DWORD PTR [rsi], 1
    add     DWORD PTR [rdi], 1
    ret
foo2:
    add     DWORD PTR [rdi], 1
    xor     eax, eax
    add     DWORD PTR [rsi], 1
    add     DWORD PTR [rdi], 1
    ret

see godbolt

What is the point of this xor instruction? In C++, GCC does not insert it and clang never does.

poypoy
  • 133
  • 1
  • 7
  • 2
    I think this is because your `foo2` doesn't properly return anything. This should be a warning. – tadman Aug 18 '23 at 16:20
  • @tadman No ____ – ikegami Aug 18 '23 at 16:25
  • I'd suspect it's the compiler clearing out the return value register as you do not have a `return` here even though *you promised to `return` an `int`*. If I make these functions `void` the `xor` operation goes away. – tadman Aug 18 '23 at 16:25
  • @tadman I understand what the xor does, but I don't understand why is it here. Yes the lack of return is a warning and the code is buggy, but why GCC/clang and C/C++ does not have the same behavior? – poypoy Aug 18 '23 at 16:26
  • @tadman Both `foo` and `foo2` have that bug. Why do they compile differently? – Barmar Aug 18 '23 at 16:26
  • @Barmar It's inconsistent behaviour in the compiler. `return 0` makes both `xor` the same, but `void` functions have no `xor` at all. – tadman Aug 18 '23 at 16:27
  • @RetiredNinja: The C standard does not prohibit a function with non-void return type from flowing to the end of its closing brace. The code shown has behavior defined by the C standard, and the compiler is required to compile it to behavior specified by the standard. – Eric Postpischil Aug 18 '23 at 16:27
  • It is interesting that, when the functions are swapped in the source code, the compiler puts the `XOR` in the second one, not the first. This is some GCC quirk, not something induced by the C standard. – Eric Postpischil Aug 18 '23 at 16:28
  • @ikegami: There is no undefined behavior in the code shown. See my comment above about non-void functions being allowed to terminate by flowing to their closing braces. – Eric Postpischil Aug 18 '23 at 16:29
  • @EricPostpischil yeah, just saw. Deleted comment answer. Do you know where that is in the spc? – ikegami Aug 18 '23 at 16:30
  • @EricPostpischil OK. Although if the caller uses the value, there's UB. http://port70.net/~nsz/c/c11/n1570.html#6.9.1p12 – Barmar Aug 18 '23 at 16:33
  • 2
    @ikegami: There is nothing that explicitly says a non-void function can flow to its closing `}`; that is just part of the overall specification of the language. You can take it as part of C 2018 6.9 11: “After all parameters have been assigned, the compound statement that constitutes the body of the function definition is executed.” C 2018 6.9.1 12 indicates there is a problem specifically if the value of the function is used: “Unless otherwise specified, if the `}` that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.” – Eric Postpischil Aug 18 '23 at 16:34
  • @Barmar: Sure, but that is not relevant to this question. The `xor eax, eax` appears to be superfluous to the function, both because we see no need for it in the semantics specified by the C standard and because GCC omits it if the function is the first in the translation unit. So, if GCC keeps it in, that appears to be a failure of its optimization feature. – Eric Postpischil Aug 18 '23 at 16:36
  • 1
    Interesting: changing to `-O1` gets rid of that instruction, but it appears in `-O2` and `-O3`. So it's a minor bug in the optimizer. – Barmar Aug 18 '23 at 16:37
  • @Eric Postpischil thanks – ikegami Aug 18 '23 at 16:46
  • Gcc is less comfortable with indeterminate values than clang and it sometimes annoyingly zeroes them (`xor %eax, %eax` <=> `int myreturn_value = 0;`). It especially tends to do this if the indeterminate is explicitly spelled out as in `int r; return r;`. Clang does nothing for this as it should. Gcc does `return 0;`. https://godbolt.org/z/zf4Y43Mqc – Petr Skocik Aug 18 '23 at 16:50
  • UB is UB. I would not fill the report as it is useless. – 0___________ Aug 18 '23 at 17:17
  • @0___________: There is no undefined behavior in the code shown. The C standard allows a non-void function to return by flowing to its closing brace. – Eric Postpischil Aug 18 '23 at 17:40

1 Answers1

1

This looks like a GCC optimizer bug. Reducing the optimization level to -O1 generates the same code for both functions, without the xor instruction.

And if you look at the color coding in Godbolt, it's unable to associate the statements in the second funcction with assembly code lines. It just colors the function header line with the entire body in the generated code.

That instruction sets the return value of the function. Since the function doesn't have a return statement, it can return any value (according to Section 6.9.12(12) it's undefined behavior to use the returned value); foo returns whatever happens to be in eax, while foo2 will always return 0. So the bug doesn't have any impact on the correctness of the generated code. It's weird that it's inconsistent, but not a violation of the standard.

Barmar
  • 741,623
  • 53
  • 500
  • 612