GCC 4.4: Избегать проверки диапазона на операторе switch / case в gcc?

Это проблема только в версиях GCC до 4.4, это было исправлено в 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;
        }
}

Я попытался xor'ing в младшие биты (как пример), используя перечисления, используя gcc_unreachable () безрезультатно. Сгенерированный код всегда проверяет, находится ли переменная внутри диапазона, добавляя бессмысленную условную ветвь и удаляя код вычисления таблицы переходов.

Примечание: это находится в самом внутреннем цикле декодера, производительность имеет большое значение.

Кажется, я нетолько один.

Нет никакого способа сказать gcc, что ветвь по умолчанию никогда не берется, хотя она пропустит ветвь по умолчанию, если сможет доказать, что значение никогда не выходит за пределы диапазона на основе предыдущих условных проверок.

Итак, как вы помогаете gcc доказать соответствие переменных, и в приведенном выше примере нет ветки по умолчанию? (Без добавления условной ветки, конечно.)

Обновления

Это было на OS X 10.6 Snow Leopard с GCC 4.2 (по умолчанию от Xcode.) Этого не произошло с GCC 4.4 / 4.3 в linux (сообщили Натон и Дженс Гастедт.)

Функции в этом примере предназначены для удобства чтения, представьте, что они встроенные или просто операторы. Выполнение вызова функции на 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;

Хорошо, я собираюсь использовать некрасивое решение и добавить специальный случай для версий gcc ниже 4.4, используя другую версию без переключателя и используя расширения ярлыков 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;
[...]
}

Обратите внимание, что массив меток является статическим, поэтому он не вычисляется при каждом вызове.

Ответы на вопрос(6)

Ваш ответ на вопрос