首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GCC跳表初始化代码生成movsxd和add?

GCC跳表初始化代码生成movsxd和add?
EN

Stack Overflow用户
提问于 2018-09-06 01:10:35
回答 2查看 494关注 0票数 2

我在GCC里面优化编译switch语句的时候,它是这样建立一个跳转表的,

代码语言:javascript
复制
(fcn) sym.foo 148
  sym.foo (unsigned int arg1);
; arg unsigned int arg1 @ rdi
0x000006e0      83ff06         cmp edi, 6                              ; arg1
0x000006e3      0f87a7000000   ja case.default.0x790
0x000006e9      488d156c0100.  lea rdx, [0x0000085c]
0x000006f0      89ff           mov edi, edi
0x000006f2      4883ec08       sub rsp, 8
0x000006f6      486304ba       movsxd rax, dword [rdx + rdi*4]
0x000006fa      4801d0         add rax, rdx                            ; '('
;-- switch.0x000006fd:
0x000006fd      ffe0           jmp rax                                 ; switch table (7 cases) at 0x85c

MOVSXDADD是实现这一目标的最佳方式吗?

代码语言:javascript
复制
movsxd rax, dword [rdx + rdi*4]
add rax, rdx

这不就等同于在displacement中使用LEA

代码语言:javascript
复制
lea rax, [rdx + rdi*4 + rdx]

我突然想到,我可能不明白这里发生了什么。RDX似乎是跳转表的开始。RDI是switch语句的传入参数。但是为什么我们要添加两次RDX呢?

这是我用-O3编译的switch语句,

代码语言:javascript
复制
int foo (int x) {
  switch(x) {
    //case 0: puts("\nzero"); break;
    case 1: puts("\none"); break;
    case 2: puts("\ntwo"); break;
    case 3: puts("\nthree"); break;
    case 4: puts("\nfour"); break;
    case 5: puts("\nfive"); break;
    case 6: puts("\nsix"); break;
  }
  return 0;
}
EN

回答 2

Stack Overflow用户

发布于 2018-09-06 04:13:56

GCC在跳转表中使用相对位移(相对于表的基数),而不是绝对地址。因此跳转表本身是位置独立的,当它被重新定位时,不需要链接地址信息,,例如作为加载PIE可执行文件或PIC共享库的一部分。

如果使用-fno-pie -no-pie编译,则gcc可能会选择使用带有jmp [table + rdi*8]的跳转目标表

像x86-64Linux这样的目标确实支持运行时数据修正,因此一个简单的跳转表将是可能的。但有些目标根本不支持链接地址信息,这就是为什么gcc -fPIC / -fpie完全避免了它。这个潜在的优化是gcc bug 84011。有关更多信息,请参阅那里的讨论。

不幸的是,gcc使用的是跳转表,而没有意识到每种情况之间唯一的区别是数据,而不是代码。所以实际上它只需要一个字符串指针的表查找。(如果需要,可以使用相对位移完成此操作。)

这是一个单独的缺失优化,我将其报告为bug 85585。(这提醒了我,我有一个写了一半的后续文章,我应该完成并发布。)

票数 3
EN

Stack Overflow用户

发布于 2018-09-06 03:17:37

是MOVSXD,并添加了最好的实现方式,

只需使用带有qword内存操作数的add即可完成此操作。当然,缺点是它使表格变得两倍大。

与使用带位移的LEA不是一样的吗

不,lea不访问内存。

为什么我们要添加两次RDX?

第一次将其用作表的基础以对其进行索引。该表保存相对于其自身的地址,因此将RDX添加到表中的值将创建绝对地址。

顺便说一句,这可以很容易地改进:

代码语言:javascript
复制
mov edi, edi     ; truncate rdi to 32bit

自移位不能在当前架构上被移位消除,因此最好移到其他寄存器。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52190313

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档