x86-64架构下用gcc汇编:switch的实现原理,以及switch的跳转表消失现象

目录

一、汇编器如何汇编switch?

二、什么是跳转表?

三、什么时候跳转表消失?

1、当 case 的数量 <= 4 时,跳转表消失。

2、当 case 的数量 / case 的范围 < 10%时,跳转表消失。

四、为什么汇编器要让跳转表消失?


一、汇编器如何汇编switch?

1、把所有 case 的值从小到大排列。

2、如果 case 最小值 < 0,那么对所有 case 的值同时加上相同的值,使 case 最小值 = 0;

如果 case 最小值 > 0,那么对所有 case 的值同时减去相同的值,使 case 最小值 = 0。

这样一来,第一个case的值就刚好为 0。

二、什么是跳转表?

跳转表Jump Table中,每行放一个case代码段的首地址。

jmp  *(jtab+8*x)

上面是通过跳转表找case的汇编指令。

jtab是跳转表的首地址。

在64位机器里,跳转表中每个(case代码段的)首地址大小为8字节(32位机器是4字节)。

x代表,我们要去第x个case。

所以,jtab+8*x 直接算出第x个代码段的首地址 Targ_x 放在哪块内存中(jtab+8*x就是那块内存的地址)。我们去那个内存里取出Targ_x。

接着,我们找到地址为 Targ_x 的内存。从这里开始,就是我们要找的 case 代码段 Block x。

三、什么时候跳转表消失?

1、当 case 的数量 <= 4 时,跳转表消失。

例如:

    switch(i)
    {
      case 1:
        	j+=1;
       	 	break;
      case 2:
       		j+=2;
        	break;
      case 3:
        	j+=3;
        	break;
      case 4:
        	j+=4;
        	break;
      default:
        	j+=7;
        	break;
    }

以上代码段只有4个case,所以汇编后跳转表消失。

2、当 case 的数量 / case 的范围 < 10%时,跳转表消失。

例如:

    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 61:
        	j+=61;
		break;
      default:
        	j+=5;
        	break;
    }

case数量=6

case范围=61-1=60

6/60 = 10%  = 10%

所以,以上代码段汇编后有跳转表。

    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 62:
        	j+=61;
		break;
      default:
        	j+=5;
        	break;
    }

case数量=6

case范围=62-1=61

6/61 < 10% 

所以,以上代码段汇编后跳转表消失。

当然,不同架构和不同版本下,情况可能会不一样。比如 10% 变成 8%,或者汇编后可能出现两个跳转表(我的版本最多只有一个)。

四、为什么汇编器要让跳转表消失?

.L4:
        .quad   .L2
        .quad   .L3
        .quad   .L5
        .quad   .L6
        .quad   .L7
        .quad   .L8
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2
        .quad   .L2

上面是一个跳转表。

.L2                跳转到default段                看做无用的部分

.L3 ~ .L8       跳转到对应的case段         看做有用的部分

我们可以看到,比起用 if-else 一个个跳转,用上面的跳转表可以更快找到 case 代码段。这是用空间换取时间。

但是,如果跳转表中无用部分占比太多,汇编器就会认为用跳转表是不值得的。此时汇编器就会让跳转表消失,转而用类似 if-else 的方式找到 case 代码段,如下:

cmp     eax, 3    //比较 eax 与 3
je      .L3       //若 eax==3 跳到 L3 处。
cmp     eax, 3    //比较eax与3
jg      .L4       //若 eax>3  跳到 L4 处。
cmp     eax, 1    //比较eax与1
je      .L5       //若 eax==1 跳到 L5 处。
cmp     eax, 2    //比较eax与2
je      .L6       //若 eax==2 跳到 L6 处。
jmp     .L2       //无条件跳到 L2

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

待到花烂漫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值