Это проблема только в версиях 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 в младшие биты (как пример), используя enums, используя 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;
[...]
}
Обратите внимание, что массив меток является статическим, поэтому он не вычисляется при каждом вызове.