зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1363426 - part 1 - remove #ifdeffery in Atomics.h; r=erahm
Every platform where we use GCC has <atomic>, so there's no need to use GCC-specific __sync* intrinsics anymore. The <atomic> header may generate better code for several operations, as well.
This commit is contained in:
Родитель
d676a8001e
Коммит
fe42aa0b9d
213
mfbt/Atomics.h
213
mfbt/Atomics.h
|
@ -164,10 +164,7 @@ enum MemoryOrdering {
|
|||
|
||||
} // namespace mozilla
|
||||
|
||||
// Build up the underlying intrinsics.
|
||||
#ifdef MOZ_HAVE_CXX11_ATOMICS
|
||||
|
||||
# include <atomic>
|
||||
#include <atomic>
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
@ -326,214 +323,6 @@ struct ToStorageTypeArgument
|
|||
static constexpr T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* The __sync_* family of intrinsics is documented here:
|
||||
*
|
||||
* http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html
|
||||
*
|
||||
* While these intrinsics are deprecated in favor of the newer __atomic_*
|
||||
* family of intrincs:
|
||||
*
|
||||
* http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html
|
||||
*
|
||||
* any GCC version that supports the __atomic_* intrinsics will also support
|
||||
* the <atomic> header and so will be handled above. We provide a version of
|
||||
* atomics using the __sync_* intrinsics to support older versions of GCC.
|
||||
*
|
||||
* All __sync_* intrinsics that we use below act as full memory barriers, for
|
||||
* both compiler and hardware reordering, except for __sync_lock_test_and_set,
|
||||
* which is a only an acquire barrier. When we call __sync_lock_test_and_set,
|
||||
* we add a barrier above it as appropriate.
|
||||
*/
|
||||
|
||||
template<MemoryOrdering Order> struct Barrier;
|
||||
|
||||
/*
|
||||
* Some processors (in particular, x86) don't require quite so many calls to
|
||||
* __sync_sychronize as our specializations of Barrier produce. If
|
||||
* performance turns out to be an issue, defining these specializations
|
||||
* on a per-processor basis would be a good first tuning step.
|
||||
*/
|
||||
|
||||
template<>
|
||||
struct Barrier<Relaxed>
|
||||
{
|
||||
static void beforeLoad() {}
|
||||
static void afterLoad() {}
|
||||
static void beforeStore() {}
|
||||
static void afterStore() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Barrier<ReleaseAcquire>
|
||||
{
|
||||
static void beforeLoad() {}
|
||||
static void afterLoad() { __sync_synchronize(); }
|
||||
static void beforeStore() { __sync_synchronize(); }
|
||||
static void afterStore() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Barrier<SequentiallyConsistent>
|
||||
{
|
||||
static void beforeLoad() { __sync_synchronize(); }
|
||||
static void afterLoad() { __sync_synchronize(); }
|
||||
static void beforeStore() { __sync_synchronize(); }
|
||||
static void afterStore() { __sync_synchronize(); }
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct AtomicStorageType
|
||||
{
|
||||
// For non-enums, just use the type directly.
|
||||
typedef T Type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AtomicStorageType<T, true>
|
||||
: Conditional<sizeof(T) == 4, uint32_t, uint64_t>
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8,
|
||||
"wrong type computed in conditional above");
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicMemoryOps
|
||||
{
|
||||
typedef typename AtomicStorageType<T>::Type ValueType;
|
||||
|
||||
static T load(const ValueType& aPtr)
|
||||
{
|
||||
Barrier<Order>::beforeLoad();
|
||||
T val = T(aPtr);
|
||||
Barrier<Order>::afterLoad();
|
||||
return val;
|
||||
}
|
||||
|
||||
static void store(ValueType& aPtr, T aVal)
|
||||
{
|
||||
Barrier<Order>::beforeStore();
|
||||
aPtr = ValueType(aVal);
|
||||
Barrier<Order>::afterStore();
|
||||
}
|
||||
|
||||
static T exchange(ValueType& aPtr, T aVal)
|
||||
{
|
||||
// __sync_lock_test_and_set is only an acquire barrier; loads and stores
|
||||
// can't be moved up from after to before it, but they can be moved down
|
||||
// from before to after it. We may want a stricter ordering, so we need
|
||||
// an explicit barrier.
|
||||
Barrier<Order>::beforeStore();
|
||||
return T(__sync_lock_test_and_set(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static bool compareExchange(ValueType& aPtr, T aOldVal, T aNewVal)
|
||||
{
|
||||
return __sync_bool_compare_and_swap(&aPtr, ValueType(aOldVal), ValueType(aNewVal));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub
|
||||
: public IntrinsicMemoryOps<T, Order>
|
||||
{
|
||||
typedef IntrinsicMemoryOps<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T add(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return T(__sync_fetch_and_add(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static T sub(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return T(__sync_fetch_and_sub(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub<T*, Order>
|
||||
: public IntrinsicMemoryOps<T*, Order>
|
||||
{
|
||||
typedef IntrinsicMemoryOps<T*, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
/*
|
||||
* The reinterpret_casts are needed so that
|
||||
* __sync_fetch_and_{add,sub} will properly type-check.
|
||||
*
|
||||
* Also, these functions do not provide standard semantics for
|
||||
* pointer types, so we need to adjust the addend.
|
||||
*/
|
||||
static ValueType add(ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
ValueType amount = reinterpret_cast<ValueType>(aVal * sizeof(T));
|
||||
return __sync_fetch_and_add(&aPtr, amount);
|
||||
}
|
||||
|
||||
static ValueType sub(ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
ValueType amount = reinterpret_cast<ValueType>(aVal * sizeof(T));
|
||||
return __sync_fetch_and_sub(&aPtr, amount);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
|
||||
{
|
||||
typedef IntrinsicAddSub<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T inc(ValueType& aPtr) { return Base::add(aPtr, 1); }
|
||||
static T dec(ValueType& aPtr) { return Base::sub(aPtr, 1); }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics : public IntrinsicIncDec<T, Order>
|
||||
{
|
||||
static T or_( T& aPtr, T aVal) { return __sync_fetch_and_or(&aPtr, aVal); }
|
||||
static T xor_(T& aPtr, T aVal) { return __sync_fetch_and_xor(&aPtr, aVal); }
|
||||
static T and_(T& aPtr, T aVal) { return __sync_fetch_and_and(&aPtr, aVal); }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics<T*, Order> : public IntrinsicIncDec<T*, Order>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct ToStorageTypeArgument
|
||||
{
|
||||
typedef typename AtomicStorageType<T>::Type ResultType;
|
||||
|
||||
static constexpr ResultType convert (T aT) { return ResultType(aT); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToStorageTypeArgument<T, false>
|
||||
{
|
||||
static constexpr T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
#else
|
||||
# error "Atomic compiler intrinsics are not supported on your platform"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class AtomicBase
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче