| /* Copyright (C) 2012-2021 Free Software Foundation, Inc. |
| Contributed by Richard Henderson <rth@redhat.com>. |
| |
| This file is part of the GNU Atomic Library (libatomic). |
| |
| Libatomic is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| Libatomic is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* This file contains data types and function declarations that are |
| private to the implementation of libatomic. */ |
| |
| #ifndef LIBATOMIC_H |
| #define LIBATOMIC_H 1 |
| |
| #include "auto-config.h" |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stddef.h> |
| #include <limits.h> |
| #include <string.h> |
| |
| |
| /* Symbol concatenation macros. */ |
| #define C2_(X,Y) X ## Y |
| #define C2(X,Y) C2_(X,Y) |
| #define C3_(X,Y,Z) X ## Y ## Z |
| #define C3(X,Y,Z) C3_(X,Y,Z) |
| #define C4_(W,X,Y,Z) W ## X ## Y ## Z |
| #define C4(W,X,Y,Z) C4_(W,X,Y,Z) |
| |
| /* Stringification macros. */ |
| #define S2(X) #X |
| #define S(X) S2(X) |
| |
| /* All of the primitive types on which we operate. */ |
| typedef unsigned U_1 __attribute__((mode(QI))); |
| #if HAVE_INT2 |
| typedef unsigned U_2 __attribute__((mode(HI))); |
| #endif |
| #if HAVE_INT4 |
| typedef unsigned U_4 __attribute__((mode(SI))); |
| #endif |
| #if HAVE_INT8 |
| typedef unsigned U_8 __attribute__((mode(DI))); |
| #endif |
| #if HAVE_INT16 |
| typedef unsigned U_16 __attribute__((mode(TI))); |
| #endif |
| |
| /* The widest type that we support. */ |
| #if HAVE_INT16 |
| # define MAX_SIZE 16 |
| #elif HAVE_INT8 |
| # define MAX_SIZE 8 |
| #elif HAVE_INT4 |
| # define MAX_SIZE 4 |
| #elif HAVE_INT2 |
| # define MAX_SIZE 2 |
| #else |
| # define MAX_SIZE 1 |
| #endif |
| typedef C2(U_,MAX_SIZE) U_MAX; |
| |
| /* Provide dummy fallback types so that stuff is syntactically correct |
| without having to overdo the ifdefs. The code using these should |
| always be protected with the HAVE_INT{n} macros. */ |
| #if !HAVE_INT2 |
| typedef U_MAX U_2; |
| #endif |
| #if !HAVE_INT4 |
| typedef U_MAX U_4; |
| #endif |
| #if !HAVE_INT8 |
| typedef U_MAX U_8; |
| #endif |
| #if !HAVE_INT16 |
| typedef U_MAX U_16; |
| #endif |
| |
| union max_size_u |
| { |
| U_1 b[MAX_SIZE]; |
| U_2 i2; |
| U_4 i4; |
| U_8 i8; |
| U_16 i16; |
| }; |
| |
| /* The "word" size of the machine. */ |
| typedef unsigned UWORD __attribute__((mode(word))); |
| |
| /* Macros for handing sub-word sized quantities. */ |
| #define MASK_1 ((UWORD)0xff) |
| #define MASK_2 ((UWORD)0xffff) |
| #define MASK_4 ((UWORD)0xffffffff) |
| #define MASK_8 ((UWORD)0xffffffffffffffff) |
| #define INVERT_MASK_1 ((UWORD)WORDS_BIGENDIAN << ((WORDSIZE - 1) * CHAR_BIT)) |
| #define INVERT_MASK_2 ((UWORD)WORDS_BIGENDIAN << ((WORDSIZE - 2) * CHAR_BIT)) |
| #define INVERT_MASK_4 ((UWORD)WORDS_BIGENDIAN << ((WORDSIZE - 4) * CHAR_BIT)) |
| #define INVERT_MASK_8 ((UWORD)WORDS_BIGENDIAN << ((WORDSIZE - 8) * CHAR_BIT)) |
| |
| /* Most of the files in this library are compiled multiple times with |
| N defined to be a power of 2 between 1 and 16. The SIZE macro is |
| then used to append _N to the symbol being manipulated. */ |
| #define SIZE(X) C3(X,_,N) |
| #define WSIZE(X) C3(X,_,WORDSIZE) |
| #define PTR(N,X) ((C2(U_,N) *)X) |
| |
| /* And thus, the type on which this compilation will be operating. */ |
| #define ITYPE SIZE(I) |
| #define UTYPE SIZE(U) |
| |
| /* Utility macros for GCC attributes. */ |
| #define UNUSED __attribute__((unused)) |
| #ifdef HAVE_ATTRIBUTE_VISIBILITY |
| # define HIDDEN __attribute__((visibility("hidden"))) |
| #else |
| # define HIDDEN |
| #endif |
| |
| /* Occasionally we have to play games with internal and external symbol |
| names, in order to work around builtin functions of the same name. |
| This macro sets the external name of the function appropriately. */ |
| #define ASMNAME(X) __asm__(S(C2(__USER_LABEL_PREFIX__,X))) |
| |
| /* Locking for a "small" operation. In the bare-metal single processor |
| cases this could be implemented by disabling interrupts. Thus the extra |
| word passed between the two functions, saving the interrupt level. |
| It is assumed that the object being locked does not cross the locking |
| granularity. |
| |
| Not actually declared here so that they can be defined static inline |
| in a target-specfic <host-config.h>. |
| |
| UWORD protect_start (void *ptr); |
| void protect_end (void *ptr, UWORD); |
| */ |
| |
| /* Locking for a "large' operation. This should always be some sort of |
| test-and-set operation, as we assume that the interrupt latency would |
| be unreasonably large. */ |
| void libat_lock_n (void *ptr, size_t n); |
| void libat_unlock_n (void *ptr, size_t n); |
| |
| /* We'll need to declare all of the sized functions a few times... */ |
| #define DECLARE_ALL_SIZED(N) DECLARE_ALL_SIZED_(N,C2(U_,N)) |
| #define DECLARE_ALL_SIZED_(N,T) \ |
| DECLARE_1(T, C2(load_,N), (T *mptr, int)); \ |
| DECLARE_1(void, C2(store_,N), (T *mptr, T val, int)); \ |
| DECLARE_1(T, C2(exchange_,N), (T *mptr, T, int)); \ |
| DECLARE_1(bool, C2(compare_exchange_,N), (T *mptr, T *, T, int, int)); \ |
| DECLARE_1(bool, C2(test_and_set_,N), (T *mptr, int)); \ |
| DECLARE_1(T, C2(fetch_add_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(fetch_sub_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(fetch_and_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(fetch_xor_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(fetch_or_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(fetch_nand_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(add_fetch_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(sub_fetch_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(and_fetch_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(xor_fetch_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(or_fetch_,N), (T *mptr, T, int)); \ |
| DECLARE_1(T, C2(nand_fetch_,N), (T *mptr, T, int)) |
| |
| /* All sized operations are implemented in hidden functions prefixed with |
| "libat_". These are either renamed or aliased to the expected prefix |
| of "__atomic". Some amount of renaming is required to avoid hiding or |
| conflicting with the builtins of the same name, but this additional |
| use of hidden symbols (where appropriate) avoids unnecessary PLT entries |
| on relevant targets. */ |
| |
| #if IFUNC_ALT |
| # define MAN(X) ASMNAME(C4(libat_,X,_i,IFUNC_ALT)) HIDDEN |
| #elif defined(HAVE_ATTRIBUTE_ALIAS) |
| # define MAN(X) HIDDEN |
| #else |
| # define MAN(X) ASMNAME(C2(__atomic_,X)) |
| #endif |
| |
| #if !defined(N) && HAVE_IFUNC |
| # define DECLARE_1(RET,NAME,ARGS) \ |
| RET C2(libat_,NAME) ARGS MAN(NAME); \ |
| RET C2(ifunc_,NAME) ARGS ASMNAME(C2(__atomic_,NAME)) |
| #else |
| # define DECLARE_1(RET,NAME,ARGS) RET C2(libat_,NAME) ARGS MAN(NAME) |
| #endif |
| |
| /* Prefix to use when calling internal, possibly ifunc'ed functions. */ |
| #if HAVE_IFUNC |
| # define local_ ifunc_ |
| #else |
| # define local_ libat_ |
| #endif |
| |
| DECLARE_ALL_SIZED(1); |
| DECLARE_ALL_SIZED(2); |
| DECLARE_ALL_SIZED(4); |
| DECLARE_ALL_SIZED(8); |
| DECLARE_ALL_SIZED(16); |
| |
| #undef DECLARE_1 |
| #undef DECLARE_ALL_SIZED |
| #undef DECLARE_ALL_SIZED_ |
| |
| /* And the generic sized versions. */ |
| void libat_load (size_t, void *, void *, int) MAN(load); |
| void libat_store (size_t, void *, void *, int) MAN(store); |
| void libat_exchange (size_t, void *, void *, void *, int) MAN(exchange); |
| bool libat_compare_exchange (size_t, void *, void *, void *, int, int) |
| MAN(compare_exchange); |
| bool libat_is_lock_free (size_t, void *) MAN(is_lock_free); |
| |
| #undef MAN |
| |
| #include <host-config.h> |
| |
| /* We don't have IFUNC_NCOND until after host-config.h. */ |
| #if !HAVE_IFUNC |
| # define IFUNC_NCOND(N) 0 |
| #endif |
| |
| #if IFUNC_ALT |
| # define EXPORT_ALIAS(X) /* exported symbol in non-alternate file */ |
| #elif defined(N) && IFUNC_NCOND(N) |
| # if IFUNC_NCOND(N) == 1 |
| # define GEN_SELECTOR(X) \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i1) HIDDEN; \ |
| static typeof(C2(libat_,X)) * C2(select_,X) (IFUNC_RESOLVER_ARGS) \ |
| { \ |
| if (IFUNC_COND_1) \ |
| return C3(libat_,X,_i1); \ |
| return C2(libat_,X); \ |
| } |
| # elif IFUNC_NCOND(N) == 2 |
| # define GEN_SELECTOR(X) \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i1) HIDDEN; \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i2) HIDDEN; \ |
| static typeof(C2(libat_,X)) * C2(select_,X) (IFUNC_RESOLVER_ARGS) \ |
| { \ |
| if (IFUNC_COND_1) \ |
| return C3(libat_,X,_i1); \ |
| if (IFUNC_COND_2) \ |
| return C3(libat_,X,_i2); \ |
| return C2(libat_,X); \ |
| } |
| # elif IFUNC_NCOND(N) == 3 |
| # define GEN_SELECTOR(X) \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i1) HIDDEN; \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i2) HIDDEN; \ |
| extern typeof(C2(libat_,X)) C3(libat_,X,_i3) HIDDEN; \ |
| static typeof(C2(libat_,X)) * C2(select_,X) (IFUNC_RESOLVER_ARGS) \ |
| { \ |
| if (IFUNC_COND_1) \ |
| return C3(libat_,X,_i1); \ |
| if (IFUNC_COND_2) \ |
| return C3(libat_,X,_i2); \ |
| if (IFUNC_COND_3) \ |
| return C3(libat_,X,_i3); \ |
| return C2(libat_,X); \ |
| } |
| # else |
| # error "Unsupported number of ifunc alternatives." |
| # endif |
| # define EXPORT_ALIAS(X) \ |
| GEN_SELECTOR(X) \ |
| typeof(C2(libat_,X)) C2(ifunc_,X) \ |
| ASMNAME(C2(__atomic_,X)) \ |
| __attribute__((ifunc(S(C2(select_,X))))) |
| #elif defined(HAVE_ATTRIBUTE_ALIAS) |
| # define EXPORT_ALIAS(X) \ |
| extern typeof(C2(libat_,X)) C2(export_,X) \ |
| ASMNAME(C2(__atomic_,X)) \ |
| __attribute__((alias(S(C2(libat_,X))))) |
| #else |
| # define EXPORT_ALIAS(X) /* original symbol is exported */ |
| #endif |
| |
| #endif /* LIBATOMIC_H */ |