Add C11/C18 stdatomic.h implementation
This implementation relies heavily on a new set of _kernel_atomic_*
functions which implement all the atomic operations.
Multiple different implementations of the functions are provided, to cater to all machines from ARMv2 to ARMv8.
When targeting multiple CPU architectures, multiple variants will be compiled into the module, and the most appropriate variant will be selected at runtime when the client's stubs are filled in (using an extended version of the existing PickRoutineVariant system).
Currently ROM clients will just use the variant for the oldest enabled architecture, to avoid the performance hit of runtime switching on every function call.
The 40 new routines have been placed in a new library chunk (#6), so that old CLibs will properly reject any client that tries to use them, and to avoid ~500B of stubs bloat for programs that don't use them.
Test & benchmark code is provided in the test/atomic folder.
Potential future improvements:
- Compiler improvements to allow both
_Atomic
and_Atomic()
to be supported (even if they're both NOPs, as_Atomic()
is now) (point 1 in the "Notes/restrictions" list in stdatomic.h) - Compiler support for the ACLE architecture profile and/or feature test macros (or similar) so that inline code can be used instead of library functions where possible, and the
ATOMIC_*_LOCK_FREE
defines can be made more accurate (points 2 & 3 in "Notes/restrictions") - Compiler support for doubleword aligned types (point 4 in "Notes/restrictions")
- Create ARMv8 optimised variants; my initial attempt generally performed worse than the ARMv7 routines when tested on a Pi 4, so I've removed them (but they're still available in the commit history for reference)
- Compiler change to remove
Non-ANSI #include <stdatomic.h>
warning
Point 6 of the "Notes/restrictions" talks about pre-ARMv6K application clients using variants that avoid disabling interrupts ("UARMa" and "UHalfword"), making them non-atomic from the perspective of interrupt handlers, callbacks, etc. E.g. if a C signal handler is called in the middle of an operation then that could cause problems if the signal handler tries to perform an atomic operation. Since these variants use a global SWP-based spinlock, there's a good chance the signal handler will get stuck in a deadlock. Should we play it safe and disable those variants? Does anyone have any other ideas on how to avoid disabling interrupts?