> On 5 Sep 2025, at 08:59, Jakub Jelinek <ja...@redhat.com> wrote:
> 
> On Fri, Sep 05, 2025 at 08:40:27AM +0100, Iain Sandoe wrote:
>>> On 5 Sep 2025, at 08:22, Jakub Jelinek <ja...@redhat.com> wrote:
>>> 
>>> On Fri, Sep 05, 2025 at 08:35:43AM +0200, Richard Biener wrote:
>>>>> i.e. keep it as a call to an optimization barrier until runtime
>>>>> but don't really do anything at that point.
>>>> 
>>>> That's probably equivalent to the builtin.
>>> 
>>> But to a builtin which is preserved until end of expansion (not sure
>>> in what form, we can easily have just inline asm for optimization
>>> barriers in RTL, or perhaps emit a CALL_INSN and somehow optimize
>>> it away during final.
>>> The reason to use inline function was just not to commit to it
>>> as ABI for now, of course we could just also export a dummy
>>> nop function from libstdc++.so and just use some attributes on it.
>> 
>> One motivation for this paper landing was to allow these epoch markers
>> to be used between contract checks, to prevent time-traveling UB from
>> a later check eliding an earlier one that might have been there to protect
>> against said UB.
>> 
>> That means that a library-only solution is very tricky, since we need to
>> implement contract-checks in code without including any system headers.
>> (so a built-in is preferable).
> 
> I must say I don't understand this.
> If it is a builtin, unless the FE does some magic to turn all
> std::observable_checkpoint () calls into that builtin immediately
> (like it has magic to handle say std::move and a few others),

There are two cases here -
1) that the FE wants to be able to generate these checkpoints during code-gen
2) the lowering of the library function

As you say, the current patch does not handle 2) specially, and although
it DTRT on the cases I’ve tested, the conditions you mention below could
defeat that;  IMO the solution is to treat it specially as per move et al.

Iain

> it will
> still be
> namespace std {
>  [[__gnu__::__always_inline__]] inline void
>  observable_checkpoint() { return __builtin_observable_checkpoint(); }
> }
> and so the FE will see it as a call to some function rather than a builtin
> (unless you want users to use the builtin directly).
> So, from FE POV of view, what is the difference if it is that or just
> namespace std {
>  [[__gnu__::__noipa__]] void observable_checkpoint();
> }
> defined in libstdc++.so?
> Only GIMPLE after einline pass would be able to see the exact builtin,
> and obviously one can call std::observable_checkpoint() in any function
> you want (unless standard describes the function), including functions
> the compiler doesn't know about (other TU without LTO, noipa).
> 
> If the builtin brings some advantage, sure, it can be handled like that
> too, not against it, but it needs to be lowered into some RTL that will
> be handled like an external call as well.  If it is a call, you get
> it automatically.  Of course, it could be a builtin in GIMPLE and
> lowered into CALL_INSN of _ZSt21observable_checkpointv with that dummy
> function exported from libstdc++.so, and perhaps during final attempt
> to assemble it as nothing if possible by magic.  Though I'm afraid that
> depends on the ABI.  For e.g. x86_64 normal ABI, just optimizing away
> the CALL_INSN for the specific function if it takes no arguments and doesn't
> return anything might work, but what about ABIs where calls also pop up
> stack or something similar (though, the question is if that matters for
> calls with no arguments)?
> 
>       Jakub
> 

Reply via email to