diff options
Diffstat (limited to 'src/bfs.h')
-rw-r--r-- | src/bfs.h | 138 |
1 files changed, 96 insertions, 42 deletions
@@ -8,6 +8,9 @@ #ifndef BFS_H #define BFS_H +#include <assert.h> // For __GLIBC__ +#include <stddef.h> // For offsetof + // Standard versions /** Possible __STDC_VERSION__ values. */ @@ -33,10 +36,15 @@ #ifndef BFS_COMMAND # define BFS_COMMAND "bfs" #endif + #ifndef BFS_HOMEPAGE # define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" #endif +#ifndef BFS_LINT +# define BFS_LINT false +#endif + // This is a symbol instead of a literal so we don't have to rebuild everything // when the version number changes extern const char bfs_version[]; @@ -48,15 +56,36 @@ extern const char bfs_cflags[]; extern const char bfs_ldflags[]; extern const char bfs_ldlibs[]; -// Get __GLIBC__ -#include <assert.h> - // Fundamental utilities /** - * Get the length of an array. + * Given `ptr = &t->member`, return `t`. + */ +#define container_of(ptr, type, member) \ + (container_of_typecheck(ptr, type, member), \ + (type *)((char *)ptr - offsetof(type, member))) + +#define container_of_typecheck(ptr, type, field) \ + (void)sizeof(ptr - &((type *)NULL)->field) + +/** + * A preprocessor conditional. + * + * BFS_VA_IF(A)(B)(C) => B + * BFS_VA_IF( )(B)(C) => C */ -#define countof(...) (sizeof(__VA_ARGS__) / sizeof(0[__VA_ARGS__])) +#define BFS_VA_IF(...) BFS_VA_IF_AB ## __VA_OPT__(C) +// BFS_VA_IF(A)(B)(C) => BFS_VA_IF_ABC(B)(C) +// BFS_VA_IF( )(B)(C) => BFS_VA_IF_AB(B)(C) + +#define BFS_VA_IF_ABC(...) __VA_ARGS__ BFS_VA_IGNORE +// BFS_VA_IF_ABC(B)(C) => B BFS_VA_IGNORE(C) + +#define BFS_VA_IF_AB(...) BFS_VA_REPEAT +// BFS_VA_IF_AB(B)(C) => BFS_VA_REPEAT(C) + +#define BFS_VA_IGNORE(...) +#define BFS_VA_REPEAT(...) __VA_ARGS__ /** * False sharing/destructive interference/largest cache line size. @@ -84,19 +113,12 @@ extern const char bfs_ldlibs[]; // Wrappers for attributes /** - * Silence warnings about switch/case fall-throughs. - */ -#if __has_attribute(fallthrough) -# define _fallthrough __attribute__((fallthrough)) -#else -# define _fallthrough ((void)0) -#endif - -/** * Silence warnings about unused declarations. */ -#if __has_attribute(unused) -# define _maybe_unused __attribute__((unused)) +#if __has_c_attribute(maybe_unused) +# define _maybe_unused maybe_unused +#elif __has_c_attribute(gnu::unused) +# define _maybe_unused gnu::unused #else # define _maybe_unused #endif @@ -104,8 +126,10 @@ extern const char bfs_ldlibs[]; /** * Warn if a value is unused. */ -#if __has_attribute(warn_unused_result) -# define _nodiscard __attribute__((warn_unused_result)) +#if __has_c_attribute(nodiscard) +# define _nodiscard nodiscard +#elif __has_c_attribute(gnu::warn_unused_result) +# define _nodiscard gnu::warn_unused_result #else # define _nodiscard #endif @@ -113,35 +137,38 @@ extern const char bfs_ldlibs[]; /** * Hint to avoid inlining a function. */ -#if __has_attribute(noinline) -# define _noinline __attribute__((noinline)) +#if __has_c_attribute(gnu::noinline) +# define _noinline gnu::noinline #else # define _noinline #endif /** - * Marks a non-returning function. + * Hint that a function is unlikely to be called. */ -#if __STDC_VERSION__ >= C23 -# define _noreturn [[noreturn]] +#if __has_c_attribute(gnu::cold) +# define _cold _noinline, gnu::cold #else -# define _noreturn _Noreturn +# define _cold _noinline #endif /** - * Hint that a function is unlikely to be called. + * Marks a non-returning function. */ -#if __has_attribute(cold) -# define _cold _noinline __attribute__((cold)) +#if __has_c_attribute(noreturn) +# define _noreturn noreturn +#elif __has_c_attribute(gnu::noreturn) +# define _noreturn gnu::noreturn #else -# define _cold _noinline +# define _noreturn #endif + /** * Adds compiler warnings for bad printf()-style function calls, if supported. */ -#if __has_attribute(format) -# define _printf(fmt, args) __attribute__((format(printf, fmt, args))) +#if __has_c_attribute(gnu::format) +# define _printf(fmt, args) gnu::format(printf, fmt, args) #else # define _printf(fmt, args) #endif @@ -149,8 +176,8 @@ extern const char bfs_ldlibs[]; /** * Annotates functions that potentially modify and return format strings. */ -#if __has_attribute(format_arg) -# define _format_arg(arg) __attribute__((format_arg(arg))) +#if __has_c_attribute(gnu::format_arg) +# define _format_arg(arg) gnu::format_arg(arg) #else # define _format_arg(arg) #endif @@ -158,11 +185,11 @@ extern const char bfs_ldlibs[]; /** * Annotates allocator-like functions. */ -#if __has_attribute(malloc) +#if __has_c_attribute(gnu::malloc) # if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC -# define _malloc(...) _nodiscard __attribute__((malloc(__VA_ARGS__))) +# define _malloc(...) _nodiscard, gnu::malloc(__VA_ARGS__) # else -# define _malloc(...) _nodiscard __attribute__((malloc)) +# define _malloc(...) _nodiscard, gnu::malloc # endif #else # define _malloc(...) _nodiscard @@ -171,8 +198,8 @@ extern const char bfs_ldlibs[]; /** * Specifies that a function returns allocations with a given alignment. */ -#if __has_attribute(alloc_align) -# define _alloc_align(param) __attribute__((alloc_align(param))) +#if __has_c_attribute(gnu::alloc_align) +# define _alloc_align(param) gnu::alloc_align(param) #else # define _alloc_align(param) #endif @@ -180,8 +207,8 @@ extern const char bfs_ldlibs[]; /** * Specifies that a function returns allocations with a given size. */ -#if __has_attribute(alloc_size) -# define _alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#if __has_c_attribute(gnu::alloc_size) +# define _alloc_size(...) gnu::alloc_size(__VA_ARGS__) #else # define _alloc_size(...) #endif @@ -189,7 +216,7 @@ extern const char bfs_ldlibs[]; /** * Shorthand for _alloc_align() and _alloc_size(). */ -#define _aligned_alloc(align, ...) _alloc_align(align) _alloc_size(__VA_ARGS__) +#define _aligned_alloc(align, ...) _alloc_align(align), _alloc_size(__VA_ARGS__) /** * Check if function multiversioning via GNU indirect functions (ifunc) is supported. @@ -197,8 +224,13 @@ extern const char bfs_ldlibs[]; * Disabled on TSan due to https://github.com/google/sanitizers/issues/342. */ #ifndef BFS_USE_TARGET_CLONES -# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) && !__SANITIZE_THREAD__ +# if __has_c_attribute(gnu::target_clones) \ + && (__GLIBC__ || __FreeBSD__) \ + && !__SANITIZE_THREAD__ \ + && !__SANITIZE_TYPE__ # define BFS_USE_TARGET_CLONES true +# else +# define BFS_USE_TARGET_CLONES false # endif #endif @@ -206,9 +238,31 @@ extern const char bfs_ldlibs[]; * Apply the target_clones attribute, if available. */ #if BFS_USE_TARGET_CLONES -# define _target_clones(...) __attribute__((target_clones(__VA_ARGS__))) +# define _target_clones(...) gnu::target_clones(__VA_ARGS__) #else # define _target_clones(...) #endif +/** + * Mark the size of a flexible array member. + */ +#if __has_c_attribute(clang::counted_by) +# define _counted_by(...) clang::counted_by(__VA_ARGS__) +#elif __has_c_attribute(gnu::counted_by) +# define _counted_by(...) gnu::counted_by(__VA_ARGS__) +#else +# define _counted_by(...) +#endif + +/** + * Optimization hint to not unroll a loop. + */ +#if BFS_HAS_PRAGMA_NOUNROLL +# define _nounroll _Pragma("nounroll") +#elif __GNUC__ && !__clang__ +# define _nounroll _Pragma("GCC unroll 0") +#else +# define _nounroll +#endif + #endif // BFS_H |