CMPXCHG16B правильно? - PullRequest
       3

CMPXCHG16B правильно?

8 голосов
/ 28 января 2011

Это не совсем верно, хотя я не уверен, почему. Совет был бы отличным, поскольку документация для CMPXCHG16B довольно минимальна (у меня нет руководств по Intel ...)

template<>
inline bool cas(volatile types::uint128_t *src, types::uint128_t cmp, types::uint128_t with)
{
    /*
    Description:
     The CMPXCHG16B instruction compares the 128-bit value in the RDX:RAX and RCX:RBX registers 
     with a 128-bit memory location. If the values are equal, the zero flag (ZF) is set, 
     and the RCX:RBX value is copied to the memory location. 
     Otherwise, the ZF flag is cleared, and the memory value is copied to RDX:RAX.
     */
    uint64_t * cmpP = (uint64_t*)&cmp;
    uint64_t * withP = (uint64_t*)&with;
    unsigned char result = 0;
    __asm__ __volatile__ (
    "LOCK; CMPXCHG16B %1\n\t"
    "SETZ %b0\n\t"
    : "=q"(result)  /* output */ 
    : "m"(*src), /* input */
      //what to compare against
      "rax"( ((uint64_t) (cmpP[1])) ), //lower bits
      "rdx"( ((uint64_t) (cmpP[0])) ),//upper bits
      //what to replace it with if it was equal
      "rbx"( ((uint64_t) (withP[1])) ), //lower bits
      "rcx"( ((uint64_t) (withP[0]) ) )//upper bits
    : "memory", "cc", "rax", "rdx", "rbx","rcx" /* clobbered items */
    );
    return result;
}

При работе с примером я получаю 0, когда должно быть 1. Любые идеи?

Ответы [ 4 ]

13 голосов
/ 28 января 2011

Заметил несколько проблем,

(1) Основная проблема заключается в ограничениях: rax не делает то, на что он похож, скорее, первый символ "r" позволяет gcc использовать любой регистр.

(2) Не знаете, как ваши типы хранения :: uint128_t, но при условии стандартного байтового порядка для платформ x86, тогда верхние и нижние мечи также меняются местами.

(3) Взятиеадрес чего-либо и приведение его к чему-то другому может нарушить правила наложения имен.Зависит от того, как ваш types :: uint128_t определен как влажный или нет, это проблема (хорошо, если это структура из двух uint64_t).GCC с -O2 оптимизирует при условии, что правила псевдонимов не нарушаются.

(4) * src действительно должен быть помечен как выход, а не задан клопбер памяти.но это действительно скорее вопрос производительности, чем правильности.Точно так же rbx и rcx не нужно указывать как закрытые.

Вот версия, которая работает,

#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 );
}
6 голосов
/ 28 января 2011
2 голосов
/ 10 августа 2014

Приятно отметить, что если вы используете GCC, вам не нужно использовать встроенный asm для получения этой инструкции.Вы можете использовать одну из функций __sync, например:

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);
}

Microsoft имеет аналогичную функцию для VC ++:

__int64 exchhi = __int64(with >> 64);
__int64 exchlo = (__int64)(with);

return _InterlockedCompareExchange128(a, exchhi, exchlo, &cmp) != 0;
0 голосов
/ 10 августа 2014

Я получил компиляцию для g ++ с небольшим изменением (удаление pordr oword в инструкции cmpxchg16b). Но, похоже, он не перезаписывает память по мере необходимости, хотя я могу ошибаться. [см. Обновление] Ниже приведен код с последующим выводом.

#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;
}

Вывод

FFFFFFADFFFFFFFBFFFFFFCAFFFFFFDE


55555555

Не уверен, что вывод имеет смысл для меня.Я ожидал, что значение before будет что-то вроде 00000000decafbad00000feedbeef в соответствии с определением структуры.Но кажется, что байты разбросаны по словам.Это из-за выровненной директивы?Между тем, похоже, что операция CAS возвращает правильное возвращаемое значение.Любая помощь в расшифровке этого?

Обновление : Я только что сделал некоторую отладку с проверкой памяти с помощью GDB.Там указаны правильные значения.Поэтому я думаю, что это должно быть проблемой с моей процедурой print_dlong.Не стесняйтесь исправить это.Я оставляю этот ответ, поскольку он должен быть исправлен, так как исправленная версия этого будет поучительной для операции cas с печатными результатами.

...