kit

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

commit 310b5ab01ab448779044ddb36f3cdf5248ae18f3
parent da7fa2922ee93c3891d072432008bfe713ff84cd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  7 May 2026 09:22:11 -0700

assert.h, stdatomic.h

Diffstat:
Dbuiltins.md | 96-------------------------------------------------------------------------------
Adoc/builtins.md | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/assert.h | 40++++++++++++++++++++++++++++++++++++++++
Ainclude/stdatomic.h | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/smoke.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 387 insertions(+), 100 deletions(-)

diff --git a/builtins.md b/builtins.md @@ -1,96 +0,0 @@ -# Compiler builtins used by cfree - -cfree's freestanding headers hardcode every value that's invariant under -its target assumptions, and delegate to compiler builtins for everything -that genuinely varies across targets. This file is the contract: if a -target violates an "assumption" below, the headers (and `test/smoke.c`) -will be wrong. - -## Target assumptions (hardcoded) - -- `CHAR_BIT == 8` -- `short == 16` bits, `int == 32` bits, `long long == 64` bits -- Two's complement integer representation -- `float` is IEEE 754 binary32 -- `double` is IEEE 754 binary64 - -## What genuinely varies (delegated) - -| Quantity | Why it varies | -| ------------------------- | -------------------------------------------------- | -| `char` signedness | ARM defaults unsigned, x86 signed; flippable with `-funsigned-char`. Not changeable from a header. | -| `long` width | LP64 (Unix 64-bit) makes it 64; LLP64 (Win64) and ILP32 keep it 32 | -| `long double` format | x86 80-bit, AArch64 binary128 *or* binary64, PowerPC double-double, MSVC binary64 | -| `FLT_ROUNDS` | Runtime rounding mode (function call required) | -| `FLT_EVAL_METHOD` | x87 vs SSE vs embedded toolchains differ | -| `intptr_t` width | 32 vs 64 bits | -| `size_t`, `ptrdiff_t` | Track pointer width | -| `wchar_t` | 16-bit on Windows, 32-bit on Unix; signedness varies | -| `intmax_t` literal type | `long` on LP64, `long long` on LLP64 | -| `int_fast{N}_t` widths | Each target picks its own "fast" width | -| `va_list` and varargs ABI | Call convention is target-defined | -| `max_align_t` | Track widest scalar alignment | - ---- - -## Builtins - -Grouped by header, every `__builtin_*` or `__*__` we still depend on. - -### `<float.h>` -- `__builtin_flt_rounds()` — runtime rounding mode → `FLT_ROUNDS` -- `__FLT_EVAL_METHOD__` -- `__DECIMAL_DIG__` -- `__LDBL_HAS_DENORM__` → `LDBL_HAS_SUBNORM` -- `__LDBL_MANT_DIG__`, `__LDBL_DECIMAL_DIG__`, `__LDBL_DIG__` -- `__LDBL_MIN_EXP__`, `__LDBL_MIN_10_EXP__` -- `__LDBL_MAX_EXP__`, `__LDBL_MAX_10_EXP__` -- `__LDBL_MAX__`, `__LDBL_MIN__`, `__LDBL_EPSILON__` -- `__LDBL_DENORM_MIN__` → `LDBL_TRUE_MIN` - -### `<limits.h>` -- `__LONG_MAX__` → `LONG_MAX` (and derived `LONG_MIN`, `ULONG_MAX`) -- `__CHAR_UNSIGNED__` — defined ⇔ plain `char` is unsigned - -### `<stddef.h>` -- `__PTRDIFF_TYPE__` → `ptrdiff_t` -- `__SIZE_TYPE__` → `size_t` -- `__WCHAR_TYPE__` → `wchar_t` (C only; in C++ it's a keyword) -- `__builtin_offsetof(t, m)` → `offsetof` - -### `<stdint.h>` -Types (aliases vary by data model even when limits don't): -- `__INT{8,16,32,64}_TYPE__`, `__UINT{N}_TYPE__` -- `__INT_LEAST{N}_TYPE__`, `__UINT_LEAST{N}_TYPE__` -- `__INT_FAST{N}_TYPE__`, `__UINT_FAST{N}_TYPE__` -- `__INTPTR_TYPE__`, `__UINTPTR_TYPE__` -- `__INTMAX_TYPE__`, `__UINTMAX_TYPE__` - -Limits that are not pinned by the target assumptions: -- `__INT_FAST{N}_MAX__`, `__UINT_FAST{N}_MAX__` -- `__INTPTR_MAX__`, `__UINTPTR_MAX__` -- `__INTMAX_MAX__`, `__UINTMAX_MAX__` -- `__PTRDIFF_MAX__` -- `__SIZE_MAX__` -- `__WCHAR_MAX__`, `__WCHAR_MIN__` -- `__WINT_MAX__`, `__WINT_MIN__` -- `__SIG_ATOMIC_MAX__`, `__SIG_ATOMIC_MIN__` - -64-bit and intmax constant macros (literal suffix tracks the alias): -- `__INT64_C(c)`, `__UINT64_C(c)` -- `__INTMAX_C(c)`, `__UINTMAX_C(c)` - -### `<stdarg.h>` -Entirely compiler-supplied — varargs ABI is target-defined: -- `__builtin_va_list` (type) -- `__builtin_va_start`, `__builtin_va_arg`, `__builtin_va_end`, `__builtin_va_copy` - ---- - -## Discovery - -To enumerate what a compiler predefines for the current target: - -```sh -cc -dM -E -x c /dev/null | sort -``` diff --git a/doc/builtins.md b/doc/builtins.md @@ -0,0 +1,123 @@ +# Compiler builtins used by cfree + +cfree's freestanding headers hardcode every value that's invariant under +its target assumptions, and delegate to compiler builtins for everything +that genuinely varies across targets. This file is the contract: if a +target violates an "assumption" below, the headers (and `test/smoke.c`) +will be wrong. + +## Target assumptions (hardcoded) + +- `CHAR_BIT == 8` +- `short == 16` bits, `int == 32` bits, `long long == 64` bits +- Two's complement integer representation +- `float` is IEEE 754 binary32 +- `double` is IEEE 754 binary64 + +## What genuinely varies (delegated) + +| Quantity | Why it varies | +| ------------------------- | -------------------------------------------------- | +| `char` signedness | ARM defaults unsigned, x86 signed; flippable with `-funsigned-char`. Not changeable from a header. | +| `long` width | LP64 (Unix 64-bit) makes it 64; LLP64 (Win64) and ILP32 keep it 32 | +| `long double` format | x86 80-bit, AArch64 binary128 *or* binary64, PowerPC double-double, MSVC binary64 | +| `FLT_ROUNDS` | Runtime rounding mode (function call required) | +| `FLT_EVAL_METHOD` | x87 vs SSE vs embedded toolchains differ | +| `intptr_t` width | 32 vs 64 bits | +| `size_t`, `ptrdiff_t` | Track pointer width | +| `wchar_t` | 16-bit on Windows, 32-bit on Unix; signedness varies | +| `intmax_t` literal type | `long` on LP64, `long long` on LLP64 | +| `int_fast{N}_t` widths | Each target picks its own "fast" width | +| `va_list` and varargs ABI | Call convention is target-defined | +| `max_align_t` | Track widest scalar alignment | + +--- + +## Builtins + +Grouped by header, every `__builtin_*` or `__*__` we still depend on. + +### `<float.h>` +- `__builtin_flt_rounds()` — runtime rounding mode → `FLT_ROUNDS` +- `__FLT_EVAL_METHOD__` +- `__DECIMAL_DIG__` +- `__LDBL_HAS_DENORM__` → `LDBL_HAS_SUBNORM` +- `__LDBL_MANT_DIG__`, `__LDBL_DECIMAL_DIG__`, `__LDBL_DIG__` +- `__LDBL_MIN_EXP__`, `__LDBL_MIN_10_EXP__` +- `__LDBL_MAX_EXP__`, `__LDBL_MAX_10_EXP__` +- `__LDBL_MAX__`, `__LDBL_MIN__`, `__LDBL_EPSILON__` +- `__LDBL_DENORM_MIN__` → `LDBL_TRUE_MIN` + +### `<limits.h>` +- `__LONG_MAX__` → `LONG_MAX` (and derived `LONG_MIN`, `ULONG_MAX`) +- `__CHAR_UNSIGNED__` — defined ⇔ plain `char` is unsigned + +### `<stddef.h>` +- `__PTRDIFF_TYPE__` → `ptrdiff_t` +- `__SIZE_TYPE__` → `size_t` +- `__WCHAR_TYPE__` → `wchar_t` (C only; in C++ it's a keyword) +- `__builtin_offsetof(t, m)` → `offsetof` + +### `<stdint.h>` +Types (aliases vary by data model even when limits don't): +- `__INT{8,16,32,64}_TYPE__`, `__UINT{N}_TYPE__` +- `__INT_LEAST{N}_TYPE__`, `__UINT_LEAST{N}_TYPE__` +- `__INT_FAST{N}_TYPE__`, `__UINT_FAST{N}_TYPE__` +- `__INTPTR_TYPE__`, `__UINTPTR_TYPE__` +- `__INTMAX_TYPE__`, `__UINTMAX_TYPE__` + +Limits that are not pinned by the target assumptions: +- `__INT_FAST{N}_MAX__`, `__UINT_FAST{N}_MAX__` +- `__INTPTR_MAX__`, `__UINTPTR_MAX__` +- `__INTMAX_MAX__`, `__UINTMAX_MAX__` +- `__PTRDIFF_MAX__` +- `__SIZE_MAX__` +- `__WCHAR_MAX__`, `__WCHAR_MIN__` +- `__WINT_MAX__`, `__WINT_MIN__` +- `__SIG_ATOMIC_MAX__`, `__SIG_ATOMIC_MIN__` + +64-bit and intmax constant macros (literal suffix tracks the alias): +- `__INT64_C(c)`, `__UINT64_C(c)` +- `__INTMAX_C(c)`, `__UINTMAX_C(c)` + +### `<stdarg.h>` +Entirely compiler-supplied — varargs ABI is target-defined: +- `__builtin_va_list` (type) +- `__builtin_va_start`, `__builtin_va_arg`, `__builtin_va_end`, `__builtin_va_copy` + +### `<stdatomic.h>` +Atomic codegen, lock-free shape, and fence semantics are target-defined. +The `__atomic_*` family must operate transparently on `_Atomic`-qualified +pointers (no separate variant for atomic-typed args). + +Memory-order constants (values for the `memory_order` enum): +- `__ATOMIC_RELAXED`, `__ATOMIC_CONSUME`, `__ATOMIC_ACQUIRE`, + `__ATOMIC_RELEASE`, `__ATOMIC_ACQ_REL`, `__ATOMIC_SEQ_CST` + +Lock-free shape (per-type, value 0/1/2 per C11 7.17.5): +- `__ATOMIC_{BOOL,CHAR,CHAR16_T,CHAR32_T,WCHAR_T,SHORT,INT,LONG,LLONG,POINTER}_LOCK_FREE` + +Types for the C11 char/wide aliases (also delegated for `<stddef.h>`): +- `__CHAR16_TYPE__`, `__CHAR32_TYPE__`, `__WCHAR_TYPE__` + +Operations (signatures match the GCC `__atomic` builtin family): +- `__atomic_load_n(ptr, order)` +- `__atomic_store_n(ptr, val, order)` +- `__atomic_exchange_n(ptr, val, order)` +- `__atomic_compare_exchange_n(ptr, expected, desired, weak, succ, fail)` +- `__atomic_fetch_add`, `__atomic_fetch_sub`, `__atomic_fetch_or`, + `__atomic_fetch_xor`, `__atomic_fetch_and` — `(ptr, val, order)` +- `__atomic_thread_fence(order)`, `__atomic_signal_fence(order)` +- `__atomic_is_lock_free(size, ptr)` +- `__atomic_test_and_set(ptr, order)`, `__atomic_clear(ptr, order)` — for + `atomic_flag` + +--- + +## Discovery + +To enumerate what a compiler predefines for the current target: + +```sh +cc -dM -E -x c /dev/null | sort +``` diff --git a/include/assert.h b/include/assert.h @@ -0,0 +1,40 @@ +/* assert.h -- C11 7.2 -- Diagnostics + * + * <assert.h> is a hosted header in C11: a faithful assert() writes to + * stderr and calls abort(), neither of which exists freestanding. cfree + * provides a slimmer contract -- on failure, assert() calls a + * user-supplied handler: + * + * _Noreturn void __cfree_assert_fail(const char *expr, + * const char *file, + * int line, + * const char *func); + * + * The user (or environment) must define this. It must not return. + * + * NOTE: this header has no include guard by design. C11 7.2 requires the + * `assert` macro to reflect the current state of NDEBUG on every + * inclusion, so re-including <assert.h> after #define/#undef NDEBUG must + * flip its definition. + */ + +#undef assert + +#ifdef NDEBUG +# define assert(ignore) ((void)0) +#else + +_Noreturn void __cfree_assert_fail(const char *expr, + const char *file, + int line, + const char *func); + +# define assert(expr) \ + ((expr) ? (void)0 \ + : __cfree_assert_fail(#expr, __FILE__, __LINE__, __func__)) + +#endif /* NDEBUG */ + +/* static_assert: identical re-definition across re-inclusions is allowed + by C11 6.10.3p2 and produces no diagnostic. */ +#define static_assert _Static_assert diff --git a/include/stdatomic.h b/include/stdatomic.h @@ -0,0 +1,160 @@ +/* stdatomic.h -- C11 7.17 -- Atomics + * + * Atomic codegen, lock-free shape, and fence semantics are entirely + * target-defined, so this header is a thin C11 surface over cfree's + * atomic builtins. The `__atomic_*` family operates transparently on + * `_Atomic`-qualified pointers; lock-free properties come from the + * `__ATOMIC_*_LOCK_FREE` macros. + */ +#ifndef CFREE_STDATOMIC_H +#define CFREE_STDATOMIC_H + +#include <stddef.h> +#include <stdint.h> + +/* ------------------------------------------------------------------ */ +/* 7.17.3 Order and consistency */ +/* ------------------------------------------------------------------ */ +typedef enum memory_order { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST, +} memory_order; + +#define kill_dependency(y) (y) + +/* ------------------------------------------------------------------ */ +/* 7.17.1 Lock-free property macros */ +/* ------------------------------------------------------------------ */ +#define ATOMIC_BOOL_LOCK_FREE __ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __ATOMIC_POINTER_LOCK_FREE + +/* ------------------------------------------------------------------ */ +/* 7.17.2 Initialization */ +/* ------------------------------------------------------------------ */ +#define ATOMIC_VAR_INIT(value) (value) +#define atomic_init(obj, value) \ + __atomic_store_n((obj), (value), __ATOMIC_RELAXED) + +/* ------------------------------------------------------------------ */ +/* 7.17.4 Fences */ +/* ------------------------------------------------------------------ */ +#define atomic_thread_fence(order) __atomic_thread_fence(order) +#define atomic_signal_fence(order) __atomic_signal_fence(order) + +/* ------------------------------------------------------------------ */ +/* 7.17.5 Lock-free property query */ +/* ------------------------------------------------------------------ */ +#define atomic_is_lock_free(obj) __atomic_is_lock_free(sizeof(*(obj)), (obj)) + +/* ------------------------------------------------------------------ */ +/* 7.17.6 Atomic integer types */ +/* ------------------------------------------------------------------ */ +typedef _Atomic _Bool atomic_bool; +typedef _Atomic char atomic_char; +typedef _Atomic signed char atomic_schar; +typedef _Atomic unsigned char atomic_uchar; +typedef _Atomic short atomic_short; +typedef _Atomic unsigned short atomic_ushort; +typedef _Atomic int atomic_int; +typedef _Atomic unsigned int atomic_uint; +typedef _Atomic long atomic_long; +typedef _Atomic unsigned long atomic_ulong; +typedef _Atomic long long atomic_llong; +typedef _Atomic unsigned long long atomic_ullong; +typedef _Atomic __CHAR16_TYPE__ atomic_char16_t; +typedef _Atomic __CHAR32_TYPE__ atomic_char32_t; +typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t; +typedef _Atomic int_least8_t atomic_int_least8_t; +typedef _Atomic uint_least8_t atomic_uint_least8_t; +typedef _Atomic int_least16_t atomic_int_least16_t; +typedef _Atomic uint_least16_t atomic_uint_least16_t; +typedef _Atomic int_least32_t atomic_int_least32_t; +typedef _Atomic uint_least32_t atomic_uint_least32_t; +typedef _Atomic int_least64_t atomic_int_least64_t; +typedef _Atomic uint_least64_t atomic_uint_least64_t; +typedef _Atomic int_fast8_t atomic_int_fast8_t; +typedef _Atomic uint_fast8_t atomic_uint_fast8_t; +typedef _Atomic int_fast16_t atomic_int_fast16_t; +typedef _Atomic uint_fast16_t atomic_uint_fast16_t; +typedef _Atomic int_fast32_t atomic_int_fast32_t; +typedef _Atomic uint_fast32_t atomic_uint_fast32_t; +typedef _Atomic int_fast64_t atomic_int_fast64_t; +typedef _Atomic uint_fast64_t atomic_uint_fast64_t; +typedef _Atomic intptr_t atomic_intptr_t; +typedef _Atomic uintptr_t atomic_uintptr_t; +typedef _Atomic size_t atomic_size_t; +typedef _Atomic ptrdiff_t atomic_ptrdiff_t; +typedef _Atomic intmax_t atomic_intmax_t; +typedef _Atomic uintmax_t atomic_uintmax_t; + +/* ------------------------------------------------------------------ */ +/* 7.17.7 Operations on atomic types */ +/* ------------------------------------------------------------------ */ +#define atomic_store(obj, desired) \ + __atomic_store_n((obj), (desired), __ATOMIC_SEQ_CST) +#define atomic_store_explicit(obj, desired, order) \ + __atomic_store_n((obj), (desired), (order)) + +#define atomic_load(obj) \ + __atomic_load_n((obj), __ATOMIC_SEQ_CST) +#define atomic_load_explicit(obj, order) \ + __atomic_load_n((obj), (order)) + +#define atomic_exchange(obj, desired) \ + __atomic_exchange_n((obj), (desired), __ATOMIC_SEQ_CST) +#define atomic_exchange_explicit(obj, desired, order) \ + __atomic_exchange_n((obj), (desired), (order)) + +#define atomic_compare_exchange_strong(obj, expected, desired) \ + __atomic_compare_exchange_n((obj), (expected), (desired), 0, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + __atomic_compare_exchange_n((obj), (expected), (desired), 0, (succ), (fail)) +#define atomic_compare_exchange_weak(obj, expected, desired) \ + __atomic_compare_exchange_n((obj), (expected), (desired), 1, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + __atomic_compare_exchange_n((obj), (expected), (desired), 1, (succ), (fail)) + +#define atomic_fetch_add(obj, arg) __atomic_fetch_add((obj), (arg), __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit(obj, arg, order) __atomic_fetch_add((obj), (arg), (order)) +#define atomic_fetch_sub(obj, arg) __atomic_fetch_sub((obj), (arg), __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit(obj, arg, order) __atomic_fetch_sub((obj), (arg), (order)) +#define atomic_fetch_or(obj, arg) __atomic_fetch_or((obj), (arg), __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit(obj, arg, order) __atomic_fetch_or((obj), (arg), (order)) +#define atomic_fetch_xor(obj, arg) __atomic_fetch_xor((obj), (arg), __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit(obj, arg, order) __atomic_fetch_xor((obj), (arg), (order)) +#define atomic_fetch_and(obj, arg) __atomic_fetch_and((obj), (arg), __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit(obj, arg, order) __atomic_fetch_and((obj), (arg), (order)) + +/* ------------------------------------------------------------------ */ +/* 7.17.8 Atomic flag */ +/* ------------------------------------------------------------------ */ +typedef struct atomic_flag { + _Atomic _Bool _Value; +} atomic_flag; + +#define ATOMIC_FLAG_INIT { 0 } + +#define atomic_flag_test_and_set(obj) \ + __atomic_test_and_set(&(obj)->_Value, __ATOMIC_SEQ_CST) +#define atomic_flag_test_and_set_explicit(obj, order) \ + __atomic_test_and_set(&(obj)->_Value, (order)) +#define atomic_flag_clear(obj) \ + __atomic_clear(&(obj)->_Value, __ATOMIC_SEQ_CST) +#define atomic_flag_clear_explicit(obj, order) \ + __atomic_clear(&(obj)->_Value, (order)) + +#endif diff --git a/test/smoke.c b/test/smoke.c @@ -1,14 +1,37 @@ /* Smoke test: every freestanding header should parse and expose its required macros / typedefs. Compile with: - cc -std=c11 -ffreestanding -nostdinc -Iinclude -Wall -Wextra \ - -Wpedantic -c test/smoke.c -o /tmp/smoke.o - No link step -- freestanding has no startup, no libc. */ + gcc -std=c11 -ffreestanding -nostdinc -Iinclude -Wall -Wextra \ + -Wpedantic -c test/smoke.c -o /tmp/smoke.o + No link step -- freestanding has no startup, no libc. + + Use gcc (not clang) until cfree itself can host the build: cfree's + `__atomic_*` builtins are defined to operate transparently over + `_Atomic`-qualified pointers (gcc-style). Clang's same-named builtins + reject `_Atomic` args -- that's a clang divergence from cfree's + contract, not something the headers should paper over. */ + +/* Bridge for hosts that predefine the same target facts under different + names than cfree's contract (see builtins.md). Strictly for running + this smoke test under stock gcc/clang; cfree itself supplies these. */ +#if !defined(__ATOMIC_INT_LOCK_FREE) && defined(__GCC_ATOMIC_INT_LOCK_FREE) +# define __ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE +# define __ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +# define __ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE +# define __ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE +# define __ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE +# define __ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +# define __ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +# define __ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +# define __ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +# define __ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE +#endif #include <float.h> #include <iso646.h> #include <limits.h> #include <stdalign.h> #include <stdarg.h> +#include <stdatomic.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -108,9 +131,46 @@ static int sum_n(int n, ...) { /* stdnoreturn: macro must expand to a usable function specifier. */ static noreturn void cfree_trap(void) { for (;;) {} } +/* stdatomic: types, memory_order, lock-free macros, plus a runtime + exercise of load, store, exchange, CAS, fetch ops, and atomic_flag. */ +_Static_assert(sizeof(atomic_int) == sizeof(int), "atomic_int matches int"); +_Static_assert(sizeof(atomic_bool) >= 1, "atomic_bool exists"); +_Static_assert(memory_order_relaxed != memory_order_seq_cst, "memory_order enum"); +_Static_assert(ATOMIC_INT_LOCK_FREE == 0 + || ATOMIC_INT_LOCK_FREE == 1 + || ATOMIC_INT_LOCK_FREE == 2, "lock-free macro range"); + +static int cfree_atomic_ok(void) { + atomic_int x = ATOMIC_VAR_INIT(0); + atomic_init(&x, 1); + atomic_store(&x, 10); + if (atomic_load(&x) != 10) return 0; + if (atomic_exchange(&x, 20) != 10) return 0; + + int expected = 20; + if (!atomic_compare_exchange_strong(&x, &expected, 30)) return 0; + if (atomic_compare_exchange_strong(&x, &expected, 40)) return 0; /* expected was 20, x is 30 */ + + if (atomic_fetch_add(&x, 5) != 30) return 0; /* x == 35 */ + if (atomic_fetch_sub(&x, 3) != 35) return 0; /* x == 32 */ + if (atomic_fetch_or(&x, 0x1) != 32) return 0; /* x == 33 */ + if (atomic_fetch_xor(&x, 0x3) != 33) return 0; /* x == 34 */ + if (atomic_fetch_and(&x, 0xF) != 34) return 0; /* x == 2 */ + + atomic_thread_fence(memory_order_seq_cst); + atomic_signal_fence(memory_order_acq_rel); + if (!atomic_is_lock_free(&x)) { /* allowed to be false; just must compile */ } + + atomic_flag f = ATOMIC_FLAG_INIT; + if (atomic_flag_test_and_set(&f)) return 0; /* was clear */ + if (!atomic_flag_test_and_set(&f)) return 0; /* now set */ + atomic_flag_clear(&f); + return 1; +} + /* Reference everything so -Wunused-* stays quiet. */ int cfree_smoke_ok(void) { (void)aligned_buf; if (0) cfree_trap(); - return sum_n(3, 1, 2, 3) == 6; + return sum_n(3, 1, 2, 3) == 6 && cfree_atomic_ok(); }