C++ Logo

std-discussion

Advanced search

Re: Guarantees over addresses from function pointers created from lambda

From: Federico Kircheis <federico_at_[hidden]>
Date: Mon, 28 Apr 2025 15:55:34 +0200
On 28/04/2025 2:51 pm, Jennifier Burnett wrote:
>> If I use in my code get3 instead of get1 the lifetime of T does not begin.
>
> Yes, if you did a ctrl-f replace of "get1" with "get3" that would be incorrect. That's not what we're talking about though.
>
> What we are talking about is if the compiler can, after having generated the ASSEMBLY for get1 and get3, eliminate the definition of one of them and redirect the label to the other definition.
>
> At this point we are well beyond the boundaries of C++ and into the machine, which doesn't have any concepts of "lifetime" or "type", everything is just bytes.

Mhm, OK, I did not think it this way...

> If you were able to tell the difference between the compiler running one function and another then merging the two definitions would be immediately incorrect, no further discussion needed. The compiler might merge the two functions is specifically because there is no difference between the two of them when fed into the CPU.
>
> For example, written in aarch64 assembly the three functions would look like:
>
> ```
> get1:
> ret
> get2:
> ret
> get3:
> ret
> ```
>
> What you are suggesting is that jumping to each of those 3 labels will produce different behaviour, which you can tell visually is incorrect.
>
>>From the documentation I've read, what merges functions/function pointers together might also merge constants
>
> No. This is why [[no_unique_address]] exists. All objects (except empty base classes) will have addresses which are unique amongst all other objects that are not marked [[no_unique_address]].

[[no_unique_address]] is a hint to the compiler, but just like the
compiler folds function pointer together, it could do so for other things.
At least the msvc documentation mentions it explicitly:

https://learn.microsoft.com/en-us/cpp/build/reference/opt-optimizations?view=msvc-170

----
Because /OPT:ICF can cause the same address to be assigned to different 
functions or read-only data members (that is, const variables when 
compiled by using /Gy), it can break a program that depends on unique 
addresses for functions or read-only data members. For more information, 
see /Gy (Enable Function-Level Linking).
----
I thought I had another resource for another compiler/linker but cannot 
find it anymore, maybe I misremember and msvc is the only one.
>> Yes, but I do not see any difference if the hand-made vtable is in the type-erased class like I did ("inline") or somewhere else ("outline") as you proposed.
> 
> There is no difference in usage. What I am proposing is that you use the address of the vtable itself to uniquely identify each type rather than the address of one of the functions in the vtable like in your original example, since as stated above each vtable will have a unique address that couldn't be confused with any of the others.
Ah OK, I did not realize that you suggested to use the address of the 
vtable, I thought you wanted to add a token in the vtable and compare that.
Yes, that should reduce the chance that they get merged together.
But since the rules for globals having unique addresses are the same as 
for functions having unique addresses, I fear that using a global object 
as tag is as effective as using a function pointer as tag.
 From a logical perspective, if two identical functions share the same 
space, why shouldn't two constants?
I also believe that for most use-cases, folding constants together is a 
sensible choice, just like for functions.

Received on 2025-04-28 13:55:42