Defekt optymalizacji kompilatora x64
Podczas dalszego kodowania mojego silnika natrafiłem na bardzo dziwny błąd kompilatora Visual Studio. Zaczynając od początku – mam oto taki kod:
enum { // Rozmiary pól MAX_BITS_INDEX = sizeof(Size_t) * 4, MAX_BITS_MAGIC = sizeof(Size_t) * 4, // Maksymalne wartości pól (Przesunięcie 1 w dwóch operacjach, ponieważ inaczej kompilator nie daje rady) MAX_INDEX = ((1 << MAX_BITS_INDEX / 2) << MAX_BITS_INDEX / 2) - 1, MAX_MAGIC = ((1 << MAX_BITS_MAGIC / 2) << MAX_BITS_MAGIC / 2) - 1 }; /* ... */ // handle = 0x0000000400000006; - przykladowo // Size_t ma rozmiar wskaźnika (jest zależny od platformy) Size_t value = handle & MAX_MAGIC; |
Otóż problem z tym kodem jest taki, że gdy zostanie skompilowany na platformę x64, zostaje wygenerowany następujący kod w assemblerze:
79: { 80: Size_t value = handle & MAX_MAGIC; 000000013FC96F94 mov rax,qword ptr [handle] 000000013FC96F9C mov qword ptr [value],rax 81: if(value == *it) return true; // Dalszy kod |
Jak widać problem polega na braku kluczowej instrukcji and, której zadaniem jest odcięcie najstarszych 32-bitów. Dla porównania kod assemblera wygenerowanego dla platformy x86 jest następujący:
79: { 80: Size_t value = handle & MAX_MAGIC; 00DC9DDC mov eax,dword ptr [handle] 00DC9DDF and eax,0FFFFh 00DC9DE4 mov dword ptr [value],eax 81: if(value == *it) return true; // Dalszy kod |
Tutaj instrukcja ta występuje.
Błąd ten jednak nie występuje gdy samemu sprecyzuję stałą, tj.:
Size_t value = handle & 0xffffffff; |
W tym przypadku zostanie wygenerowany poprawny kod.
Wniosek jest taki, że kompilator widząc operację and zmiennej ze stałą typu enum (który przechowuje wartości 32-bitowe), traktuje tą zmienną jako typ 32-bitowy. W tym przypadku stwierdza, że instrukcja and nie ma sensu, bo wartość MAX_MAGIC zawiera maksymalną wartość (0xffffffff). Według mnie jest to ewidentny błąd i nie powinno takie coś wystąpić.
// Edit :)
Problemem oczywiście jest to, że enum jest tak naprawdę typem int, więc się dokładnie tak zachowuje.