switch语句的汇编实现

C语言中switch语句的汇编实现。

一般来说,C语言中switch语句相当于一堆的if-else语句的累积,除非case很多的时候,
如果case之间的跨度不是很大,此时编译器一般会作优化,不再使用if-else集合,而是
使用一个跳转表,这样可以避免大量的compare计算。

少量case时的switch语句

代码示例如下,

switch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void translate(int n) {
char *p;
switch(n) {
case 1:
p = "one";
break;
case 2:
p = "two";
break;
case 3:
p = "three";
break;
}
}

编译成汇编代码,

1
gcc -S switch.c

汇编代码如下,

1
# ignore some text ...
.LC0:
    .string "one"
.LC1:
    .string "two"
.LC2:
    .string "three"
.text
.global translate
    .type translate, @function
translate:
    # ignore some text
    # %eax keep the value of 'n'
    cmpl    $2, %eax
    je  .L4
    cmpl    $3, %eax
    je  .L5
    cmpl    $1, %eax
    jne .L1
.L3:
    movq $.LC0, -16(%rbp)   # -16(%rbp) is variable p
    jmp .L1
.L4:
    movq $.LC1, -16(%rbp)   # -16(%rbp) is variable p
    jmp .L1
.L5:
    movq $.LC2, -16(%rbp)   # -16(%rbp) is variable p
    nop
.L1:
    leave
    ret
# ignore some text...

这完全就是一堆的if-else集合。

大量case时的switch语句

假如我们把上面的switch中的case数增加到8个,从case 10:一直到case 17
则汇编代码变为,

1
# ignore some text
# %eax keep the value of 'n'
    subl    $10, %eax
    cmpl    $7, %eax
    ja  .L1 # 如果%eax > 7则跳转,
            # ja是无符号比较,所以即使n < 10,也就是说这里的%eax < 0,也会跳转
     # 根据跳转表进行跳转
     movq   .L11(,%eax,8), %rax
     jmp    *%rax
     .section   .rodata
     .align 8
     .align 4
.L11:
    .quad   .L3
    .quad   .L4
    .quad   .L5
    .quad   .L6
    .quad   .L7
    .quad   .L8
    .quad   .L9
    .quad   .L10
.L3: # 具体的case内容
    # xxx
.L4: # 具体的case内容
    # xxx
# ignore some text