Reputation: 15040
The documentation for _InterlockedCompareExchange
says for every parameter
The sign is ignored.
So does it mean that numbers like 0xffff
and 0x7fff
(for 16-bit version) will be considered equal by _InterlockedCompareExchange16
etc. by other width intrinsics? Or does this mean that the intrinsics accept both signed and unsigned integers? Or something else?
If it's not a bug in the documentation, it seems at least ambiguous.
Upvotes: 4
Views: 367
Reputation: 101666
The sign bit is not ignored, it is compared just like the other bits.
The ..CompareExchange..
functions only care about the equality of the bits and does not interpret them in any special way. On x86 based systems they are implemented with the CMPXCHG
/CMPXCHG8B
instruction and it compares a CPU register against a memory location. The sign issue becomes a question about types and parameter passing, not the comparison itself.
Because most of the interlocked functions also exist as Windows API functions we can take a look at those first. The basic version takes 32-bit parameters with a LONG
type. Smaller signed types will be sign extended to 32-bits:
__declspec(noinline) void WINAPI Number(LONG val)
{
printf("Number: %5d %#.8x (%d bit)\n", val, val, sizeof(void*) * 8);
}
__declspec(noinline) INT16 WINAPI GetI16(INT16 num)
{
return num;
}
...
Number(0xffff); // Not sign extended
const INT16 numi16 = -42;
Number(numi16); // Optimized to 32-bit parameter by the compiler
Number(GetI16(-42)); // Use a helper function to prevent compiler tricks
and this prints:
Number: 65535 0x0000ffff (64 bit)
Number: -42 0xffffffd6 (64 bit)
Number: -42 0xffffffd6 (64 bit)
32-bit x86:
; 1040 : Number(0xffff);
00022 68 ff ff 00 00 push 65535 ; 0000ffffH
00027 e8 00 00 00 00 call ?Number@@YGXJ@Z ; Number
; 1041 : const INT16 numi16 = -42;
; 1042 : Number(numi16);
0002c 6a d6 push -42 ; ffffffd6H
0002e e8 00 00 00 00 call ?Number@@YGXJ@Z ; Number
; 1047 : Number(GetI16(-42));
00033 6a d6 push -42 ; ffffffd6H
00035 e8 00 00 00 00 call ?GetI16@@YGFF@Z ; GetI16
0003a 0f bf c0 movsx eax, ax
0003d 50 push eax
0003e e8 00 00 00 00 call ?Number@@YGXJ@Z ; Number
64-bit x86_64/AMD64:
; 1040 : Number(0xffff);
00027 b9 ff ff 00 00 mov ecx, 65535 ; 0000ffffH
0002c e8 00 00 00 00 call ?Number@@YAXJ@Z ; Number
; 1041 : const INT16 numi16 = -42;
; 1042 : Number(numi16);
00031 b9 d6 ff ff ff mov ecx, -42 ; ffffffffffffffd6H
00036 e8 00 00 00 00 call ?Number@@YAXJ@Z ; Number
; 1047 : Number(GetI16(-42));
0003b 66 b9 d6 ff mov cx, -42 ; ffffffffffffffd6H
0003f e8 00 00 00 00 call ?GetI16@@YAFF@Z ; GetI16
00044 0f bf c8 movsx ecx, ax
00047 e8 00 00 00 00 call ?Number@@YAXJ@Z ; Number
We can see that the generated code uses MOVSX
to sign extend the 16-bit number. This is required by the Windows ABI.
When you use #pragma intrinsic(_InterlockedCompareExchange)
things are a little less clear. I could not find a definitive statement in the documentation regarding the ABI of intrinsic functions but we can assume it follows the Windows ABI when it comes to sign extension. The compiler will generate a LOCK CMPXCHG
instruction directly without a function call but it will use MOVSX
when required.
Upvotes: 2
Reputation: 33744
the _InterlockedCompareExchange
this is compiler intrinsic implemented as CMPXCHG
instruction. the The sign is ignored
mean that when we compare 2 integers for equal only - no different how we interpret high bit - as sign bit or no. this affected only compare for >
or <
but not for =
. and 0xffff
of course not equal to 0x7fff
Upvotes: 0