c - 海湾合作委员会 4.4 : Avoid range check on switch/case statement in gcc?

标签 c assembly switch-statement x86-64 gcc4.4

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;
        }
}

我尝试使用枚举对低位进行异或运算(作为示例),使用 gcc_unreachable() 无济于事。生成的代码始终检查变量是否在范围内,添加无意义的分支条件并移走跳表计算代码。

注意:这是在解码器的最内层循环中,性能非常重要。

看来我不是only one .

There is no way to tell gcc that the default branch is never taken, although it will omit the default branch if it can prove that the value is never out of range based on earlier conditional checks.

那么,您如何帮助 gcc 证明变量适合并且上面的示例中没有默认分支? (当然,不添加条件分支。)

更新

  1. 这是在带有 GCC 4.2(Xcode 的默认设置)的 OS X 10.6 Snow Leopard 上发生的。Linux 中的 GCC 4.4/4.3 没有发生(Nathon 和 Jens Gustedt 报告。)

    <
  2. 示例中的函数是为了提高可读性,认为它们是内联的或只是语句。在 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;

  3. 好的,我将采用丑陋的解决方案,并使用不带开关的不同版本并使用 goto 和 gcc 的 &&label 扩展为 4.4 以下的 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;
    [...]
    }
    

    请注意,标签数组是静态的,因此不会在每次调用时都对其进行计算。

最佳答案

或许您可以使用函数指针数组来代替开关?

#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;
}

关于c - 海湾合作委员会 4.4 : Avoid range check on switch/case statement in gcc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3250178/

相关文章:

assembly - 从汇编中的字符串中提取子字符串

c - 我该怎么做才能使我的程序在 do while 循环中保持在开关中输入的最小数字?

javascript - "eval"是不是只能用 JS "switch"作为表达式?

C/C++ 以编程方式获取空闲内存

将 HTML 页面转换为十六进制值数组以在 Arduino 服务器中使用

c - 当格式字符串末尾有换行符时,为什么 scanf 会要求输入两次?

android - 使用 OpenSL ES 在 Android NDK 中实现 outputMixObject 会导致应用程序崩溃

assembly - 为什么mov指令使用ax而不是直接使用两个段寄存器?

c# - 在控制台应用程序中使用图像而不是文本

class - 带类的Clojure案例陈述