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 }