[cfe-dev] atomic intrinsics

Howard Hinnant hhinnant at apple.com
Fri Oct 15 17:03:34 PDT 2010


Thanks for looking at this John.

On Oct 15, 2010, at 7:21 PM, John McCall wrote:

> On Oct 7, 2010, at 5:30 PM, Howard Hinnant wrote:
>> I see what you mean.  I'll mention this in the design doc.  I believe it will be an issue for any design we choose (A, B, or C).
> 
> 
> After some thought, I think I personally prefer proposal A, slightly modified such that __atomic_is_lock_free is a pseudofunction like __is_pod().

I see, like:

   bool __atomic_is_lock_free(type);

?

>  The builtins have exactly those names and are "overloaded";  memory ordering parameters are required to be constant expressions but not necessarily literals.

If the memory orderings are required to be constant expressions, this is really Design B, just with some renaming.  It means the library must still switch() to convert the run time memory orderings it receives into compile time orderings.  E.g.:

inline _LIBCPP_INLINE_VISIBILITY
bool atomic_compare_exchange_weak_explicit(volatile atomic_bool* __obj,
                                           bool* __exp, bool __desr,
                                           memory_order __s, memory_order __f)
{
    __f = __translate_memory_order(__f);
    switch (__s)
    {
    case memory_order_relaxed:
        return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 0, 0);
    case memory_order_consume:
        switch (__f)
        {
        case memory_order_relaxed:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 1, 0);
        case memory_order_consume:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 1, 1);
        }
    case memory_order_acquire:
        switch (__f)
        {
        case memory_order_relaxed:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 2, 0);
        case memory_order_consume:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 2, 1);
        case memory_order_acquire:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 2, 2);
        }
    case memory_order_release:
        switch (__f)
        {
        case memory_order_relaxed:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 3, 0);
        case memory_order_consume:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 3, 1);
        case memory_order_acquire:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 3, 2);
        }
    case memory_order_acq_rel:
        switch (__f)
        {
        case memory_order_relaxed:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 4, 0);
        case memory_order_consume:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 4, 1);
        case memory_order_acquire:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 4, 2);
        }
    case memory_order_seq_cst:
        switch (__f)
        {
        case memory_order_relaxed:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 5, 0);
        case memory_order_consume:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 5, 1);
        case memory_order_acquire:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 5, 2);
        case memory_order_seq_cst:
            return __atomic_compare_exchange_weak(&__obj->__v_, __exp, __desr, 5, 5);
        }
    }
}

This is not a problem for libc++.  However I've heard rumblings that this won't be ok for the C library.  Want to comment Blaine?

>  Arguments not of fundamental type, or not exactly matching a permitted type for the intrinsic, are not required to be supported by the implementation (so calling swap with a long* and an int, or a Base** and a Derived*, or a ConvertibleToIntPtr and a ConvertibleToInt, may or may not have sensible results).

I'm afraid my spec is ambiguous, sorry.  In:

bool __atomic_compare_exchange_strong(type* atomic_obj,
                                      type* expected, type desired,
                                      int mem_success, int mem_failure);

It was my intention that in each argument that specifies "type", the types are all required to be the same.  In C++ templates, this would be:

template <class type>
bool __atomic_compare_exchange_strong(type* atomic_obj,
                                      type* expected, type desired,
                                      int mem_success, int mem_failure);

Concerning arguments "type" that are not scalars: I believe I can handle this in libc++ with some fancy template meta programming, and pass them through to the front end type-punned as scalars if there is a scalar of the appropriate size, and otherwise route them to a library-lock function.  I can do this because "type" is a-priori known to be trivially copyable.

However, and again, this is significant functionality at the library level that I don't know will be addressible for the C library.  Also it misses the optimization Jeffrey mentioned for pair<void*,void*> on x86-64.  I.e. x86-64 can atomically handle 16 byte objects, but there is no 16 byte scalar which can be used to slip it to the front end (unless the front end accepts long double I guess).

> This doesn't strictly matter for you, but we would implement this in Clang by having the frontend do the ABI-compliant lowerings for the locking implementations and then emitting everything that can be done lock-free as an LLVM intrinsic call.

Does this translate to a call to compiler-rt for the locking implementations?  I ask because I'm probably going to be the one to implement these in compiler-rt if they are needed.

> Anyway, just my vote.

Thanks much John.

-Howard





More information about the cfe-dev mailing list