> On 8 Oct 2025, at 16:04, Jason Merrill <[email protected]> wrote:
>
> On 10/8/25 3:08 PM, Iain Sandoe wrote:
>>>> Otherwise, I’m looking for guidance on what’s needed to land this.
>>> IIRC the paper talks about a call to an unknown function having the desired
>>> semantics. I’ll go with that, making it not throwing, const but looping
>>> and leaf. We can make it stronger if necessary, weakening it after people
>>> rely on semantics is less useful IMO. On RTL we have nothing at all, I’ve
>>> heard ,volatile‘ asm is only about missing outputs, not side effects in
>>> general. So I’d expand to nothing on RTL, watching what breaks.
>> with a follow up to a query
>>> I suggest to remove the assert or replace it with one that checks that if
>>> ECF_LOOPING_CONST_OR_PURE is set the function is either ECF_CONST or
>>> ECF_PURE.
>> Done and re-tested on x86_64-darwin24 and powerpc64le-linux, OK for trunk
>> now?
>> thanks
>> Iain
>> --- 8< ---
>> P1494 provides a mechanism that serves to demarc epochs within the code
>> preventing UB-based optimisations from 'time traveling' across such
>> boundaries. The additional paper, P3641, alters the name of the function
>> to 'observable_checkpoint' which is the name used here.
>> This implementation maintains the observable function call through to
>> expand, where it produces no code.
>> PR c++/119060
>> gcc/ChangeLog:
>> * builtins.cc (expand_builtin): Handle BUILT_IN_OBSERVABLE_CHKPT.
>> * builtins.def (BUILT_IN_OBSERVABLE_CHKPT): New.
>> * tree.cc (build_common_builtin_nodes): Build observable
>> checkpoint builtin.
>> gcc/cp/ChangeLog:
>> * cp-gimplify.cc (cp_fold): Handle std::observable_checkpoint
>> specially, lowering it to the builtin.
>> * cxxapi-data.csv: Add observable_checkpoint to <utility>.
>> * std-name-hint.gperf: Add observable_checkpoint to <utility>.
>> * std-name-hint.h: Regenerate.
>> gcc/testsuite/ChangeLog:
>> * g++.dg/cpp26/observable-checkpoint.C: New test.
>> Signed-off-by: Iain Sandoe <[email protected]>
>> ---
>> gcc/builtins.cc | 4 +
>> gcc/builtins.def | 1 +
>> gcc/cp/cp-gimplify.cc | 16 ++
>> gcc/cp/cxxapi-data.csv | 1 +
>> gcc/cp/std-name-hint.gperf | 1 +
>> gcc/cp/std-name-hint.h | 142 +++++++++---------
>> .../g++.dg/cpp26/observable-checkpoint.C | 25 +++
>> gcc/tree.cc | 9 +-
>> 8 files changed, 128 insertions(+), 71 deletions(-)
>> create mode 100644 gcc/testsuite/g++.dg/cpp26/observable-checkpoint.C
>> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
>> index 78b561529f5..fb294ce58cd 100644
>> --- a/gcc/builtins.cc
>> +++ b/gcc/builtins.cc
>> @@ -8427,6 +8427,10 @@ expand_builtin (tree exp, rtx target, rtx subtarget,
>> machine_mode mode,
>> expand_builtin_unreachable ();
>> return const0_rtx;
>> + case BUILT_IN_OBSERVABLE_CHKPT:
>> + /* Generate no code. */
>> + return const0_rtx;
>> +
>> CASE_FLT_FN (BUILT_IN_SIGNBIT):
>> case BUILT_IN_SIGNBITD32:
>> case BUILT_IN_SIGNBITD64:
>> diff --git a/gcc/builtins.def b/gcc/builtins.def
>> index 3dc2333c6f2..7cd5353bcb1 100644
>> --- a/gcc/builtins.def
>> +++ b/gcc/builtins.def
>> @@ -1142,6 +1142,7 @@ DEF_C2Y_BUILTIN (BUILT_IN_ULABS, "ulabs",
>> BT_FN_ULONG_LONG, ATTR_CONST_NO
>> DEF_C2Y_BUILTIN (BUILT_IN_ULLABS, "ullabs",
>> BT_FN_ULONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST)
>> DEF_GCC_BUILTIN (BUILT_IN_UNREACHABLE_TRAP, "unreachable trap",
>> BT_FN_VOID, ATTR_CONST_NORETURN_NOTHROW_LEAF_COLD_LIST)
>> DEF_GCC_BUILTIN (BUILT_IN_UNREACHABLE, "unreachable", BT_FN_VOID,
>> ATTR_CONST_NORETURN_NOTHROW_LEAF_COLD_LIST)
>> +DEF_GCC_BUILTIN (BUILT_IN_OBSERVABLE_CHKPT, "observable_checkpoint",
>> BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
>> DEF_GCC_BUILTIN (BUILT_IN_UNWIND_INIT, "unwind_init", BT_FN_VOID,
>> ATTR_NULL)
>> DEF_GCC_BUILTIN (BUILT_IN_UPDATE_SETJMP_BUF, "update_setjmp_buf",
>> BT_FN_VOID_PTR, ATTR_NULL)
>> DEF_GCC_BUILTIN (BUILT_IN_VA_COPY, "va_copy",
>> BT_FN_VOID_VALIST_REF_VALIST_ARG, ATTR_NOTHROW_LEAF_LIST)
>> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
>> index 1662336e165..483d964bd62 100644
>> --- a/gcc/cp/cp-gimplify.cc
>> +++ b/gcc/cp/cp-gimplify.cc
>> @@ -3452,6 +3452,22 @@ cp_fold (tree x, fold_flags_t flags)
>> break;
>> }
>> + /* We need to convert std::observable_checkpoint () into its equivalent
>> + builtin for correctness, but must not rely on middle-end inlining
>> + to do this; special-case it here. */
>
> Why is this necessary? If it isn't inlined, it's still a call, and we talked
> about an unknown call having the desired semantics.
As I understand the intention of the paper, the function is not supposed to
generate any code - if, for some reason, it was not inlined then it would
generate a call.
> There doesn't seem to be a testcase for std::observable_checkpoint to test
> whether this change is needed.
I’d suppose we could just generate a case with -fno-inline and that would then
produce a call that survives optimisation.
The std library function should just resolve to the builtin.
Iain
>> +
>> + if (call_expr_nargs (x) == 0
>> + && decl_in_std_namespace_p (callee)
>> + && DECL_NAME (callee) != NULL_TREE
>> + && id_equal (DECL_NAME (callee), "observable_checkpoint"))
>> + {
>> + r = builtin_decl_explicit (BUILT_IN_OBSERVABLE_CHKPT);
>> + releasing_vec vec;
>> + r = finish_call_expr (r, &vec, false, false, tf_warning_or_error);
>> + x = cp_fold (r, flags);
>> + break;
>> + }
>> +