kit

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

smoke.c (11410B)


      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 <inttypes.h>
     32 #include <iso646.h>
     33 #include <kit/backtrace.h>
     34 #include <kit/coro.h>
     35 #include <limits.h>
     36 #include <setjmp.h>
     37 #include <stdalign.h>
     38 #include <stdarg.h>
     39 #include <stdatomic.h>
     40 #include <stdbit.h>
     41 #include <stdbool.h>
     42 #include <stdckdint.h>
     43 #include <stddef.h>
     44 #include <stdint.h>
     45 #include <stdnoreturn.h>
     46 
     47 /* ---- compile-time assertions over the contracts ------------------ */
     48 
     49 /* hardcoded contract -- kit assumes 8-bit byte, 32-bit int, 64-bit long long,
     50    IEEE 754 binary32/64. Pin the values so a compiler/target violating these
     51    fails loudly here. */
     52 _Static_assert(CHAR_BIT == 8, "kit assumption");
     53 _Static_assert(SHRT_MAX == 32767, "kit assumption");
     54 _Static_assert(INT_MAX == 2147483647, "kit assumption");
     55 _Static_assert(UINT_MAX == 4294967295U, "kit assumption");
     56 _Static_assert(LLONG_MAX == 9223372036854775807LL, "kit assumption");
     57 _Static_assert(FLT_RADIX == 2, "kit assumption");
     58 _Static_assert(FLT_MANT_DIG == 24, "binary32");
     59 _Static_assert(DBL_MANT_DIG == 53, "binary64");
     60 
     61 _Static_assert(CHAR_BIT >= 8, "C11 5.2.4.2.1");
     62 _Static_assert(SCHAR_MIN <= -127, "C11 5.2.4.2.1");
     63 _Static_assert(SCHAR_MAX >= 127, "C11 5.2.4.2.1");
     64 _Static_assert(UCHAR_MAX >= 255, "C11 5.2.4.2.1");
     65 _Static_assert(SHRT_MIN <= -32767, "C11 5.2.4.2.1");
     66 _Static_assert(SHRT_MAX >= 32767, "C11 5.2.4.2.1");
     67 _Static_assert(INT_MIN <= -32767, "C11 5.2.4.2.1");
     68 _Static_assert(INT_MAX >= 32767, "C11 5.2.4.2.1");
     69 _Static_assert(LONG_MAX >= 2147483647L, "C11 5.2.4.2.1");
     70 _Static_assert(LLONG_MAX >= 9223372036854775807LL, "C11 5.2.4.2.1");
     71 _Static_assert(MB_LEN_MAX >= 1, "C11 5.2.4.2.1");
     72 
     73 _Static_assert(sizeof(int8_t) == 1, "exact width");
     74 _Static_assert(sizeof(int16_t) == 2, "exact width");
     75 _Static_assert(sizeof(int32_t) == 4, "exact width");
     76 _Static_assert(sizeof(int64_t) == 8, "exact width");
     77 _Static_assert(sizeof(uint8_t) == 1, "exact width");
     78 _Static_assert(sizeof(uint16_t) == 2, "exact width");
     79 _Static_assert(sizeof(uint32_t) == 4, "exact width");
     80 _Static_assert(sizeof(uint64_t) == 8, "exact width");
     81 
     82 _Static_assert(INT8_MAX == 127, "limits 7.20.2");
     83 _Static_assert(INT8_MIN == -128, "limits 7.20.2");
     84 _Static_assert(UINT8_MAX == 255, "limits 7.20.2");
     85 _Static_assert(INT64_MAX == 9223372036854775807LL, "limits 7.20.2");
     86 _Static_assert(UINT64_MAX == 18446744073709551615ULL, "limits 7.20.2");
     87 
     88 _Static_assert(sizeof(intptr_t) == sizeof(void*), "intptr_t holds void*");
     89 _Static_assert(sizeof(uintptr_t) == sizeof(void*), "uintptr_t holds void*");
     90 _Static_assert(sizeof(intmax_t) >= sizeof(long long), "intmax >= long long");
     91 
     92 _Static_assert(INT32_C(1) + INT32_C(2) == 3, "INT_C macros work");
     93 _Static_assert(UINT64_C(0xFFFFFFFFFFFFFFFF) == UINT64_MAX, "UINT64_C");
     94 _Static_assert(INT_WIDTH == 32, "C23 width macro");
     95 _Static_assert(UINTPTR_WIDTH == sizeof(uintptr_t) * CHAR_BIT, "uintptr width");
     96 _Static_assert(__STDC_VERSION_INTTYPES_H__ == 202311L, "inttypes version");
     97 _Static_assert(__STDC_VERSION_STDBIT_H__ == 202311L, "stdbit version");
     98 _Static_assert(__STDC_VERSION_STDCKDINT_H__ == 202311L, "stdckdint version");
     99 _Static_assert(__STDC_ENDIAN_NATIVE__ == __STDC_ENDIAN_LITTLE__,
    100                "kit targets little-endian");
    101 
    102 /* iso646 substitutions */
    103 _Static_assert((1 and 1) == 1, "and");
    104 _Static_assert((1 or 0) == 1, "or");
    105 _Static_assert((compl 0) == -1, "compl");
    106 _Static_assert((1 xor 1) == 0, "xor");
    107 
    108 /* stdbool */
    109 _Static_assert(sizeof(bool) >= 1, "bool exists");
    110 _Static_assert(true == 1, "true");
    111 _Static_assert(false == 0, "false");
    112 _Static_assert(__bool_true_false_are_defined == 1, "marker macro");
    113 
    114 /* stdalign */
    115 _Static_assert(__alignof_is_defined == 1, "alignof marker");
    116 _Static_assert(__alignas_is_defined == 1, "alignas marker");
    117 _Static_assert(alignof(int) >= 1, "alignof works");
    118 alignas(16) static char aligned_buf[16];
    119 _Static_assert(alignof(max_align_t) >= alignof(long double), "max_align_t");
    120 
    121 /* stddef */
    122 _Static_assert(sizeof(size_t) == sizeof(sizeof(int)), "size_t");
    123 _Static_assert(sizeof(ptrdiff_t) == sizeof((int*)0 - (int*)0), "ptrdiff_t");
    124 
    125 struct s_off {
    126   char a;
    127   int b;
    128 };
    129 _Static_assert(offsetof(struct s_off, b) > 0, "offsetof");
    130 
    131 /* float.h: a few of the C11 minimums (5.2.4.2.2). */
    132 _Static_assert(FLT_RADIX >= 2, "5.2.4.2.2");
    133 _Static_assert(FLT_MANT_DIG >= 6, "5.2.4.2.2");
    134 _Static_assert(DBL_MANT_DIG >= FLT_MANT_DIG, "double >= float precision");
    135 _Static_assert(LDBL_MANT_DIG >= DBL_MANT_DIG, "long double >= double");
    136 _Static_assert(DECIMAL_DIG >= 10, "5.2.4.2.2");
    137 
    138 /* stdarg: must be usable in a vararg function. */
    139 static int sum_n(int n, ...) {
    140   va_list ap;
    141   va_start(ap, n);
    142   int s = 0;
    143   for (int i = 0; i < n; ++i) s += va_arg(ap, int);
    144   va_end(ap);
    145   return s;
    146 }
    147 
    148 /* stdnoreturn: macro must expand to a usable function specifier. */
    149 static noreturn void kit_trap(void) {
    150   for (;;) {
    151   }
    152 }
    153 
    154 /* setjmp: jmp_buf is an array type, setjmp is callable in the contexts
    155    permitted by C11 7.13.1.1p4, longjmp is _Noreturn. Compile-only --
    156    smoke.c never links against a setjmp implementation. */
    157 _Static_assert(sizeof(jmp_buf) >= 64, "jmp_buf room for regs");
    158 _Static_assert(_Alignof(jmp_buf) >= 16, "jmp_buf 16-byte aligned");
    159 static jmp_buf kit_jb;
    160 static int kit_setjmp_compiles(int x) {
    161   if (setjmp(kit_jb) != 0) return 1; /* allowed context */
    162   if (x) longjmp(kit_jb, 42);
    163   return 0;
    164 }
    165 
    166 /* kit/coro: coro_ctx and coro_t storage exists; the asymmetric API
    167    surface compiles and resolves. Compile-only -- smoke.c never links
    168    against a libkit_rt. */
    169 _Static_assert(sizeof(coro_ctx) >= 64, "coro_ctx room for regs");
    170 _Static_assert(_Alignof(coro_ctx) >= 16, "coro_ctx 16-byte aligned");
    171 _Static_assert(sizeof(coro_t) >= sizeof(coro_ctx) + 2 * sizeof(void*),
    172                "coro_t room for ctx + resumer + user_fn");
    173 _Static_assert(_Alignof(coro_t) >= 16, "coro_t 16-byte aligned");
    174 _Static_assert(CORO_STACK_ALIGN >= 8, "stack align reasonable");
    175 _Static_assert(CORO_INIT != CORO_DEAD, "status enum distinct");
    176 
    177 static coro_t kit_co;
    178 static _Alignas(16) unsigned char kit_co_stack[4096];
    179 static uintptr_t kit_co_body(uintptr_t v) { return coro_yield(v + 1); }
    180 static uintptr_t kit_coro_compiles(void) {
    181   coro_init(&kit_co, kit_co_body, kit_co_stack, sizeof(kit_co_stack));
    182   coro_result_t r = coro_resume(&kit_co, 0xC0FFEEu);
    183   coro_t* me = coro_self();
    184   return r.value + (uintptr_t)me + (uintptr_t)coro_status(&kit_co);
    185 }
    186 
    187 /* stdatomic: types, memory_order, lock-free macros, plus a runtime
    188    exercise of load, store, exchange, CAS, fetch ops, and atomic_flag. */
    189 _Static_assert(sizeof(atomic_int) == sizeof(int), "atomic_int matches int");
    190 _Static_assert(sizeof(atomic_bool) >= 1, "atomic_bool exists");
    191 _Static_assert(memory_order_relaxed != memory_order_seq_cst,
    192                "memory_order enum");
    193 _Static_assert(ATOMIC_INT_LOCK_FREE == 0 || ATOMIC_INT_LOCK_FREE == 1 ||
    194                    ATOMIC_INT_LOCK_FREE == 2,
    195                "lock-free macro range");
    196 
    197 static int kit_atomic_ok(void) {
    198   atomic_int x = ATOMIC_VAR_INIT(0);
    199   atomic_init(&x, 1);
    200   atomic_store(&x, 10);
    201   if (atomic_load(&x) != 10) return 0;
    202   if (atomic_exchange(&x, 20) != 10) return 0;
    203 
    204   int expected = 20;
    205   if (!atomic_compare_exchange_strong(&x, &expected, 30)) return 0;
    206   if (atomic_compare_exchange_strong(&x, &expected, 40))
    207     return 0; /* expected was 20, x is 30 */
    208 
    209   if (atomic_fetch_add(&x, 5) != 30) return 0;   /* x == 35 */
    210   if (atomic_fetch_sub(&x, 3) != 35) return 0;   /* x == 32 */
    211   if (atomic_fetch_or(&x, 0x1) != 32) return 0;  /* x == 33 */
    212   if (atomic_fetch_xor(&x, 0x3) != 33) return 0; /* x == 34 */
    213   if (atomic_fetch_and(&x, 0xF) != 34) return 0; /* x ==  2 */
    214 
    215   atomic_thread_fence(memory_order_seq_cst);
    216   atomic_signal_fence(memory_order_acq_rel);
    217   if (!atomic_is_lock_free(&x)) { /* allowed to be false; just must compile */
    218   }
    219 
    220   atomic_flag f = ATOMIC_FLAG_INIT;
    221   if (atomic_flag_test_and_set(&f)) return 0;  /* was clear */
    222   if (!atomic_flag_test_and_set(&f)) return 0; /* now set */
    223   atomic_flag_clear(&f);
    224   return 1;
    225 }
    226 
    227 /* ctype: header must expose classifiers and case conversion. Compile-only --
    228    runtime behavior is covered by test/rt/cases/ctype_runtime.c. */
    229 static int kit_ctype_compiles(void) {
    230   return isalnum('A') && isalpha('z') && isblank('\t') && iscntrl('\n') &&
    231          isdigit('7') && isgraph('!') && islower('q') && isprint(' ') &&
    232          ispunct('#') && isspace('\r') && isupper('Q') && isxdigit('f') &&
    233          tolower('Z') == 'z' && toupper('z') == 'Z';
    234 }
    235 
    236 static int kit_c23_headers_compile(void) {
    237   int checked;
    238   imaxdiv_t d = imaxdiv((intmax_t)17, (intmax_t)5);
    239   if (imaxabs((intmax_t)-3) != (intmax_t)3) return 0;
    240   if (d.quot != 3 || d.rem != 2) return 0;
    241   if (strtoimax("7", (char**)0, 10) != (intmax_t)7) return 0;
    242   if (strtoumax("10", (char**)0, 10) != (uintmax_t)10) return 0;
    243   if (stdc_leading_zeros_uc((unsigned char)0x10u) != 3) return 0;
    244   if (stdc_trailing_zeros_ui(0x20u) != 5) return 0;
    245   if (stdc_count_ones_ui(0xb1u) != 4) return 0;
    246   if (!stdc_has_single_bit_ui(0x80u)) return 0;
    247   if (stdc_bit_floor_ui(130u) != 128u) return 0;
    248   if (stdc_bit_ceil_ui(129u) != 256u) return 0;
    249   if (ckd_add(&checked, 40, 2)) return 0;
    250   return checked == 42;
    251 }
    252 
    253 /* kit/backtrace: the capture/print surface compiles and resolves. Compile-only
    254    -- smoke.c never links against a libkit_rt, so the actual walk never runs. */
    255 static int kit_backtrace_compiles(void) {
    256   void* frames[8];
    257   int n = __kit_backtrace(frames, 8, 1);
    258   __kit_print_backtrace();
    259   return n;
    260 }
    261 
    262 /* Reference everything so -Wunused-* stays quiet. */
    263 int kit_smoke_ok(void) {
    264   (void)aligned_buf;
    265   if (0) kit_trap();
    266   if (0) (void)kit_setjmp_compiles(0);
    267   if (0) (void)kit_coro_compiles();
    268   if (0) (void)kit_backtrace_compiles();
    269   if (0) (void)kit_ctype_compiles();
    270   if (0) (void)kit_c23_headers_compile();
    271   return sum_n(3, 1, 2, 3) == 6 && kit_atomic_ok();
    272 }