题目
对于以下c程序:
#include <stdio.h>
int main()
{
int i=0;
int j=0;
switch(i)
{
case 1:
j+=1;
break;
case 2:
j+=2;
break;
case 3:
j+=3;
break;
case 4:
j+=4;
break;
case 5:
j+=5;
case 6:
j+=70;
break;
default:
j+=5;
break;
}
return 0;
}
(1)将其编译成汇编代码,找到跳转表,并分析汇编代码是如何通过跳转表来完成switch功能的;
(2)将分支条件调整为case 6,case 2,case 5,case 3,case 4,case 1(即交换一下分支条件顺序),观察跳转表的变化情况。
(3)将分支条件调整为case 5, case 3, case 2, case1,或是调整为case 138,case 106, case 2, case 9, case 68后,汇编后的代码中不包括跳转表,而是采用cmpl,je, jmp等指令来实现switch功能,请再找出几组能生成跳转表与不生成跳转表的分支条件组合,并分析编译器在哪些情况下更有可能采用跳转表的方式来实现switch功能。同时考察不同的编译器是否有不同的行为。
第一问:跳转表
编译:gcc switch.c -S -o switch.s -O0
汇编如下:
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, -4(%rbp)
movl $0, -8(%rbp)
cmpl $6, -4(%rbp)
ja .L2
movl -4(%rbp), %eax
leaq 0(,%rax,4), %rdx
leaq .L4(%rip), %rax
movl (%rdx,%rax), %eax
cltq
leaq .L4(%rip), %rdx
addq %rdx, %rax
notrack jmp *%rax
.section .rodata
.align 4
.align 4
.L4:
.L4:
.long .L2-.L4
.long .L9-.L4
.long .L8-.L4
.long .L7-.L4
.long .L6-.L4
.long .L5-.L4
.long .L3-.L4
.text
.L9:
addl $1, -8(%rbp)
jmp .L10
.L8:
addl $2, -8(%rbp)
jmp .L10
.L7:
addl $3, -8(%rbp)
jmp .L10
.L6:
addl $4, -8(%rbp)
jmp .L10
.L5:
addl $5, -8(%rbp)
.L3:
addl $70, -8(%rbp)
jmp .L10
.L2:
addl $5, -8(%rbp)
nop
.L10:
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret