// bsls_atomicoperations_x64_all_gcc.h                                -*-C++-*-
#ifndef INCLUDED_BSLS_ATOMICOPERATIONS_X64_ALL_GCC
#define INCLUDED_BSLS_ATOMICOPERATIONS_X64_ALL_GCC

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

//@PURPOSE: Provide implementations of atomic operations for X86_64/GCC.
//
//@CLASSES:
// bsls::AtomicOperations_X64_ALL_GCC: implementation of atomics for X86_64/GCC
//
//@DESCRIPTION: This component provides classes necessary to implement atomics
// on the Linux X86_64 platform with GCC.  The classes are for private use
// only.  See `bsls_atomicoperations` and `bsls_atomic` for the public
// interface to atomics.
//
///Notes
///-----
// * GCC atomic intrinsics are used where possible instead of the assembly
//   code,
// * "+m" constraint may cause an `inconsistent operand constraint` error in
//   GCC 3.x in optimized builds, therefore sometimes a combination of output
//   "=m" and input "m" constraints is used.

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

#if defined(BSLS_PLATFORM_CPU_X86_64) \
    && (defined(BSLS_PLATFORM_CMP_GNU) || defined(BSLS_PLATFORM_CMP_CLANG))

namespace BloombergLP {

namespace bsls {

struct AtomicOperations_X64_ALL_GCC;
typedef AtomicOperations_X64_ALL_GCC  AtomicOperations_Imp;

               // ===============================================
               // Atomic_TypeTraits<AtomicOperations_X64_ALL_GCC>
               // ===============================================

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

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

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

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

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

                     // ===================================
                     // struct AtomicOperations_X64_ALL_GCC
                     // ===================================

struct AtomicOperations_X64_ALL_GCC
    : AtomicOperations_Default64<AtomicOperations_X64_ALL_GCC>
{
    typedef Atomic_TypeTraits<AtomicOperations_X64_ALL_GCC> AtomicTypes;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                     // -----------------------------------
                     // struct AtomicOperations_X64_ALL_GCC
                     // -----------------------------------

inline
int AtomicOperations_X64_ALL_GCC::
    getInt(const AtomicTypes::Int *atomicInt)
{
    int ret;

    asm volatile (
        "       movl %[obj], %[ret]     \n\t"

                : [ret] "=r" (ret)
                : [obj] "m"  (*atomicInt)
                : "memory");

    return ret;
}

inline
void AtomicOperations_X64_ALL_GCC::
    setInt(AtomicTypes::Int *atomicInt, int value)
{
    asm volatile (
        "       movl %[val], %[obj]     \n\t"
        "       mfence                  \n\t"

                : [obj] "=m" (*atomicInt)
                : [val] "r"  (value)
                : "memory");
}

inline
void AtomicOperations_X64_ALL_GCC::
    setIntRelease(AtomicTypes::Int *atomicInt, int value)
{
    asm volatile (
        "       movl %[val], %[obj]     \n\t"

                : [obj] "=m" (*atomicInt)
                : [val] "r"  (value)
                : "memory");
}

inline
int AtomicOperations_X64_ALL_GCC::
    swapInt(AtomicTypes::Int *atomicInt, int swapValue)
{
    asm volatile (
        "       lock xchgl %[val], %[obj]   \n\t"

                : [obj] "=m" (*atomicInt),
                  [val] "=r" (swapValue)
                : "1" (swapValue), "m" (*atomicInt)
                : "memory");

    return swapValue;
}

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

inline
int AtomicOperations_X64_ALL_GCC::
    addIntNv(AtomicTypes::Int *atomicInt, int value)
{
    return __sync_add_and_fetch(&atomicInt->d_value, value);
}

inline
Types::Int64 AtomicOperations_X64_ALL_GCC::
    getInt64(const AtomicTypes::Int64 *atomicInt)
{
    Types::Int64 result;

    asm volatile (
        "       movq %[obj], %[res]         \n\t"

                : [res] "=r" (result)
                : [obj] "m"  (*atomicInt)
                : "memory");

    return result;
}

inline
void AtomicOperations_X64_ALL_GCC::
    setInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    asm volatile (
        "       movq %[val], %[obj]         \n\t"
        "       mfence                      \n\t"

                : [obj] "=m" (*atomicInt)
                : [val] "r"  (value)
                : "memory");
}

inline
void AtomicOperations_X64_ALL_GCC::
    setInt64Release(AtomicTypes::Int64 *atomicInt,
                    Types::Int64 value)
{
    asm volatile (
        "       movq %[val], %[obj]         \n\t"

                : [obj] "=m" (*atomicInt)
                : [val] "r"  (value)
                : "memory");
}

inline
Types::Int64 AtomicOperations_X64_ALL_GCC::
    swapInt64(AtomicTypes::Int64 *atomicInt,
              Types::Int64 swapValue)
{
    asm volatile (
        "       lock xchgq %[val], %[obj]   \n\t"

                : [val] "+r" (swapValue),
                  [obj] "=m" (*atomicInt)
                : "m" (*atomicInt)
                : "memory");

    return swapValue;
}

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

inline
Types::Int64 AtomicOperations_X64_ALL_GCC::
    addInt64Nv(AtomicTypes::Int64 *atomicInt,
               Types::Int64 value)
{
    return __sync_add_and_fetch(&atomicInt->d_value, value);
}

}  // close package namespace

}  // close enterprise namespace

#endif // defined(BSLS_PLATFORM_CPU_X86_64) && (CMP_GNU || CMP_CLANG)

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 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 ----------------------------------
