这两天小研究了一把跳转表

switch语句使用一种跳转表(jump table)的结构来实现程序的转移。在某些情况下可以得到比if~else高效的跳转效果。
跳转表是一个数组,表项i是一个代码段的地址。程序代码用switch索引值来执行一个跳转表内的数据引用,确认跳转指令的目的。

首先我看了这个网页 http://www.cublog.cn/u1/46723/showart_1272331.html “switch与跳转表(jump table)” 讲了一些跳转表的扫盲知识。

这篇文章中作者说源代码中case语句比较少时,switch会使用cmpl与jmp组合的跳转方式,类似于if~else的方式。当case语句较多时(大于4个),就会使用跳转表。

我经过实验,发现并不完全是这样。

首先,有些编译器会忽略空case,比如GCC。

比如源程序如下

int switch_test_first(int x)
{
    long int res;
    switch(x)
    {
        case 100:
            res = 1;
            break;
        case 102:
            res = 2;
            break;
        case 104:
            res = 3;
            break;
        case 106:
        case 108:
        case 110:
        case 112:
        case 114:
        case 116:
            break;
    }
    return res;
}


编译出来的汇编码为:(GCC)

    .file   "da.c"
    .text
.globl switch_test_first
    .type   switch_test_first, @function
switch_test_first:
.LFB2:
    pushq   %rbp
.LCFI0:
    movq    %rsp, %rbp
.LCFI1:
    movl    %edi, -20(%rbp)
    movl    -20(%rbp), %eax
    movl    %eax, -24(%rbp)
    cmpl    $102, -24(%rbp)
    je  .L4
    cmpl    $104, -24(%rbp)
    je  .L5
    cmpl    $100, -24(%rbp)
    je  .L3
    jmp .L2
.L3:
    movq    $1, -8(%rbp)
    jmp .L2
.L4:
    movq    $2, -8(%rbp)
    jmp .L2
.L5:
    movq    $3, -8(%rbp)
.L2:
    movq    -8(%rbp), %rax
    leave
    ret


而ICC仍然会将这些空case放出来。。。。。

# -- Machine type EFI2
# mark_description "Intel(R) C++ Compiler Professional for applications running on Intel(R) 64, Version 11.0    Build 20090131 %";
# mark_description "s";
# mark_description "-S";
    .file "da.c"
    .text
..TXTST0:
# -- Begin  switch_test_first
# mark_begin;
       .align    16,0x90
    .globl switch_test_first
switch_test_first:
# parameter 1: %edi
..B1.1:                         # Preds ..B1.0
..___tag_value_switch_test_first.1:                             #2.1
        addl      $-100, %edi                                   #4.2
        cmpl      $16, %edi                                     #4.2
        ja        ..B1.23       # Prob 50%                      #4.2
                                # LOE rax rbx rbp r12 r13 r14 r15 edi
..B1.2:                         # Preds ..B1.1
        movl      %edi, %edi                                    #4.2
        movq      ..1..TPKT.1_0.0.1(,%rdi,8), %rax              #4.2
        jmp       *%rax                                         #4.2
                                # LOE rbx rbp r12 r13 r14 r15
..1.1_0.TAG.074.0.1:
..1.1_0.TAG.072.0.1:
..1.1_0.TAG.070.0.1:
..1.1_0.TAG.06e.0.1:
..1.1_0.TAG.06c.0.1:
..1.1_0.TAG.06a.0.1:
..1.1_0.TAG.DEFAULT.0.1:
..B1.4:                         # Preds ..B1.2 ..B1.2 ..B1.2 ..B1.2 ..B1.2
                                #       ..B1.2 ..B1.2
        jmp       ..B1.23       # Prob 100%                     #23.9
                                # LOE rax rbx rbp r12 r13 r14 r15
..1.1_0.TAG.068.0.1:
..B1.16:                        # Preds ..B1.2
        movl      $3, %eax                                      #13.4
        jmp       ..B1.23       # Prob 100%                     #13.4
                                # LOE rax rbx rbp r12 r13 r14 r15
..1.1_0.TAG.066.0.1:
..B1.18:                        # Preds ..B1.2
        movl      $2, %eax                                      #10.4
        jmp       ..B1.23       # Prob 100%                     #10.4
                                # LOE rax rbx rbp r12 r13 r14 r15
..1.1_0.TAG.064.0.1:
..B1.22:                        # Preds ..B1.2
        movl      $1, %eax                                      #7.4
                                # LOE rax rbx rbp r12 r13 r14 r15
..B1.23:                        # Preds ..B1.4 ..B1.16 ..B1.18 ..B1.22 ..B1.1
                                #
        ret                                                     #23.9
        .align    16,0x90
..___tag_value_switch_test_first.2:                             #
                                # LOE
# mark_end;
    .type   switch_test_first,@function
    .size   switch_test_first,.-switch_test_first
    .section .rodata, "a"
    .align 32
    .align 32
..1..TPKT.1_0.0.1:
    .quad   ..1.1_0.TAG.064.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.066.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.068.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.06a.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.06c.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.06e.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.070.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.072.0.1
    .quad   ..1.1_0.TAG.DEFAULT.0.1
    .quad   ..1.1_0.TAG.074.0.1
    .data
# -- End  switch_test_first


看那一串quad的地方,ICC好像没有这么智能哦。。。

另外一个问题,就是跳转表使用索引来实现跳转的一个前提就是:case的数一定要有规律!!!!
就是说这几个数最好差的不太多,最好是等差数列。比如100,102,104,106,108,110这样的。如果出来一堆随机的数,那编译器也会傻眼的。
比如下面这个例子。

int switch_test_first(int x)
{
    long int res;
    switch(x)
    {
        case 1100:
            res = 1;
            break;
        case 102:
            res = 2;
            break;
        case 4603:
            res = 3;
            break;
        case 11044:
            res = 1;
            break;
        case 1505:
            res = 2;
            break;
        case 8106:
            res = 3;
            break;
        case 14017:
            res = 1;
            break;
        case 1908:
            res = 2;
            break;
        case 9:
            res = 3;
            break;

    }
    return res;
}


这种情况下,编译器就不会使用跳转表来实现跳转了。
有趣的是,gcc和icc对这种情况的处理也都不同
先看GCC的:

    .file   "ta.c"
    .text
.globl switch_test_first
    .type   switch_test_first, @function
switch_test_first:
.LFB2:
    pushq   %rbp
.LCFI0:
    movq    %rsp, %rbp
.LCFI1:
    movl    %edi, -20(%rbp)
    movl    -20(%rbp), %eax
    movl    %eax, -24(%rbp)
    cmpl    $1908, -24(%rbp)
    je  .L7
    cmpl    $1908, -24(%rbp)
    jg  .L12
    cmpl    $102, -24(%rbp)
    je  .L4
    cmpl    $102, -24(%rbp)
    jg  .L13
    cmpl    $9, -24(%rbp)
    je  .L3
    jmp .L2
.L13:
    cmpl    $1100, -24(%rbp)
    je  .L5
    cmpl    $1505, -24(%rbp)
    je  .L6
    jmp .L2
.L12:
    cmpl    $8106, -24(%rbp)
    je  .L9
    cmpl    $8106, -24(%rbp)
    jg  .L14
    cmpl    $4603, -24(%rbp)
    je  .L8
    jmp .L2
.L14:
    cmpl    $11044, -24(%rbp)
    je  .L10
    cmpl    $14017, -24(%rbp)
    je  .L11
    jmp .L2
.L5:
    movq    $1, -8(%rbp)
    jmp .L2
.L4:
    movq    $2, -8(%rbp)
    jmp .L2
.L8:
    movq    $3, -8(%rbp)
    jmp .L2
.L10:
    movq    $1, -8(%rbp)
    jmp .L2
.L6:
    movq    $2, -8(%rbp)
    jmp .L2
.L9:
    movq    $3, -8(%rbp)
    jmp .L2
.L11:
    movq    $1, -8(%rbp)
    jmp .L2
.L7:
    movq    $2, -8(%rbp)
    jmp .L2
.L3:
    movq    $3, -8(%rbp)
.L2:
    movq    -8(%rbp), %rax
    leave
    ret


GCC使用类似于if~else的跳转方式。

看看ICC怎么办:

# -- Machine type EFI2
# mark_description "Intel(R) C++ Compiler Professional for applications running on Intel(R) 64, Version 11.0    Build 20090131 %";
# mark_description "s";
# mark_description "-S";
    .file "da.c"
    .text
..TXTST0:
# -- Begin  switch_test_first
# mark_begin;
       .align    16,0x90
    .globl switch_test_first
switch_test_first:
# parameter 1: %edi
..B1.1:                         # Preds ..B1.0
..___tag_value_switch_test_first.1:                             #2.1
        cmpl      $1100, %edi                                   #4.9
        je        ..B1.11       # Prob 10%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.2:                         # Preds ..B1.1
        cmpl      $102, %edi                                    #4.9
        je        ..B1.9        # Prob 11%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.3:                         # Preds ..B1.2
        cmpl      $4603, %edi                                   #4.9
        je        ..B1.13       # Prob 12%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.4:                         # Preds ..B1.3
        cmpl      $11044, %edi                                  #4.9
        je        ..B1.11       # Prob 14%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.5:                         # Preds ..B1.4
        cmpl      $1505, %edi                                   #4.9
        je        ..B1.9        # Prob 16%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.6:                         # Preds ..B1.5
        cmpl      $8106, %edi                                   #4.9
        je        ..B1.13       # Prob 20%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.7:                         # Preds ..B1.6
        cmpl      $14017, %edi                                  #4.9
        je        ..B1.11       # Prob 25%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.8:                         # Preds ..B1.7
        cmpl      $1908, %edi                                   #4.9
        jne       ..B1.10       # Prob 67%                      #4.9
                                # LOE rbx rbp r12 r13 r14 r15 edi
..B1.9:                         # Preds ..B1.2 ..B1.5 ..B1.8
        movl      $2, %edx                                      #28.4
        jmp       ..B1.12       # Prob 100%                     #28.4
..B1.10:                        # Preds ..B1.8
        movl      $3, %eax                                      #31.4
        xorl      %edx, %edx                                    #31.4
        cmpl      $9, %edi                                      #31.4
        cmove     %rax, %rdx                                    #31.4
        jmp       ..B1.12       # Prob 100%                     #31.4
                                # LOE rdx rbx rbp r12 r13 r14 r15
..B1.11:                        # Preds ..B1.1 ..B1.4 ..B1.7
        movl      $1, %edx                                      #25.4
                                # LOE rdx rbx rbp r12 r13 r14 r15
..B1.12:                        # Preds ..B1.13 ..B1.11 ..B1.9 ..B1.10
        movl      %edx, %eax                                    #35.9
        ret                                                     #35.9
                                # LOE
..B1.13:                        # Preds ..B1.3 ..B1.6           # Infreq
        movl      $3, %edx                                      #22.4
        jmp       ..B1.12       # Prob 100%                     #22.4
        .align    16,0x90
..___tag_value_switch_test_first.2:                             #
                                # LOE rdx rbx rbp r12 r13 r14 r15
# mark_end;
    .type   switch_test_first,@function
    .size   switch_test_first,.-switch_test_first
    .data
# -- End  switch_test_first


哥们很霸气啊,直接原封不动抄下来了。。。。。

不知道这两种方式谁的效率更高一些,等有时间了我写个程序测试一下。

 

你可能感兴趣的:(function,gcc,Build,compiler,编译器)