assembly c compiler-optimization language-lawyer ternary-operator

Is the conditional move optimization against the C standard?

It is a common optimization to use conditional move (assembly cmov) to optimize the conditional expression ?: in C. However, the C standard says:

The first operand is evaluated; there is a sequence point between its evaluation and the
evaluation of the second or third operand (whichever is evaluated). The second operand
is evaluated only if the first compares unequal to 0; the third operand is evaluated only if
the first compares equal to 0; the result is the value of the second or third operand
(whichever is evaluated), converted to the type described below.110)

For example, the following C code

#include <stdio.h>
int main() {
int a, b;
scanf("%d %d", &a, &b);
int c= a > b ? a + 1 : 2 + b;
printf("%d", c);
return 0;

will generate optimized related asm code as follows:

call    __isoc99_scanf
movl (%rsp), %esi
movl 4(%rsp), %ecx
movl $1, %edi
leal 2(%rcx), %eax
leal 1(%rsi), %edx
cmpl %ecx, %esi
movl $.LC1, %esi
cmovle %eax, %edx
xorl %eax, %eax
call __printf_chk

According to the standard, the conditional expression will only have one branch evaluated. But here both branches are evaluated, which is against the standard’s semantics. Is this optimization against the C standard? Or do many compiler optimizations have something inconsistent with the language standard?

The optimization is legal, due to the “as-if rule”, i.e. C11

A conforming implementation is just required to produce a program that when run produces the same observable behaviour as the execution of the program using the abstract semantics would have produced. The rest of the standard just describes these abstract semantics.

What the compiled program does internally does not matter at all, the only thing that matters is that when the program ends it does not have any other observable behaviour, except reading the a and b and printing the value of a + 1 or b + 2 depending on which one a or bis greater, unless something occurs that causes the behaviour be undefined. (Bad input causes a, b be uninitialized and therefore accesses undefined; range error and signed overflow can occur too.) If undefined behaviour occurs, then all bets are off.

Since accesses to volatile variables must be evaluated strictly according to the abstract semantics, you can get rid of the conditional move by using volatile here:

#include <stdio.h>
int main() {
volatile int a, b;
scanf("%d %d", &a, &b);
int c = a > b ? a + 1 : 2 + b;
printf("%d", c);
return 0;

compiles to

        call    [email protected]
movl (%rsp), %edx
movl 4(%rsp), %eax
cmpl %eax, %edx
jg .L7
movl 4(%rsp), %edx
addl $2, %edx
leaq .LC1(%rip), %rsi
xorl %eax, %eax
movl $1, %edi
call [email protected]
movl (%rsp), %edx
addl $1, %edx
jmp .L3

by my GCC Ubuntu 7.2.0-8ubuntu3.2