Open sorear opened 7 years ago
Atomic_is_lock_free returns true if we can directly emit an atomic sequence, otherwise it calls into libatomic to see if libatomic can emit an atomic sequence. But atomic_always_lock_free is intended to be used in cases where we require a compile-time constant, so it returns true if we can directly emit an atomic sequence, otherwise it returns false. It doesn't matter that libatomic might be able to emit the sequence. So the results are correct, given that we don't have subword atomics.
The only fix is to implement subword atomics. It does appear that every interesting target except RISC-V has subword atomics.
IIRC, the correct thing to do here is to implement the shorter atomic sequences as LR/SC sequences. I think something like this should do it, unless @aswaterman has a smarter idea?
char atomic_add_1(char *addr, char addend) {
__asm__ volatile (
1b:
lr.w %[out], %[addr]
add %[out], %[out], %[addend]
sc.w %[ok], %[out], %[addr]
bnez %[ok], 1b
: [addr]"r"(addr / 4 * 4), [addend]"r"(addend << 8 * addr % 4)
: [ok]"=r"(ok), [out]"=r"(out)
}
For the bitwise logical atomics, you can just use AMOx.W, with an address of addr & -4
and operand of (value & 0xFF) << (8 * (addr % 4))
.
.For addition, you need to use LR/SC, but that code's not quite right because the addition can carry out into the next-highest byte within the word. So you need to do something like
mask = 0xFF << (8 (addr % 4)); out = ((out + (addend << (8 (addr % 4)))) & mask) | (out & ~mask)
Also don't forget to mask off the LSBs of the address before doing the LR/SC.
The necessary code already exists in libatomic (__atomic_fetch_add_2 et al do not use the mutex, they are simple lr/sc loops) but needs to be moved into libgcc or inlined by the compiler to match behavior of other targets.
the issue has been solved since 13.2.0 version, test on https://godbolt.org/
Since the fallback code is lock-free, we should be getting 1 in the first two cases. This is probably related to not having patterns for size 1/size 2 atomics. (??? is this actually true)
(while factually incorrect, I can't find anything in either the gcc documentation or the C11 final draft which specifically forbids this)