1.15. SWITCH()/CASE/DEFAULT
This implies that switch() is like syntactic sugar for a large number of nested if()s.
There is nothing especially new to us in the generated code, with the exception of the compiler moving
input variableato a temporary local variabletv64^92.
If we compile this in GCC 4.4.1, we’ll get almost the same result, even with maximal optimization turned
on (-O3option).
Optimizing MSVC
Now let’s turn on optimization in MSVC (/Ox):cl 1.c /Fa1.asm /Ox
Listing 1.150: MSVC
_a$ = 8 ; size = 4
_f PROC
mov eax, DWORD PTR _a$[esp-4]
sub eax, 0
je SHORT $LN4@f
sub eax, 1
je SHORT $LN3@f
sub eax, 1
je SHORT $LN2@f
mov DWORD PTR _a$[esp-4], OFFSET $SG791 ; 'something unknown', 0aH, 00H
jmp _printf
$LN2@f:
mov DWORD PTR _a$[esp-4], OFFSET $SG789 ; 'two', 0aH, 00H
jmp _printf
$LN3@f:
mov DWORD PTR _a$[esp-4], OFFSET $SG787 ; 'one', 0aH, 00H
jmp _printf
$LN4@f:
mov DWORD PTR _a$[esp-4], OFFSET $SG785 ; 'zero', 0aH, 00H
jmp _printf
_f ENDP
Here we can see some dirty hacks.
First: the value ofais placed inEAXand 0 is subtracted from it. Sounds absurd, but it is done to check if
the value inEAXis 0. If yes, theZFflag is to be set (e.g. subtracting from 0 is 0) and the first conditional
jumpJE(Jump if Equalor synonymJZ—Jump if Zero) is to be triggered and control flow is to be passed
to the$LN4@flabel, where the'zero'message is being printed. If the first jump doesn’t get triggered,
1 is subtracted from the input value and if at some stage the result is 0, the corresponding jump is to be
triggered.
And if no jump gets triggered at all, the control flow passes toprintf()with string argument
'something unknown'.
Second: weseesomethingunusualforus: astringpointerisplacedintotheavariable, andthenprintf()
is called not viaCALL, but viaJMP. There is a simple explanation for that: thecallerpushes a value to the
stack and calls our function viaCALL.CALLitself pushes the return address (RA) to the stack and does an
unconditional jump to our function address. Our function at any point of execution (since it do not contain
any instruction that moves the stack pointer) has the following stack layout:
- ESP—points toRA
- ESP+4—points to theavariable
On the other side, when we have to callprintf()here we need exactly the same stack layout, except
for the firstprintf()argument, which needs to point to the string. And that is what our code does.
It replaces the function’s first argument with the address of the string and jumps toprintf(), as if we
didn’t call our functionf(), but directlyprintf().printf()prints a string tostdoutand then executes
theRETinstruction, which POPsRAfrom the stack and control flow is returned not tof()but rather to
f()’scaller, bypassing the end of thef()function.
All this is possible becauseprintf()is called right at the end of thef()function in all cases. In some
way, it is similar to thelongjmp()^93 function. And of course, it is all done for the sake of speed.
(^92) Local variables in stack are prefixed withtv—that’s how MSVC names internal variables for its needs
(^93) wikipedia