gcc atomics: sync_and_and_fetch - зачем там цикл?

yolki

для 64-битного линукса оно сгенерило такой код:

saaf:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp %rdi
movq -8(%rbp %rdx
movq (%rdx %rax
.L4:
movq %rax, %rcx
andq %rdi, %rcx
movq %rcx, %rsi
lock cmpxchgq %rcx, (%rdx)
sete %cl
testb %cl, %cl
je .L4
movq %rsi, %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

исходник:

long saaf(volatile long *x, long v)
{
return __sync_and_and_fetch(x,v);
}

нафига там цикл?

ppplva

А как без цикла?
"lock and" оcтавит результат в памяти, и атомарно его оттуда уже не вытащить.
И да, для читабельности надо компилировать с оптимизацией:
 
0: 48 8b 07 mov (%rdi%rax
3: 48 89 c2 mov %rax,%rdx
6: 48 21 f2 and %rsi,%rdx
9: f0 48 0f b1 17 lock cmpxchg %rdx%rdi)
e: 75 f3 jne 3 <_Z4saafPVll+0x3>
10: 48 89 d0 mov %rdx,%rax
13: c3 retq

salamander

А как без цикла?
"lock and" оcтавит результат в памяти, и атомарно его оттуда уже не вытащить.
Для иллюстрации:
void saaf(volatile long *x, long v)
{
__sync_and_and_fetch(x,v);
}

saaf:
lock andq %rsi, (%rdi)
ret

ppplva

Exactly.
А зачем он у меня две лишние инструкции mov rax -> rdx -> rax нахреначил?

salamander

Да вроде не лишние они...
В адресах 3 и 6 в %rax лежит старое значение (%rdi а в %rdx вычисляется новое.
После cmpxhg выолняется одно из двух:
1) значение в (%rdi) поменялось пока мы вычисляли %rdx -> загружаем в %rax новое значение и retry
2) значение в (%rdi) не поменялось -> пишем туда значение из %rdx и выходим из цикла
теперь нам нужно вернуть новое значение, которое в %rdx -> записываем его в %rax.
Поскольку cmpxchg работает чисто с rax и возвращаемое значение тоже, то никак mov не сэкономить.

yolki

Ясно!
Спасибо всем, разобрался
Оставить комментарий
Имя или ник:
Комментарий: