[Edit: Кажется, это проблема в версиях gcc до 4.4, я запутался из-за записи gcc bugzilla, сообщающей об этом для 4.5 (последняя). К сожалению, я должен был протестировать более поздние версии. Тем не менее, проблема несколько верна, так как большинство людей не запускают gcc 4.4 +.]
Можно ли сообщить компилятору, что переменная, используемая в коммутаторе, соответствует указанным операторам 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 доказать, что переменная подходит, и в примере выше нет ветки по умолчанию? (Без добавления условной ветки, конечно.)
EDIT1: Это было на OS X 10.6 Snow Leopard с GCC 4.2 (по умолчанию от Xcode.) Это не произошло с GCC 4.4/4.3 в Linux (сообщено Nathon и Jens Gustedt.)
EDIT2: Функции в этом примере доступны для читаемости, считая, что это встроенные или просто инструкции. Выполнение вызова функции на 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.
EDIT3:
ОК, я пойду с уродливым решением и добавлю специальный случай для версий 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;
[...]
}
Обратите внимание, что массив меток является статическим, поэтому он не вычисляет каждый вызов.
Спасибо всем за большую помощь! И жаль тех, у кого есть достоверные ответы, которые не получили оценку: (