// bsls_atomicoperations_x86_all_gcc.h                                -*-C++-*-
#ifndef INCLUDED_BSLS_ATOMICOPERATIONS_X86_ALL_GCC
#define INCLUDED_BSLS_ATOMICOPERATIONS_X86_ALL_GCC

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

//@PURPOSE: Provide implementations of atomic operations for X86/GCC.
//
//@CLASSES:
//  bsls::AtomicOperations_X86_ALL_GCC: implementation of atomics for X86/GCC.
//
//@DESCRIPTION: This component provides classes necessary to implement atomics
// on the Linux X86 platform with GCC.  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_CPU_X86) \
    && (defined(BSLS_PLATFORM_CMP_GNU) || defined(BSLS_PLATFORM_CMP_CLANG))

namespace BloombergLP {

namespace bsls {

struct AtomicOperations_X86_ALL_GCC;
typedef AtomicOperations_X86_ALL_GCC  AtomicOperations_Imp;

           // ======================================================
           // struct Atomic_TypeTraits<AtomicOperations_X86_ALL_GCC>
           // ======================================================

template <>
struct Atomic_TypeTraits<AtomicOperations_X86_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_X86_ALL_GCC
                     // ===================================

struct AtomicOperations_X86_ALL_GCC
    : AtomicOperations_Default32<AtomicOperations_X86_ALL_GCC>
{
    typedef Atomic_TypeTraits<AtomicOperations_X86_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 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_X86_ALL_GCC
                     // -----------------------------------

inline
int AtomicOperations_X86_ALL_GCC::
    getInt(const AtomicTypes::Int *atomicInt)
{
    int result;

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

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

    return result;
}

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

                : [obj] "=m" (*atomicInt)
                : [val] "r"  (value)
                : "memory");
#else
    asm volatile (
        "       movl %[val], %[obj]     \n\t"
        "       lock addl $0, 0(%%esp)  \n\t"

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

inline
void AtomicOperations_X86_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_X86_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_X86_ALL_GCC::
    testAndSwapInt(AtomicTypes::Int *atomicInt,
                   int compareValue,
                   int swapValue)
{
    return __sync_val_compare_and_swap(&atomicInt->d_value,
                                       compareValue,
                                       swapValue);
}

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

inline
Types::Int64 AtomicOperations_X86_ALL_GCC::
    getInt64(const AtomicTypes::Int64 *atomicInt)
{
#if BSLS_PLATFORM_CMP_VER_MAJOR >= 40300 // gcc >= 4.3
    Types::Int64 value = atomicInt->d_value;
    return __sync_val_compare_and_swap(
                const_cast<Types::Int64 *>(&atomicInt->d_value),
                value,
                value);
#else
    Types::Int64 result;
    asm volatile (
#ifdef __PIC__
        "       pushl %%ebx                 \n\t"
#endif
        "       movl %%ebx, %%eax           \n\t"
        "       movl %%ecx, %%edx           \n\t"
#if __GNUC__ != 3
        "       lock cmpxchg8b %[obj]       \n\t"
#else
        // gcc 3.4 seems to think that it can take edx as %1.
        "       lock cmpxchg8b (%[obj])     \n\t"
#endif
#ifdef __PIC__
        "       popl %%ebx                  \n\t"
#endif
                : [res] "=&A" (result)
                :
#if __GNUC__ != 3
                  [obj] "m" (*atomicInt),
#else
                  [obj] "S" (atomicInt),
#endif
                  "0" (0)
                :
#ifndef __PIC__
                  "ebx",
#endif

#if defined(BSLS_PLATFORM_CMP_CLANG) && defined(__PIC__)
                  "ebx",    // Clang wants to reuse 'ebx' even in PIC mode
                            // and generates invalid code.
                            // Mark 'ebx' as clobbered to prevent that.
#endif
                  "ecx", "cc", "memory");
    return result;
#endif

}

inline
void AtomicOperations_X86_ALL_GCC::
    setInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 value)
{
    swapInt64(atomicInt, value);
}

inline
Types::Int64 AtomicOperations_X86_ALL_GCC::
    swapInt64(AtomicTypes::Int64 *atomicInt,
              Types::Int64 swapValue)
{
#if BSLS_PLATFORM_CMP_VER_MAJOR >= 40300 // gcc >= 4.3
    Types::Int64 oldValue;

    do
    {
        oldValue = atomicInt->d_value;
    } while (__sync_val_compare_and_swap(&atomicInt->d_value,
                                         oldValue,
                                         swapValue)
             != oldValue);

    return oldValue;
#else
    Types::Int64 result;
    asm volatile (
#ifdef __PIC__
        "       pushl %%ebx             \n\t"
        "       movl  %[val], %%ebx     \n\t"
#endif
        "1:                             \n\t"
        "       lock cmpxchg8b %[obj]   \n\t"
        "       jnz 1b                  \n\t"
#ifdef __PIC__
        "       popl %%ebx              \n\t"
#endif
                : [res] "=A" (result),
                  [obj] "+m" (*atomicInt)
                :
#ifdef __PIC__
                  [val] "g" ((unsigned int) swapValue),
#else
                  [val] "b" ((unsigned int) swapValue),
#endif
                  "c" ((int) (swapValue >> 32)),
                  "A" (*atomicInt)
                :
#if defined(BSLS_PLATFORM_CMP_CLANG) && defined(__PIC__)
                  "ebx",    // Clang wants to reuse 'ebx' even in PIC mode
                            // and generates invalid code.
                            // Mark 'ebx' as clobbered to prevent that.
#endif
                  "memory", "cc");

    return result;
#endif
}

inline
Types::Int64 AtomicOperations_X86_ALL_GCC::
    testAndSwapInt64(AtomicTypes::Int64 *atomicInt,
                     Types::Int64 compareValue,
                     Types::Int64 swapValue)
{
#if BSLS_PLATFORM_CMP_VER_MAJOR >= 40300 // gcc >= 4.3
    return __sync_val_compare_and_swap(&atomicInt->d_value,
                                       compareValue,
                                       swapValue);
#else
    asm volatile (
#ifdef __PIC__
        "       pushl   %%ebx               \n\t"
        "       movl    %[val], %%ebx       \n\t"
#endif
        "       lock cmpxchg8b %[obj]       \n\t"
#ifdef __PIC__
        "       popl    %%ebx               \n\t"
#endif
                : [cmp] "=A" (compareValue),
                  [obj] "+m" (*atomicInt)
                :
#ifdef __PIC__
                  [val] "g" ((unsigned int) swapValue),
#else
                  [val] "b" ((unsigned int) swapValue),
#endif
                  "c" ((int) (swapValue >> 32)),
                  "0" (compareValue)
                :
#if defined(BSLS_PLATFORM_CMP_CLANG) && defined(__PIC__)
                  "ebx",    // Clang wants to reuse 'ebx' even in PIC mode
                            // and generates invalid code.
                            // Mark 'ebx' as clobbered to prevent that.
#endif
                  "memory", "cc");

    return compareValue;
#endif
}

inline
Types::Int64 AtomicOperations_X86_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) && (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 ----------------------------------