Obviously, if the code does modify a
and b
the semantic of the first and the last two functions is entirely different; that would obviously force the compiler to generate different code; besides this, if the three functions actually acted the same in every circumstance in a given program, then of course the compiler is free to reduce all them to a single function thanks to the "as if" rule.
But then this question wouldn't make much sense - or at least, asking about specific differences in binary code generation (a very specific implementation detail) in a "perfect system" (something that cannot exist) to me isn't really meaningful.
To get back to real systems, first of all we must consider inlining; if the functions are inlined, then they get mixed with the rest of the code of the parent function and the output may be different in each expansion (there can be different register allocation, different instruction intermixing to maximize pipeline utilization, ...)
So, the comparison should be about "standalone" output for each function.
I would expect the second and the third one to expand to almost the same exact same code: references are syntactic sugar for pointers, and the fact that you cannot have a NULL
reference is already taken into account by the fact that *a
and *b
are probably dereferenced without checks in Bar
(which tells the optimizer to assume that they won't ever be NULL
). Also, I don't know of any C++ ABI that differentiates pointers from references.
As for Foo
, it would depend on many factors:
- if we are compiling a library, the compiler isn't free to do whatever it wants, and functions must adhere to some ABI; for this reason, first of all the parameters would actually be pointers in the last two cases and values in the first one (with varying consequences depending from the platform ABI);
- this can hold also if the compiler isn't capable of LTCG and we are expecting to use these functions from other modules (i.e. the functions aren't marked as
static
); - in the last two cases, to generate the same output the compiler may be required to prove that the references/pointers point to different values to generate the same output as for
Foo
; - also, it must be able to prove that
a
andb
aren't changed throughout the function; in particular, after each external (=non-completely-inlined) function call the pointed objects may have changed; both of these can be complicated tasks, and, again, they may require LTCG if the program is made of several modules.
So what I actually expect to happen is:
- for standalone versions,
Foo!= Bar
andFooBar
;Bar==FooBar
; - as for inlined versions, the compiler will probably have a simpler time to determine if the conditions for "converting"
Bar
andFooBar
to the same semantic ofFoo
, but of course the fact that the generated code is intermixed with code of a different function will result in different assembly output (to the point that it may be difficult to understand where starts/ends the code of the subroutine).