【发布时间】:2011-03-02 00:27:44
【问题描述】:
我在 cygwin windows 上编程 C。在完成了一些 C 编程并熟悉了该语言之后,我想深入了解一下编译器对我编写的代码做了什么。
所以我写了一个包含 switch case 语句的代码块,并使用以下方法将它们转换为程序集:
gcc -S foo.c
这里是 C 源代码:
switch(i)
{
case 1:
{
printf("Case 1\n");
break;
}
case 2:
{ printf("Case 2\n");
break;
}
case 3:
{
printf("Case 3\n");
break;
}
case 4:
{
printf("Case 4\n");
break;
}
case 5:
{
printf("Case 5\n");
break;
}
case 6:
{
printf("Case 6\n");
break;
}
case 7:
{
printf("Case 7\n");
break;
}
case 8:
{
printf("Case 8\n");
break;
}
case 9:
{
printf("Case 9\n");
break;
}
case 10:
{
printf("Case 10\n");
break;
}
default:
{
printf("Nothing\n");
break;
}
}
现在相同的结果程序集是:
movl $5, -4(%ebp)
cmpl $10, -4(%ebp)
ja L13
movl -4(%ebp), %eax
sall $2, %eax
movl L14(%eax), %eax
jmp *%eax
.section .rdata,"dr"
.align 4
L14:
.long L13
.long L3
.long L4
.long L5
.long L6
.long L7
.long L8
.long L9
.long L10
.long L11
.long L12
.text
L3:
movl $LC0, (%esp)
call _printf
jmp L2
L4:
movl $LC1, (%esp)
call _printf
jmp L2
L5:
movl $LC2, (%esp)
call _printf
jmp L2
L6:
movl $LC3, (%esp)
call _printf
jmp L2
L7:
movl $LC4, (%esp)
call _printf
jmp L2
L8:
movl $LC5, (%esp)
call _printf
jmp L2
L9:
movl $LC6, (%esp)
call _printf
jmp L2
L10:
movl $LC7, (%esp)
call _printf
jmp L2
L11:
movl $LC8, (%esp)
call _printf
jmp L2
L12:
movl $LC9, (%esp)
call _printf
jmp L2
L13:
movl $LC10, (%esp)
call _printf
L2:
现在,在程序集中,代码首先检查最后一个案例(即案例 10)。这很奇怪。然后它将“i”复制到“eax”中并做超出我的事情。
我听说编译器为 switch..case 实现了一些跳转表。这是这段代码在做什么吗?或者它在做什么,为什么?因为在案件数量较少的情况下, 该代码与为 if...else 阶梯生成的代码非常相似,但是当案例数量增加时,会看到这种看起来不寻常的实现。
提前致谢。
【问题讨论】:
-
很遗憾,它没有针对字符串指针和
call _printf的表查找进行优化。即使在-O3,gcc/clang/icc 也没有这样做。 godbolt.org/g/JrSwU3(不过,他们确实将printf优化为puts,并将尾调用优化为jmp而不是call/ret)