Dies ist nur ein Problem auf GCC-Versionen vor 4.4, dies wurde in GCC 4.5 behoben.
Ist es möglich, dem Compiler mitzuteilen, dass die in einem Switch verwendete Variable in die vorgesehenen Case-Anweisungen passt? Insbesondere, wenn es sich um einen kleinen Bereich handelt und eine Sprungtabelle erzeugt wird.
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;
}
}
Ich habe versucht, xor'ing zu niedrigen Bits (wie das Beispiel), mit enums, mit gcc_unreachable() ohne Erfolg. Der generierte Code prüft immer, ob die Variable innerhalb des Bereichs liegt, fügt eine sinnlose Verzweigungsbedingung hinzu und verschiebt den Sprungtabellenberechnungscode weg.
Hinweis: Diese Funktion befindet sich in der innersten Schleife eines Decoders, was die Leistung erheblich beeinflusst.
Ich bin wohl nicht der sólo eine .
Es gibt keine Möglichkeit, dem gcc mitzuteilen, dass der Standardzweig nie genommen wird, obwohl er den Standardzweig auslässt, wenn er beweisen kann, dass die Wert niemals außerhalb des Bereichs liegt, basierend auf früheren bedingten Prüfungen.
Also, wie helfen Sie gcc beweisen, dass die Variable passt und es keine Standardverzweigung im obigen Beispiel gibt? (Ohne eine bedingte Verzweigung hinzuzufügen, natürlich.)
Aktualisierungen
-
Dies war auf OS X 10.6 Snow Leopard mit GCC 4.2 (Standard von Xcode.) Es geschah nicht mit GCC 4.4/4.3 in Linux (berichtet von Nathon und Jens Gustedt.)
-
Die Funktionen in dem Beispiel dienen der Lesbarkeit, denken Sie daran, dass sie inlined oder nur Anweisungen sind. Ein Funktionsaufruf auf x86 ist teuer.
Auch das Beispiel gehört, wie in der Anmerkung erwähnt, in eine Schleife über Daten (Big Data).
Der generierte Code mit gcc 4.2/OS X ist:
[...] 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: [...]
Das Problem liegt in
cmp $7, %eax;
ja L11;
-
OK, ich gehe mit der hässlichen Lösung und fügen Sie einen speziellen Fall für gcc Versionen unter 4.4 mit einer anderen Version ohne einen Schalter und mit goto und gcc &&label Erweiterungen.
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; [...] }
Beachten Sie, dass das Array der Labels statisch ist und nicht bei jedem Aufruf neu berechnet wird.