MSVC rolls over
Posted on March 22, 2007 12:44 AM
If you are using MSVC7 or earlier and you want to rotate some uints, you'll be unpleasantly surprised when
#define rotl(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
unsigned int test( unsigned int a, unsigned int b, unsigned int c ) {
a -= c; a ^= rotl(c, 4); c += b;
b -= a; b ^= rotl(a, 6); a += c;
c -= b; c ^= rotl(b, 8); b += a;
a -= c; a ^= rotl(c,16); c += b;
b -= a; b ^= rotl(a,19); a += c;
c -= b; c ^= rotl(b, 4); b += a;
return ( c );
}
compiles to code that looks like:
mov edi,eax shl edi,6 shr ecx,1A or ecx,edi xor ecx,edx add eax,esi sub esi,ecx mov edx,ecx mov edi,ecx shl edi,8 shr edx,18 or edx,edi xor edx,esi
No! Bad dog! Unlike gcc and icc, MSVC7 and earlier don't like to recognize rol's / ror's when they see one and will instead generate dreadful shifts; this obviously slows your code down a bit, especially if it's in a tight innerloop.
The solution to the problem is to use _rotl (or _rotr), which Microsoft compilers implicitly generate rol / ror for. If you define your macros as _rotl / _rotr, you can do something like
#if !defined(_MSC_VER) #define _rotl(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) #endif
and let the macro fall through to the built-in functions on Microsoft compilers. MSVC7 and earlier will now generate rotates properly:
rol ecx,6 xor ecx,edx add eax,esi sub esi,ecx mov edx,ecx rol edx,8 xor edx,esi