2011-01-28 19 views
7

Esto no parece exactamente correcto aunque no estoy seguro de por qué. consejo sería grande como la documentación para CMPXCHG16B es bastante escaso (No soy dueño de todos los manuales de inteligencia ...)CMPXCHG16B ¿correcto?

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

Cuando se ejecuta con un ejemplo que estoy recibiendo 0 cuando debería ser 1. Todas las ideas ?

Respuesta

11

notado algunas cuestiones,

(1) El principal problema son las limitaciones, "rax" no hace lo que parece, más bien el primer carácter "r" permite el uso gcc cualquier registro.

(2) No estoy seguro de cómo su tipo de almacenamiento :: uint128_t, pero asumiendo el pequeño endian estándar para las plataformas x86, entonces las palabras clave alta y baja también se intercambian.

(3) Tomar la dirección de algo y convertirlo a otra cosa puede romper las reglas de aliasing. Depende de cómo se definan tus tipos :: uint128_t en cuanto a si esto es un problema (bien si es una estructura de dos uint64_t's). GCC con -O2 optimizará asumiendo que las reglas de aliasing no son violadas.

(4) * src realmente debería marcarse como una salida, en lugar de especificar la memoria de clobber. pero esto es realmente más un problema de rendimiento que de corrección. Del mismo modo, rbx y rcx no necesitan ser especificados como triturados.

Aquí está un una versión que funciona,

#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); 
} 
  • Lucas
+0

Muchas gracias, tiene sentido. –

+2

He copiado y pegado tu código, y al compilar con "g ++ - 4.7 -g -DDEBUG = 1 -std = C++ 0x -pthread dwcas.c -o dwcas.o -ldl -lpthread" Obtengo dwcas.c : 29: Error: basura 'ptr 'después de la expresión. alguna idea ¿por qué? –

+0

Simplemente debería ser 'lock cmpxchg16b% 1'. El tamaño en este caso no es necesario ya que está implícito en la instrucción 'cmpxchg16b'. El uso de 'oword ptr' me sugiere que crees que se trata de un ensamblador deMASM_ que no se ensamblará con el ensamblador de GNU. –

0

lo tengo compilación de g ++ con un ligero cambio (eliminación oWord PTR en la instrucción cmpxchg16b). Pero no parece sobrescribir la memoria como se requiere, aunque puedo estar equivocado.[Ver actualización] El código se proporciona a continuación, seguido de la salida.

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

salida

FFFFFFADFFFFFFFBFFFFFFCAFFFFFFDE 


55555555 

No es que la salida tiene sentido para mí. Esperaba que el valor anterior fuera algo así como 00000000decafbad00000feedbeef según la definición de estructura. Pero los bytes parecen estar distribuidos dentro de las palabras. ¿Es eso debido a una directiva alineada? Por cierto, la operación CAS parece devolver el valor de retorno correcto. ¿Alguna ayuda para descifrar esto?

Actualización: Acabo de realizar algunas depuraciones con la inspección de memoria con gdb. Allí se muestran los valores correctos. Así que supongo que esto debe ser un problema con mi procedimiento print_dlong. Siéntase libre de corregirlo. Dejo esta respuesta tal como se va a corregir, ya que una versión corregida de esto sería instructiva de la operación cas con resultados impresos.

1

Es bueno tener en cuenta que si está utilizando GCC, no necesita usar asm en línea para seguir estas instrucciones.Puede utilizar una de las funciones __sync, como:

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 tiene una función similar para VC++:

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

return _InterlockedCompareExchange128(a, exchhi, exchlo, &cmp) != 0;