// bsls_atomicoperations_all_all_clangintrinsics.h                    -*-C++-*-
#ifndef INCLUDED_BSLS_ATOMICOPERATIONS_ALL_ALL_CLANGINTRINSICS
#define INCLUDED_BSLS_ATOMICOPERATIONS_ALL_ALL_CLANGINTRINSICS

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Implement atomic operations using clang `__c11_atomic_*` intrinsics
//
//@CLASSES:
//  bsls::AtomicOperations_ALL_ALL_ClangIntrinsics:
//  clang `__c11_atomic_*` intrinsics
//
//@DESCRIPTION: This component provides classes necessary to implement atomics
// in 32bit/64bit mode using clang compiler intrinsics.  The classes are for
// private use only.  See `bsls_atomicoperations` and `bsls_atomic` for the
// public interface to atomics.

#include <bsls_atomicoperations_default.h>
#include <bsls_platform.h>
#include <bsls_types.h>

#if defined(BSLS_PLATFORM_CMP_CLANG)
#if __has_extension(c_atomic) || __has_extension(cxx_atomic)

namespace BloombergLP {

namespace bsls {

struct AtomicOperations_ALL_ALL_ClangIntrinsics;
typedef AtomicOperations_ALL_ALL_ClangIntrinsics  AtomicOperations_Imp;

     // ==================================================================
     // struct Atomic_TypeTraits<AtomicOperations_ALL_ALL_ClangIntrinsics>
     // ==================================================================

template <>
struct Atomic_TypeTraits<AtomicOperations_ALL_ALL_ClangIntrinsics>
{
    struct __attribute__((__aligned__(sizeof(int)))) Int
    {
          _Atomic(int) d_value;
    };

    struct __attribute__((__aligned__(sizeof(Types::Int64)))) Int64
    {
          _Atomic(Types::Int64) d_value;
    };

    struct __attribute__((__aligned__(sizeof(unsigned int)))) Uint
    {
          _Atomic(unsigned int) d_value;
    };

    struct __attribute__((__aligned__(sizeof(Types::Uint64)))) Uint64
    {
          _Atomic(Types::Uint64) d_value;
    };

    struct __attribute__((__aligned__(sizeof(void *)))) Pointer
    {
          _Atomic(void *) d_value;
    };
};

              // ===============================================
              // struct AtomicOperations_ALL_ALL_ClangIntrinsics
              // ===============================================

struct AtomicOperations_ALL_ALL_ClangIntrinsics
  #ifdef BSLS_PLATFORM_CPU_64_BIT
    : AtomicOperations_Default64<AtomicOperations_ALL_ALL_ClangIntrinsics>
  #else
    : AtomicOperations_Default32<AtomicOperations_ALL_ALL_ClangIntrinsics>
  #endif
{
    typedef Atomic_TypeTraits<AtomicOperations_ALL_ALL_ClangIntrinsics>
            AtomicTypes;

    /// compile-time assert
    typedef char AtomicInt_SizeCheck[sizeof(int) == 4 ? 1 : -1];

        // *** atomic functions for int ***

    static void initInt(AtomicTypes::Int *atomicInt, int value);

    static int getInt(const AtomicTypes::Int *atomicInt);

    static int getIntAcquire(const AtomicTypes::Int *atomicInt);

    static int getIntRelaxed(const AtomicTypes::Int *atomicInt);

    static void setInt(AtomicTypes::Int *atomicInt, int value);

    static void setIntRelease(AtomicTypes::Int *atomicInt, int value);

    static void setIntRelaxed(AtomicTypes::Int *atomicInt, int value);

    static int swapInt(AtomicTypes::Int *atomicInt, int swapValue);

    static int swapIntAcqRel(AtomicTypes::Int *atomicInt, int swapValue);

    static int testAndSwapInt(AtomicTypes::Int *atomicInt,
                              int               compareValue,
                              int               swapValue);

    static int testAndSwapIntAcqRel(AtomicTypes::Int *atomicInt,
                                    int               compareValue,
                                    int               swapValue);

    static int addIntNv(AtomicTypes::Int *atomicInt, int value);

    static int addIntNvAcqRel(AtomicTypes::Int *atomicInt, int value);

    static int addIntNvRelaxed(AtomicTypes::Int *atomicInt, int value);

        // *** atomic functions for Int64 ***

    static void initInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value);

    static Types::Int64 getInt64(const AtomicTypes::Int64 *atomicInt);

    static Types::Int64 getInt64Acquire(const AtomicTypes::Int64 *atomicInt);

    static Types::Int64 getInt64Relaxed(const AtomicTypes::Int64 *atomicInt);

    static void setInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value);

    static void setInt64Release(AtomicTypes::Int64 *atomicInt,
                                Types::Int64        value);

    static void setInt64Relaxed(AtomicTypes::Int64 *atomicInt,
                                Types::Int64        value);

    static Types::Int64 swapInt64(AtomicTypes::Int64  *atomicInt,
                                  Types::Int64         swapValue);

    static Types::Int64 swapInt64AcqRel(AtomicTypes::Int64  *atomicInt,
                                        Types::Int64         swapValue);

    static Types::Int64 testAndSwapInt64(AtomicTypes::Int64 *atomicInt,
                                         Types::Int64        compareValue,
                                         Types::Int64        swapValue);

    static Types::Int64 testAndSwapInt64AcqRel(
        AtomicTypes::Int64 *atomicInt,
        Types::Int64        compareValue,
        Types::Int64        swapValue);

    static Types::Int64 addInt64Nv(AtomicTypes::Int64 *atomicInt,
                                   Types::Int64        value);

    static Types::Int64 addInt64NvAcqRel(AtomicTypes::Int64 *atomicInt,
                                         Types::Int64        value);

    static Types::Int64 addInt64NvRelaxed(AtomicTypes::Int64 *atomicInt,
                                          Types::Int64        value);
};

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

              // -----------------------------------------------
              // struct AtomicOperations_ALL_ALL_ClangIntrinsics
              // -----------------------------------------------

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    initInt(AtomicTypes::Int *atomicInt, int value)
{
    __c11_atomic_init(&atomicInt->d_value, value);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    getInt(const AtomicTypes::Int *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(int) *>(&atomicInt->d_value),
               __ATOMIC_SEQ_CST);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    getIntAcquire(const AtomicTypes::Int *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(int) *>(&atomicInt->d_value),
               __ATOMIC_ACQUIRE);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    getIntRelaxed(const AtomicTypes::Int *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(int) *>(&atomicInt->d_value),
               __ATOMIC_RELAXED);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setInt(AtomicTypes::Int *atomicInt, int value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_SEQ_CST);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setIntRelease(AtomicTypes::Int *atomicInt, int value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_RELEASE);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setIntRelaxed(AtomicTypes::Int *atomicInt, int value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_RELAXED);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    swapInt(AtomicTypes::Int *atomicInt, int swapValue)
{
    return
      __c11_atomic_exchange(&atomicInt->d_value, swapValue, __ATOMIC_SEQ_CST);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    swapIntAcqRel(AtomicTypes::Int *atomicInt, int swapValue)
{
    return
      __c11_atomic_exchange(&atomicInt->d_value, swapValue, __ATOMIC_ACQ_REL);
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    testAndSwapInt(AtomicTypes::Int *atomicInt,
                   int               compareValue,
                   int               swapValue)
{
    __c11_atomic_compare_exchange_strong(&atomicInt->d_value,
                                         &compareValue,
                                         swapValue,
                                         __ATOMIC_SEQ_CST,
                                         __ATOMIC_SEQ_CST);
    return compareValue;
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    testAndSwapIntAcqRel(AtomicTypes::Int *atomicInt,
                         int               compareValue,
                         int               swapValue)
{
    __c11_atomic_compare_exchange_strong(&atomicInt->d_value,
                                         &compareValue,
                                         swapValue,
                                         __ATOMIC_ACQ_REL,
                                         __ATOMIC_ACQUIRE);
    return compareValue;
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    addIntNv(AtomicTypes::Int *atomicInt, int value)
{
    return static_cast<unsigned>(__c11_atomic_fetch_add(&atomicInt->d_value,
                                                        value,
                                                        __ATOMIC_SEQ_CST))
           + value;
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    addIntNvAcqRel(AtomicTypes::Int *atomicInt, int value)
{
    return static_cast<unsigned>(__c11_atomic_fetch_add(&atomicInt->d_value,
                                                        value,
                                                        __ATOMIC_ACQ_REL))
           + value;
}

inline
int AtomicOperations_ALL_ALL_ClangIntrinsics::
    addIntNvRelaxed(AtomicTypes::Int *atomicInt, int value)
{
    return static_cast<unsigned>(__c11_atomic_fetch_add(&atomicInt->d_value,
                                                        value,
                                                        __ATOMIC_RELAXED))
           + value;
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    initInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    __c11_atomic_init(&atomicInt->d_value, value);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    getInt64(const AtomicTypes::Int64 *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(Types::Int64) *>(&atomicInt->d_value),
               __ATOMIC_SEQ_CST);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    getInt64Acquire(const AtomicTypes::Int64 *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(Types::Int64) *>(&atomicInt->d_value),
               __ATOMIC_ACQUIRE);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    getInt64Relaxed(const AtomicTypes::Int64 *atomicInt)
{
    return __c11_atomic_load(
               const_cast<_Atomic(Types::Int64) *>(&atomicInt->d_value),
               __ATOMIC_RELAXED);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_SEQ_CST);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setInt64Release(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_RELEASE);
}

inline
void AtomicOperations_ALL_ALL_ClangIntrinsics::
    setInt64Relaxed(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    __c11_atomic_store(&atomicInt->d_value, value, __ATOMIC_RELAXED);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    swapInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 swapValue)
{
    return
      __c11_atomic_exchange(&atomicInt->d_value,
                            swapValue,
                            __ATOMIC_SEQ_CST);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    swapInt64AcqRel(AtomicTypes::Int64 *atomicInt, Types::Int64 swapValue)
{
    return
      __c11_atomic_exchange(&atomicInt->d_value,
                            swapValue,
                            __ATOMIC_ACQ_REL);
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    testAndSwapInt64(AtomicTypes::Int64 *atomicInt,
                     Types::Int64        compareValue,
                     Types::Int64        swapValue)
{
    __c11_atomic_compare_exchange_strong(&atomicInt->d_value,
                                         &compareValue,
                                         swapValue,
                                         __ATOMIC_SEQ_CST,
                                         __ATOMIC_SEQ_CST);
    return compareValue;
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    testAndSwapInt64AcqRel(AtomicTypes::Int64 *atomicInt,
                           Types::Int64        compareValue,
                           Types::Int64        swapValue)
{
    __c11_atomic_compare_exchange_strong(&atomicInt->d_value,
                                         &compareValue,
                                         swapValue,
                                         __ATOMIC_ACQ_REL,
                                         __ATOMIC_ACQUIRE);
    return compareValue;
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    addInt64Nv(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    return static_cast<Types::Uint64>(
                                    __c11_atomic_fetch_add(&atomicInt->d_value,
                                                           value,
                                                           __ATOMIC_SEQ_CST))
           + value;
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    addInt64NvAcqRel(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    return static_cast<Types::Uint64>(
                                    __c11_atomic_fetch_add(&atomicInt->d_value,
                                                           value,
                                                           __ATOMIC_ACQ_REL))
           + value;
}

inline
Types::Int64 AtomicOperations_ALL_ALL_ClangIntrinsics::
    addInt64NvRelaxed(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    return static_cast<Types::Uint64>(
                                    __c11_atomic_fetch_add(&atomicInt->d_value,
                                                           value,
                                                           __ATOMIC_RELAXED))
           + value;
}

}  // close package namespace

}  // close enterprise namespace

#endif  //  __has_extension(c_atomic) || __has_extension(cxx_atomic)

#endif  // BSLS_PLATFORM_CMP_CLANG

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
