Пример cmpxchg для 64-битного целого - PullRequest
5 голосов
/ 07 мая 2009

Я использую cmpxchg (сравнение и обмен) в архитектуре i686 для 32-битного сравнения и замены следующим образом.

(Примечание редактора: исходный 32-разрядный пример содержал ошибки, но вопрос не в этом. Я считаю, что эта версия безопасна и в качестве бонуса корректно компилируется и для x86-64. Также обратите внимание этот встроенный asm не нужен или не рекомендуется для этого; __atomic_compare_exchange_n или более старый __sync_bool_compare_and_swap работают на int32_t или int64_t на i486 и x86-64 . Но этот вопрос о том, как сделать это с помощью встроенного asm, если вы все еще хотите.)

// note that this function doesn't return the updated oldVal
static int CAS(int *ptr, int oldVal, int newVal)
{
    unsigned char ret;
    __asm__ __volatile__ (
            "  lock\n"
            "  cmpxchgl %[newval], %[mem]\n"
            "  sete %0\n"
            : "=q" (ret), [mem] "+m" (*ptr), "+a" (oldVal)
            : [newval]"r" (newVal)
            : "memory");    // barrier for compiler reordering around this

    return ret;   // ZF result, 1 on success else 0
}

Что является эквивалентом архитектуры x86_64 для сравнения и обмена 64 битами

static int CAS(long *ptr, long oldVal, long newVal)
{
    unsigned char ret;
    // ?
    return ret;
}

Ответы [ 4 ]

7 голосов
/ 07 мая 2009

Набор команд x86_64 содержит инструкцию cmpxchgq (q для четырех слов) для 8-байтового (64-битного) сравнения и замены.

Существует также инструкция cmpxchg8b, которая будет работать с 8-байтовыми величинами, но ее сложнее настроить, требуя, чтобы вы использовали edx:eax и ecx:ebx, а не более естественные 64-битные rax. Причина, по которой это существует, почти наверняка связана с тем фактом, что Intel потребовались 64-битные операции сравнения и замены задолго до появления x86_64. Он все еще существует в 64-битном режиме, но больше не является единственным вариантом.

Но, как уже говорилось, cmpxchgq, вероятно, является лучшим вариантом для 64-битного кода.


Если вам необходимо cmpxchg 16-байтового объекта, 64-битная версия cmpxchg8b будет cmpxchg16b. Он отсутствовал в самых ранних процессорах AMD64, поэтому компиляторы не сгенерируют его для std :: atomic :: compare_exchange на объектах 16B, если вы не включите -mcx16 (для gcc). Однако ассемблеры будут его собирать, но имейте в виду, что ваш двоичный файл не будет работать на самых ранних процессорах K8. (Это относится только к cmpxchg16b, а не к cmpxchg8b в 64-битном режиме или к cmpxchgq).

2 голосов
/ 07 мая 2009

CMPXCHG8B

__forceinline int64_t interlockedCompareExchange(volatile int64_t & v,int64_t exValue,int64_t cmpValue)
{
  __asm {
    mov         esi,v
    mov         ebx,dword ptr exValue
    mov         ecx,dword ptr exValue + 4
    mov         eax,dword ptr cmpValue
    mov         edx,dword ptr cmpValue + 4
    lock cmpxchg8b qword ptr [esi]
  }
}
1 голос
/ 07 мая 2009

Архитектура x64 поддерживает 64-битный сравнительный обмен с использованием старой доброй инструкции cmpexch. Или вы могли бы также использовать более сложную инструкцию cmpexch8b (из " Руководство для программиста архитектуры AMD64 Том 1: Программирование приложений "):

Инструкция CMPXCHG сравнивает значение в регистре AL или rAX с первый (целевой) операнд и устанавливает арифметические флаги (ZF, OF, SF, AF, CF, PF) в зависимости от результата. Если сравниваемые значения равны, исходный операнд загружается в целевой операнд Если они не равно первый операнд загружен в аккумулятор. CMPXCHG может быть раньше пытался перехватить семафор, то есть проверить, является ли его состояние свободным, и если Итак, загрузите новое значение в семафор, делающий его состояние занятым. тест и нагрузка выполнены атомарно, так что одновременно процессы или потоки, которые используют семафор для доступа к общему объекту не будет конфликтовать

CMPXCHG8B Инструкция сравнивает 64-битные значения в EDX:EAX регистры с 64-битным место в памяти. Если значения равный, нулевой флаг (ZF) установлен, и ECX:EBX значение копируется в место в памяти. В противном случае ZF флаг очищается, а значение памяти копируется в EDX:EAX.

The CMPXCHG16B Инструкция сравнивает 128-битное значение в регистрах RDX:RAX и RCX:RBX с 128-битной ячейкой памяти. Если значения равны, нулевой флаг (ZF) и значение RCX:RBX скопированы в ячейку памяти. В противном случае флаг ZF сбрасывается, и значение памяти копируется в rDX:rAX.

Разные синтаксисы ассемблера могут иметь длину операций, указанную в мнемонике инструкции, если размер операндов не может быть определен. Это может быть в случае встроенного ассемблера GCC - я не знаю.

0 голосов
/ 01 декабря 2013

использование cmpxchg8B из Руководства по программированию для архитектуры AMD64 V3:

Сравнить EDX: регистр EAX в 64-битной ячейке памяти. Если равен, установите флаг нуля (ZF) на 1 и скопируйте регистр ECX: EBX в ячейку памяти. Иначе, скопируйте ячейку памяти в EDX: EAX и очистите флаг нуля.

Я использую cmpxchg8B для реализации простой функции блокировки мьютекса в машине x86-64. вот код

.text
.align 8
.global mutex_lock
mutex_lock:
    pushq   %rbp
    movq    %rsp,   %rbp

    jmp .L1

.L1:
    movl    $0, %edx
    movl    $0, %eax
    movl    $0, %ecx
    movl    $1, %ebx
    lock    cmpxchg8B   (%rdi)
    jne .L1
    popq    %rbp
    ret
...