On 09/11/15 14:56, Richard Earnshaw wrote: > On 09/11/15 14:29, Andrew Haley wrote: >> On 11/09/2015 02:16 PM, Florian Weimer wrote: >>> On 11/09/2015 03:08 PM, Andrew Haley wrote: >>>> On 11/09/2015 12:09 PM, Florian Weimer wrote: >>>>> On 11/09/2015 11:11 AM, Andrew Haley wrote: >>>>>> On 08/11/15 19:34, Segher Boessenkool wrote: >>>>>>> The compiler is free to transform it to >>>>>>> >>>>>>> int foo(int x) { >>>>>>> int t = x*x*x; >>>>>>> if (x > 1290) { >>>>>>> printf("X is wrong here %d, but we don't care\n", x); >>>>>>> } >>>>>>> return t; >>>>>>> } >>>>>>> >>>>>>> because x*x*x does not have any observable behaviour, and then it is >>>>>>> obvious it _can_ remove the printf and conditional. >>>>> >>>>> I'm not sure if this is a valid transformation for printf, even if >>>>> targets stdout and does not use any custom format specifiers. Isn't it >>>>> a cancellation point? But let's assume it's not. >>>>> >>>>>> Yes, that is correct. And, indeed, the hardware is free to do taht >>>>>> too. With speculative execution, the "as if" rule is not limited to >>>>>> the compiler. >>>>> >>>>> Can we disallow that optimization as a quality-of-implementation matter? >>>>> What would be the benefit of such optimizations, other than >>>>> discouraging programmers from using C or C++? >>>> >>>> There isn't really any way to distinguish between wanted optimizations >>>> and unwanted ones. >>> >>> Of course there is—you define the semantics you want, and then any >>> optimization which breaks them is a bug. >>> >>>> If GCC determines that a statement is unreachable >>>> it can be deleted, and this depends on its knowledge of UB. Like this: >>>> >>>> void foo(int b) { >>>> if (b > 0) { >>>> int m = b * 3 / 6; >>>> if (m < 0) >>>> die(); >>>> } >>>> } >>>> } >>>> >>>> Deleting such unreachable code happens all the time. IMO we should >>>> not disable this optimization. >>> >>> This is very different from the printf example. The call to die is >>> unreachable according to the standard semantics. The original printf >>> call is reachable, and according to my interpretation, the >>> transformation shown above is invalid because the abstract machine >>> performs the side effect from the printf before undefined behavior is >>> reached. >> >> Here it is again: >> >> int foo(int x) { >> if (x > 1290) { >> printf("X is wrong here %d, but we don't care\n", x); >> } >> return x*x*x; >> >> Here, the printf writes to a stream then the UB happens. > > > Not if setvbuf has been used to make the stream unbuffered. Sorry, that shouldn't appear here... > >> But the >> stream is buffered and the UB kills the process before the stream is >> flushed. There is nothing in the C specification to prevent this, and >> neither should there be. I don't think it's even possible. ... it should be here. If the stream is unbuffered, then the ordering is certainly well defined. R. >> >>>>> I'm worried that this particular line of argument would also allow the >>>>> movement of undefined behavior which occurs after an infinite loop in >>>>> front of it, even if this loop performs I/O. >>>> >>>> Sure. But it can already do that even if the compiler does not move >>>> anything. The I/O writes to a stream, the UB causes a segfault which >>>> kills a process, the stream never gets written. >>> >>> Based on my C semantics, the UB is never reached because the loop never >>> exits. >> >> a. You have your own C semantics? >> b. Which loop? You need to let us look at it. >> >> I don't think that there is a program which will exhibit such >> behaviour. >> >>> I just think that C semantics which only deal with terminating programs, >>> Turing-machine-style, are not very useful for the programs we generally >>> write. >> >> This is an incomprehensible statement. At leas, I don't know what it >> means. >> >> Andrew. >> >