I assume these should be defined in compiler-rt. However, I’m not sure where these functions should be defined for a particular target. Is there further information as to where to define them?
Thanks for any additional information on this.
I assume you’re targeting a device that doesn’t have the atomics extension? In that case, I’d expect the __sync_* libcall implementation risks being too specific to the system to be upstreamed into compiler-rt and you’d be best off providing them in your own library. There’s some relevant discussion about the forced-atomics feature in this Rust issue that might provide further background.
There’s some discussion here of the various ways you may want to disable interrupts in your in order to implement these primitives on a system without native atomics. I might have expected a full reference implementation would be floating around somewhere but I haven’t come across one so far. Perhaps @nikic knows of one?
Thanks Alex, you are correct about the device being targeted. I am clear on how I wish to implement each __sync* function. Yes it seems better to implement these in a library and not compiler-rt. I’m just not clear on how to structure and place the library so that custom __sync* implementations are used when compiling.
Thanks for the links, I’ll study them and hopefully I can understand how to correctly implement this. I have looked for a reference implementation but I haven’t found anything so far.
Unless I’m misunderstanding your question, I think you just want to put them in a .o or .a and link with it, so the __sync_* symbols will be found in your lib.
Initially I didn’t think it could be that simple, I thought __sync functions were special in some way and that would not work. I’m writing the library now. Thanks for your help, much appreciated. I was overthinking.
Sadly, when I build the functions I get a ‘cannot redeclare builtin function’ errors from clang e.g.
In file included from ./atomics.cpp:5:
./atomics.h:3:9: error: cannot redeclare builtin function ‘__sync_fetch_and_add_8’
3 | int64_t __sync_fetch_and_add_8(int64_t *Ptr, int64_t Val);
| ^
./atomics.h:3:9: note: ‘__sync_fetch_and_add_8’ is a builtin with type ‘long long (volatile long long *, long long, …) noexcept’
./atomics.cpp:7:9: error: cannot redeclare builtin function ‘__sync_fetch_and_add_8’
7 | int64_t __sync_fetch_and_add_8(int64_t *Ptr, int64_t Val) {
| ^
./atomics.h:3:9: note: ‘__sync_fetch_and_add_8’ is a builtin with type ‘long long (volatile long long *, long long, …) noexcept’
3 | int64_t __sync_fetch_and_add_8(int64_t *Ptr, int64_t Val);
| ^
2 errors generated.
I’ve looked at clang’s implementation for detecting and rejecting certain builtins and I can think of a workaround, but it’s not particularly nice.
Looking into the compiler-rt source, ARM have overridden some of the atomic __sync functions, but I don’t quite understand how yet. The actual implementations have different names, so I suspect there might be an ifdef or similar somewhere that redirects to the functions in e.g.
To avoid that conflict you can specify eventual function name (symbol name) in assembler using __ asm __ keyword in function declaration.
So declare sth like:
int64_t sync_fetch_and_add_8(int64_t *Ptr, int64_t Val) __ asm __ (“__sync_fetch_and_add_8”);
and all is well now. The build completes and llvm-objdump displays the desired name specified by asm(). I’m not sure what was wrong, a rogue character perhaps !!?
Great to hear that!
I’ve forgotten to add that I couldn’t write underscore+underscore+“asm”+underscore+underscore
as it is displayed as bold asm without underscore marks around “asm”.
That is way I’ve added white spaces between underscores and “asm”.