Hello,
On Mon, 22 Mar 2021, Dmitry Selyutin wrote:
"is it really the same type?"
A bit ambiguous wording. What I mean is that, for a naturally-aligned type
N, the corresponding call can be generated to routine __atomic_X_N. The code
generator doesn't need to care whether types in the _implementation_ of
__atomic_X_N must literally match those passed; if the semantics match, this
is enough. And, indeed, the code below...
#include <stdatomic.h>
#include <stdint.h>
struct combo {
uint16_t lo;
uint16_t hi;
};
struct combo xchg(_Atomic(struct combo) *atom, struct combo value)
{
return atomic_exchange(atom, value);
}
...causes the compiler to generate the same code as if it operated on
uint32_t.
gcc and clang for x86 inline this code, and this is explicitly allowed by
the documentation, with some additional remarks.
It appears to me that the documentation speaks of code generation. We could
have emitted a call, and this also would have been OK.
I think at this point we have managed to talk past each other. You do
seem to agree that the size specific __atomic_X_N "functions" are an
implementation detail, and hence how and if they are implemented in TCC
doesn't matter (much). In the broader sense you agree that how exactly
the secret sauce of atomics is implemented doesn't matter.
So, what's left to decide is how to deal with generic functions, here the
<stdatomic.h> ones that can accept and return rvalues of arbitrary
(including struct) type, ideally in a way that doesn't add much code to
TCC.
grischka proposed a compact way involving no compiler changes at all, and
I basically tried to find out why you didn't want to go that route, given
that this would be an implementation detail.
(FWIW, in the full glory the generic implementation of e.g. atomic_load
would need statement expressions, a special typeof and a temporary:
#define atomic_load(p) ({T(*p) r; __atomic_load_impl(p, &r, sizeof(r)); r; })
where T(*p) is basically __typeof__(*p) but removes _Atomic, that could
be another extension, or further games with _Generic. )
Now, given that the above hackery would enable the features without any
TCC compiler help it's reasonable to ask what kind of compiler changes are
required to make the hackery a bit nicer, without having to implement
everything in it.
Maybe it also turns out that your current approach is in fact the nicest
one (it certainly generates better code than the above). But there's
certainly duplication between parse_atomic and the function call parser.
So maybe another way that could be rectified is by adding an extension to
TCC that would allow to declare the generic functions directly, ala:
_Generic atomic_load(const volatile _Atomic _Generic *p);
with the following semantic: _Generic is a valid (unqualified?) base type,
and when used in function decls there must be at least one argument with
it, and all mentions need to refer to the same type at calls. This would
also allow to implement the type generic math functions nicely
(<tgmath.h>). (I'm using the same keyword as for the generic selection,
possibly that's a bad idea :) ). You would also have to extend the
function call parser somewhat (and of course the type parser and matcher),
but it would be a general change that wouldn't need amendements in the
compiler whenever a new generic function is added to some new standard.
That still leaves the question how to make the connection between this
decl and which functions are ultimately called (and how), and maybe that
deserves hardcoding in the compiler for now to be done in the same way as
the above macro hackery would do by hand. Not really thought through
completely, but merely meant as idea trigger.
Ciao,
Michael.
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel