mesytec-mnode/external/taskflow-3.8.0/3rd-party/ff/mpmc/asm/abstraction_dcas.h
2025-01-04 01:25:05 +01:00

361 lines
10 KiB
C++

#ifndef FF_ABSTRACT_DCAS_H
#define FF_ABSTRACT_DCAS_H
/*
* Marco Aldinucci, Oct 2015: Housekeeping for C++11 FF port
* Massimo Torquati, Nov 2019: adding support to 64bit ARMs (thanks to Zerodivision)
*
*/
#include <ff/platforms/liblfds.h>
// ------------------------------ abstraction_dcas.c file --------------------------------
/****************************************************************************/
#if (defined _WIN64 && defined _MSC_VER)
/* TRD : 64 bit Windows (user-mode or kernel) on any CPU with the Microsoft C compiler
_WIN64 indicates 64 bit Windows
_MSC_VER indicates Microsoft C compiler
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
cas_result = _InterlockedCompareExchange128( (volatile __int64 *) destination, (__int64) *(exchange+1), (__int64) *exchange, (__int64 *) compare );
return( cas_result );
}
#endif
/****************************************************************************/
#if (!defined _WIN64 && defined _WIN32 && defined _MSC_VER)
/* TRD : 32 bit Windows (user-mode or kernel) on any CPU with the Microsoft C compiler
(!defined _WIN64 && defined _WIN32) indicates 32 bit Windows
_MSC_VER indicates Microsoft C compiler
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
__int64
original_compare;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
*(__int64 *) &original_compare = *(__int64 *) compare;
*(__int64 *) compare = _InterlockedCompareExchange64( (volatile __int64 *) destination, *(__int64 *) exchange, *(__int64 *) compare );
return( (unsigned char) (*(__int64 *) compare == *(__int64 *) &original_compare) );
}
#endif
/****************************************************************************/
#if (defined __x86_64__ && __GNUC__ && !defined __pic__)
/* TRD : any OS on x64 with GCC for statically linked code
__x86_64__ indicates x64
__GNUC__ indicates GCC
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
__asm__ __volatile__
(
"lock;" // make cmpxchg16b atomic
"cmpxchg16b %0;" // cmpxchg16b sets ZF on success
"setz %3;" // if ZF set, set cas_result to 1
// output
: "+m" (*(volatile atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)
// input
: "b" (*exchange), "c" (*(exchange+1))
// clobbered
: "cc", "memory"
);
return( cas_result );
}
//#endif
/****************************************************************************/
#elif (defined __i686__ && __GNUC__ && !defined __pic__)
/* TRD : any OS on x86 with GCC for statically linked code
__i686__ indicates x86
__GNUC__ indicates GCC
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
__asm__ __volatile__
(
"lock;" // make cmpxchg8b atomic
"cmpxchg8b %0;" // cmpxchg8b sets ZF on success
"setz %3;" // if ZF set, set cas_result to 1
// output
: "+m" (*(volatile atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)
// input
: "b" (*exchange), "c" (*(exchange+1))
// clobbered
: "cc", "memory"
);
return( cas_result );
}
//#endif
/****************************************************************************/
#elif (defined __x86_64__ && __GNUC__ && defined __pic__)
/* TRD : any OS on x64 with GCC for position independent code (e.g. a shared object)
__x86_64__ indicates x64
__GNUC__ indicates GCC
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
/* TRD : with a shared object, we cannot clobber RBX
as such, we borrow RSI - we load half of the exchange value into it
then swap it with RBX
then do the compare-and-swap
then swap the original value of RBX back from RSI
*/
__asm__ __volatile__
(
"xchg %%rsi, %%rbx;" // swap RBI and RBX
"lock;" // make cmpxchg16b atomic
"cmpxchg16b %0;" // cmpxchg16b sets ZF on success
"setz %3;" // if ZF set, set cas_result to 1
"xchg %%rbx, %%rsi;" // re-swap RBI and RBX
// output
: "+m" (*(volatile atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)
// input
: "S" (*exchange), "c" (*(exchange+1))
// clobbered
: "cc", "memory"
);
return( cas_result );
}
//#endif
/****************************************************************************/
#elif (defined __i686__ && __GNUC__ && defined __pic__)
/* TRD : any OS on x86 with GCC for position independent code (e.g. a shared object)
__i686__ indicates x86
__GNUC__ indicates GCC
*/
INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
/* TRD : with a shared object, we cannot clobber EBX
as such, we borrow ESI - we load half of the exchange value into it
then swap it with EBX
then do the compare-and-swap
then swap the original value of EBX back from ESI
*/
__asm__ __volatile__
(
"xchg %%esi, %%ebx;" // swap EBI and EBX
"lock;" // make cmpxchg8b atomic
"cmpxchg8b %0;" // cmpxchg8b sets ZF on success
"setz %3;" // if ZF set, set cas_result to 1
"xchg %%ebx, %%esi;" // re-swap EBI and EBX
// output
: "+m" (*(volatile atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)
// input
: "S" (*exchange), "c" (*(exchange+1))
// clobbered
: "cc", "memory"
);
return( cas_result );
}
//#endif
/****************************************************************************/
#elif ((defined __i686__ || defined __arm__ || defined __aarch64__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 1 && __GNUC_PATCHLEVEL__ >= 0)
/* TRD : any OS on x86 or ARM with GCC 4.1.0 or better
GCC 4.1.0 introduced the __sync_*() atomic intrinsics
__GNUC__ / __GNUC_MINOR__ / __GNUC_PATCHLEVEL__ indicates GCC and which version
*/
static INLINE unsigned char abstraction_dcas( volatile atom_t *destination, atom_t *exchange, atom_t *compare )
{
unsigned char
cas_result = 0;
unsigned long long int
original_destination;
assert( destination != NULL );
assert( exchange != NULL );
assert( compare != NULL );
__asm__ __volatile__ ( "" : : : "memory" );
original_destination = __sync_val_compare_and_swap( (volatile unsigned long long int *) destination, *(unsigned long long int *) compare, *(unsigned long long int *) exchange );
__asm__ __volatile__ ( "" : : : "memory" );
if( original_destination == *(unsigned long long int *) compare )
cas_result = 1;
*(unsigned long long int *) compare = original_destination;
return( cas_result );
}
#endif
// ------------------------------ abstraction_cas.c file --------------------------------
/****************************************************************************/
#if (defined _WIN32 && defined _MSC_VER)
/* TRD : 64 bit and 32 bit Windows (user-mode or kernel) on any CPU with the Microsoft C compiler
_WIN32 indicates 64-bit or 32-bit Windows
_MSC_VER indicates Microsoft C compiler
*/
INLINE atom_t abstraction_cas( volatile atom_t *destination, atom_t exchange, atom_t compare )
{
assert( destination != NULL );
// TRD : exchange can be any value in its range
// TRD : compare can be any value in its range
return( (atom_t) _InterlockedCompareExchangePointer((void * volatile *) destination, (void *) exchange, (void *) compare) );
}
#define _ACAS_
#endif
/****************************************************************************/
#if (!defined __arm__ && !defined __aarch64__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 1 && __GNUC_PATCHLEVEL__ >= 0) && !defined(_ACAS_)
/* TRD : any OS on any CPU except ARM with GCC 4.1.0 or better
GCC 4.1.0 introduced the __sync_*() atomic intrinsics
__GNUC__ / __GNUC_MINOR__ / __GNUC_PATCHLEVEL__ indicates GCC and which version
*/
inline atom_t abstraction_cas( volatile atom_t *destination, atom_t exchange, atom_t compare )
{
assert( destination != NULL );
// TRD : exchange can be any value in its range
// TRD : compare can be any value in its range
// TRD : note the different argument order for the GCC instrinsic to the MSVC instrinsic
//std::cerr << "CAS(dest,comp,exch) " << destination << " " << compare << " " << exchange << "\n\n";
//std::cerr.flush();
return( (atom_t) __sync_val_compare_and_swap(destination, compare, exchange) );
}
#endif
/****************************************************************************/
#if ((defined __arm__ || defined __aarch64__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 1 && __GNUC_PATCHLEVEL__ >= 0)
static INLINE atom_t abstraction_cas( volatile atom_t *destination, atom_t exchange, atom_t compare )
{
atom_t
rv;
assert( destination != NULL );
// TRD : exchange can be any value in its range
// TRD : compare can be any value in its range
// TRD : note the different argument order for the GCC instrinsic to the MSVC instrinsic
__asm__ __volatile__ ( "" : : : "memory" );
rv = (atom_t) __sync_val_compare_and_swap( destination, compare, exchange );
__asm__ __volatile__ ( "" : : : "memory" );
return( rv );
}
#endif
#endif