2011-01-28 4 views
7

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

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. Любых идей ?

ответ

11

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

(1) Основная проблема заключается в ограничении, «Ракс» не делать то, что он выглядит, а первый символ «R» позволяет GCC использовать какой-либо регистр.

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

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

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

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

#include <stdint.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 oword ptr %1\n\t" 
     "setz %0" 
     : "=q" (result) 
     , "+m" (*src) 
     , "+d" (cmp.hi) 
     , "+a" (cmp.lo) 
     : "c" (with.hi) 
     , "b" (with.lo) 
     : "cc" 
    ); 
    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); 
} 
  • Люк
+0

Спасибо, кучка, имеет смысл. –

+2

Я скопировал и вставил ваш код и при компиляции с помощью «g ++ - 4.7 -g -DDEBUG = 1 -std = C++ 0x -pthread dwcas.c -o dwcas.o -ldl -lpthread» Я получаю dwcas.c : 29: Ошибка: мусор 'ptr 'после выражения. любые идеи почему? –

+0

Это должно быть просто 'lock cmpxchg16b% 1'. Размер в этом случае не нужен, поскольку это подразумевается инструкцией 'cmpxchg16b'. Использование 'oword ptr' подсказывает мне, что вы думали, что это ассемблер _MASM_, который не собирается с помощью сборщика GNU. –

0

Я получил его компиляции для г ++ с небольшим изменением (удаление oword PTR в инструкции 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 

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

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

1

Приятно отметить, что если вы используете 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;