【发布时间】:2011-03-16 01:51:56
【问题描述】:
This is only an issue on GCC versions prior to 4.4, this was fixed in GCC 4.5.
是否可以告诉编译器 switch 中使用的变量适合提供的 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;
}
}
我尝试 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: [...]问题出在
cmp $7, %eax;ja L11; -
好的,我将采用丑陋的解决方案,并为低于 4.4 的 gcc 版本添加一个特殊情况,使用不带开关的不同版本并使用 goto 和 gcc 的 &&label 扩展。
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; [...] }注意标签数组是静态的,所以不是每次调用都计算。
【问题讨论】:
标签: c assembly switch-statement gcc4.4