// SPDX-FileCopyrightText: 2023 Olivier Dion <odion@efficios.com>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

/*
 * urcu/uatomic/builtins-generic.h
 */

#ifndef _URCU_UATOMIC_BUILTINS_GENERIC_H
#define _URCU_UATOMIC_BUILTINS_GENERIC_H

#include <urcu/compiler.h>
#include <urcu/system.h>

/*
 * Whether the memory order `CMM_SEQ_CST_FENCE` should emit a fence after an
 * atomic load operation. Can be overriden by the architecture.
 *
 * Default: Emit the memory barrier.
 */
#ifndef cmm_seq_cst_fence_after_atomic_load
#  define cmm_seq_cst_fence_after_atomic_load cmm_seq_cst_fence_after_atomic
#endif

/*
 * Whether the memory order `CMM_SEQ_CST_FENCE` should emit a fence after an
 * atomic store operation. Can be overriden by the architecture.
 *
 * Default: Emit the memory barrier.
 */
#ifndef cmm_seq_cst_fence_after_atomic_store
#  define cmm_seq_cst_fence_after_atomic_store cmm_seq_cst_fence_after_atomic
#endif

/*
 * Whether the memory order `CMM_SEQ_CST_FENCE` should emit a fence after an
 * atomic read-modify-write operation. Can be overriden by the architecture.
 *
 * Default: Emit the memory barrier.
 */
#ifndef cmm_seq_cst_fence_after_atomic_rmw
#  define cmm_seq_cst_fence_after_atomic_rmw cmm_seq_cst_fence_after_atomic
#endif

#define uatomic_store_mo(addr, v, mo)				\
	do {							\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));	\
		__atomic_store_n(cmm_cast_volatile(addr), v,	\
				cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_store(mo);	\
	} while (0)

#define uatomic_load_mo(addr, mo)					\
	__extension__							\
	({								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		__typeof__(*(addr)) _value =				\
			__atomic_load_n(cmm_cast_volatile(addr),	\
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_load(mo);		\
									\
		_value;							\
	})

#define uatomic_cmpxchg_mo(addr, old, new, mos, mof)			\
	__extension__							\
	({								\
		__typeof__(*(addr)) _old = (__typeof__(*(addr)))old;	\
									\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		if (__atomic_compare_exchange_n(cmm_cast_volatile(addr), \
							&_old, new, 0,	\
							cmm_to_c11(mos), \
							cmm_to_c11(mof))) { \
			cmm_seq_cst_fence_after_atomic_rmw(mos);	\
		} else {						\
			cmm_seq_cst_fence_after_atomic_rmw(mof);	\
		}							\
		_old;							\
	})

#define uatomic_xchg_mo(addr, v, mo)					\
	__extension__							\
	({								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		__typeof__((*addr)) _old =				\
			__atomic_exchange_n(cmm_cast_volatile(addr), v,	\
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_rmw(mo);			\
		_old;							\
	})

#define uatomic_add_return_mo(addr, v, mo)				\
	__extension__							\
	({								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		__typeof__(*(addr)) _old =				\
			__atomic_add_fetch(cmm_cast_volatile(addr), v,	\
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_rmw(mo);			\
		_old;							\
	})


#define uatomic_sub_return_mo(addr, v, mo)				\
	__extension__							\
	({								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		__typeof__(*(addr)) _old =				\
			__atomic_sub_fetch(cmm_cast_volatile(addr), v,	\
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_rmw(mo);			\
		_old;							\
	})


#define uatomic_and_mo(addr, mask, mo)					\
	do {								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		(void) __atomic_and_fetch(cmm_cast_volatile(addr), mask, \
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_rmw(mo);			\
	} while (0)


#define uatomic_or_mo(addr, mask, mo)					\
	do {								\
		_cmm_static_assert__atomic_lf(sizeof(*(addr)));		\
		(void) __atomic_or_fetch(cmm_cast_volatile(addr), mask,	\
					cmm_to_c11(mo));		\
		cmm_seq_cst_fence_after_atomic_rmw(mo);			\
	} while (0)


#define uatomic_add_mo(addr, v, mo)			\
	(void) uatomic_add_return_mo(addr, v, mo)

#define uatomic_sub_mo(addr, v, mo)			\
	(void) uatomic_sub_return_mo(addr, v, mo)

#define uatomic_inc_mo(addr, mo)		\
	uatomic_add_mo(addr, 1, mo)

#define uatomic_dec_mo(addr, mo)		\
	uatomic_sub_mo(addr, 1, mo)

#define cmm_smp_mb__before_uatomic_and() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_and()  cmm_smp_mb()

#define cmm_smp_mb__before_uatomic_or() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_or()  cmm_smp_mb()

#define cmm_smp_mb__before_uatomic_add() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_add()  cmm_smp_mb()

#define cmm_smp_mb__before_uatomic_sub() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_sub()  cmm_smp_mb()

#define cmm_smp_mb__before_uatomic_inc() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_inc() cmm_smp_mb()

#define cmm_smp_mb__before_uatomic_dec() cmm_smp_mb()
#define cmm_smp_mb__after_uatomic_dec() cmm_smp_mb()

#endif /* _URCU_UATOMIC_BUILTINS_GENERIC_H */
