【问题标题】:Meaning of "The sign is ignored" in _InterlockedCompareExchange documentation_InterlockedCompareExchange 文档中“符号被忽略”的含义
【发布时间】:2017-09-06 11:05:04
【问题描述】:

_InterlockedCompareExchange 的文档说明了每个参数

符号被忽略。

那么这是否意味着像0xffff0x7fff(对于16 位版本)这样的数字将被_InterlockedCompareExchange16 等视为相等的宽度内在函数?或者这是否意味着内在函数同时接受有符号和无符号整数?还是别的什么?

如果它不是文档中的错误,它至少看起来是模棱两可的。

【问题讨论】:

  • 这意味着接受有符号和无符号整数 - 因为只比较相等(但不比较小于或更大) - 没有带符号或无符号的区别
  • 文档看起来不正确或至少模棱两可,因为无论如何类型都是有符号类型,所以它可能无关紧要,msdn.microsoft.com/en-us/library/windows/desktop/… 方法根本没有提到这一点。我会用 MS 提出这个问题
  • @EdChum,我已在该页面上提交了反馈。
  • 当有诸如 32 位和 64 位版本之类的显式方法时,我很少使用内部函数,所以我很惊讶它甚至声明了这一点。我的理解是,这样说是没有意义的,尤其是当它采用有符号类型时,它仍然适用于有符号和无符号类型,这是我的期望
  • 任何像样的std::atomic 实现都会为非无锁对象使用一组锁,使用对象地址的哈希来选择哪个锁。 (可以像使用低位作为索引一样简单)因此不同的对象将使用不同的锁,而不是所有对象的单个全局锁!

标签: c++ winapi visual-c++ intrinsics interlocked


【解决方案1】:

符号位不会被忽略,它就像其他位一样进行比较。

..CompareExchange.. 函数只关心位的相等性,并不以任何特殊方式解释它们。在基于 x86 的系统上,它们是使用 CMPXCHG/CMPXCHG8B 指令实现的,它将 CPU 寄存器与内存位置进行比较。符号问题变成了关于类型和参数传递的问题,而不是比较本身。

因为大多数互锁函数也以 Windows API 函数的形式存在,我们可以先看看这些函数。 basic version 采用 LONG 类型的 32 位参数。较小的有符号类型将符号扩展到 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

【讨论】:

  • value 传递给函数的方式将仅取决于 value 类型,而不取决于 parameter 类型。当无符号值为零扩展 (movzx) 时,有符号值将被签名扩展 (movsx)。假设您可以声明 Number(ULONG val)Number(LONG val) 并查看结果将 notdependparameter 类型(ULONGLONG )。结果将仅取决于值类型:例如Number((signed char)-1); -> 0xffffffffNumber((unsigned char)-1);->0xff。所以这一切(如何扩展价值)根本不依赖于_InterlockedCompareExchange - 这是分开的
  • 如果值类型是有符号的,则值(当作为参数传递时)将进行符号扩展,并将符号扩展至参数类型的大小。参数类型的有符号无所谓,没错。
  • 是的,但我正是这个和(尝试)在我的评论中说,只针对糟糕的英语。所以值(作为参数传递时)将如何进行符号扩展完全不依赖于 _InterlockedCompareExchange - 这是常见的 c/c++ 规则。所以情况绝对清楚 - 相比之下(cmpxchg)使用了所有位以及值如何扩展 - 在所有单独的问题中
  • 你不能这样说,“如何将值(作为参数传递时)进行符号扩展而不依赖于_InterlockedCompareExchange”是不正确的,因为函数签名确实起作用. 如果它将被符号扩展取决于值类型,“宽度”(至少)将取决于函数参数类型。
  • 是的,你更正了“宽”的程度,我的意思是它的类型参数是如何声明的 - 有符号或无符号。
【解决方案2】:

_InterlockedCompareExchange 这是编译器内部实现为CMPXCHG 指令。 The sign is ignored 的意思是,当我们仅比较 equal 的 2 个整数时 - 我们如何解释高位没有不同 - 作为符号位或否。这只受影响的比较>< 而不是=。而0xffff当然不等于0x7fff

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 2011-03-20
    • 2018-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多