Nous avons un bug gênant, je ne peux pas l'expliquer autour de ce morceau de code:
unsigned char bitmap[K_BITMAP_SIZE] = {0} ;
SetBit(bitmap, K_18); // Sets the bit #18 to 1
for(size_t i = 0; i < K_END; ++i)
{
if(TestBit(bitmap, i)) // true for 18
{
size_t i2 = getData(i); // for 18, will return 15
SetBit(bitmap, i2); // BUG: IS SUPPOSED TO set the bit #15 to 1
}
}
- Il arrive sur Visual C++ 2010
- Il arrive à la fois sur 32 bits et les versions 64 bits
- Il ne se produit que sur les versions Release (avec "Optimiser la Vitesse (/O2)" ensemble
- Il ne se passe pas uniquement sur les versions Release avec "Réduire la Taille (/O1)" ensemble
- Il arrive sur Visual C++ 2008 seulement si nous
__forceinline
la fonction getData (par défaut, VC++2008 n'a pas inline cette fonction, tandis que VC++2010) - Il arrive sur le morceau de code donné ci-dessous, probablement parce que massive inline l'intérieur de la boucle
- Il ne se passe pas si nous enlever la boucle et de définir directement l'intéressantes (18)
Bonus info:
1 - BenJ, a commenté le problème n'apparaît pas sur Visual C++ 2012, ce qui signifie que ce pourrait bien être un bug du compilateur
2 - Si l'on ajoute un cast unsigned char
dans le Test/Régler/ResetBit fonctions, le bug disparaît, trop
size_t TestBit(const unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) & (1 << (unsigned char)((pos) & 7))) ; }
size_t SetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) |= (1 << (unsigned char)((pos) & 7))) ; }
size_t ResetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) &= ~(1 << (unsigned char)((pos) & 7))) ; }
La question est:
Ne ce bug se produit parce que notre code s'appuie sur un comportement indéterminé, ou est-il un bug dans le VC++2010 compilateur?
La source suivante est auto-suffisante, et peut être compilé en tant que tel dans vos favoris compilateur:
#include <iostream>
const size_t K_UNKNOWN = (-1) ;
const size_t K_START = (0) ;
const size_t K_12 = (K_START + 12) ;
const size_t K_13 = (K_START + 13) ;
const size_t K_15 = (K_START + 15) ;
const size_t K_18 = (K_START + 18) ;
const size_t K_26 = (K_START + 26) ;
const size_t K_27 = (K_START + 27) ;
const size_t K_107 = (K_START + 107) ;
const size_t K_128 = (K_START + 128) ;
const size_t K_END = (K_START + 208) ;
const size_t K_BITMAP_SIZE = ((K_END/8) + 1) ;
size_t TestBit(const unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) & (1 << ((pos) & 7))) ; }
size_t SetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) |= (1 << ((pos) & 7))) ; }
size_t ResetBit(unsigned char * bits, size_t pos) { return (((bits)[(pos) >> 3]) &= ~(1 << ((pos) & 7))) ; }
size_t getData(size_t p_value)
{
size_t value = K_UNKNOWN;
switch(p_value)
{
case K_13: value = K_12; break;
case K_18: value = K_15; break;
case K_107: value = K_15; break;
case K_27: value = K_26; break;
case K_128: value = K_12; break;
default: value = p_value; break;
}
return value;
}
void testBug(const unsigned char * p_bitmap)
{
const size_t byte = p_bitmap[1] ;
const size_t bit = 1 << 7 ;
const size_t value = byte & bit ;
if(value == 0)
{
std::cout << "ERROR : The bit 15 should NOT be 0" << std::endl ;
}
else
{
std::cout << "Ok : The bit 15 is 1" << std::endl ;
}
}
int main(int argc, char * argv[])
{
unsigned char bitmap[K_BITMAP_SIZE] = {0} ;
SetBit(bitmap, K_18);
for(size_t i = 0; i < K_END; ++i)
{
if(TestBit(bitmap, i))
{
size_t i2 = getData(i);
SetBit(bitmap, i2);
}
}
testBug(bitmap) ;
return 0;
}
Certaines des informations de base: dans un premier temps:
- le Test/Régler/ResetBit fonctions étaient des macros.
- les constantes ont été définit
- les indices ont été soit
long
ouint
(sur Windows 32 bits, ils ont la même taille)
Si nécessaire, je vais ajouter un peu plus d'info (par exemple, l'assembleur généré pour les deux configurations, la mise à jour sur la façon g++ traiter le problème), dès que possible.