简体   繁体   English

切换机箱组件级别代码

[英]Switch case assembly level code

I am programming C on cygwin windows. 我在cygwin windows上编写C语言。 After having done a bit of C programming and getting comfortable with the language, I wanted to look under the hood and see what the compiler is doing for the code that I write. 在完成了一些C编程并熟悉语言之后,我想深入了解编译器为我编写的代码做了些什么。

So I wrote down a code block containing switch case statements and converted them into assembly using: 所以我写下了一个包含switch case语句的代码块,并使用以下方法将它们转换为汇编:

gcc -S foo.c  

Here is the C source: 这是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;
    }
}  

Now the resultant assembly for the same is: 现在,相同的组件是:

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:  

Now, in the assembly, the code is first checking the last case (ie case 10) first. 现在,在程序集中,代码首先首先检查最后一个案例(即案例10)。 This is very strange. 这很奇怪。 And then it is copying 'i' into 'eax' and doing things that are beyond me. 然后它将'我'复制到'eax'并做一些超出我的事情。

I have heard that the compiler implements some jump table for switch..case. 我听说编译器为switch..case实现了一些跳转表。 Is it what this code is doing? 这个代码在做什么? Or what is it doing and why? 或者它在做什么以及为什么? Because in case of less number of cases, the code is pretty similar to that generated for if...else ladder, but when number of cases increases, this unusual-looking implementation is seen. 因为在案例数较少的情况下,代码与if ... else梯形图的代码非常相似,但是当案例数增加时,可以看到这种看似不寻常的实现。

Thanks in advance. 提前致谢。

First the code is comparing the i to 10 and jumping to the default case when the value is greater then 10 ( cmpl $10, -4(%ebp) followed by ja L13 ). 首先,代码将i与10进行比较,并在值大于10时跳转到默认情况( cmpl $10, -4(%ebp)后跟ja L13 )。

The next bit of code is shifting the input to the left by two ( sall $2, %eax ) which is the same as multiple by four which generates an offset into the jump table (because each entry in the table is 4 bytes long) 下一位代码将输入向左移动两个( sall $2, %eax ),它与乘以4的倍数相同,后者在跳转表中生成一个偏移量(因为表中的每个条目长度为4个字节)

It then loads an address from the jump table ( movl L14(%eax), %eax ) and jumps to it ( jmp *%eax ). 然后它从跳转表( movl L14(%eax), %eax )加载一个地址并跳转到它( jmp *%eax )。

The jump table is simply a list of addresses (represented in assembly code by labels): 跳转表只是一个地址列表(按标签用汇编代码表示):

L14:
.long   L13
.long   L3
.long   L4
...

One thing to notice is that L13 represents the default case. 需要注意的一点是, L13代表默认情况。 It is both the first entry in the jump table (for when i is 0) and is handled specially at the beginning (when i > 10). 它既是跳转表中的第一个条目(当我为0时)并且在开始时特别处理(当i> 10时)。

Yes it is a jump table. 是的,它是一个跳转表。 The first checking is to check if the value is in the cases and jump to default if it is not. 第一个检查是检查值是否在案例中,如果不是则跳转到默认值。 Don't forget that in such a table, if %eax is 0, L14(%eax) points to the first element of the table (L13). 不要忘记在这样的表中,如果%eax为0,则L14(%eax)指向表的第一个元素(L13)。 So in the table the case 10: is indexed with 9, not 10. 所以在表格中, case 10:用9而不是10来索引。

The way the switch can be done depends on the values you have in case ; 切换的方式取决于你的case ; in this case they are in "sequence", so the simple jump table is possible. 在这种情况下,它们处于“序列”中,因此简单的跳转表是可能的。

For [ 1..10 ] the compiler will generate a table so that it doesn't need to compare the value to go somewhere, it directly do a: goto table[i] . 对于[ 1..10 ],编译器将生成一个表,以便它不需要比较值去某处,它直接执行: goto table[i] That way it's faster. 这样它更快。

But in case i > 10 it jumps to your default statement. 但是如果i > 10它会跳转到您的默认语句。 It has to check first before jumping otherwise, the program would miserably crash. 它必须在跳跃之前先检查,否则程序会崩溃。

If you had sparse values (like, 23, 9233, 91238, and not 1, 2, 3...), the compiler would not generate such a table, and compare each value. 如果您有稀疏值(例如,23,9233,91238,而不是1,2,3 ......),编译器将不会生成这样的表,并比较每个值。

Yes, first eax is calculated by the switch value ( sall shift as multiplication) to get the adress from the jump table (following label L14: ) 是的,第一个eax是通过开关值( sall shift as multiplication)来计算的,以从跳转表中获取地址(在标签L14:

jmp *%eax is a near jump to the label of your case. jmp *%eax是一个接近你的案件标签的跳转。 (jmp near eax) (靠近eax的jmp)

The code following the other labels is just printing and skips the other cases. 其他标签后面的代码只是打印并跳过其他情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM