It looks like defect report 1400: Function pointer equality deals with this issue and seems to me to say that it is okay to do this optimization but as comments indicate, there is disagreement. It says (emphasis mine):
According to 5.10 [expr.eq] paragraph 2, two function pointers only
compare equal if they point to the same function. However, as an
optimization, implementations are currently aliasing functions that
have identical definitions. It is not clear whether the Standard needs
to deal explicitly with this optimization or not.
and the response was:
The Standard is clear on the requirements, and implementations are
free to optimize within the constraints of the “as-if” rule.
The question is asking about two issues:
- Is it okay for these pointers to be considered equal
- Is it okay to coalesce the functions
Based on comments I see two interpretations of the response:
This optimization is ok, the standard gives the implementation this freedom under the as-if rule. The as-if rule is covered in section 1.9
and means the implementation only has to emulate the observable behavior with respect to the requirements of the standard. This is still my interpretation of the response.
The issue is at hand is completely ignored and the statement merely says no adjustment to the standard is required because clearly the as-if rules covers this but the interpretation is left as an exercise to the reader. Although I acknowledge due to the terseness of the response I can not dismiss this view, it ends up being a totally unhelpful response. It also seems inconsistent with the responses in the other NAD
issues which as far as I can tell point out issue if they exist.
What the draft standard says
Since we know we are dealing with the as-if rule, we can start there and note that section 1.8
says:
Unless an object is a bit-field or a base class subobject of zero
size, the address of that object is the address of the first byte it
occupies. Two objects that are not bit-fields may have the same
address if one is a subobject of the other, or if at least one is a
base class subobject of zero size and they are of different types;
otherwise, they shall have distinct addresses.4
and note 4
says:
Under the “as-if” rule an implementation is allowed to store two
objects at the same machine address or not store an object at all if
the program cannot observe the difference
but a note from that section says:
A function is not an object, regardless of whether or not it occupies
storage in the way that objects do
although it is not normative, the requirements for an object laid out in paragraph 1
do not make sense in the context of a function and so it is consistent with this note. So we are explicitly restricted from aliasing objects with some exceptions but not such restriction applies to functions.
Next we have section 5.10
Equality operators which says (emphasis mine):
[...]Two pointers compare equal if they are both null, both point to
the same function, or both represent the same address (3.9.2),
otherwise they compare unequal.
which tells us two pointers are equal if they are:
- Null pointers
- Point to the same function
- Represent the same address
The or both represent the same address seems to give enough latitude to allow a compiler to alias two different functions and does not require pointers to different functions to compare unequal.
Observations
Keith Thompson has made some great observations that I feel are worth adding to the answer since they get to core issues involved, he says:
If a program prints the result of &foo == &bar, that's observable behavior; the optimization in question changes the observable behavior.
which I agree with and if we could shows that there is a requirement for the pointers to be unequal that would indeed violate the as-if rule but so far we can not show that.
and:
[...]consider a program that defines empty function and uses their
addresses as unique values (think about SIG_DFL, SIG_ERR, and SIG_IGN
in <signal.h> / <csignal>). Assigning them the same address would
break such a program
As I noted in my comment the C standard requires these macros to generate distinct values, from 7.14
in C11:
[...]which expand to constant expressions with distinct values that
have type compatible with the second argument to, and the return value
of, the signal function, and whose values compare unequal to the
address of any declarable function[...]
So although this case is covered perhaps there are other cases that would make this optimization dangerous.
Update
Jan Hubička a gcc
developer wrote a blog post Link time and inter-procedural optimization improvements in GCC 5, code folding was one of many topics he covered.
I asked him to comment on whether folding identical functions to the same address was conforming behavior or not and he says it is not conforming behavior and indeed such an optimization would break gcc
itself:
It is not conforming to turn two functions to have same address, so MSVC is quite aggressive here. Doing so, for example, breaks GCC itself because to my surprise address compare is done in the precompiled headers code. It works for many other projects, including Firefox.
In hindsight, after months more of reading defect reports and thinking about optimization issues, I am biased towards a more conservative reading of the committee's response. Taking the address of a function is observable behavior and therefore folding identical functions would violate the as-if rule.
Update 2
Also see this llvm-dev discussion: Zero length function pointer equality:
This is a well-known conformance-violating bug in link.exe; LLVM should not
be making things worse by introducing a similar bug itself. Smarter linkers
(for example, I think both lld and gold) will do identical function
combining only if all but one of the function symbols is only used as the
target of calls (and not to actually observe the address). And yes, this
non-conforming behavior (rarely) breaks things in practice. See this
research paper.
--icf [none,all,safe] Identical Code Folding. '--icf=safe' Folds ctors, dtors and functions whose pointers are definitely not taken.
– Hypallagesafe
setting. Really, you could always fold, except when you ask for an address possibly spawn extra copies (or use a jump table). I'm wondering if MSVC is technically legal. – Woodfordsafe
setting because they knowall
is illegal ( research.google.com/pubs/pub36912.html ). But I don't want to participate in this legal dispute. – Hypallageconst
data that are logically distinct are being folded into one: the standard is very clear that distinct objects have distinct addresses, so that is illegal. Only after that illegal fold do the two functions implementations become the same, and function-level ICF causes the two functions to have the same value. This question is "is the function-level fold legal". – Woodford