[RESOLVIDO] Use __sync_fetch_and_or
em vez de __sync_or_and_fetch
! A instrução double amoor
talvez ainda seja um bug.
[Aviso] Isso pode ser um bug no GCC, mas ainda sou novo na montagem RISC-V, com certeza!
Estou tentando implementar uma função spinlock " rápidaasm()
" em RISC-V (64 bits, por sinal) com C misto e usando compilação cruzada com GCC v14.2.0 e GCC 15.1.0. Este código (bem simplificado):
#define LOCK_BIT ((unsigned long)(1ul<<(8*sizeof(long)-1)))
void lock_hold(long* lock) {
while(__sync_or_and_fetch(lock,LOCK_BIT) < 0);
__sync_synchronize();
}
É renderizado por ambas as versões com -O3
e -O2
como:
lock_hold:
li a5,-1
slli a5,a5,63
.L2:
amoor.d.aqrl a4,a5,0(a0)
amoor.d.aqrl a4,a5,0(a0)
j .L2
que parece ser um loop infinito com 2 amoor
instruções idênticas consecutivas.
Primeiro, não estou esperando um loop infinito aí!
Se eu mudar para -O1
, recebo este código:
lock_hold:
li a4,-1
slli a4,a4,63
.L2:
amoor.d.aqrl a5,a4,0(a0)
or a5,a5,a4. #USELESS?
blt a5,zero,.L2
fence rw,rw
ret
que se parece mais com o que eu esperaria, enquanto -Os
produz este código:
lock_hold:
li a5,-1
slli a5,a5,63
.L2:
amoor.d.aqrl a4,a5,0(a0)
j .L2
novamente com um loop infinito.
Por fim, usando -O0
obtenho basicamente o mesmo que -O1
com algumas instruções extras.
Será que encontrei algum bug ou estou esquecendo de algo? O que estou esquecendo?
Além disso, gostaria de obter também uma resposta de " alguém que saiba muito mais do que eu ".
No código produzido por, -O1
rotulei uma or
instrução como #USELESS?
. Preciso mesmo executar alguma operação a5
para definir o "sinalizador de sinal" após uma amoor
instrução que escreve sobre a5
si mesma?
No caso, algo assim não or a5,zero,a5
seria suficiente?
__sync_or_and_fetch(lock,LOCK_BIT)
define o bit mais alto no longo - o bit de sinal. Como você usa__sync_or_and_fetch
, e não ,__sync_fetch_and_or
você recebe o novo valor salvo emlock
, após definir o bit.Portanto, como você sempre define o bit de sinal e verifica o valor com o bit de sinal definido, sua verificação para o valor de
lock
após esta operação ser negativo sempre será verdadeira. É por isso que você termina em um loop infinito.