The ternary conditional operator in C/C++ has the following syntax:
expression ? expression : expression
Here is an example:
const char* f (int a) {
return a==10 ? "it is ten" : "it is not ten"; };
12.3.1
x86
Old and non-optimizing compilers generate assembly code just as if anif/elsestatement was used: Listing 12.18: Non-optimizing MSVC 2008
$SG746 DB 'it is ten', 00H $SG747 DB 'it is not ten', 00H
tv65 = -4 ; this will be used as a temporary variable _a$ = 8
_f PROC
push ebp mov ebp, esp push ecx
; compare input value with 10
cmp DWORD PTR _a$[ebp], 10 ; jump to $LN3@f if not equal
jne SHORT $LN3@f
; store pointer to the string into temporary variable:
mov DWORD PTR tv65[ebp], OFFSET $SG746 ; 'it is ten' ; jump to exit
jmp SHORT $LN4@f $LN3@f:
; store pointer to the string into temporary variable:
mov DWORD PTR tv65[ebp], OFFSET $SG747 ; 'it is not ten' $LN4@f:
; this is exit. copy pointer to the string from temporary variable to EAX. mov eax, DWORD PTR tv65[ebp]
mov esp, ebp pop ebp
ret 0
_f ENDP
Listing 12.19: Optimizing MSVC 2008
$SG792 DB 'it is ten', 00H $SG793 DB 'it is not ten', 00H _a$ = 8 ; size = 4
_f PROC
; compare input value with 10
cmp DWORD PTR _a$[esp-4], 10
mov eax, OFFSET $SG792 ; 'it is ten' ; jump to $LN4@f if equal
je SHORT $LN4@f
mov eax, OFFSET $SG793 ; 'it is not ten' $LN4@f:
ret 0
_f ENDP
Newer compilers are more concise:
Listing 12.20: Optimizing MSVC 2012 x64
$SG1355 DB 'it is ten', 00H $SG1356 DB 'it is not ten', 00H a$ = 8
f PROC
; load pointers to the both strings
lea rdx, OFFSET FLAT:$SG1355 ; 'it is ten' lea rax, OFFSET FLAT:$SG1356 ; 'it is not ten' ; compare input value with 10
cmp ecx, 10
; if equal, copy value from RDX ("it is ten")
; if not, do nothing. pointer to the string "it is not ten" is still in RAX as for now. cmove rax, rdx
ret 0
f ENDP
12.3.2
ARM
Optimizing Keil for ARM mode also uses the conditional instructionsADRcc:
Listing 12.21: Optimizing Keil 6/2013 (ARM mode)
f PROC
; compare input value with 10 CMP r0,#0xa
; if comparison result is EQual, copy pointer to the "it is ten" string into R0 ADREQ r0,|L0.16| ; "it is ten"
; if comparison result is Not Equal, copy pointer to the "it is not ten" string into R0 ADRNE r0,|L0.28| ; "it is not ten"
BX lr
ENDP |L0.16|
DCB "it is ten",0 |L0.28|
DCB "it is not ten",0
Without manual intervention, the two instructionsADREQandADRNEcannot be executed in the same run.
Optimizing Keil for Thumb mode needs to use conditional jump instructions, since there are no load instructions that support conditional flags:
Listing 12.22: Optimizing Keil 6/2013 (Thumb mode)
f PROC
; compare input value with 10 CMP r0,#0xa ; jump to |L0.8| if EQual
BEQ |L0.8|
ADR r0,|L0.12| ; "it is not ten"
BX lr
|L0.8|
ADR r0,|L0.28| ; "it is ten"
BX lr
ENDP |L0.12|
DCB "it is not ten",0 |L0.28|
DCB "it is ten",0
12.3.3
ARM64
Optimizing GCC (Linaro) 4.9 for ARM64 also uses conditional jumps:
Listing 12.23: Optimizing GCC (Linaro) 4.9
f:
cmp x0, 10
beq .L3 ; branch if equal adrp x0, .LC1 ; "it is ten" add x0, x0, :lo12:.LC1
ret .L3:
adrp x0, .LC0 ; "it is not ten" add x0, x0, :lo12:.LC0
ret .LC0:
.string "it is ten" .LC1:
.string "it is not ten"
That is because ARM64 does not have a simple load instruction with conditional flags, likeADRccin 32-bit ARM mode or CMOVcc in x86 [ARM13a, p390, C5.5]. It has, however, “Conditional SELect” instruction (CSEL), but GCC 4.9 does not seem to be smart enough to use it in such piece of code.
12.3.4
MIPS
Unfortunately, GCC 4.4.5 for MIPS is not very smart, either:
Listing 12.24: Optimizing GCC 4.4.5 (assembly output)
$LC0:
.ascii "it is not ten\000" $LC1:
.ascii "it is ten\000" f:
li $2,10 # 0xa
; compare $a0 and 10, jump if equal: beq $4,$2,$L2
nop ; branch delay slot
; leave address of "it is not ten" string in $v0 and return: lui $2,%hi($LC0)
j $31
addiu $2,$2,%lo($LC0) $L2:
; leave address of "it is ten" string in $v0 and return: lui $2,%hi($LC1)
j $31
addiu $2,$2,%lo($LC1)
12.3.5
Let’s rewrite it in anif/elseway
const char* f (int a){
if (a==10)
return "it is ten"; else
return "it is not ten"; };
Interestingly, optimizing GCC 4.8 for x86 was also able to useCMOVccin this case: Listing 12.25: Optimizing GCC 4.8
.LC0:
.string "it is ten" .LC1:
.string "it is not ten" f:
.LFB0:
; compare input value with 10
cmp DWORD PTR [esp+4], 10
mov edx, OFFSET FLAT:.LC1 ; "it is not ten" mov eax, OFFSET FLAT:.LC0 ; "it is ten"
; if comparison result is Not Equal, copy EDX value to EAX ; if not, do nothing
cmovne eax, edx ret
Optimizing Keil in ARM mode generates code identical to listing.12.21. But the optimizing MSVC 2012 is not that good (yet).