/// Macros header /// /// # Features /// * `_EVAL_DEBUG_ONLY_STMTS` - Evaluate expressions passed to macros that are no-ops when not in debug mode. #ifndef _MACROS_H #define _MACROS_H #ifdef __cplusplus #define _Static_assert static_assert extern "C" { #endif #include #include #include #include #include #include "ifunc.h" // Attribute macros #define _pure __attribute__((const)) #define _readonly __attribute__((pure)) #define _cold __attribute__((cold)) #define _hot __attribute__((hot)) #define _dead __attribute__((unused)) #ifndef __cplusplus #define noreturn __attribute__((noreturn)) #define _deprecated __attribute__((deprecated)) #define deprecated_message(str) __attribute__((deprecated(str))) #define _deprecated_msg(str) deprecated_message(str) #endif #define noinline __attribute__((noinline)) #define always_inline __attribute__((always_inline)) #define force_inline __attribute__((gnu_inline)) #ifdef DEBUG #define _mixin static inline force_inline #else #define _mixin extern inline force_inline #endif #define unrestrict __attribute__((may_alias)) #define _shared unrestrict #ifdef __cplusplus #define restrict __restrict__ #endif // Function conventions #define _cconv__mixin _mixin #define _cconv__pure _pure #define _cconv__readonly _readonly #define _cconv__cold _cold #define _cconv__dead _dead #define _cconv__unused _dead #define _cconv__hot _hot #define _cconv__entry __attribute__((constructor)) #define _cconv__exit __attribute__((destructor)) // Allocates #define _cconv__alloc __attribute__((malloc)) // Infallible alloc #define _cconv__ialloc __attribute__((malloc)) __attribute__((returns_nonnull)) #define _cconv__nonnull __attribute__((returns_nonnull)) #define _callcv(name) _cconv__ ## name #define _callconv(name) _callcv(name) #define _callat_entry _callcv(entry) #define _callat_exit _callcv(exit) #define _callat(when) _callat_ ## when // Argument attribute // Used like: `int *pOUT output, const int *pIN input` #define pOUT restrict #define pIN const restrict #define pINOUT restrict // Used like: `int _OUT output, const int _IN input` #define _IN *pIN #define _OUT *pOUT #define _INOUT *pINOUT // Type macros #define AS(x, t) ((t)(x)) #ifdef __cplusplus #define let auto #define var(x) decltype(x) #else #define let __auto_type #define var(x) __typeof((x)) #endif // Control flow macros #define LIKELY(x) __builtin_expect(!!(x), 1) #define UNLIKELY(x) __builtin_expect(!!(x), 0) #define ifL(x) if(LIKELY((x))) #define ifU(x) if(UNLIKELY((x))) #define COLD_EXPR(expr) ({ \ goto _cold_expr; \ UNREACHABLE_UNSAFE(); \ _cold_expr: \ __attribute__((cold)); \ expr; \ }) #define HOT_EXPR(expr) ({ \ goto _hot_expr; \ UNREACHABLE_UNSAFE(); \ _hot_expr: \ __attribute__((hot)); \ expr; \ }) // Use like: COLD_BLOCK { ... } // XXX: I'm not exactly sure if these hacks work.. Should test. #define COLD_BLOCK if(UNLIKELY(1)) // Use like: HOT_BLOCK { ... } #define HOT_BLOCK if(LIKELY(1)) /// Equivalent to GNU C `x ?: y` operator. #define TERN_OR(x, y) ({ let _x = (x); _x ? _x : (y); }) // Statement macros #define _NO_OP ((void)0) #define IGNORE(x) ((void)(x)) _mixin void _drain_val(void* x, ...) { IGNORE(x); } // This compiles to no-op on -O0 and -O2+ due to `gnu_inline` (_mixin) #define _drain(...) _drain_val(NULL, __VA_ARGS__) #define IGNORE_ALL(...) _drain(__VA_ARGS__) // Allocation macros #define box(t) ((t*)aligned_alloc(_Alignof(t), sizeof(t))) #define box_value(v) ({ let _box = box(var(v)); *_box = (v); _box; }) #define unbox_value(v) ({ let _v = (v); let _res = *_v; free(_v); _res; }) #define stackalloc(t) __builtin_alloca_with_align(sizeof(t), _Alignof(t)) // Function macros // debug-only statements #ifdef DEBUG #define DBG(x) x #else #define DBG(x) ((void)0) #endif // Assertions #define assert_msg(expr, ...) ( (expr) ? 1 : FATAL(__VA_ARGS__)) #define UNREACHABLE_UNSAFE() __builtin_unreachable() #ifdef DEBUG #define UNREACHABLE() do { COLD_EXPR(FATAL("unreachable code entered")); } while(0) #else #define UNREACHABLE() UNREACHABLE_UNSAFE() #endif #ifdef DEBUG #define debug_assert(x) assert((x)) #elif defined(_EVAL_DEBUG_ONLY_STMTS) #define debug_assert(x) IGNORE(x) #else #define debug_assert(x) _NO_OP #endif #define assert_eq(x,y) assert((x) == (y)) #define assert_ne(x,y) assert((x) != (y)) #define debug_assert_eq(x,y) debug_assert((x) == (y)) #define debug_assert_ne(x,y) debug_assert((x) != (y)) #ifdef DEBUG #define debug_static_assert(x, msg) _Static_assert((x), msg) #else #define debug_static_assert(x, msg) _Static_assert(1, msg) #endif #define static_assert_eq(x,y,msg) _Static_assert((x) == (y), msg) #define static_assert_ne(x,y,msg) _Static_assert((x) != (y), msg) // Bitmasking #define _BSWAP(x) __builtin_bswa x #define BSWAP_16(x) _BSWAP(p16) (AS((x), uint16_t)) #define BSWAP_32(x) _BSWAP(p32) (AS((x), uint32_t)) #define BSWAP_64(x) _BSWAP(p64) (AS((x), uint64_t)) #define BSWAP_128(x) _BSWAP(p128)(AS((x), uint128_t)) #undef _BSWAP #define _BSWAP(n) __builtin_bswap ## n #define bswap(x) _Generic((x), \ uint64_t: _BSWAP(64), int64_t: _BSWAP(64), \ uint32_t: _BSWAP(32), int32_t: _BSWAP(32), \ uint16_t: _BSWAP(16), int16_t: _BSWAP(16), \ __uint128_t: _BSWAP(128), __int128_t: _BSWAP(128)) \ ((x)) static_assert_eq(bswap(128lu), 9223372036854775808lu, "bswap128 (lu) failed (1)"); static_assert_eq(128lu, bswap(9223372036854775808lu), "bswap128 (lu) failed (2)"); static_assert_eq(bswap(bswap(128lu)), 128, "bswap128 (lu) failed (3)"); // Transmute #define TRANSMUTE(val, type) ({ union _trans { var(val) input; type output; }; \ _Static_assert(sizeof(var(val)) == sizeof(type), "Cannot transmute values of different sizes"); \ _Static_assert(sizeof(union _trans) == sizeof(type), "Cannot transmute values of different sizes"); \ /* XXX: Do we need do check this for equality? Can we re-word it or do we even need it at all? _Static_assert(_Alignof(union _trans) == _Alignof(type), "Cannot transmute values of different alignments");*/ \ union _trans _trans__value = { .input = (val), }; \ _trans__value.output; }) // Trace message output #include "trace.h" #define TRACEx(L, X, msg, ...) (_t_fprintf(TRACE_LEVEL_ ## L, stderr, "[" X "] " __FILE__ ":%d->%s(): " msg "\n", __LINE__, __func__, ## __VA_ARGS__)) #ifdef DEBUG #define dprintf(msg, ...) TRACEx(DEBUG, "debug", msg, ## __VA_ARGS__) #elif defined(_EVAL_DEBUG_ONLY_STMTS) #define dprintf(msg, ...) _drain(msg, ## __VA_ARGS__) #endif #define TRACE(msg, ...) TRACEx(DEBUG, "trace", msg, ## __VA_ARGS__) #define INFO(msg, ...) TRACEx(INFO, "info", msg, ## __VA_ARGS__) #define WARN(msg, ...) TRACEx(WARN, "warning", msg, ## __VA_ARGS__) #define ERROR(msg, ...) TRACEx(ERROR, "error", msg, ## __VA_ARGS__) #define FATAL(msg, ...) (TRACEx(FATAL, "FATAL", msg, ## __VA_ARGS__), abort()) #define TODO(x, ...) FATAL("function %s() is unimplemented: " x, __func__, ## __VA_ARGS__) // Debug testing #define PASSERT_OR(expr, err, message) do { if ((expr)) break; perror("[FATAL] `" #expr "`: " message); ({ err; }) } while(0) #define PASSERT(expr, message) do { if ((expr)) break; perror("[FATAL] `" #expr "`: " message); exit(-1); } while(0) #define PASSERT_R(expr, rv, message) PASSERT_OR(expr, return (rv), message) #define TEST_OK 1 #define TEST_FAILED 0 #define TESTFUN(name) _test__ ## name #define CALLTEST(name) (assert_msg(TESTFUN(name)() == TEST_OK, "Test `" #name "` failed"), TRACE("Test `" #name "` passed!")) #define DEFTEST(name) int _test__ ## name(void) #define TEST_ASSERT(x) do { if (!(x)) {ERROR("Test `%s' assertion failure: `" #x "`", __func__ + 7); return TEST_FAILED;} } while(0) #define TEST_ASSERT_EQ(x,y) TEST_ASSERT( (x) == (y) ) #define TEST_ASSERT_NE(x,y) TEST_ASSERT( (x) != (y) ) // Version macros #define _AS_u32(x) ((uint32_t)(x)) #define VER_COMP_MAJ 24u #define VER_COMP_MIN 16u #define VER_COMP_BF 8u #define VER_COMP_REV 0u /// Create a `uint32_t` from these version componants #define VERSION(maj,min,bf,rev) _AS_u32( _AS_u32(maj) << 24u | _AS_u32(min) << 16u | _AS_u32(bf) << 8u | _AS_u32(rev) ) /// Mask `ver` to retain only the version component specified by `mask` /// /// # Example /// `_Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u)` #define VERSION_COMP(ver, mask) _AS_u32( _AS_u32(ver) & (0xffu << _AS_u32(mask)) /*>> _AS_u32(mask)*/ ) #define VERSION_MAJ(ver) _AS_u32(VERSION_COMP((ver), 24u) >> 24u) #define VERSION_MIN(ver) _AS_u32(VERSION_COMP((ver), 16u) >> 16u) #define VERSION_BF(ver) _AS_u32(VERSION_COMP((ver), 8u) >> 8u) #define VERSION_REV(ver) _AS_u32(_AS_u32(ver) & 0xffu) _Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u, "invalid native version spec"); // Misc. #define PTR_ASSIGN(ptr, val) ( (ptr) ? (*(ptr) = (val), (ptr)) : (ptr) ) #define PTR_ASSIGNv(ptr, val) ({ let _ptr = (ptr); let _val = (val); PTR_ASSIGN(_ptr, _val); }) #ifdef __cplusplus #undef _Static_assert } #endif #endif /* _MACROS_H */