ale*_*cco 15 c assembly switch-statement gcc4.4
这只是4.4之前的GCC版本的问题,这在GCC 4.5中已得到修复.
是否有可能告诉编译器交换机中使用的变量是否适合所提供的case语句?特别是如果它是一个小范围并且有一个跳转表生成.
extern int a;
main()
{
switch (a & 0x7) { // 0x7 == 111 values are 0-7
case 0: f0(); break;
case 1: f1(); break;
case 2: f2(); break;
case 3: f3(); break;
case 4: f4(); break;
case 5: f5(); break;
case 6: f6(); break;
case 7: f7(); break;
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试xor'ing到低位(作为例子),使用枚举,使用gcc_unreachable()无济于事.生成的代码总是检查变量是否在范围内,添加无条件分支条件并移走跳转表计算代码.
注意:这是在解码器的最内层循环中,性能非常重要.
没有办法告诉gcc永远不会采用默认分支,尽管它可以省略默认分支,如果它可以证明该值永远不会超出先前的条件检查范围.
那么,你如何帮助gcc证明变量适合并且上面的例子中没有默认分支?(当然,不添加条件分支.)
这是在OS X 10.6 Snow Leopard上使用GCC 4.2(默认来自Xcode.)它没有发生在Linux中的GCC 4.4/4.3(由Nathon和Jens Gustedt报道).
示例中的函数是为了可读性,认为这些是内联的或只是语句.在x86上进行函数调用是很昂贵的.
此外,如注释中所述,该示例属于数据循环(大数据).
使用gcc 4.2/OS X生成的代码是:
[...]
andl $7, %eax
cmpl $7, %eax
ja L11
mov %eax, %eax
leaq L20(%rip), %rdx
movslq (%rdx,%rax,4),%rax
addq %rdx, %rax
jmp *%rax
.align 2,0x90
L20:
.long L12-L20
.long L13-L20
.long L14-L20
.long L15-L20
.long L16-L20
.long L17-L20
.long L18-L20
.long L19-L20
L19:
[...]
Run Code Online (Sandbox Code Playgroud)
问题在于 cmp $7, %eax; ja L11;
好的,我将使用丑陋的解决方案并为4.4以下的gcc版本添加一个特殊情况,使用不带开关的不同版本并使用goto和gcc的&&标签扩展.
static void *jtb[] = { &&c_1, &&c_2, &&c_3, &&c_4, &&c_5, &&c_6, &&c_7, &&c_8 };
[...]
goto *jtb[a & 0x7];
[...]
while(0) {
c_1:
// something
break;
c_2:
// something
break;
[...]
}
Run Code Online (Sandbox Code Playgroud)
请注意,标签数组是静态的,因此不会在每次调用时计算.
也许您可以使用函数指针数组而不是开关?
#include <stdio.h>
typedef void (*func)(void);
static void f0(void) { printf("%s\n", __FUNCTION__); }
static void f1(void) { printf("%s\n", __FUNCTION__); }
static void f2(void) { printf("%s\n", __FUNCTION__); }
static void f3(void) { printf("%s\n", __FUNCTION__); }
static void f4(void) { printf("%s\n", __FUNCTION__); }
static void f5(void) { printf("%s\n", __FUNCTION__); }
static void f6(void) { printf("%s\n", __FUNCTION__); }
static void f7(void) { printf("%s\n", __FUNCTION__); }
int main(void)
{
const func f[8] = { f0, f1, f2, f3, f4, f5, f6, f7 };
int i;
for (i = 0; i < 8; ++i)
{
f[i]();
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我尝试编译一些简单且与 -O5 和 -fno-inline 相当的东西(我的 f0-f7 函数很简单),它生成了以下内容:
8048420: 55 push %ebp ;; function preamble
8048421: 89 e5 mov %esp,%ebp ;; Yeah, yeah, it's a function.
8048423: 83 ec 04 sub $0x4,%esp ;; do stuff with the stack
8048426: 8b 45 08 mov 0x8(%ebp),%eax ;; x86 sucks, we get it
8048429: 83 e0 07 and $0x7,%eax ;; Do the (a & 0x7)
804842c: ff 24 85 a0 85 04 08 jmp *0x80485a0(,%eax,4) ;; Jump table!
8048433: 90 nop
8048434: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048438: 8d 45 08 lea 0x8(%ebp),%eax
804843b: 89 04 24 mov %eax,(%esp)
804843e: e8 bd ff ff ff call 8048400
8048443: 8b 45 08 mov 0x8(%ebp),%eax
8048446: c9 leave
Run Code Online (Sandbox Code Playgroud)
您尝试过优化级别吗?