Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ CMPXCHG16B正确吗?_C++_Gcc_X86 64_Inline Assembly_Compare And Swap - Fatal编程技术网

C++ CMPXCHG16B正确吗?

C++ CMPXCHG16B正确吗?,c++,gcc,x86-64,inline-assembly,compare-and-swap,C++,Gcc,X86 64,Inline Assembly,Compare And Swap,这似乎并不完全正确,尽管我不确定为什么。 建议非常好,因为CMPXCHG16B的文档非常少(我没有任何英特尔手册…) 模板 内联布尔cas(易失性类型::uint128\u t*src,类型::uint128\u t cmp,类型::uint128\u t with) { /* 说明: CMPXCHG16B指令比较RDX:RAX和RCX:RBX寄存器中的128位值 具有128位内存位置。如果值相等,则设置零标志(ZF), 并将RCX:RBX值复制到内存位置。 否则,将清除ZF标志,并将内存值复制

这似乎并不完全正确,尽管我不确定为什么。 建议非常好,因为CMPXCHG16B的文档非常少(我没有任何英特尔手册…)

模板
内联布尔cas(易失性类型::uint128\u t*src,类型::uint128\u t cmp,类型::uint128\u t with)
{
/*
说明:
CMPXCHG16B指令比较RDX:RAX和RCX:RBX寄存器中的128位值
具有128位内存位置。如果值相等,则设置零标志(ZF),
并将RCX:RBX值复制到内存位置。
否则,将清除ZF标志,并将内存值复制到RDX:RAX。
*/
uint64_t*cmpP=(uint64_t*)和cmp;
uint64_t*带p=(uint64_t*)和;
无符号字符结果=0;
__asm\uuuuuu挥发性(
“锁定;CMPXCHG16B%1\n\t”
“SETZ%b0\n\t”
:“=q”(结果)/*输出*/
:“m”(*src),/*输入*/
//比较什么
“rax”((uint64_t)(cmpP[1]),//低位
“rdx”((uint64_t)(cmpP[0]),//高位
//如果它相等,用什么来代替它
“rbx”((uint64_t)(带p[1]),//低位
“rcx”((uint64_t)(带p[0])//高位
:“内存”、“抄送”、“rax”、“rdx”、“rbx”、“rcx”/*删除的项目*/
);
返回结果;
}

当运行一个示例时,我得到了0,而它应该是1。有什么想法吗?

所有英特尔文档都是免费提供的:。

注意到一些问题

(1) 主要的问题是约束,“rax”并不像它看起来那样,而是第一个字符“r”允许gcc使用任何寄存器

(2) 不确定您的存储类型::uint128_t如何,但假设x86平台使用标准的little endian,那么高位和低位DWORD也会互换

(3) 获取某个对象的地址并将其强制转换为其他对象可以打破别名规则。取决于types::uint128\u t是如何定义的,这是否是一个问题(如果它是两个uint64\u t的结构,则可以)。带有-O2的GCC将在不违反别名规则的情况下进行优化

(4) *src实际上应该标记为输出,而不是指定内存缓冲。但这实际上更多的是性能问题,而不是正确性问题。类似地,rbx和rcx不需要指定为clobbered

这是一个有效的版本

#include <stdint.h>

namespace types
{
    // alternative: union with  unsigned __int128
    struct uint128_t
    {
        uint64_t lo;
        uint64_t hi;
    }
    __attribute__ (( __aligned__( 16 ) ));
}

template< class T > inline bool cas( volatile T * src, T cmp, T with );

template<> inline bool cas( volatile types::uint128_t * src, types::uint128_t cmp, types::uint128_t with )
{
    // cmp can be by reference so the caller's value is updated on failure.

    // suggestion: use __sync_bool_compare_and_swap and compile with -mcx16 instead of inline asm
    bool result;
    __asm__ __volatile__
    (
        "lock cmpxchg16b %1\n\t"
        "setz %0"       // on gcc6 and later, use a flag output constraint instead
        : "=q" ( result )
        , "+m" ( *src )
        , "+d" ( cmp.hi )
        , "+a" ( cmp.lo )
        : "c" ( with.hi )
        , "b" ( with.lo )
        : "cc", "memory" // compile-time memory barrier.  Omit if you want memory_order_relaxed compile-time ordering.
    );
    return result;
}

int main()
{
    using namespace types;
    uint128_t test = { 0xdecafbad, 0xfeedbeef };
    uint128_t cmp = test;
    uint128_t with = { 0x55555555, 0xaaaaaaaa };
    return ! cas( & test, cmp, with );
}
#包括
命名空间类型
{
//备选方案:与未签名的_int128并集
结构uint128\u t
{
uint64_t lo;
uint64_t hi;
}
__属性_uuu((uuu对齐_uuu(16));
}
模板内联bool cas(volatile T*src、tcmp、T with);
模板内联布尔cas(易失性类型::uint128\u t*src,类型::uint128\u t cmp,类型::uint128\u t with)
{
//cmp可以通过引用进行更新,以便在失败时更新调用方的值。
//建议:使用u_sync_bool_compare_和_swap并使用-mcx16编译,而不是使用内联asm
布尔结果;
__asm\uuuuuuuuuuuuuuuuuu挥发性__
(
“锁定cmpxchg16b%1\n\t”
在gcc6及更高版本上设置“setz%0”//时,请改用标志输出约束
:“=q”(结果)
,“+m”(*src)
“+d”(cmp.hi)
,“+a”(cmp.lo)
:“c”(with.hi)
,“b”(带lo)
:“cc”,“memory”//compile-time内存屏障。如果您希望内存\u顺序\u轻松的编译时顺序,请省略。
);
返回结果;
}
int main()
{
使用名称空间类型;
uint128_t检验={0xdecafbad,0xfeedbeef};
uint128\u t cmp=测试;
uint128_t with={0x555555,0xaaaaaa};
返回!cas(&测试,cmp,带);
}

我在为g++编译时做了一点小改动(删除了cmpxchg16b指令中的oword ptr)。但它似乎并没有按照要求覆盖内存,尽管我可能错了[参见更新]下面给出了代码,后面是输出

#include <stdint.h>
#include <stdio.h>

namespace types
{
  struct uint128_t
  {
    uint64_t lo;
    uint64_t hi;
  }
  __attribute__ (( __aligned__( 16 ) ));
 }

 template< class T > inline bool cas( volatile T * src, T cmp, T with );

 template<> inline bool cas( volatile types::uint128_t * src, types::uint128_t cmp,  types::uint128_t with )
 {
   bool result;
   __asm__ __volatile__
   (
    "lock cmpxchg16b %1\n\t"
    "setz %0"
    : "=q" ( result )
    , "+m" ( *src )
    , "+d" ( cmp.hi )
    , "+a" ( cmp.lo )
    : "c" ( with.hi )
    , "b" ( with.lo )
    : "cc"
   );
   return result;
}

void print_dlong(char* address) {

  char* byte_array = address;
  int i = 0;
  while (i < 4) {
     printf("%02X",(int)byte_array[i]);
     i++;
  }

  printf("\n");
  printf("\n");

}

int main()
{
  using namespace types;
  uint128_t test = { 0xdecafbad, 0xfeedbeef };
  uint128_t cmp = test;
  uint128_t with = { 0x55555555, 0xaaaaaaaa };

  print_dlong((char*)&test);
  bool result = cas( & test, cmp, with );
  print_dlong((char*)&test);

  return result;
}
我不确定输出是否有意义。我希望before值是这样的 00000000脱咖啡因10000饲料牛肉根据结构定义。但字节似乎在单词中展开。这是因为我的指令吗?顺便说一句,CAS操作似乎返回了正确的返回值。有人帮我破译这个吗


更新:我刚刚用gdb对内存检查进行了一些调试。此处显示了正确的值。所以我想这一定是我的打印程序有问题。请随意更正它。我将此回复保留为待更正的回复,因为此回复的更正版本将有助于cas操作的打印结果。

注意,如果您使用的是GCC,则不需要使用内联asm来获取此说明。您可以使用其中一个同步功能,如:

template<>
inline bool cas(volatile types::uint128_t *src,
                types::uint128_t cmp,
                types::uint128_t with)
{
    return __sync_bool_compare_and_swap(src, cmp, with);
}

以下是比较的一些备选方案:

  • 内联程序集,例如的答案

  • \uuuuu sync\u bool\u compare_uand_uswap()
    :,仅限gcc/clang/ICC,已弃用的伪函数,编译器将至少为
    -mcx16
    发出
    CMPXCHG16B
    指令

  • atomic\u compare\u exchange\u弱()。对于GNU,这不会在gcc 7和更高版本中发出
    CMPXCHG16B
    ,而是调用
    libatomic
    (因此必须链接到)。动态链接的
    libatomic
    将根据CPU的能力决定使用哪个版本的函数,并且在CPU能够
    CMPXCHG16B
    的机器上,它将使用该函数

  • 显然,对于
    原子比较交换弱()

  • 我还没有尝试过机器语言,但看看(2)的反汇编,它看起来很完美,我不知道(1)如何能打败它。(我对x86知之甚少,但编程了很多6502。)此外,如果可以避免的话,有很多建议永远不要使用汇编,我
    FFFFFFADFFFFFFFBFFFFFFCAFFFFFFDE
    
    
    55555555
    
    template<>
    inline bool cas(volatile types::uint128_t *src,
                    types::uint128_t cmp,
                    types::uint128_t with)
    {
        return __sync_bool_compare_and_swap(src, cmp, with);
    }
    
    __int64 exchhi = __int64(with >> 64);
    __int64 exchlo = (__int64)(with);
    
    return _InterlockedCompareExchange128(a, exchhi, exchlo, &cmp) != 0;
    
    Thread 2 "mybinary" hit Breakpoint 1, MyFunc() at myfile.c:586
    586               if ( __sync_bool_compare_and_swap( &myvar,
    => 0x0000000000407262 <MyFunc+904>:       49 89 c2        mov    %rax,%r10
       0x0000000000407265 <MyFunc+907>:       49 89 d3        mov    %rdx,%r11
    (gdb) n
    587                                                  was, new ) ) {
    => 0x0000000000407268 <MyFunc+910>:       48 8b 45 a0     mov    -0x60(%rbp),%rax
       0x000000000040726c <MyFunc+914>:       48 8b 55 a8     mov    -0x58(%rbp),%rdx
    (gdb) n
    586               if ( __sync_bool_compare_and_swap( &myvar,
    => 0x0000000000407270 <MyFunc+918>:       48 c7 c6 00 d3 42 00    mov    $0x42d300,%rsi
       0x0000000000407277 <MyFunc+925>:       4c 89 d3        mov    %r10,%rbx
       0x000000000040727a <MyFunc+928>:       4c 89 d9        mov    %r11,%rcx
       0x000000000040727d <MyFunc+931>:       f0 48 0f c7 8e 70 04 00 00      lock cmpxchg16b 0x470(%rsi)
       0x0000000000407286 <MyFunc+940>:       0f 94 c0        sete   %al
    
    Thread 2 "tsquark" hit Breakpoint 2, 0x0000000000403210 in 
    __atomic_compare_exchange_16@plt ()
    => 0x0000000000403210 <__atomic_compare_exchange_16@plt+0>:     ff 25 f2 8e 02 00       jmpq   *0x28ef2(%rip)        # 0x42c108 <__atomic_compare_exchange_16@got.plt>
    (gdb) disas
    Dump of assembler code for function __atomic_compare_exchange_16@plt:
    => 0x0000000000403210 <+0>:     jmpq   *0x28ef2(%rip)        # 0x42c108 <__atomic_compare_exchange_16@got.plt>
       0x0000000000403216 <+6>:     pushq  $0x1e
       0x000000000040321b <+11>:    jmpq   0x403020
    End of assembler dump.
    (gdb) s
    Single stepping until exit from function __atomic_compare_exchange_16@plt,
    ...
    
    0x00007ffff7fab250 in libat_compare_exchange_16_i1 () from /lib64/libatomic.so.1
    => 0x00007ffff7fab250 <libat_compare_exchange_16_i1+0>: f3 0f 1e fa     endbr64
    (gdb) disas
    Dump of assembler code for function libat_compare_exchange_16_i1:
    => 0x00007ffff7fab250 <+0>:     endbr64
       0x00007ffff7fab254 <+4>:     mov    (%rsi),%r8
       0x00007ffff7fab257 <+7>:     mov    0x8(%rsi),%r9
       0x00007ffff7fab25b <+11>:    push   %rbx
       0x00007ffff7fab25c <+12>:    mov    %rdx,%rbx
       0x00007ffff7fab25f <+15>:    mov    %r8,%rax
       0x00007ffff7fab262 <+18>:    mov    %r9,%rdx
       0x00007ffff7fab265 <+21>:    lock cmpxchg16b (%rdi)
       0x00007ffff7fab26a <+26>:    mov    %r9,%rcx
       0x00007ffff7fab26d <+29>:    xor    %rax,%r8
       0x00007ffff7fab270 <+32>:    mov    $0x1,%r9d
       0x00007ffff7fab276 <+38>:    xor    %rdx,%rcx
       0x00007ffff7fab279 <+41>:    or     %r8,%rcx
       0x00007ffff7fab27c <+44>:    je     0x7ffff7fab288 <libat_compare_exchange_16_i1+56>
       0x00007ffff7fab27e <+46>:    mov    %rax,(%rsi)
       0x00007ffff7fab281 <+49>:    xor    %r9d,%r9d
       0x00007ffff7fab284 <+52>:    mov    %rdx,0x8(%rsi)
       0x00007ffff7fab288 <+56>:    mov    %r9d,%eax
       0x00007ffff7fab28b <+59>:    pop    %rbx
       0x00007ffff7fab28c <+60>:    retq
    End of assembler dump.