commit 310b5ab01ab448779044ddb36f3cdf5248ae18f3
parent da7fa2922ee93c3891d072432008bfe713ff84cd
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 7 May 2026 09:22:11 -0700
assert.h, stdatomic.h
Diffstat:
| D | builtins.md | | | 96 | ------------------------------------------------------------------------------- |
| A | doc/builtins.md | | | 123 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | include/assert.h | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
| A | include/stdatomic.h | | | 160 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | test/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();
}