2011-01-28 8 views
7

Questo non sembra proprio giusto anche se non sono sicuro del perché. consiglio sarebbe grande come la documentazione per CMPXCHG16B è piuttosto limitata (non possiedo tutti i manuali intel ...)CMPXCHG16B corretto?

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

Quando si esegue con un esempio io sono sempre 0 quando dovrebbe essere 1. Tutte le idee ?

risposta

11

notato alcune questioni,

(1) Il problema principale è i vincoli, "Rax" non fa quello che sembra, piuttosto il primo carattere "r" consente l'uso gcc qualsiasi registro.

(2) Non sono sicuro del tipo di archiviazione: uint128_t, ma assumendo il little endian standard per le piattaforme x86, vengono scambiate anche le password alta e bassa.

(3) Prendere l'indirizzo di qualcosa e inviarlo a qualcos'altro può infrangere le regole di aliasing. Dipende da come i tuoi tipi :: uint128_t sono definiti da wether o meno questo è un problema (bene se è una struttura di due uint64_t). GCC con -O2 ottimizzerà l'ipotesi che le regole di aliasing non siano violate.

(4) * src deve essere contrassegnato come output, anziché specificare il clobber della memoria. ma questa è davvero più una prestazione piuttosto che un problema di correttezza. allo stesso modo, rbx e rcx non devono essere specificati come danneggiati.

Ecco una versione che funziona,

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

Grazie mille, ha senso. –

+2

Ho copiato e incollato il codice e durante la compilazione con "g ++ - 4.7 -g -DDEBUG = 1 -std = C++ 0x -pthread dwcas.c -o dwcas.o -ldl -lpthread" Ricevo dwcas.c : 29: Errore: junk 'ptr 'dopo l'espressione. qualche idea perché? –

+0

Dovrebbe essere semplicemente 'lock cmpxchg16b% 1'. La dimensione in questo caso non è necessaria poiché è implicita dall'istruzione 'cmpxchg16b'. L'uso di 'oword ptr 'mi suggerirebbe che stavi pensando che questo fosse _MASM_ assembler che non si assemblerebbe con l'assemblatore GNU. –

0

ho preso la compilazione per g ++ con una lieve modifica (rimozione oWord ptr nell'istruzione cmpxchg16b). Ma non sembra sovrascrivere la memoria come richiesto anche se potrei sbagliarmi.[Vedere l'aggiornamento] Il codice è riportato di seguito seguito dall'output.

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

uscita

FFFFFFADFFFFFFFBFFFFFFCAFFFFFFDE 


55555555 

Non che l'uscita ha senso per me. Mi aspettavo che il valore precedente fosse qualcosa come 00000000decafbad00000feedbeef in base alla definizione della struttura. Ma i byte sembrano essere distribuiti nelle parole. È dovuto alla direttiva allineata? Tuttavia, l'operazione CAS sembra restituire il valore di ritorno corretto. Qualche aiuto per decifrarlo?

Aggiornamento: Ho appena fatto un po 'di debug con l'ispezione di memoria con gdb. Qui vengono mostrati i valori corretti. Quindi penso che questo dovrebbe essere un problema con la mia procedura print_dlong. Sentiti libero di correggerlo. Lascio questa risposta in quanto è da correggere, poiché una versione corretta di questo sarebbe istruttiva dell'operazione cas con risultati stampati.

1

È bene notare che se si utilizza GCC, non è necessario utilizzare in linea asm per ottenere questa istruzione.È possibile utilizzare una delle funzioni __sync, come:

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 ha una funzione simile per VC++:

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

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