2

intro.abstract p8 says:

The following specify the observable behavior of the program:

  • Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine.
  • Data is delivered to the host environment to be written into files (See also: ISO/IEC 9899:2024, 7.23.3).
  • The input and output dynamics of interactive devices shall take place in such a fashion that prompting output is actually delivered before a program waits for input. What constitutes an interactive device is implementation-defined.

In other words, the behaviors not mentioned in this list are not the C++'s observable behaviors. Consider this example:

// "INT 3"
void call_asm_int_3(){
   asm("INT $3");
}
int main(){
  std::this_thread::sleep_for(std::chrono::minutes(10));
  call_asm_int_3();
}

The specification of sleep_for is thread.thread.this p7:

Effects: Blocks the calling thread for the relative timeout ([thread.req.timing]) specified by rel_time.

Synchronization: None.

It doesn't have the actual observable behavior defined in [intro.abstract] p8. In addition, the function call_asm_int_3 has the effect that hitting a breakpoint, whose external effect can be observed by a human; however, it is also not the C++ defined observable behavior.

So, I wonder, can conforming implementations remove or reorder these functions?

8
  • It is clear what is removing. A function can be optimized out. But what do you mean by reordering? Changing the order of the calls, order of execution? Something else? Note that reordering the execution order requires the analysis used to make sure there are no side effects, and that is a very non-trivial issue. Commented Nov 21 at 7:26
  • In general, as soon as you use assembler, the compiler doesn't do any optimisation Commented Nov 21 at 7:48
  • 2
    @AlanBirtles It's a practical approach, same as treating volatiles as more-or-less atomics, or treating UB type puns to integers in a sane way. This question seems to be implying some imaginary adversary implementation what strives to screw its users while still being totally conforming to C++ standard. Commented Nov 21 at 8:05
  • 1
    The language standard doesn't say what the effects of using asm are, only that an implementation might define it (or not). eel.is/c++draft/dcl.asm Commented Nov 21 at 9:09
  • @BoP The implementation can define asm("INT $3") as hitting a breakpoint; however, it's still not the C++ defined observable behavior. Commented Nov 24 at 1:56

1 Answer 1

2

So, I wonder, can conforming implementations remove or reorder these functions?

I would say yes following the as-if rule.

  • until C++26:

The C++ compiler is permitted to perform any changes to the program as long as given the same input, the observable behavior of the program is one of the possible observable behaviors corresponding to that input.

However, if certain input will result in undefined behavior, the compiler cannot guarantee any observable behavior of the program with that input, even if any operation of the observable behavior happens before any possible undefined operation.

And since C++26

A program may contain observable checkpoints .

An operation OP is undefined-free if for every undefined operation U, there is an observable checkpoint CP such that OP happens before CP and CP happens before U. The defined prefix of the program with a given input comprises all its undefined-free operations.

The C++ compiler is permitted to perform any changes to the program as long as given the same input, the observable behavior of the defined prefix of the program is one of the possible observable behaviors corresponding to that defined prefix.

If certain input will result in undefined behavior, the compiler cannot guarantee any observable behavior of the program with that input that does not belong to the defined prefix.

Now compilers try to be usable and give "expected" behavior, so might treat more stuff as observable. In addition, any "unknown" functions should be treated as-if they might have observable behaviors.

Timing is indeed not an observable behavior, making optimization possible and benchmarking more complicated.

Sign up to request clarification or add additional context in comments.

4 Comments

If timing is not an "observable behaviour", would an implementation that just ignores all calls to std::this_thread::sleep_for be OK? While clearly the defined effect of sleep_for will not happen in this case, not having that effect seems to not change "observable behaviour".
While an implementation that just ignores all calls to std::this_thread::sleep_for would be conformant, it would not be OK for users.
My interpretation: According to the standard "The relative difference in durations between those reported by a given clock and the SI definition is a measure of the quality of implementation." For all we know, any sleep function simply adds to an internal counter that acts as the implementation's steady_clock. As long as the observed time values across threads are kept logically consistent, real world time does not factor into it.
AFAIK, the current wording and the wording before C++26 are basically the same. Even the observable behavior has a strict definition in C++26; in other words, "the least requirement" was removed from C++26.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.