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(...);