stdatomic: ld/st/xchg/cmpxchg on simple types

Some complex types can still be small and simple enough to fit into
register. Other compilers allow some operations on these types, and it
seems to be quite a reasonable choice. From now on, we should be able
to compile the following artificial example:

    struct combo {
        uint16_t lo;
        uint16_t hi;
    };

    struct combo load(const _Atomic(struct combo) *atom)
    {
        return atomic_load(atom);
    }

    void store(_Atomic(struct combo) *atom, struct combo value)
    {
        atomic_store(atom, value);
    }

    struct combo xchg(_Atomic(struct combo) *atom, struct combo value)
    {
        return atomic_exchange(atom, value);
    }

    bool cmpxchg(_Atomic(struct combo) *atom,
            struct combo *cmp, struct combo xchg)
    {
        return atomic_compare_exchange_strong(atom, cmp, xchg);
    }

This might be useful for some corner cases, though it is quite likely
that many programmers will prefer operating on a single 32-bit value
instead of using the structure consisting of 16-bit pair.

Things will work as long as the overall structure size happens to be
the same as for any integer type we support in atomics.
This commit is contained in:
Dmitry Selyutin 2021-03-19 00:54:24 +03:00
parent 82b0af7450
commit 47da8e450e

View File

@ -5766,6 +5766,7 @@ static void parse_atomic(int atok)
int mode;
size_t arg;
SValue *call;
CType rv;
CType atom;
static const char *const templates[] = {
/*
@ -5795,6 +5796,8 @@ static void parse_atomic(int atok)
next();
memset(&rv, 0, sizeof(rv));
memset(&atom, 0, sizeof(atom));
mode = 0; /* pacify compiler */
vpush_helper_func(atok);
call = vtop;
@ -5824,7 +5827,7 @@ static void parse_atomic(int atok)
expect_arg("qualified pointer to atomic value", arg);
if ((template[arg] == 'a') && (atom.t & VT_CONSTANT))
expect_arg("pointer to writable atomic", arg);
switch (btype_size(atom.t & VT_BTYPE)) {
switch (type_size(&atom, &(int){0})) {
case 8: mode = 4; break;
case 4: mode = 3; break;
case 2: mode = 2; break;
@ -5840,8 +5843,13 @@ static void parse_atomic(int atok)
break;
case 'v':
if (!is_integer_btype(vtop->type.t & VT_BTYPE))
if (atom.ref && is_integer_btype(vtop->type.t & VT_BTYPE)) {
if ((tok != TOK___atomic_store) &&
(tok != TOK___atomic_load) &&
(tok != TOK___atomic_exchange) &&
(tok != TOK___atomic_compare_exchange))
expect_arg("integer type", arg);
}
break;
case 'm':
@ -5865,15 +5873,32 @@ static void parse_atomic(int atok)
call->sym = external_helper_sym(atok + mode);
gfunc_call(argc);
vpushi(0);
switch (template[argc]) {
case 'b': PUT_R_RET(vtop, VT_BOOL); break;
case 'v': PUT_R_RET(vtop, atom.t); break;
case 'p': PUT_R_RET(vtop, VT_SIZE_T); break;
case '?': PUT_R_RET(vtop, VT_VOID); break;
default: tcc_error("incorrect atomic template");
case 'b':
vpushi(0);
PUT_R_RET(vtop, VT_BOOL);
break;
case 'p':
vpushs(0);
PUT_R_RET(vtop, VT_SIZE_T);
break;
case 'v':
vpush(&atom);
PUT_R_RET(vtop, atom.t);
break;
case '?':
vpushi(0);
PUT_R_RET(vtop, VT_VOID);
break;
default:
tcc_error("incorrect atomic template");
}
vtop->r2 = VT_CONST;
}
ST_FUNC void unary(void)