Документация для _InterlockedCompareExchange
говорит по каждому параметру
Знак игнорируется.
Значит ли это, что числа как 0xffff
а также 0x7fff
(для 16-битной версии) будет считаться равным _InterlockedCompareExchange16
и т.д. по другим характеристикам ширины? Или это означает, что встроенные функции принимают как целые числа со знаком, так и без знака? Или что-то другое?
Если это не ошибка в документации, это кажется, по крайней мере, неоднозначным.
Знаковый бит не игнорируется, он сравнивается так же, как и другие биты.
..CompareExchange..
функции заботятся только о равенстве битов и не интерпретируют их каким-либо особым образом. В системах на базе x86 они реализованы с CMPXCHG
/CMPXCHG8B
инструкции и сравнивает регистр процессора с местом в памяти. Проблема знака становится вопросом о типах и передаче параметров, а не о самом сравнении.
Поскольку большинство взаимосвязанных функций также существуют как функции Windows API, мы можем сначала взглянуть на них. базовая версия принимает 32-битные параметры с LONG
тип. Меньшие типы со знаком будут расширяться до 32 бит:
__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
и это печатает:
Number: 65535 0x0000ffff (64 bit)
Number: -42 0xffffffd6 (64 bit)
Number: -42 0xffffffd6 (64 bit)
32-битный 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-разрядная версия 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
Мы можем видеть, что сгенерированный код использует MOVSX
подписать продлить 16-битное число. Это требуется для Windows ABI.
Когда вы используете #pragma intrinsic(_InterlockedCompareExchange)
все немного менее понятно. Я не смог найти однозначного утверждения в документации, касающегося ABI встроенных функций, но мы можем предположить, что оно соответствует Windows ABI, когда речь идет о расширении знака. Компилятор сгенерирует LOCK CMPXCHG
инструкция напрямую без вызова функции, но она будет использовать MOVSX
когда требуется.
_InterlockedCompareExchange
это встроенная компилятор как CMPXCHG
инструкция. The sign is ignored
означает, что когда мы сравниваем 2 целых числа для равный только — без разницы, как мы интерпретируем старший бит — как знаковый бит или нет. это влияет только на сравнение >
или же <
но не для =
, а также 0xffff
конечно не равно 0x7fff