kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

smoke.c (10121B)


      1 /* Smoke test: every freestanding header should parse and expose its
      2    required macros / typedefs. Compile with:
      3        gcc -std=c11 -ffreestanding -nostdinc -Iinclude -Wall -Wextra \
      4            -Wpedantic -c test/smoke.c -o /tmp/smoke.o
      5    No link step -- freestanding has no startup, no libc.
      6 
      7    Use gcc (not clang) until kit itself can host the build: kit's
      8    `__atomic_*` builtins are defined to operate transparently over
      9    `_Atomic`-qualified pointers (gcc-style). Clang's same-named builtins
     10    reject `_Atomic` args -- that's a clang divergence from kit's
     11    contract, not something the headers should paper over. */
     12 
     13 /* Bridge for hosts that predefine the same target facts under different
     14    names than kit's contract (see builtins.md). Strictly for running
     15    this smoke test under stock gcc/clang; kit itself supplies these. */
     16 #if !defined(__ATOMIC_INT_LOCK_FREE) && defined(__GCC_ATOMIC_INT_LOCK_FREE)
     17 #define __ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE
     18 #define __ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
     19 #define __ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE
     20 #define __ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE
     21 #define __ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE
     22 #define __ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
     23 #define __ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
     24 #define __ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
     25 #define __ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
     26 #define __ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
     27 #endif
     28 
     29 #include <ctype.h>
     30 #include <float.h>
     31 #include <iso646.h>
     32 #include <kit/backtrace.h>
     33 #include <kit/coro.h>
     34 #include <limits.h>
     35 #include <setjmp.h>
     36 #include <stdalign.h>
     37 #include <stdarg.h>
     38 #include <stdatomic.h>
     39 #include <stdbool.h>
     40 #include <stddef.h>
     41 #include <stdint.h>
     42 #include <stdnoreturn.h>
     43 
     44 /* ---- compile-time assertions over the contracts ------------------ */
     45 
     46 /* hardcoded contract -- kit assumes 8-bit byte, 32-bit int, 64-bit long long,
     47    IEEE 754 binary32/64. Pin the values so a compiler/target violating these
     48    fails loudly here. */
     49 _Static_assert(CHAR_BIT == 8, "kit assumption");
     50 _Static_assert(SHRT_MAX == 32767, "kit assumption");
     51 _Static_assert(INT_MAX == 2147483647, "kit assumption");
     52 _Static_assert(UINT_MAX == 4294967295U, "kit assumption");
     53 _Static_assert(LLONG_MAX == 9223372036854775807LL, "kit assumption");
     54 _Static_assert(FLT_RADIX == 2, "kit assumption");
     55 _Static_assert(FLT_MANT_DIG == 24, "binary32");
     56 _Static_assert(DBL_MANT_DIG == 53, "binary64");
     57 
     58 _Static_assert(CHAR_BIT >= 8, "C11 5.2.4.2.1");
     59 _Static_assert(SCHAR_MIN <= -127, "C11 5.2.4.2.1");
     60 _Static_assert(SCHAR_MAX >= 127, "C11 5.2.4.2.1");
     61 _Static_assert(UCHAR_MAX >= 255, "C11 5.2.4.2.1");
     62 _Static_assert(SHRT_MIN <= -32767, "C11 5.2.4.2.1");
     63 _Static_assert(SHRT_MAX >= 32767, "C11 5.2.4.2.1");
     64 _Static_assert(INT_MIN <= -32767, "C11 5.2.4.2.1");
     65 _Static_assert(INT_MAX >= 32767, "C11 5.2.4.2.1");
     66 _Static_assert(LONG_MAX >= 2147483647L, "C11 5.2.4.2.1");
     67 _Static_assert(LLONG_MAX >= 9223372036854775807LL, "C11 5.2.4.2.1");
     68 _Static_assert(MB_LEN_MAX >= 1, "C11 5.2.4.2.1");
     69 
     70 _Static_assert(sizeof(int8_t) == 1, "exact width");
     71 _Static_assert(sizeof(int16_t) == 2, "exact width");
     72 _Static_assert(sizeof(int32_t) == 4, "exact width");
     73 _Static_assert(sizeof(int64_t) == 8, "exact width");
     74 _Static_assert(sizeof(uint8_t) == 1, "exact width");
     75 _Static_assert(sizeof(uint16_t) == 2, "exact width");
     76 _Static_assert(sizeof(uint32_t) == 4, "exact width");
     77 _Static_assert(sizeof(uint64_t) == 8, "exact width");
     78 
     79 _Static_assert(INT8_MAX == 127, "limits 7.20.2");
     80 _Static_assert(INT8_MIN == -128, "limits 7.20.2");
     81 _Static_assert(UINT8_MAX == 255, "limits 7.20.2");
     82 _Static_assert(INT64_MAX == 9223372036854775807LL, "limits 7.20.2");
     83 _Static_assert(UINT64_MAX == 18446744073709551615ULL, "limits 7.20.2");
     84 
     85 _Static_assert(sizeof(intptr_t) == sizeof(void*), "intptr_t holds void*");
     86 _Static_assert(sizeof(uintptr_t) == sizeof(void*), "uintptr_t holds void*");
     87 _Static_assert(sizeof(intmax_t) >= sizeof(long long), "intmax >= long long");
     88 
     89 _Static_assert(INT32_C(1) + INT32_C(2) == 3, "INT_C macros work");
     90 _Static_assert(UINT64_C(0xFFFFFFFFFFFFFFFF) == UINT64_MAX, "UINT64_C");
     91 
     92 /* iso646 substitutions */
     93 _Static_assert((1 and 1) == 1, "and");
     94 _Static_assert((1 or 0) == 1, "or");
     95 _Static_assert((compl 0) == -1, "compl");
     96 _Static_assert((1 xor 1) == 0, "xor");
     97 
     98 /* stdbool */
     99 _Static_assert(sizeof(bool) >= 1, "bool exists");
    100 _Static_assert(true == 1, "true");
    101 _Static_assert(false == 0, "false");
    102 _Static_assert(__bool_true_false_are_defined == 1, "marker macro");
    103 
    104 /* stdalign */
    105 _Static_assert(__alignof_is_defined == 1, "alignof marker");
    106 _Static_assert(__alignas_is_defined == 1, "alignas marker");
    107 _Static_assert(alignof(int) >= 1, "alignof works");
    108 alignas(16) static char aligned_buf[16];
    109 _Static_assert(alignof(max_align_t) >= alignof(long double), "max_align_t");
    110 
    111 /* stddef */
    112 _Static_assert(sizeof(size_t) == sizeof(sizeof(int)), "size_t");
    113 _Static_assert(sizeof(ptrdiff_t) == sizeof((int*)0 - (int*)0), "ptrdiff_t");
    114 
    115 struct s_off {
    116   char a;
    117   int b;
    118 };
    119 _Static_assert(offsetof(struct s_off, b) > 0, "offsetof");
    120 
    121 /* float.h: a few of the C11 minimums (5.2.4.2.2). */
    122 _Static_assert(FLT_RADIX >= 2, "5.2.4.2.2");
    123 _Static_assert(FLT_MANT_DIG >= 6, "5.2.4.2.2");
    124 _Static_assert(DBL_MANT_DIG >= FLT_MANT_DIG, "double >= float precision");
    125 _Static_assert(LDBL_MANT_DIG >= DBL_MANT_DIG, "long double >= double");
    126 _Static_assert(DECIMAL_DIG >= 10, "5.2.4.2.2");
    127 
    128 /* stdarg: must be usable in a vararg function. */
    129 static int sum_n(int n, ...) {
    130   va_list ap;
    131   va_start(ap, n);
    132   int s = 0;
    133   for (int i = 0; i < n; ++i) s += va_arg(ap, int);
    134   va_end(ap);
    135   return s;
    136 }
    137 
    138 /* stdnoreturn: macro must expand to a usable function specifier. */
    139 static noreturn void kit_trap(void) {
    140   for (;;) {
    141   }
    142 }
    143 
    144 /* setjmp: jmp_buf is an array type, setjmp is callable in the contexts
    145    permitted by C11 7.13.1.1p4, longjmp is _Noreturn. Compile-only --
    146    smoke.c never links against a setjmp implementation. */
    147 _Static_assert(sizeof(jmp_buf) >= 64, "jmp_buf room for regs");
    148 _Static_assert(_Alignof(jmp_buf) >= 16, "jmp_buf 16-byte aligned");
    149 static jmp_buf kit_jb;
    150 static int kit_setjmp_compiles(int x) {
    151   if (setjmp(kit_jb) != 0) return 1; /* allowed context */
    152   if (x) longjmp(kit_jb, 42);
    153   return 0;
    154 }
    155 
    156 /* kit/coro: coro_ctx and coro_t storage exists; the asymmetric API
    157    surface compiles and resolves. Compile-only -- smoke.c never links
    158    against a libkit_rt. */
    159 _Static_assert(sizeof(coro_ctx) >= 64, "coro_ctx room for regs");
    160 _Static_assert(_Alignof(coro_ctx) >= 16, "coro_ctx 16-byte aligned");
    161 _Static_assert(sizeof(coro_t) >= sizeof(coro_ctx) + 2 * sizeof(void*),
    162                "coro_t room for ctx + resumer + user_fn");
    163 _Static_assert(_Alignof(coro_t) >= 16, "coro_t 16-byte aligned");
    164 _Static_assert(CORO_STACK_ALIGN >= 8, "stack align reasonable");
    165 _Static_assert(CORO_INIT != CORO_DEAD, "status enum distinct");
    166 
    167 static coro_t kit_co;
    168 static _Alignas(16) unsigned char kit_co_stack[4096];
    169 static uintptr_t kit_co_body(uintptr_t v) { return coro_yield(v + 1); }
    170 static uintptr_t kit_coro_compiles(void) {
    171   coro_init(&kit_co, kit_co_body, kit_co_stack, sizeof(kit_co_stack));
    172   coro_result_t r = coro_resume(&kit_co, 0xC0FFEEu);
    173   coro_t* me = coro_self();
    174   return r.value + (uintptr_t)me + (uintptr_t)coro_status(&kit_co);
    175 }
    176 
    177 /* stdatomic: types, memory_order, lock-free macros, plus a runtime
    178    exercise of load, store, exchange, CAS, fetch ops, and atomic_flag. */
    179 _Static_assert(sizeof(atomic_int) == sizeof(int), "atomic_int matches int");
    180 _Static_assert(sizeof(atomic_bool) >= 1, "atomic_bool exists");
    181 _Static_assert(memory_order_relaxed != memory_order_seq_cst,
    182                "memory_order enum");
    183 _Static_assert(ATOMIC_INT_LOCK_FREE == 0 || ATOMIC_INT_LOCK_FREE == 1 ||
    184                    ATOMIC_INT_LOCK_FREE == 2,
    185                "lock-free macro range");
    186 
    187 static int kit_atomic_ok(void) {
    188   atomic_int x = ATOMIC_VAR_INIT(0);
    189   atomic_init(&x, 1);
    190   atomic_store(&x, 10);
    191   if (atomic_load(&x) != 10) return 0;
    192   if (atomic_exchange(&x, 20) != 10) return 0;
    193 
    194   int expected = 20;
    195   if (!atomic_compare_exchange_strong(&x, &expected, 30)) return 0;
    196   if (atomic_compare_exchange_strong(&x, &expected, 40))
    197     return 0; /* expected was 20, x is 30 */
    198 
    199   if (atomic_fetch_add(&x, 5) != 30) return 0;   /* x == 35 */
    200   if (atomic_fetch_sub(&x, 3) != 35) return 0;   /* x == 32 */
    201   if (atomic_fetch_or(&x, 0x1) != 32) return 0;  /* x == 33 */
    202   if (atomic_fetch_xor(&x, 0x3) != 33) return 0; /* x == 34 */
    203   if (atomic_fetch_and(&x, 0xF) != 34) return 0; /* x ==  2 */
    204 
    205   atomic_thread_fence(memory_order_seq_cst);
    206   atomic_signal_fence(memory_order_acq_rel);
    207   if (!atomic_is_lock_free(&x)) { /* allowed to be false; just must compile */
    208   }
    209 
    210   atomic_flag f = ATOMIC_FLAG_INIT;
    211   if (atomic_flag_test_and_set(&f)) return 0;  /* was clear */
    212   if (!atomic_flag_test_and_set(&f)) return 0; /* now set */
    213   atomic_flag_clear(&f);
    214   return 1;
    215 }
    216 
    217 /* ctype: header must expose classifiers and case conversion. Compile-only --
    218    runtime behavior is covered by test/rt/cases/ctype_runtime.c. */
    219 static int kit_ctype_compiles(void) {
    220   return isalnum('A') && isalpha('z') && isblank('\t') && iscntrl('\n') &&
    221          isdigit('7') && isgraph('!') && islower('q') && isprint(' ') &&
    222          ispunct('#') && isspace('\r') && isupper('Q') && isxdigit('f') &&
    223          tolower('Z') == 'z' && toupper('z') == 'Z';
    224 }
    225 
    226 /* kit/backtrace: the capture/print surface compiles and resolves. Compile-only
    227    -- smoke.c never links against a libkit_rt, so the actual walk never runs. */
    228 static int kit_backtrace_compiles(void) {
    229   void* frames[8];
    230   int n = __kit_backtrace(frames, 8, 1);
    231   __kit_print_backtrace();
    232   return n;
    233 }
    234 
    235 /* Reference everything so -Wunused-* stays quiet. */
    236 int kit_smoke_ok(void) {
    237   (void)aligned_buf;
    238   if (0) kit_trap();
    239   if (0) (void)kit_setjmp_compiles(0);
    240   if (0) (void)kit_coro_compiles();
    241   if (0) (void)kit_backtrace_compiles();
    242   if (0) (void)kit_ctype_compiles();
    243   return sum_n(3, 1, 2, 3) == 6 && kit_atomic_ok();
    244 }