https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71191

--- Comment #6 from dhowells at redhat dot com <dhowells at redhat dot com> ---
There are a couple of ways the problem could be reduced in scope.  Most of the
constructs that the kernel has that fall into this category are conditional
adds/subtracts:

    typedef struct { int counter; } atomic_t;
    bool atomic_inc_unless_negative(atomic_t *v)
    bool atomic_dec_unless_positive(atomic_t *v)
    bool atomic_inc_not_zero(atomic_t *v)
    bool atomic_dec_if_positive(atomic_t *v)
    bool atomic_add_unless(atomic_t *v, int addend, int unless)

all of which conform to a pattern that looks like:

    ({
        TYPE cur = __atomic_load_n(PTR, __ATOMIC_RELAXED);
        TYPE new;
        bool added = true;

        do {
                if (cur COMPARISON COMPAREND) {
                        added = false;
                        break;
                }
                new = cur + ADDEND;
        } while (!__atomic_compare_exchange_n(PTR,
                                              &cur, new,
                                              false,
                                              __ATOMIC_SEQ_CST,
                                              __ATOMIC_RELAXED));
        added;
    })

where PTR is a pointer to the memory variable to be modified, TYPE is the type
of *PTR, ADDEND and COMPAREND are integer values and COMPARISON is a numeric
comparison.  Then there's:

    bool atomic_inc_not_zero_hint(atomic_t *v, int hint)

This is similar to the above, except that hint is used in place of the initial
__atomic_load_n().  Note that if you're doing LL/SC rather than a CAS loop, the
hint is of no interest.

And then there is:

    int __atomic_add_unless(atomic_t *v, int addend, int unless)

which returns the original value of v->counter instead of an indication as to
the success.  I would probably replace this with something like:

    bool atomic_add_unless_return(atomic_t *v, int addend, int unless, int
*_orig)

and return the original value of v->counter through the *_orig so that it
conforms more closely with the pattern above.


So the two ways to reduce the scope of the problem that I'm thinking of are:

 (1) Add very restricted patterns.  Just a single comparison and an add.

 (2) Add extra intrinsics.  I presume there would need to be one per
comparison:

        bool __atomic_fetch_add_if_eq(T *ptr, T addend, T comparend, T *_orig,
                                         int memorder_yes, int memorder_no);
        bool __atomic_fetch_add_if_ne(...);
        bool __atomic_fetch_add_if_lt(...);
        bool __atomic_fetch_add_if_le(...);
        bool __atomic_fetch_add_if_gt(...);
        bool __atomic_fetch_add_if_ge(...);

Reply via email to