commit f7ac167dc65d43ed7b189508db8a2f6a4a82f14a
parent fb02a54edf304b1e8cad51f7985b123e1e809e2e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 18 May 2026 15:20:47 -0700
Tighten C11 parse constraints
Diffstat:
25 files changed, 986 insertions(+), 240 deletions(-)
diff --git a/doc/C11_CONFORMANCE_CHECKLIST.md b/doc/C11_CONFORMANCE_CHECKLIST.md
@@ -10,13 +10,9 @@ make the implementation pass it.
- [x] `make test-lex` passes: 16/16.
- [x] `make test-pp test-pp-err` passes: 82/82 and 15/15.
-- [ ] `make test-parse-err` passes with expanded C11 constraint coverage:
- currently 48/56 pass, 8 fail. Current red cases are
- `6_5_addr_of_bitfield`, `6_5_cast_scalar_from_struct`,
- `6_5_cond_incompatible_ptr`, `6_5_pointer_add_pointer`,
- `6_5_rel_incompatible_ptr`, `6_5_scalar_required_if`,
- `6_5_sizeof_bitfield`, and `6_7_2_1_bitfield_bad_type`.
-- [ ] `make test-parse` passes without skips: currently 2504 pass, 0 fail,
+- [x] `make test-parse-err` passes with expanded C11 constraint coverage:
+ currently 56/56 pass.
+- [ ] `make test-parse` passes without skips: currently 2528 pass, 0 fail,
4 skip. Skips are `long double` and file-scope `asm`.
- [x] `make test-cg-api test-opt test-dwarf test-debug` passes.
- [ ] `make rt` builds the default runtime archives. Currently fails in
@@ -71,9 +67,9 @@ are implemented.
- [x] Reject non-power-of-two positive `aligned(N)` values.
Test: `test/parse/cases_err/attr_p2_aligned_not_pow2.c`.
Code: attribute argument parsing in `parse_type.c`.
-- [ ] Reject the newly covered expression/type constraint failures now exposed
+- [x] Reject the newly covered expression/type constraint failures now exposed
by `test/parse/cases_err/6_5_*`.
- Current red: address of bit-field, cast struct to scalar, incompatible
+ Covered cases: address of bit-field, cast struct to scalar, incompatible
conditional pointer arms, pointer-plus-pointer, incompatible pointer
relational compare, struct used as scalar condition, and `sizeof` on a
bit-field.
@@ -81,9 +77,8 @@ are implemented.
non-constant static initializer, excess scalar initializer, invalid
array/struct/union designators, wrong-kind tag redeclaration, functions
returning array/function, and variadic marker not last.
-- [ ] Decide whether floating-point bit-fields are an extension. The
- conformance test `6_7_2_1_bitfield_bad_type` currently expects rejection
- and is red because `float f : 1` is accepted.
+- [x] Reject non-integer bit-field types.
+ Test: `test/parse/cases_err/6_7_2_1_bitfield_bad_type.c`.
Suggested cadence:
@@ -143,7 +138,7 @@ CFREE_TEST_FILTER=asm_02_file_scope make test-parse
- [ ] Add positive bit-field lowering cases from `test/parse/CORPUS.md`,
including zero-width bit-fields.
Positive bit-field, signed bit-field, zero-width, and `_Bool` bit-field
- cases pass; `float` bit-field rejection is still red.
+ cases pass; `float` bit-field rejection now passes.
## Expressions and conversions
@@ -157,11 +152,11 @@ CFREE_TEST_FILTER=asm_02_file_scope make test-parse
no incomplete object type, no function type, no bit-field, VLA operand
evaluated, non-VLA operand not evaluated.
New coverage: `sizeof(function)` is rejected and VLA/deref pointer
- positive cases pass; `sizeof(bit-field)` is red because it is accepted.
+ positive cases pass; `sizeof(bit-field)` is rejected.
- [ ] Complete conditional operator usual-conversion behavior for arithmetic
and pointer/null arms.
Positive arithmetic and pointer/null cases pass; incompatible pointer
- arms are red because they are accepted.
+ arms are rejected.
- [ ] Complete pointer compound assignment (`p += n`, `p -= n`).
Positive `p += n` coverage passes.
- [ ] Expand `_Generic` tests for default selection, compatible types, and
@@ -170,9 +165,9 @@ CFREE_TEST_FILTER=asm_02_file_scope make test-parse
- [ ] Add negative tests for invalid pointer arithmetic, invalid relational
comparisons, invalid casts, modifying non-lvalues, and scalar-required
operators.
- New negative tests are checked in. Current red: pointer-plus-pointer,
- incompatible pointer relational compare, struct-to-int cast, and struct
- condition accepted; array assignment is rejected.
+ New negative tests are checked in. Pointer-plus-pointer, incompatible
+ pointer relational compare, struct-to-int cast, struct condition, and
+ array assignment are rejected.
## Constant expressions and initializers
diff --git a/doc/RT_CFREERT_CHECKLIST.md b/doc/RT_CFREERT_CHECKLIST.md
@@ -0,0 +1,107 @@
+# Building libcfree_rt.a With cfree
+
+Goal: build the runtime archive with cfree's own `cc`, `as`, and `ar` instead
+of clang/llvm-ar. This is separate from stage-2 self-hosting of the main
+compiler binary.
+
+Current focused probe: `aarch64-apple-darwin`. This variant is useful because
+it covers LP64, int128 declarations, coroutine assembly, Mach-O symbol names,
+and the freestanding runtime headers without requiring a system SDK.
+
+## Probe Command
+
+```sh
+make bin
+rm -rf rt/build/aarch64-apple-darwin
+env CC="$PWD/build/cfree cc" \
+ RT_AS="$PWD/build/cfree as" \
+ RT_AR="$PWD/build/cfree ar" \
+ make -e -k rt-aarch64-apple-darwin \
+ RT_COMMON_CFLAGS= \
+ RT_AS_COMPILE_FLAGS=
+```
+
+`RT_COMMON_CFLAGS=` drops flags cfree does not accept yet:
+`-ffreestanding -fno-builtin -std=c11 -Wpedantic -Wall -Wextra -Werror`.
+`RT_AS_COMPILE_FLAGS=` drops clang's `-c`, which `cfree as` does not use.
+
+## Current Status
+
+As of this probe, cfree builds or assembles:
+
+- [x] `rt/lib/int/int.c`
+- [ ] `rt/lib/fp/fp.c`
+- [x] `rt/lib/mem/mem.c`
+- [x] `rt/lib/atomic/atomic_freestanding.c`
+- [x] `rt/lib/cfree/ifunc_init.c`
+- [x] `rt/lib/int64/int64.c`
+- [x] `rt/lib/coro/aarch64.c`
+- [x] `rt/lib/coro/coro.c`
+- [x] `rt/lib/coro/aarch64_macho.s`
+
+That is 8 / 9 compile or assemble steps green for this variant. The archive
+does not build yet because `rt/lib/fp/fp.c` still fails.
+
+## Completed
+
+- [x] Split AArch64 file-scope coroutine assembly out of
+ `rt/lib/coro/aarch64.c`.
+- [x] Added standalone AArch64 coroutine assembly sources:
+ `rt/lib/coro/aarch64_elf.s` and `rt/lib/coro/aarch64_macho.s`.
+- [x] Added runtime Makefile support for assembling `.s` / `.S` sources via
+ `RT_AS`, so cfree can use `cfree as` while the default clang build can keep
+ using `clang -c`.
+- [x] Switched the runtime target flag from `--target=TRIPLE` to
+ `-target TRIPLE`, accepted by both clang and cfree.
+- [x] Wired `__builtin_ctzl` and `__builtin_ctzll` through the existing
+ `INTRIN_CTZ` path. `rt/lib/int64/int64.c` now compiles in the focused
+ aarch64-apple-darwin cfree probe.
+- [x] Converted `__atomic_*_n` value operands to the atomic object type before
+ lowering. This clears the pointer-sized literal mismatch in
+ `__atomic_store_n((uintptr_t*)l, 0, __ATOMIC_RELEASE)` and the same class for
+ RMW / compare-exchange desired operands.
+- [x] Added target-aware folding for `__atomic_always_lock_free(size, ptr)` and
+ constant-size `__atomic_is_lock_free(size, ptr)`. The parser asks
+ `cfree_cg_atomic_is_lock_free` for representative scalar types, so results
+ follow the active target instead of an aarch64-only table.
+- [x] Exposed a general `cfree_cg_top_const_int` query for compile-time-known
+ integer-like values on the CG value stack.
+- [x] Used that query in parser `if`, `&&`, and `||` lowering. Dead arms are
+ still parsed for semantic/type-stack effects, but target code emission is
+ suppressed, so constant-false 16-byte atomic fast paths no longer trip the
+ 8-byte lock-free backend limit.
+- [x] Lowered `__builtin_memcpy`, `__builtin_memmove`, `__builtin_memset`, and
+ `__builtin_memcmp` as builtins even when their byte count is runtime-sized.
+ The parser now synthesizes the standard libc call directly, instead of
+ rewriting the token to an undeclared plain identifier.
+- [x] Added parser support for `__atomic_fetch_nand`, mapped through the
+ existing target-independent atomic NAND RMW operation.
+
+## Remaining Blockers
+
+- [ ] Fix the `rt/lib/fp/fp.c` compiler crash.
+ Current symptom: cfree segfaults while compiling this file; lldb stops in
+ `cfree_arena_alloc`. Reduce to the smallest function or included helper that
+ reproduces the stack growth / allocator crash.
+
+- [ ] Lift remaining coroutine file-scope assembly.
+ Current file-scope asm remains in:
+ `rt/lib/coro/x86_64.c`, `rt/lib/coro/x86_64_win.c`,
+ `rt/lib/coro/i386.c`, `rt/lib/coro/riscv64.c`,
+ `rt/lib/coro/riscv32.c`, `rt/lib/coro/arm32.c`, and
+ `rt/lib/coro/arm32_thumb1.c`.
+
+- [ ] Decide whether cfree should eventually accept the standard runtime C
+ flags, or whether the runtime Makefile should grow a first-class
+ cfree-toolchain mode that drops/translates them.
+
+- [ ] Run the same cfree-toolchain probe for the other default runtime
+ variants after `aarch64-apple-darwin` archives cleanly.
+
+## Notes
+
+The ordinary clang `rt-aarch64-apple-darwin` target currently also stops in
+`atomic_freestanding.c` because clang treats several `__atomic_*` library
+entry points as builtins. That is separate from the cfree bootstrap path, but
+it means the default target is not a clean regression signal for the assembly
+split until the atomic source/build flags are addressed.
diff --git a/include/cfree/cg.h b/include/cfree/cg.h
@@ -483,6 +483,10 @@ void cfree_cg_swap(CfreeCg*);
void cfree_cg_drop(CfreeCg*);
void cfree_cg_rot3(CfreeCg*); /* [..., a, b, c] -> [..., b, c, a] */
+/* Returns nonzero when the current top-of-stack value is a compile-time known
+ * integer-like immediate. The value is not popped. */
+int cfree_cg_top_const_int(CfreeCg*, int64_t* out_value);
+
void cfree_cg_push_int(CfreeCg*, uint64_t value, CfreeCgTypeId type);
void cfree_cg_push_float(CfreeCg*, double value, CfreeCgTypeId type);
void cfree_cg_push_null(CfreeCg*, CfreeCgTypeId ptr_type);
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -12,6 +12,8 @@ static u32 pcg_alignof(Parser* p, const Type* ty) {
return (u32)cfree_cg_type_align(p->c, pcg_tid(p->c, ty));
}
+#define PCG_VALUE_BITFIELD 1u
+
CfreeCgMemAccess pcg_mem(Parser* p, const Type* ty) {
CfreeCgMemAccess m;
memset(&m, 0, sizeof m);
@@ -23,22 +25,31 @@ CfreeCgMemAccess pcg_mem(Parser* p, const Type* ty) {
static void pcg_stack_grow(Parser* p, u32 want) {
const Type** ns;
+ u8* nf;
u32 nc;
if (p->cg_type_cap >= want) return;
nc = p->cg_type_cap ? p->cg_type_cap * 2u : 64u;
while (nc < want) nc *= 2u;
ns = arena_array(p->pool->arena, const Type*, nc);
if (!ns) perr(p, "out of memory in CG type stack");
+ nf = arena_zarray(p->pool->arena, u8, nc);
+ if (!nf) perr(p, "out of memory in CG value stack");
if (p->cg_type_stack && p->cg_type_sp) {
memcpy(ns, p->cg_type_stack, sizeof(*ns) * p->cg_type_sp);
}
+ if (p->cg_value_flags && p->cg_type_sp) {
+ memcpy(nf, p->cg_value_flags, sizeof(*nf) * p->cg_type_sp);
+ }
p->cg_type_stack = ns;
+ p->cg_value_flags = nf;
p->cg_type_cap = nc;
}
void pcg_push_type(Parser* p, const Type* ty) {
pcg_stack_grow(p, p->cg_type_sp + 1u);
- p->cg_type_stack[p->cg_type_sp++] = ty;
+ p->cg_type_stack[p->cg_type_sp] = ty;
+ p->cg_value_flags[p->cg_type_sp] = 0;
+ ++p->cg_type_sp;
}
void pcg_drop_type(Parser* p) {
@@ -47,23 +58,35 @@ void pcg_drop_type(Parser* p) {
void pcg_dup_type(Parser* p) {
const Type* ty = pcg_top_type(p);
+ u8 flags = p->cg_type_sp ? p->cg_value_flags[p->cg_type_sp - 1u] : 0;
pcg_push_type(p, ty);
+ if (p->cg_type_sp) p->cg_value_flags[p->cg_type_sp - 1u] = flags;
}
void pcg_swap_type(Parser* p) {
if (p->cg_type_sp >= 2) {
const Type* a = p->cg_type_stack[p->cg_type_sp - 1u];
+ u8 af = p->cg_value_flags[p->cg_type_sp - 1u];
p->cg_type_stack[p->cg_type_sp - 1u] = p->cg_type_stack[p->cg_type_sp - 2u];
+ p->cg_value_flags[p->cg_type_sp - 1u] =
+ p->cg_value_flags[p->cg_type_sp - 2u];
p->cg_type_stack[p->cg_type_sp - 2u] = a;
+ p->cg_value_flags[p->cg_type_sp - 2u] = af;
}
}
void pcg_rot3_type(Parser* p) {
if (p->cg_type_sp >= 3) {
const Type* a = p->cg_type_stack[p->cg_type_sp - 3u];
+ u8 af = p->cg_value_flags[p->cg_type_sp - 3u];
p->cg_type_stack[p->cg_type_sp - 3u] = p->cg_type_stack[p->cg_type_sp - 2u];
+ p->cg_value_flags[p->cg_type_sp - 3u] =
+ p->cg_value_flags[p->cg_type_sp - 2u];
p->cg_type_stack[p->cg_type_sp - 2u] = p->cg_type_stack[p->cg_type_sp - 1u];
+ p->cg_value_flags[p->cg_type_sp - 2u] =
+ p->cg_value_flags[p->cg_type_sp - 1u];
p->cg_type_stack[p->cg_type_sp - 1u] = a;
+ p->cg_value_flags[p->cg_type_sp - 1u] = af;
}
}
@@ -76,7 +99,31 @@ const Type* pcg_top2_type(Parser* p) {
}
void pcg_retag_top(Parser* p, const Type* ty) {
- if (p->cg_type_sp) p->cg_type_stack[p->cg_type_sp - 1u] = ty;
+ if (p->cg_type_sp) {
+ p->cg_type_stack[p->cg_type_sp - 1u] = ty;
+ p->cg_value_flags[p->cg_type_sp - 1u] = 0;
+ }
+}
+
+int pcg_top_is_bitfield(Parser* p) {
+ return p->cg_type_sp &&
+ (p->cg_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_BITFIELD) != 0;
+}
+
+void pcg_set_top_bitfield(Parser* p) {
+ if (p->cg_type_sp) p->cg_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_BITFIELD;
+}
+
+int pcg_emit_enabled(Parser* p) { return p && p->suppress_codegen == 0; }
+
+void pcg_codegen_suppress_push(Parser* p) {
+ if (p) ++p->suppress_codegen;
+}
+
+void pcg_codegen_suppress_pop(Parser* p) {
+ if (!p) return;
+ if (!p->suppress_codegen) perr(p, "internal parser codegen suppression underflow");
+ --p->suppress_codegen;
}
int pcg_type_is_fp(const Type* ty) {
@@ -220,6 +267,7 @@ CfreeCgMemOrder pcg_mem_order(MemOrder ord) { return (CfreeCgMemOrder)ord; }
FrameSlot pcg_local(Parser* p, const FrameSlotDesc* fsd) {
CfreeCgLocalAttrs attrs;
memset(&attrs, 0, sizeof attrs);
+ if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE;
attrs.name = fsd->name;
attrs.align = fsd->align;
if (fsd->flags & FSF_ADDR_TAKEN) attrs.flags |= CFREE_CG_LOCAL_ADDRESS_TAKEN;
@@ -241,34 +289,39 @@ void pcg_param(Parser* p, const CGParamDesc* pd) {
}
void pcg_func_begin(Parser* p, const CGFuncDesc* fd) {
- cfree_cg_func_begin(p->cg, fd->sym);
+ if (pcg_emit_enabled(p)) cfree_cg_func_begin(p->cg, fd->sym);
}
void pcg_push_int(Parser* p, i64 v, const Type* ty) {
- cfree_cg_push_int(p->cg, (uint64_t)v, pcg_tid(p->c, ty));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_push_int(p->cg, (uint64_t)v, pcg_tid(p->c, ty));
+ }
pcg_push_type(p, ty);
}
void pcg_push_float(Parser* p, double v, const Type* ty) {
- cfree_cg_push_float(p->cg, v, pcg_tid(p->c, ty));
+ if (pcg_emit_enabled(p)) cfree_cg_push_float(p->cg, v, pcg_tid(p->c, ty));
pcg_push_type(p, ty);
}
void pcg_push_local_typed(Parser* p, FrameSlot s, const Type* ty) {
- cfree_cg_push_local(p->cg, s);
+ if (pcg_emit_enabled(p)) cfree_cg_push_local(p->cg, s);
pcg_push_type(p, ty);
}
void pcg_push_global(Parser* p, ObjSymId sym, const Type* ty) {
- cfree_cg_push_symbol_lvalue(p->cg, sym, 0);
+ if (pcg_emit_enabled(p)) cfree_cg_push_symbol_lvalue(p->cg, sym, 0);
pcg_push_type(p, ty);
}
-void pcg_load(Parser* p) { cfree_cg_load(p->cg, pcg_mem(p, pcg_top_type(p))); }
+void pcg_load(Parser* p) {
+ if (pcg_emit_enabled(p)) cfree_cg_load(p->cg, pcg_mem(p, pcg_top_type(p)));
+ if (p->cg_type_sp) p->cg_value_flags[p->cg_type_sp - 1u] = 0;
+}
void pcg_addr(Parser* p) {
const Type* ty = pcg_top_type(p);
- cfree_cg_addr(p->cg);
+ if (pcg_emit_enabled(p)) cfree_cg_addr(p->cg);
pcg_retag_top(p, type_ptr(p->pool, ty));
}
@@ -276,52 +329,59 @@ void pcg_store(Parser* p) {
const Type* lv_ty = pcg_top2_type(p);
const Type* rv_ty = pcg_top_type(p);
const Type* mem_ty = lv_ty;
+ int emit = pcg_emit_enabled(p);
if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) {
mem_ty = rv_ty;
}
- cfree_cg_dup(p->cg);
+ if (emit) cfree_cg_dup(p->cg);
pcg_dup_type(p);
- cfree_cg_rot3(p->cg);
+ if (emit) cfree_cg_rot3(p->cg);
pcg_rot3_type(p);
- cfree_cg_swap(p->cg);
+ if (emit) cfree_cg_swap(p->cg);
pcg_swap_type(p);
- cfree_cg_store(p->cg, pcg_mem(p, mem_ty ? mem_ty : rv_ty));
+ if (emit) cfree_cg_store(p->cg, pcg_mem(p, mem_ty ? mem_ty : rv_ty));
pcg_drop_type(p);
pcg_drop_type(p);
pcg_push_type(p, rv_ty);
}
void pcg_deref(Parser* p, const Type* pointee) {
- cfree_cg_indirect(p->cg);
+ if (pcg_emit_enabled(p)) cfree_cg_indirect(p->cg);
pcg_retag_top(p, pointee);
}
void pcg_binop(Parser* p, BinOp op) {
const Type* result = pcg_top2_type(p);
- if (op == BO_FADD || op == BO_FSUB || op == BO_FMUL || op == BO_FDIV)
- cfree_cg_fp_binop(p->cg, pcg_fp_binop(op), CFREE_CG_FP_NONE);
- else
- cfree_cg_int_binop(p->cg, pcg_int_binop(op), CFREE_CG_INTOP_NONE);
+ if (op == BO_FADD || op == BO_FSUB || op == BO_FMUL || op == BO_FDIV) {
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_fp_binop(p->cg, pcg_fp_binop(op), CFREE_CG_FP_NONE);
+ }
+ } else {
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_int_binop(p->cg, pcg_int_binop(op), CFREE_CG_INTOP_NONE);
+ }
+ }
pcg_drop_type(p);
pcg_retag_top(p, result);
}
void pcg_unop(Parser* p, UnOp op) {
if (op == UO_NEG && pcg_type_is_fp(pcg_top_type(p))) {
- cfree_cg_fp_unop(p->cg, CFREE_CG_FP_NEG, CFREE_CG_FP_NONE);
+ if (pcg_emit_enabled(p)) cfree_cg_fp_unop(p->cg, CFREE_CG_FP_NEG, CFREE_CG_FP_NONE);
} else {
CfreeCgIntUnOp iop = op == UO_NOT ? CFREE_CG_INT_NOT
: op == UO_BNOT ? CFREE_CG_INT_BNOT
: CFREE_CG_INT_NEG;
- cfree_cg_int_unop(p->cg, iop, CFREE_CG_INTOP_NONE);
+ if (pcg_emit_enabled(p)) cfree_cg_int_unop(p->cg, iop, CFREE_CG_INTOP_NONE);
}
}
void pcg_cmp(Parser* p, CmpOp op) {
- if (op == CMP_LT_F || op == CMP_LE_F || op == CMP_GT_F || op == CMP_GE_F)
- cfree_cg_fp_cmp(p->cg, pcg_fp_cmp(op));
- else
- cfree_cg_int_cmp(p->cg, pcg_int_cmp(op));
+ if (op == CMP_LT_F || op == CMP_LE_F || op == CMP_GT_F || op == CMP_GE_F) {
+ if (pcg_emit_enabled(p)) cfree_cg_fp_cmp(p->cg, pcg_fp_cmp(op));
+ } else {
+ if (pcg_emit_enabled(p)) cfree_cg_int_cmp(p->cg, pcg_int_cmp(op));
+ }
pcg_drop_type(p);
pcg_retag_top(p, type_prim(p->pool, TY_BOOL));
}
@@ -335,72 +395,96 @@ void pcg_convert(Parser* p, const Type* dst) {
int sf = pcg_type_is_fp(src);
int df = pcg_type_is_fp(dst);
CfreeCgTypeId id = pcg_tid(p->c, dst);
+ int emit = pcg_emit_enabled(p);
if (src == dst) return;
if (type_is_ptr(src) && type_is_ptr(dst)) {
- cfree_cg_bitcast(p->cg, id);
+ if (emit) cfree_cg_bitcast(p->cg, id);
pcg_retag_top(p, dst);
return;
}
if (si && di) {
- if (ds < ss)
- cfree_cg_trunc(p->cg, id);
- else if (ds > ss && type_is_int(src) && pcg_type_is_signed(src))
- cfree_cg_sext(p->cg, id);
- else if (ds > ss)
- cfree_cg_zext(p->cg, id);
+ if (ds < ss) {
+ if (emit) cfree_cg_trunc(p->cg, id);
+ } else if (ds > ss && type_is_int(src) && pcg_type_is_signed(src)) {
+ if (emit) cfree_cg_sext(p->cg, id);
+ } else if (ds > ss) {
+ if (emit) cfree_cg_zext(p->cg, id);
+ }
} else if (type_is_int(src) && df) {
- if (pcg_type_is_signed(src))
- cfree_cg_sint_to_float(p->cg, id, CFREE_CG_ROUND_DEFAULT);
- else
- cfree_cg_uint_to_float(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ if (pcg_type_is_signed(src)) {
+ if (emit) cfree_cg_sint_to_float(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ } else {
+ if (emit) cfree_cg_uint_to_float(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ }
} else if (sf && type_is_int(dst)) {
- if (pcg_type_is_signed(dst))
- cfree_cg_float_to_sint(p->cg, id, CFREE_CG_ROUND_DEFAULT);
- else
- cfree_cg_float_to_uint(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ if (pcg_type_is_signed(dst)) {
+ if (emit) cfree_cg_float_to_sint(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ } else {
+ if (emit) cfree_cg_float_to_uint(p->cg, id, CFREE_CG_ROUND_DEFAULT);
+ }
} else if (sf && df) {
- if (ds > ss)
- cfree_cg_fpext(p->cg, id);
- else if (ds < ss)
- cfree_cg_fptrunc(p->cg, id);
+ if (ds > ss) {
+ if (emit) cfree_cg_fpext(p->cg, id);
+ } else if (ds < ss) {
+ if (emit) cfree_cg_fptrunc(p->cg, id);
+ }
} else {
- cfree_cg_bitcast(p->cg, id);
+ if (emit) cfree_cg_bitcast(p->cg, id);
}
pcg_retag_top(p, dst);
}
void pcg_inc_dec(Parser* p, BinOp op, int post) {
const Type* ty = pcg_top_type(p);
- cfree_cg_inc_dec(p->cg, pcg_int_binop(op), post, pcg_tid(p->c, ty),
- pcg_mem(p, ty));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_inc_dec(p->cg, pcg_int_binop(op), post, pcg_tid(p->c, ty),
+ pcg_mem(p, ty));
+ }
}
void pcg_call(Parser* p, u32 nargs, const Type* fn_type) {
- cfree_cg_call_default(p->cg, nargs, pcg_tid(p->c, fn_type));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_call_default(p->cg, nargs, pcg_tid(p->c, fn_type));
+ }
for (u32 i = 0; i < nargs + 1u; ++i) pcg_drop_type(p);
if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) {
pcg_push_type(p, fn_type->fn.ret);
}
}
+void pcg_call_symbol(Parser* p, CfreeCgSym sym, u32 nargs,
+ const Type* fn_type) {
+ if (pcg_emit_enabled(p)) {
+ CfreeCgCallAttrs attrs;
+ memset(&attrs, 0, sizeof attrs);
+ cfree_cg_call_symbol(p->cg, sym, nargs, attrs);
+ }
+ for (u32 i = 0; i < nargs; ++i) pcg_drop_type(p);
+ if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) {
+ pcg_push_type(p, fn_type->fn.ret);
+ }
+}
+
void pcg_ret(Parser* p, int has_value) {
if (has_value) {
- cfree_cg_ret(p->cg);
+ if (pcg_emit_enabled(p)) cfree_cg_ret(p->cg);
pcg_drop_type(p);
} else {
- cfree_cg_ret_void(p->cg);
+ if (pcg_emit_enabled(p)) cfree_cg_ret_void(p->cg);
}
}
void pcg_alloca(Parser* p) {
- cfree_cg_alloca(p->cg, 16,
- pcg_tid(p->c, type_ptr(p->pool, type_void(p->pool))));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_alloca(p->cg, 16,
+ pcg_tid(p->c, type_ptr(p->pool, type_void(p->pool))));
+ }
pcg_drop_type(p);
pcg_push_type(p, type_ptr(p->pool, type_void(p->pool)));
}
void pcg_va_arg(Parser* p, const Type* ty) {
- cfree_cg_vararg_next(p->cg, pcg_tid(p->c, ty));
+ if (pcg_emit_enabled(p)) cfree_cg_vararg_next(p->cg, pcg_tid(p->c, ty));
pcg_drop_type(p);
pcg_push_type(p, ty);
}
@@ -408,14 +492,18 @@ void pcg_va_arg(Parser* p, const Type* ty) {
void pcg_atomic_load(Parser* p, MemOrder ord) {
const Type* pty = pcg_top_type(p);
const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
- cfree_cg_atomic_load(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_atomic_load(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
+ }
pcg_retag_top(p, ty);
}
void pcg_atomic_store(Parser* p, MemOrder ord) {
const Type* pty = pcg_top2_type(p);
const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
- cfree_cg_atomic_store(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_atomic_store(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
+ }
pcg_drop_type(p);
pcg_drop_type(p);
}
@@ -423,21 +511,25 @@ void pcg_atomic_store(Parser* p, MemOrder ord) {
void pcg_atomic_rmw(Parser* p, AtomicOp op, MemOrder ord) {
const Type* pty = pcg_top2_type(p);
const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
- cfree_cg_atomic_rmw(p->cg, pcg_mem(p, ty), pcg_atomic_op(op),
- pcg_mem_order(ord));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_atomic_rmw(p->cg, pcg_mem(p, ty), pcg_atomic_op(op),
+ pcg_mem_order(ord));
+ }
pcg_drop_type(p);
pcg_retag_top(p, ty);
}
void pcg_atomic_cas(Parser* p, MemOrder succ, MemOrder fail) {
const Type* ty = pcg_top_type(p);
- cfree_cg_atomic_cmpxchg(p->cg, pcg_mem(p, ty), pcg_mem_order(succ),
- pcg_mem_order(fail), 0);
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_atomic_cmpxchg(p->cg, pcg_mem(p, ty), pcg_mem_order(succ),
+ pcg_mem_order(fail), 0);
+ }
pcg_retag_top(p, type_prim(p->pool, TY_BOOL));
}
void pcg_fence(Parser* p, MemOrder ord) {
- cfree_cg_atomic_fence(p->cg, pcg_mem_order(ord));
+ if (pcg_emit_enabled(p)) cfree_cg_atomic_fence(p->cg, pcg_mem_order(ord));
}
void pcg_intrinsic_unary_to_int(Parser* p, IntrinKind k) {
@@ -445,15 +537,20 @@ void pcg_intrinsic_unary_to_int(Parser* p, IntrinKind k) {
: k == INTRIN_CTZ ? CFREE_CG_INTRIN_CTZ
: CFREE_CG_INTRIN_POPCOUNT;
const Type* ity = type_prim(p->pool, TY_INT);
- cfree_cg_intrinsic(p->cg, ck, 1, pcg_tid(p->c, ity));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_intrinsic(p->cg, ck, 1, pcg_tid(p->c, ity));
+ }
pcg_retag_top(p, ity);
}
void pcg_intrinsic_void(Parser* p, IntrinKind k) {
- if (k == INTRIN_UNREACHABLE)
- cfree_cg_unreachable(p->cg);
- else
- cfree_cg_intrinsic(p->cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
+ if (k == INTRIN_UNREACHABLE) {
+ if (pcg_emit_enabled(p)) cfree_cg_unreachable(p->cg);
+ } else {
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_intrinsic(p->cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
+ }
+ }
}
void pcg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs,
@@ -494,5 +591,5 @@ void pcg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs,
a.ninputs = nin;
a.clobbers = cl;
a.nclobbers = nclob;
- cfree_cg_inline_asm(p->cg, a);
+ if (pcg_emit_enabled(p)) cfree_cg_inline_asm(p->cg, a);
}
diff --git a/lang/c/parse/cg_public_compat.h b/lang/c/parse/cg_public_compat.h
@@ -173,7 +173,12 @@ void pcg_push_type(Parser*, const Type*);
void pcg_drop_type(Parser*);
void pcg_dup_type(Parser*);
void pcg_swap_type(Parser*);
+int pcg_top_is_bitfield(Parser*);
+void pcg_set_top_bitfield(Parser*);
void pcg_rot3_type(Parser*);
+int pcg_emit_enabled(Parser*);
+void pcg_codegen_suppress_push(Parser*);
+void pcg_codegen_suppress_pop(Parser*);
CfreeCgIntBinOp pcg_int_binop(BinOp);
CfreeCgFpBinOp pcg_fp_binop(BinOp);
@@ -202,6 +207,7 @@ void pcg_cmp(Parser*, CmpOp);
void pcg_convert(Parser*, const Type*);
void pcg_inc_dec(Parser*, BinOp, int);
void pcg_call(Parser*, u32, const Type*);
+void pcg_call_symbol(Parser*, CfreeCgSym, u32, const Type*);
void pcg_ret(Parser*, int);
void pcg_alloca(Parser*);
void pcg_va_arg(Parser*, const Type*);
@@ -215,11 +221,17 @@ void pcg_intrinsic_void(Parser*, IntrinKind);
void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32,
const AsmConstraint*, u32, const Sym*, u32);
-#define cg_set_loc(g, loc) cfree_cg_set_loc((g), (loc))
+#define cg_set_loc(g, loc) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_set_loc((g), (loc)); \
+ } while (0)
#define cg_local(g, fsd) pcg_local(p, (fsd))
#define cg_param(g, pd) pcg_param(p, (pd))
#define cg_func_begin(g, fd) pcg_func_begin(p, (fd))
-#define cg_func_end(g) cfree_cg_func_end((g))
+#define cg_func_end(g) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_func_end((g)); \
+ } while (0)
#define cg_push_int(g, v, ty) pcg_push_int(p, (v), (ty))
#define cg_push_float(g, v, ty) pcg_push_float(p, (v), (ty))
#define cg_push_local_typed(g, s, ty) pcg_push_local_typed(p, (s), (ty))
@@ -228,17 +240,17 @@ void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32,
#define cg_addr(g) pcg_addr(p)
#define cg_dup(g) \
do { \
- cfree_cg_dup((g)); \
+ if (pcg_emit_enabled(p)) cfree_cg_dup((g)); \
pcg_dup_type(p); \
} while (0)
#define cg_swap(g) \
do { \
- cfree_cg_swap((g)); \
+ if (pcg_emit_enabled(p)) cfree_cg_swap((g)); \
pcg_swap_type(p); \
} while (0)
#define cg_drop(g) \
do { \
- cfree_cg_drop((g)); \
+ if (pcg_emit_enabled(p)) cfree_cg_drop((g)); \
pcg_drop_type(p); \
} while (0)
#define cg_store(g) pcg_store(p)
@@ -254,9 +266,18 @@ void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32,
#define cg_call(g, nargs, fn_type) pcg_call(p, (nargs), (fn_type))
#define cg_ret(g, has_value) pcg_ret(p, (has_value))
#define cg_alloca(g) pcg_alloca(p)
-#define cg_va_start_(g) cfree_cg_vararg_start((g))
-#define cg_va_end_(g) cfree_cg_vararg_end((g))
-#define cg_va_copy_(g) cfree_cg_vararg_copy((g))
+#define cg_va_start_(g) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_vararg_start((g)); \
+ } while (0)
+#define cg_va_end_(g) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_vararg_end((g)); \
+ } while (0)
+#define cg_va_copy_(g) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_vararg_copy((g)); \
+ } while (0)
#define cg_va_arg_(g, ty) pcg_va_arg(p, (ty))
#define cg_atomic_load(g, ord) pcg_atomic_load(p, (ord))
#define cg_atomic_store(g, ord) pcg_atomic_store(p, (ord))
@@ -266,16 +287,22 @@ void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32,
#define cg_intrinsic_unary_to_int(g, k) pcg_intrinsic_unary_to_int(p, (k))
#define cg_intrinsic_void(g, k) pcg_intrinsic_void(p, (k))
#define cg_label_new(g) cfree_cg_label_new((g))
-#define cg_label_place(g, l) cfree_cg_label_place((g), (l))
-#define cg_jump(g, l) cfree_cg_jump((g), (l))
+#define cg_label_place(g, l) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_label_place((g), (l)); \
+ } while (0)
+#define cg_jump(g, l) \
+ do { \
+ if (pcg_emit_enabled(p)) cfree_cg_jump((g), (l)); \
+ } while (0)
#define cg_branch_true(g, l) \
do { \
- cfree_cg_branch_true((g), (l)); \
+ if (pcg_emit_enabled(p)) cfree_cg_branch_true((g), (l)); \
pcg_drop_type(p); \
} while (0)
#define cg_branch_false(g, l) \
do { \
- cfree_cg_branch_false((g), (l)); \
+ if (pcg_emit_enabled(p)) cfree_cg_branch_false((g), (l)); \
pcg_drop_type(p); \
} while (0)
#define cg_inline_asm(g, tmpl, outs, nout, ins, nin, clob, nclob) \
diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c
@@ -1202,6 +1202,8 @@ void parse_c(Compiler* c, Pool* pool, Pp* pp, DeclTable* decls, CG* cg) {
p.sym_b_alloca = pool_intern_cstr(p.pool, "__builtin_alloca");
p.sym_b_ctz = pool_intern_cstr(p.pool, "__builtin_ctz");
+ p.sym_b_ctzl = pool_intern_cstr(p.pool, "__builtin_ctzl");
+ p.sym_b_ctzll = pool_intern_cstr(p.pool, "__builtin_ctzll");
p.sym_b_clz = pool_intern_cstr(p.pool, "__builtin_clz");
p.sym_b_clzl = pool_intern_cstr(p.pool, "__builtin_clzl");
p.sym_b_clzll = pool_intern_cstr(p.pool, "__builtin_clzll");
@@ -1235,7 +1237,11 @@ void parse_c(Compiler* c, Pool* pool, Pp* pp, DeclTable* decls, CG* cg) {
p.sym_a_fetch_and = pool_intern_cstr(p.pool, "__atomic_fetch_and");
p.sym_a_fetch_or = pool_intern_cstr(p.pool, "__atomic_fetch_or");
p.sym_a_fetch_xor = pool_intern_cstr(p.pool, "__atomic_fetch_xor");
+ p.sym_a_fetch_nand = pool_intern_cstr(p.pool, "__atomic_fetch_nand");
p.sym_a_cas_n = pool_intern_cstr(p.pool, "__atomic_compare_exchange_n");
+ p.sym_a_always_lock_free =
+ pool_intern_cstr(p.pool, "__atomic_always_lock_free");
+ p.sym_a_is_lock_free = pool_intern_cstr(p.pool, "__atomic_is_lock_free");
p.sym_a_thread_fence = pool_intern_cstr(p.pool, "__atomic_thread_fence");
p.sym_a_signal_fence = pool_intern_cstr(p.pool, "__atomic_signal_fence");
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -35,6 +35,49 @@ static void require_sizeof_type(Parser* p, const Type* ty) {
}
}
+static int type_is_void_ptr(const Type* ty) {
+ return ty && ty->kind == TY_PTR && ty->ptr.pointee &&
+ ty->ptr.pointee->kind == TY_VOID;
+}
+
+static int pointer_pointees_compatible(Parser* p, const Type* lhs,
+ const Type* rhs) {
+ const Type* lp;
+ const Type* rp;
+ if (!lhs || !rhs || lhs->kind != TY_PTR || rhs->kind != TY_PTR) return 0;
+ lp = lhs->ptr.pointee;
+ rp = rhs->ptr.pointee;
+ if (!lp || !rp) return 0;
+ return type_compatible(type_unqual(p->pool, lp), type_unqual(p->pool, rp));
+}
+
+static int null_pointer_constant(Parser* p, const Type* ty) {
+ i64 v = 1;
+ return type_is_int(ty) && cfree_cg_top_const_int(p->cg, &v) && v == 0;
+}
+
+static void require_scalar(Parser* p, const Type* ty, const char* what) {
+ if (!c_type_is_scalar(ty)) perr(p, "%s requires scalar operand", what);
+}
+
+static void require_arith(Parser* p, const Type* ty, const char* what) {
+ if (!type_is_arith(ty)) perr(p, "%s requires arithmetic operand", what);
+}
+
+static const Type* conditional_pointer_type(Parser* p, const Type* then_ty,
+ int then_null,
+ const Type* else_ty,
+ int else_null) {
+ if (then_ty && then_ty->kind == TY_PTR && else_null) return then_ty;
+ if (else_ty && else_ty->kind == TY_PTR && then_null) return else_ty;
+ if (!then_ty || !else_ty || then_ty->kind != TY_PTR || else_ty->kind != TY_PTR)
+ return NULL;
+ if (type_is_void_ptr(then_ty)) return then_ty;
+ if (type_is_void_ptr(else_ty)) return else_ty;
+ if (pointer_pointees_compatible(p, then_ty, else_ty)) return then_ty;
+ return NULL;
+}
+
/* ============================================================
* Literal parsing
* ============================================================ */
@@ -463,6 +506,7 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
parse_unary(p);
{
const Type* ty = cg_top_type(p->cg);
+ if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field");
require_sizeof_type(p, ty);
i64 sz = (i64)c_abi_sizeof(p->abi, ty);
cg_drop(p->cg);
@@ -559,7 +603,7 @@ i64 eval_const_int(Parser* p, SrcLoc loc) { return cexpr_bor(p, loc); }
static void decay_array_to_pointer(Parser* p, const Type* arr_ty) {
const Type* ptr_ty = type_ptr(p->pool, arr_ty->arr.elem);
- cfree_cg_addr_offset(p->cg, 0, pcg_tid(p->c, ptr_ty));
+ if (pcg_emit_enabled(p)) cfree_cg_addr_offset(p->cg, 0, pcg_tid(p->c, ptr_ty));
pcg_retag_top(p, ptr_ty);
}
@@ -596,6 +640,158 @@ void coerce_top_to_lvalue(Parser* p) {
}
}
+static void coerce_top_to_type(Parser* p, const Type* dst) {
+ const Type* src = cg_top_type(p->cg);
+ if (!src || !dst || src == dst) return;
+ if (type_is_arith(src) && type_is_arith(dst)) {
+ cg_convert(p->cg, dst);
+ } else if (type_is_arith(src) && type_is_ptr(dst)) {
+ cg_convert(p->cg, dst);
+ } else if (type_is_ptr(src) && type_is_ptr(dst)) {
+ cg_convert(p->cg, dst);
+ }
+}
+
+static const Type* atomic_pointee_type(Parser* p, const Type* ptr_ty,
+ const char* who) {
+ if (!ptr_ty || ptr_ty->kind != TY_PTR) {
+ perr(p, "%s: pointer argument must have pointer type", who);
+ }
+ return ptr_ty->ptr.pointee;
+}
+
+static const Type* atomic_lock_free_type_for_size(Parser* p, i64 size) {
+ const Type* ty = NULL;
+ switch (size) {
+ case 1:
+ ty = type_prim(p->pool, TY_UCHAR);
+ break;
+ case 2:
+ ty = type_prim(p->pool, TY_USHORT);
+ break;
+ case 4:
+ ty = type_prim(p->pool, TY_UINT);
+ break;
+ case 8:
+ ty = type_prim(p->pool, TY_ULLONG);
+ break;
+ case 16:
+ ty = type_prim(p->pool, TY_UINT128);
+ break;
+ default:
+ return NULL;
+ }
+ return c_abi_sizeof(p->abi, ty) == (u32)size ? ty : NULL;
+}
+
+static int atomic_lock_free_for_const_size(Parser* p, i64 size) {
+ if (size <= 0 || size > 16) return 0;
+ const Type* ty = atomic_lock_free_type_for_size(p, size);
+ return ty ? cfree_cg_atomic_is_lock_free(p->c, pcg_mem(p, ty)) : 0;
+}
+
+static CfreeCgSym builtin_libcall_sym(Parser* p, const char* name,
+ const Type* fn_ty) {
+ CfreeCgDecl decl;
+ Sym source_name = pool_intern_cstr(p->pool, name);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = CFREE_CG_DECL_FUNC;
+ decl.display_name = source_name;
+ decl.linkage_name = cfree_cg_c_linkage_name(p->c, source_name);
+ decl.type = pcg_tid(p->c, fn_ty);
+ decl.sym.bind = CFREE_SB_GLOBAL;
+ decl.sym.visibility = CFREE_CG_VIS_DEFAULT;
+ return cfree_cg_decl(p->cg, decl);
+}
+
+static int parse_builtin_mem_call(Parser* p, Sym name, SrcLoc loc) {
+ const Type* void_ty = type_void(p->pool);
+ const Type* void_ptr_ty = type_ptr(p->pool, void_ty);
+ const Type* const_void_ptr_ty =
+ type_ptr(p->pool, type_qualified(p->pool, void_ty, Q_CONST));
+ const Type* size_ty = ty_size_t(p);
+ const Type* int_ty = ty_int(p);
+ const Type* params[3];
+ const Type* fn_ty;
+ const char* libname;
+ CfreeCgSym sym;
+
+ advance(p); /* IDENT */
+ expect_punct(p, '(', "'(' after builtin");
+
+ if (name == p->sym_b_memcpy || name == p->sym_b_memmove) {
+ libname = name == p->sym_b_memcpy ? "memcpy" : "memmove";
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, void_ptr_ty);
+ expect_punct(p, ',', "',' in memory builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, const_void_ptr_ty);
+ expect_punct(p, ',', "',' in memory builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, size_ty);
+ expect_punct(p, ')', "')' after memory builtin");
+ params[0] = void_ptr_ty;
+ params[1] = const_void_ptr_ty;
+ params[2] = size_ty;
+ fn_ty = type_func(p->pool, void_ptr_ty, params, 3, 0);
+ } else if (name == p->sym_b_memset) {
+ libname = "memset";
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, void_ptr_ty);
+ expect_punct(p, ',', "',' in __builtin_memset");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, int_ty);
+ expect_punct(p, ',', "',' in __builtin_memset");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, size_ty);
+ expect_punct(p, ')', "')' after __builtin_memset");
+ params[0] = void_ptr_ty;
+ params[1] = int_ty;
+ params[2] = size_ty;
+ fn_ty = type_func(p->pool, void_ptr_ty, params, 3, 0);
+ } else {
+ libname = "memcmp";
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, const_void_ptr_ty);
+ expect_punct(p, ',', "',' in __builtin_memcmp");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, const_void_ptr_ty);
+ expect_punct(p, ',', "',' in __builtin_memcmp");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, size_ty);
+ expect_punct(p, ')', "')' after __builtin_memcmp");
+ params[0] = const_void_ptr_ty;
+ params[1] = const_void_ptr_ty;
+ params[2] = size_ty;
+ fn_ty = type_func(p->pool, int_ty, params, 3, 0);
+ }
+
+ sym = pcg_emit_enabled(p) ? builtin_libcall_sym(p, libname, fn_ty)
+ : CFREE_CG_SYM_NONE;
+ cg_set_loc(p->cg, loc);
+ pcg_call_symbol(p, sym, 3, fn_ty);
+ return 1;
+}
+
+static MemOrder parse_atomic_mem_order(Parser* p) {
+ if (p->cur.kind == TOK_NUM) {
+ return (MemOrder)eval_const_int(p, p->cur.loc);
+ }
+ parse_assign_expr(p);
+ to_rvalue(p);
+ cg_drop(p->cg);
+ return MO_SEQ_CST;
+}
+
/* ============================================================
* Builtin call handling
* ============================================================ */
@@ -663,24 +859,13 @@ static int try_parse_builtin_call(Parser* p) {
Sym name = p->cur.v.ident;
SrcLoc loc = p->cur.loc;
- /* `__builtin_mem{cpy,move,cmp,set}` are GCC/Clang's compiler-inlinable
- * aliases for the libc functions. cfree's INTRIN_MEMCPY/MEMMOVE
- * backend paths only handle constant byte counts, but the rt code
- * calls them with runtime sizes. Rewrite each builtin into a plain
- * call and let the normal function-call path handle it. The caller
- * (parse_primary) reports a clean "undeclared identifier" if the TU
- * forgot to declare the underlying libc function. */
if (name == p->sym_b_memcpy || name == p->sym_b_memmove ||
name == p->sym_b_memcmp || name == p->sym_b_memset) {
- const char* libname = (name == p->sym_b_memcpy) ? "memcpy"
- : (name == p->sym_b_memmove) ? "memmove"
- : (name == p->sym_b_memcmp) ? "memcmp"
- : "memset";
- p->cur.v.ident = pool_intern_cstr(p->pool, libname);
- return 0;
+ return parse_builtin_mem_call(p, name, loc);
}
- if (name != p->sym_b_alloca && name != p->sym_b_ctz && name != p->sym_b_clz &&
+ if (name != p->sym_b_alloca && name != p->sym_b_ctz &&
+ name != p->sym_b_ctzl && name != p->sym_b_ctzll && name != p->sym_b_clz &&
name != p->sym_b_clzl && name != p->sym_b_clzll &&
name != p->sym_b_trap && name != p->sym_b_unreachable &&
name != p->sym_b_expect && name != p->sym_b_offsetof &&
@@ -690,8 +875,9 @@ static int try_parse_builtin_call(Parser* p) {
name != p->sym_a_exchange_n && name != p->sym_a_fetch_add &&
name != p->sym_a_fetch_sub && name != p->sym_a_fetch_and &&
name != p->sym_a_fetch_or && name != p->sym_a_fetch_xor &&
- name != p->sym_a_cas_n && name != p->sym_a_thread_fence &&
- name != p->sym_a_signal_fence) {
+ name != p->sym_a_fetch_nand && name != p->sym_a_cas_n &&
+ name != p->sym_a_always_lock_free && name != p->sym_a_is_lock_free &&
+ name != p->sym_a_thread_fence && name != p->sym_a_signal_fence) {
return 0;
}
advance(p); /* IDENT */
@@ -726,7 +912,7 @@ static int try_parse_builtin_call(Parser* p) {
return 1;
}
- if (name == p->sym_b_ctz) {
+ if (name == p->sym_b_ctz || name == p->sym_b_ctzl || name == p->sym_b_ctzll) {
parse_assign_expr(p);
to_rvalue(p);
expect_punct(p, ')', "')' after __builtin_ctz");
@@ -811,40 +997,56 @@ static int try_parse_builtin_call(Parser* p) {
parse_assign_expr(p);
to_rvalue(p);
expect_punct(p, ',', "',' in __atomic_load_n");
- i64 ord = eval_const_int(p, p->cur.loc);
+ MemOrder ord = parse_atomic_mem_order(p);
expect_punct(p, ')', "')' after __atomic_load_n");
cg_set_loc(p->cg, loc);
- cg_atomic_load(p->cg, (MemOrder)ord);
+ cg_atomic_load(p->cg, ord);
return 1;
}
if (name == p->sym_a_store_n) {
parse_assign_expr(p);
to_rvalue(p);
+ const Type* val_ty =
+ atomic_pointee_type(p, cg_top_type(p->cg), "__atomic_store_n");
expect_punct(p, ',', "',' in __atomic_store_n");
parse_assign_expr(p);
to_rvalue(p);
+ coerce_top_to_type(p, val_ty);
expect_punct(p, ',', "',' in __atomic_store_n");
- i64 ord = eval_const_int(p, p->cur.loc);
+ MemOrder ord = parse_atomic_mem_order(p);
expect_punct(p, ')', "')' after __atomic_store_n");
cg_set_loc(p->cg, loc);
- cg_atomic_store(p->cg, (MemOrder)ord);
+ cg_atomic_store(p->cg, ord);
cg_push_int(p->cg, 0, ty_int(p));
return 1;
}
if (name == p->sym_a_thread_fence || name == p->sym_a_signal_fence) {
- i64 ord = eval_const_int(p, p->cur.loc);
+ MemOrder ord = parse_atomic_mem_order(p);
expect_punct(p, ')', "')' after atomic fence");
cg_set_loc(p->cg, loc);
- cg_fence(p->cg, (MemOrder)ord);
+ cg_fence(p->cg, ord);
cg_push_int(p->cg, 0, ty_int(p));
return 1;
}
+ if (name == p->sym_a_always_lock_free || name == p->sym_a_is_lock_free) {
+ i64 size = eval_const_int(p, p->cur.loc);
+ expect_punct(p, ',', "',' in atomic lock-free builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ cg_drop(p->cg);
+ expect_punct(p, ')', "')' after atomic lock-free builtin");
+ cg_push_int(p->cg, atomic_lock_free_for_const_size(p, size), ty_int(p));
+ return 1;
+ }
+
if (name == p->sym_a_cas_n) {
parse_assign_expr(p);
to_rvalue(p); /* ptr */
+ const Type* obj_ty = atomic_pointee_type(p, cg_top_type(p->cg),
+ "__atomic_compare_exchange_n");
expect_punct(p, ',', "',' in __atomic_compare_exchange_n");
parse_assign_expr(p);
@@ -854,6 +1056,9 @@ static int try_parse_builtin_call(Parser* p) {
perr(p, "__atomic_compare_exchange_n: arg 2 must be a pointer");
}
const Type* val_ty = eptr_ty->ptr.pointee;
+ if (val_ty != obj_ty) {
+ val_ty = obj_ty;
+ }
FrameSlotDesc fsd;
memset(&fsd, 0, sizeof fsd);
@@ -875,17 +1080,18 @@ static int try_parse_builtin_call(Parser* p) {
expect_punct(p, ',', "',' in __atomic_compare_exchange_n");
parse_assign_expr(p);
to_rvalue(p); /* desired */
+ coerce_top_to_type(p, val_ty);
expect_punct(p, ',', "',' in __atomic_compare_exchange_n");
(void)eval_const_int(p, p->cur.loc); /* weak */
expect_punct(p, ',', "',' in __atomic_compare_exchange_n");
- i64 succ = eval_const_int(p, p->cur.loc);
+ MemOrder succ = parse_atomic_mem_order(p);
expect_punct(p, ',', "',' in __atomic_compare_exchange_n");
- i64 fail = eval_const_int(p, p->cur.loc);
+ MemOrder fail = parse_atomic_mem_order(p);
expect_punct(p, ')', "')' after __atomic_compare_exchange_n");
cg_set_loc(p->cg, loc);
- cg_atomic_cas(p->cg, (MemOrder)succ, (MemOrder)fail);
+ cg_atomic_cas(p->cg, succ, fail);
const Type* ok_ty = cg_top_type(p->cg);
FrameSlotDesc okd;
@@ -943,20 +1149,25 @@ static int try_parse_builtin_call(Parser* p) {
op = AO_OR;
else if (name == p->sym_a_fetch_xor)
op = AO_XOR;
+ else if (name == p->sym_a_fetch_nand)
+ op = AO_NAND;
else {
perr(p, "internal: unhandled builtin");
}
parse_assign_expr(p);
to_rvalue(p);
+ const Type* val_ty =
+ atomic_pointee_type(p, cg_top_type(p->cg), "__atomic read-modify-write");
expect_punct(p, ',', "',' in atomic builtin");
parse_assign_expr(p);
to_rvalue(p);
+ coerce_top_to_type(p, val_ty);
expect_punct(p, ',', "',' in atomic builtin");
- i64 ord = eval_const_int(p, p->cur.loc);
+ MemOrder ord = parse_atomic_mem_order(p);
expect_punct(p, ')', "')' after atomic builtin");
cg_set_loc(p->cg, loc);
- cg_atomic_rmw(p->cg, op, (MemOrder)ord);
+ cg_atomic_rmw(p->cg, op, ord);
return 1;
}
@@ -1078,7 +1289,8 @@ static void parse_primary(Parser* p) {
static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
const Type** out_ty, u32 path[2],
- u32* out_depth) {
+ u32* out_depth,
+ const Field** out_field) {
const ABIRecordLayout* L;
if (!rec_ty || (rec_ty->kind != TY_STRUCT && rec_ty->kind != TY_UNION))
return 0;
@@ -1090,6 +1302,7 @@ static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
*out_ty = f->type;
path[0] = i;
*out_depth = 1;
+ if (out_field) *out_field = f;
return 1;
}
if ((f->flags & FIELD_ANON) &&
@@ -1103,6 +1316,7 @@ static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
path[0] = i;
path[1] = j;
*out_depth = 2;
+ if (out_field) *out_field = ff;
return 1;
}
}
@@ -1112,11 +1326,13 @@ static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
}
static void cg_record_member_path(Parser* p, const Type* member_ty,
- const u32* path, u32 depth) {
+ const u32* path, u32 depth,
+ const Field* field) {
for (u32 i = 0; i < depth; ++i) {
- cfree_cg_field(p->cg, path[i]);
+ if (pcg_emit_enabled(p)) cfree_cg_field(p->cg, path[i]);
}
pcg_retag_top(p, member_ty);
+ if (field && (field->flags & FIELD_BITFIELD)) pcg_set_top_bitfield(p);
}
static void parse_postfix(Parser* p) {
@@ -1213,6 +1429,7 @@ static void parse_postfix(Parser* p) {
const Type* lt = cg_top_type(p->cg);
Sym mname;
const Type* mty = NULL;
+ const Field* mf = NULL;
u32 path[2];
u32 depth = 0;
advance(p); /* '.' */
@@ -1225,9 +1442,9 @@ static void parse_postfix(Parser* p) {
}
mname = p->cur.v.ident;
advance(p);
- if (!find_record_member_path(p, lt, mname, &mty, path, &depth))
+ if (!find_record_member_path(p, lt, mname, &mty, path, &depth, &mf))
perr(p, "no such member");
- cg_record_member_path(p, mty, path, depth);
+ cg_record_member_path(p, mty, path, depth, mf);
continue;
}
if (is_punct(&t, P_ARROW)) {
@@ -1235,6 +1452,7 @@ static void parse_postfix(Parser* p) {
const Type* rec_ty;
Sym mname;
const Type* mty = NULL;
+ const Field* mf = NULL;
u32 path[2];
u32 depth = 0;
advance(p); /* `->` */
@@ -1252,10 +1470,10 @@ static void parse_postfix(Parser* p) {
}
mname = p->cur.v.ident;
advance(p);
- if (!find_record_member_path(p, rec_ty, mname, &mty, path, &depth))
+ if (!find_record_member_path(p, rec_ty, mname, &mty, path, &depth, &mf))
perr(p, "no such member");
cg_deref(p->cg, rec_ty);
- cg_record_member_path(p, mty, path, depth);
+ cg_record_member_path(p, mty, path, depth, mf);
continue;
}
break;
@@ -1297,12 +1515,15 @@ void parse_unary(Parser* p) {
}
parse_unary(p);
to_rvalue(p);
+ src = cg_top_type(p->cg);
if (dst && dst->kind == TY_VOID) {
cg_drop(p->cg);
cg_push_int(p->cg, 0, ty_int(p));
return;
}
- src = cg_top_type(p->cg);
+ if (!c_type_is_scalar(dst) || !c_type_is_scalar(src)) {
+ perr(p, "cast requires scalar type");
+ }
if (src && src->kind == TY_PTR && dst->kind == TY_PTR) {
cg_convert(p->cg, dst);
return;
@@ -1315,12 +1536,14 @@ void parse_unary(Parser* p) {
advance(p);
parse_unary(p);
to_rvalue(p);
+ require_arith(p, cg_top_type(p->cg), "unary '+'");
return;
}
if (is_punct(&t, '-')) {
advance(p);
parse_unary(p);
to_rvalue(p);
+ require_arith(p, cg_top_type(p->cg), "unary '-'");
cg_unop(p->cg, UO_NEG);
return;
}
@@ -1328,6 +1551,7 @@ void parse_unary(Parser* p) {
advance(p);
parse_unary(p);
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "unary '!'");
cg_push_int(p->cg, 0, ty_int(p));
cg_cmp(p->cg, CMP_EQ);
return;
@@ -1336,12 +1560,16 @@ void parse_unary(Parser* p) {
advance(p);
parse_unary(p);
to_rvalue(p);
+ if (!type_is_int(cg_top_type(p->cg))) {
+ perr(p, "unary '~' requires integer operand");
+ }
cg_unop(p->cg, UO_BNOT);
return;
}
if (is_punct(&t, '&')) {
advance(p);
parse_unary(p);
+ if (pcg_top_is_bitfield(p)) perr(p, "cannot take address of bit-field");
cg_addr(p->cg);
return;
}
@@ -1384,6 +1612,7 @@ void parse_unary(Parser* p) {
parse_unary(p);
ty = cg_top_type(p->cg);
vla_slot = p->last_pushed_vla_slot;
+ if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field");
cg_drop(p->cg);
}
} else {
@@ -1391,6 +1620,7 @@ void parse_unary(Parser* p) {
parse_unary(p);
ty = cg_top_type(p->cg);
vla_slot = p->last_pushed_vla_slot;
+ if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field");
cg_drop(p->cg);
}
if (vla_slot != FRAME_SLOT_NONE) {
@@ -1618,6 +1848,12 @@ static void parse_mul(Parser* p) {
const Type* lt = cg_top2_type(p->cg);
const Type* rt = cg_top_type(p->cg);
const Type* common = common_fp_type(p, lt, rt);
+ if (bop == BO_SREM) {
+ if (!type_is_int(lt) || !type_is_int(rt))
+ perr(p, "operator '%' requires integer operands");
+ } else if (!type_is_arith(lt) || !type_is_arith(rt)) {
+ perr(p, "multiplicative operator requires arithmetic operands");
+ }
if (common) {
emit_fp_binop(p, bop, common);
} else {
@@ -1632,6 +1868,9 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
int l_is_ptr = lt && lt->kind == TY_PTR;
int r_is_ptr = rt && rt->kind == TY_PTR;
if (bop == BO_IADD) {
+ if (l_is_ptr && r_is_ptr) {
+ perr(p, "invalid operands to binary +");
+ }
if (l_is_ptr && type_is_int(rt)) {
u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee);
if (esz != 1) {
@@ -1662,6 +1901,9 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
return;
}
if (l_is_ptr && r_is_ptr) {
+ if (!pointer_pointees_compatible(p, lt, rt)) {
+ perr(p, "subtraction of incompatible pointer types");
+ }
u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee);
cg_binop(p->cg, BO_ISUB);
if (esz != 1) {
@@ -1671,7 +1913,13 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
return;
}
}
+ if (l_is_ptr || r_is_ptr) {
+ perr(p, "invalid operands to additive operator");
+ }
const Type* common = common_fp_type(p, lt, rt);
+ if (!common && (!type_is_arith(lt) || !type_is_arith(rt))) {
+ perr(p, "additive operator requires arithmetic operands");
+ }
if (common) {
emit_fp_binop(p, bop, common);
return;
@@ -1715,6 +1963,9 @@ static void parse_shift(Parser* p) {
to_rvalue(p);
parse_add(p);
to_rvalue(p);
+ if (!type_is_int(cg_top2_type(p->cg)) || !type_is_int(cg_top_type(p->cg))) {
+ perr(p, "shift operator requires integer operands");
+ }
cg_binop(p->cg, bop);
}
}
@@ -1739,6 +1990,17 @@ static void parse_rel(Parser* p) {
to_rvalue(p);
parse_shift(p);
to_rvalue(p);
+ {
+ const Type* lt = cg_top2_type(p->cg);
+ const Type* rt = cg_top_type(p->cg);
+ if (lt && lt->kind == TY_PTR && rt && rt->kind == TY_PTR) {
+ if (!pointer_pointees_compatible(p, lt, rt)) {
+ perr(p, "comparison of incompatible pointer types");
+ }
+ } else if (!type_is_arith(lt) || !type_is_arith(rt)) {
+ perr(p, "relational operator requires arithmetic or compatible pointer operands");
+ }
+ }
cg_cmp(p->cg, cop);
}
}
@@ -1755,10 +2017,27 @@ static void parse_eq(Parser* p) {
} else {
break;
}
+ int lhs_null = null_pointer_constant(p, cg_top_type(p->cg));
advance(p);
to_rvalue(p);
parse_rel(p);
to_rvalue(p);
+ {
+ const Type* lt = cg_top2_type(p->cg);
+ const Type* rt = cg_top_type(p->cg);
+ int lnull = lhs_null;
+ int rnull = null_pointer_constant(p, rt);
+ if (lt && lt->kind == TY_PTR && rt && rt->kind == TY_PTR) {
+ if (!type_is_void_ptr(lt) && !type_is_void_ptr(rt) &&
+ !pointer_pointees_compatible(p, lt, rt)) {
+ perr(p, "comparison of incompatible pointer types");
+ }
+ } else if ((lt && lt->kind == TY_PTR) || (rt && rt->kind == TY_PTR)) {
+ if (!lnull && !rnull) perr(p, "comparison between pointer and integer");
+ } else if (!type_is_arith(lt) || !type_is_arith(rt)) {
+ perr(p, "equality operator requires scalar operands");
+ }
+ }
cg_cmp(p->cg, cop);
}
}
@@ -1796,6 +2075,14 @@ static void parse_bor(Parser* p) {
}
}
+static int cg_top_const_truth(Parser* p, int* truth_out) {
+ i64 v = 0;
+ if (!pcg_emit_enabled(p)) return 0;
+ if (!cfree_cg_top_const_int(p->cg, &v)) return 0;
+ *truth_out = v != 0;
+ return 1;
+}
+
static FrameSlot ll_tmp_slot(Parser* p, const Type* ty) {
FrameSlotDesc fsd;
memset(&fsd, 0, sizeof fsd);
@@ -1821,11 +2108,33 @@ static void parse_land(Parser* p) {
CGLabel L_end = cg_label_new(p->cg);
const Type* result_ty = ty_int(p);
FrameSlot tmp = ll_tmp_slot(p, result_ty);
+ int lhs_known;
+ int lhs_truth = 0;
+ int rhs_truth = 0;
advance(p);
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '&&'");
+ lhs_known = cg_top_const_truth(p, &lhs_truth);
+ if (lhs_known && !lhs_truth) {
+ cg_drop(p->cg);
+ pcg_codegen_suppress_push(p);
+ parse_bor(p);
+ to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '&&'");
+ cg_drop(p->cg);
+ pcg_codegen_suppress_pop(p);
+ cg_push_int(p->cg, 0, result_ty);
+ continue;
+ }
cg_branch_false(p->cg, L_false);
parse_bor(p);
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '&&'");
+ if (lhs_known && lhs_truth && cg_top_const_truth(p, &rhs_truth)) {
+ cg_drop(p->cg);
+ cg_push_int(p->cg, rhs_truth ? 1 : 0, result_ty);
+ continue;
+ }
cg_branch_false(p->cg, L_false);
ll_store_const(p, tmp, result_ty, 1);
cg_jump(p->cg, L_end);
@@ -1843,11 +2152,33 @@ static void parse_lor(Parser* p) {
CGLabel L_end = cg_label_new(p->cg);
const Type* result_ty = ty_int(p);
FrameSlot tmp = ll_tmp_slot(p, result_ty);
+ int lhs_known;
+ int lhs_truth = 0;
+ int rhs_truth = 0;
advance(p);
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '||'");
+ lhs_known = cg_top_const_truth(p, &lhs_truth);
+ if (lhs_known && lhs_truth) {
+ cg_drop(p->cg);
+ pcg_codegen_suppress_push(p);
+ parse_land(p);
+ to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '||'");
+ cg_drop(p->cg);
+ pcg_codegen_suppress_pop(p);
+ cg_push_int(p->cg, 1, result_ty);
+ continue;
+ }
cg_branch_true(p->cg, L_true);
parse_land(p);
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "logical '||'");
+ if (lhs_known && !lhs_truth && cg_top_const_truth(p, &rhs_truth)) {
+ cg_drop(p->cg);
+ cg_push_int(p->cg, rhs_truth ? 1 : 0, result_ty);
+ continue;
+ }
cg_branch_true(p->cg, L_true);
ll_store_const(p, tmp, result_ty, 0);
cg_jump(p->cg, L_end);
@@ -1866,14 +2197,17 @@ static void parse_ternary(Parser* p) {
CGLabel L_else = cg_label_new(p->cg);
CGLabel L_end = cg_label_new(p->cg);
const Type* result_ty = ty_int(p);
+ int then_null = 0;
FrameSlot tmp;
FrameSlotDesc fsd;
advance(p); /* '?' */
to_rvalue(p);
+ require_scalar(p, cg_top_type(p->cg), "conditional operator");
cg_branch_false(p->cg, L_else);
parse_assign_expr(p);
to_rvalue(p);
result_ty = cg_top_type(p->cg);
+ then_null = null_pointer_constant(p, result_ty);
if (!result_ty) result_ty = ty_int(p);
memset(&fsd, 0, sizeof fsd);
fsd.type = result_ty;
@@ -1893,6 +2227,19 @@ static void parse_ternary(Parser* p) {
to_rvalue(p);
const Type* else_ty = cg_top_type(p->cg);
const Type* common = common_fp_type(p, result_ty, else_ty);
+ const Type* ptr_result =
+ conditional_pointer_type(p, result_ty, then_null, else_ty,
+ null_pointer_constant(p, else_ty));
+ if ((result_ty && result_ty->kind == TY_PTR) ||
+ (else_ty && else_ty->kind == TY_PTR)) {
+ if (!ptr_result) perr(p, "conditional operator pointer type mismatch");
+ result_ty = ptr_result;
+ } else if (type_is_arith(result_ty) && type_is_arith(else_ty)) {
+ /* Usual arithmetic conversions are handled by the existing `common`
+ * materialization below; keep the first temporary's type stable here. */
+ } else if (!type_compatible(result_ty, else_ty)) {
+ perr(p, "conditional operator type mismatch");
+ }
if (cg_top_type(p->cg) != result_ty) {
cg_convert(p->cg, result_ty);
}
diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c
@@ -53,7 +53,9 @@ void push_subobject_lv(Parser* p, FrameSlot slot, const Type* arr_ty,
u32 offset, const Type* elem_ty) {
const Type* elem_ptr_ty = type_ptr(p->pool, elem_ty);
cg_push_local_typed(p->cg, slot, arr_ty);
- cfree_cg_addr_offset(p->cg, (i64)offset, pcg_tid(p->c, elem_ptr_ty));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_addr_offset(p->cg, (i64)offset, pcg_tid(p->c, elem_ptr_ty));
+ }
pcg_retag_top(p, elem_ptr_ty);
cg_deref(p->cg, elem_ty);
}
@@ -67,7 +69,9 @@ static void emit_copy_leaf(Parser* p, FrameSlot dst_slot,
push_subobject_lv(p, dst_slot, dst_arr_ty, dst_off, leaf_ty);
cg_push_local_typed(p->cg, src_ptr_slot, src_ptr_ty);
cg_load(p->cg);
- cfree_cg_addr_offset(p->cg, (i64)src_off, pcg_tid(p->c, leaf_ptr_ty));
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_addr_offset(p->cg, (i64)src_off, pcg_tid(p->c, leaf_ptr_ty));
+ }
pcg_retag_top(p, leaf_ptr_ty);
cg_deref(p->cg, leaf_ty);
cg_load(p->cg);
diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h
@@ -168,6 +168,7 @@ typedef struct Parser {
Pool* pool;
const Type** cg_type_stack;
+ u8* cg_value_flags;
u32 cg_type_sp;
u32 cg_type_cap;
@@ -182,6 +183,8 @@ typedef struct Parser {
Sym sym_b_alloca;
Sym sym_b_ctz;
+ Sym sym_b_ctzl;
+ Sym sym_b_ctzll;
Sym sym_b_clz;
Sym sym_b_clzl;
Sym sym_b_clzll;
@@ -218,7 +221,10 @@ typedef struct Parser {
Sym sym_a_fetch_and;
Sym sym_a_fetch_or;
Sym sym_a_fetch_xor;
+ Sym sym_a_fetch_nand;
Sym sym_a_cas_n;
+ Sym sym_a_always_lock_free;
+ Sym sym_a_is_lock_free;
Sym sym_a_thread_fence;
Sym sym_a_signal_fence;
@@ -237,6 +243,7 @@ typedef struct Parser {
FrameSlot last_pushed_vla_slot;
u8 in_param_decl;
+ u32 suppress_codegen;
u32 static_local_counter;
@@ -340,6 +347,10 @@ static inline int is_kw(const Parser* p, const Tok* t, CKw k) {
return 0;
}
+static inline int c_type_is_scalar(const Type* ty) {
+ return type_is_arith(ty) || type_is_ptr(ty);
+}
+
/* ============================================================
* Shared types (needed across multiple modules)
* ============================================================ */
diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c
@@ -23,6 +23,12 @@ static int accept_kw_stmt(Parser* p, CKw k) {
return 1;
}
+static void parse_stmt_suppressed(Parser* p) {
+ pcg_codegen_suppress_push(p);
+ parse_stmt(p);
+ pcg_codegen_suppress_pop(p);
+}
+
/* ============================================================
* Statement parsers
* ============================================================ */
@@ -30,10 +36,27 @@ static int accept_kw_stmt(Parser* p, CKw k) {
static void parse_if_stmt(Parser* p) {
CGLabel L_else = cg_label_new(p->cg);
CGLabel L_end = cg_label_new(p->cg);
+ i64 cond = 0;
+ int cond_known;
expect_punct(p, '(', "'('");
parse_expr(p);
to_rvalue(p);
+ if (!c_type_is_scalar(cg_top_type(p->cg))) {
+ perr(p, "if condition requires scalar type");
+ }
+ cond_known = cfree_cg_top_const_int(p->cg, &cond);
expect_punct(p, ')', "')'");
+ if (cond_known) {
+ cg_drop(p->cg);
+ if (cond) {
+ parse_stmt(p);
+ if (accept_kw_stmt(p, KW_ELSE)) parse_stmt_suppressed(p);
+ } else {
+ parse_stmt_suppressed(p);
+ if (accept_kw_stmt(p, KW_ELSE)) parse_stmt(p);
+ }
+ return;
+ }
cg_branch_false(p->cg, L_else);
parse_stmt(p);
if (accept_kw_stmt(p, KW_ELSE)) {
@@ -55,6 +78,9 @@ static void parse_while_stmt(Parser* p) {
cg_label_place(p->cg, L_top);
parse_expr(p);
to_rvalue(p);
+ if (!c_type_is_scalar(cg_top_type(p->cg))) {
+ perr(p, "while condition requires scalar type");
+ }
expect_punct(p, ')', "')'");
cg_branch_false(p->cg, L_end);
p->cur_break = L_end;
@@ -92,6 +118,9 @@ static void parse_for_stmt(Parser* p) {
if (!is_punct(&p->cur, ';')) {
parse_expr(p);
to_rvalue(p);
+ if (!c_type_is_scalar(cg_top_type(p->cg))) {
+ perr(p, "for condition requires scalar type");
+ }
cg_branch_false(p->cg, L_end);
}
expect_punct(p, ';', "';'");
@@ -173,6 +202,9 @@ static void parse_do_stmt(Parser* p) {
expect_punct(p, '(', "'('");
parse_expr(p);
to_rvalue(p);
+ if (!c_type_is_scalar(cg_top_type(p->cg))) {
+ perr(p, "do-while condition requires scalar type");
+ }
expect_punct(p, ')', "')' after do-while condition");
expect_punct(p, ';', "';' after do-while");
cg_branch_true(p->cg, L_top);
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -698,6 +698,7 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
memset(&f, 0, sizeof f);
if (is_punct(&p->cur, ':')) {
advance(p);
+ if (!type_is_int(specs.type)) perr(p, "bit-field has non-integer type");
i64 w = eval_const_int(p, mloc);
if (w < 0) perr(p, "negative bit-field width");
if (w > (i64)c_abi_sizeof(p->abi, specs.type) * 8) {
@@ -718,6 +719,7 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
mty = parse_declarator_full_ex(p, specs.type, /*allow_abstract=*/0,
&mname, &mloc, &mattrs);
if (accept_punct(p, ':')) {
+ if (!type_is_int(mty)) perr(p, "bit-field has non-integer type");
i64 w = eval_const_int(p, mloc);
if (w < 0) perr(p, "negative bit-field width");
if (w == 0 && mname != 0)
diff --git a/rt/Makefile b/rt/Makefile
@@ -1,6 +1,8 @@
# Included by the repository-root Makefile. Paths are root-relative.
RT_AR ?= llvm-ar
+RT_AS ?= $(CC)
+RT_AS_COMPILE_FLAGS ?= -c
RT_BUILD_DIR = rt/build
RT_COMMON_CFLAGS = -ffreestanding -fno-builtin -std=c11 -Wpedantic -Wall -Wextra -Werror
@@ -62,11 +64,13 @@ RT_aarch64-linux_ABI = lp64
RT_aarch64-linux_INT128 = 1
RT_aarch64-linux_CORO = aarch64
RT_aarch64-linux_LDBL128 = 1
+RT_EXTRA_SRCS_aarch64-linux = rt/lib/coro/aarch64_elf.s
RT_aarch64-apple-darwin_TARGET = aarch64-apple-darwin
RT_aarch64-apple-darwin_ABI = lp64
RT_aarch64-apple-darwin_INT128 = 1
RT_aarch64-apple-darwin_CORO = aarch64
+RT_EXTRA_SRCS_aarch64-apple-darwin = rt/lib/coro/aarch64_macho.s
RT_riscv64-linux_TARGET = riscv64-linux-gnu
RT_riscv64-linux_ABI = lp64
@@ -172,10 +176,11 @@ RT_SRCS_$(1) := \
$$(RT_CORO_SRCS_$$(RT_$(1)_CORO)) \
$$(if $$(RT_$(1)_LDBL128),$$(RT_LDBL128_SRCS)) \
$$(if $$(RT_$(1)_SAVE_RESTORE),$$(RT_SAVE_RESTORE_SRCS_$$(RT_$(1)_ABI))) \
- $$(RT_AEABI_SRCS_$$(RT_$(1)_AEABI))
+ $$(RT_AEABI_SRCS_$$(RT_$(1)_AEABI)) \
+ $$(RT_EXTRA_SRCS_$(1))
RT_CFLAGS_$(1) := \
$$(RT_COMMON_CFLAGS) $$(RT_LIB_INCS) \
- --target=$$(RT_$(1)_TARGET) \
+ -target $$(RT_$(1)_TARGET) \
-DHAS_INT128=$$(RT_$(1)_INT128) \
$$(RT_ABI_INC_$$(RT_$(1)_ABI)) \
$$(RT_$(1)_ARCH_FLAGS) \
@@ -191,6 +196,14 @@ $$(RT_BUILD_DIR)/$(1)/libcfree_rt.a: $$(RT_OBJS_$(1))
@mkdir -p $$(dir $$@)
$$(RT_AR) rcs $$@ $$^
+$$(RT_BUILD_DIR)/$(1)/%.s.o: rt/lib/%.s
+ @mkdir -p $$(dir $$@)
+ $$(RT_AS) $$(RT_CFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@
+
+$$(RT_BUILD_DIR)/$(1)/%.S.o: rt/lib/%.S
+ @mkdir -p $$(dir $$@)
+ $$(RT_AS) $$(RT_CFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@
+
$$(RT_BUILD_DIR)/$(1)/%.o: rt/lib/%
@mkdir -p $$(dir $$@)
$$(CC) $$(RT_CFLAGS_$(1)) -c $$< -o $$@
diff --git a/rt/lib/coro/aarch64.c b/rt/lib/coro/aarch64.c
@@ -14,13 +14,8 @@
* sizeof = 176 (alignof-16 padded), 16-byte aligned. Fits in the
* 256-byte storage carved out by jmp_buf and coro_ctx.
*
- * SAVE_/RESTORE_ are C string-concat macros so the same byte
- * sequence is emitted in setjmp, longjmp, and __cfree_coro_switch without
- * any duplication or gas-specific .macro tricks.
- *
- * Symbol naming uses __USER_LABEL_PREFIX__ so labels match the C
- * compiler's call-site mangling on both ELF (no prefix) and Mach-O
- * (leading "_").
+ * The assembly primitives live in aarch64_elf.s / aarch64_macho.s so this
+ * translation unit stays plain C and does not require file-scope asm support.
*/
#include <cfree/coro.h>
@@ -61,100 +56,3 @@ void __cfree_coro_ctx_init(coro_ctx* ctx, void* stack_base, size_t stack_len,
c->regs[11] = (uintptr_t)__cfree_coro_trampoline; /* lr */
c->regs[12] = top; /* sp */
}
-
-#define STR_(x) #x
-#define STR(x) STR_(x)
-#define SYM(n) STR(__USER_LABEL_PREFIX__) #n
-
-/* Save callee-saved state into [reg]; clobbers x9 (caller-saved). */
-#define SAVE_INTO(reg) \
- " stp x19, x20, [" reg \
- ", #0]\n" \
- " stp x21, x22, [" reg \
- ", #16]\n" \
- " stp x23, x24, [" reg \
- ", #32]\n" \
- " stp x25, x26, [" reg \
- ", #48]\n" \
- " stp x27, x28, [" reg \
- ", #64]\n" \
- " stp x29, x30, [" reg \
- ", #80]\n" \
- " mov x9, sp\n" \
- " str x9, [" reg \
- ", #96]\n" \
- " stp d8, d9, [" reg \
- ", #104]\n" \
- " stp d10, d11, [" reg \
- ", #120]\n" \
- " stp d12, d13, [" reg \
- ", #136]\n" \
- " stp d14, d15, [" reg ", #152]\n"
-
-/* Restore callee-saved state from [reg]; clobbers x9. */
-#define RESTORE_FROM(reg) \
- " ldp d8, d9, [" reg \
- ", #104]\n" \
- " ldp d10, d11, [" reg \
- ", #120]\n" \
- " ldp d12, d13, [" reg \
- ", #136]\n" \
- " ldp d14, d15, [" reg \
- ", #152]\n" \
- " ldp x19, x20, [" reg \
- ", #0]\n" \
- " ldp x21, x22, [" reg \
- ", #16]\n" \
- " ldp x23, x24, [" reg \
- ", #32]\n" \
- " ldp x25, x26, [" reg \
- ", #48]\n" \
- " ldp x27, x28, [" reg \
- ", #64]\n" \
- " ldp x29, x30, [" reg \
- ", #80]\n" \
- " ldr x9, [" reg \
- ", #96]\n" \
- " mov sp, x9\n"
-
-__asm__ (
- ".text\n"
- ".align 4\n"
-
- /* setjmp(env) -- env in x0. lr at call time is the return address
- into the caller, exactly what longjmp must restore. */
- ".globl " SYM(setjmp) "\n"
- SYM(setjmp) ":\n"
- SAVE_INTO("x0")
- " mov x0, #0\n"
- " ret\n"
-
- /* longjmp(env, val) -- env in x0, val in x1.
- longjmp(_, 0) must deliver 1 (C11 7.13.2.1p4); csinc gives
- x0 = (x1 != 0) ? x1 : 1 branch-free. */
- ".globl " SYM(longjmp) "\n"
- SYM(longjmp) ":\n"
- RESTORE_FROM("x0")
- " cmp x1, #0\n"
- " csinc x0, x1, xzr, ne\n"
- " ret\n"
-
- /* __cfree_coro_switch(from, to, value) -- x0, x1, x2. Save into [x0],
- restore from [x1], deliver x2 in x0 (which is both the return
- register here and the first-arg register the trampoline reads
- on a fresh context's first run). */
- ".globl " SYM(__cfree_coro_switch) "\n"
- SYM(__cfree_coro_switch) ":\n"
- SAVE_INTO("x0")
- RESTORE_FROM("x1")
- " mov x0, x2\n"
- " ret\n"
-
- /* __cfree_coro_trampoline -- on first entry x0 = value (delivered),
- x19 = entry fn (set by __cfree_coro_ctx_init), sp aligned to 16. brk if entry
- returns. */
- ".globl " SYM(__cfree_coro_trampoline) "\n"
- SYM(__cfree_coro_trampoline) ":\n"
- " blr x19\n"
- " brk #0\n"
-);
diff --git a/rt/lib/coro/aarch64_elf.s b/rt/lib/coro/aarch64_elf.s
@@ -0,0 +1,71 @@
+.text
+.align 4
+
+.globl setjmp
+setjmp:
+ stp x19, x20, [x0, #0]
+ stp x21, x22, [x0, #16]
+ stp x23, x24, [x0, #32]
+ stp x25, x26, [x0, #48]
+ stp x27, x28, [x0, #64]
+ stp x29, x30, [x0, #80]
+ mov x9, sp
+ str x9, [x0, #96]
+ stp d8, d9, [x0, #104]
+ stp d10, d11, [x0, #120]
+ stp d12, d13, [x0, #136]
+ stp d14, d15, [x0, #152]
+ mov x0, #0
+ ret
+
+.globl longjmp
+longjmp:
+ ldp d8, d9, [x0, #104]
+ ldp d10, d11, [x0, #120]
+ ldp d12, d13, [x0, #136]
+ ldp d14, d15, [x0, #152]
+ ldp x19, x20, [x0, #0]
+ ldp x21, x22, [x0, #16]
+ ldp x23, x24, [x0, #32]
+ ldp x25, x26, [x0, #48]
+ ldp x27, x28, [x0, #64]
+ ldp x29, x30, [x0, #80]
+ ldr x9, [x0, #96]
+ mov sp, x9
+ cmp x1, #0
+ csinc x0, x1, xzr, ne
+ ret
+
+.globl __cfree_coro_switch
+__cfree_coro_switch:
+ stp x19, x20, [x0, #0]
+ stp x21, x22, [x0, #16]
+ stp x23, x24, [x0, #32]
+ stp x25, x26, [x0, #48]
+ stp x27, x28, [x0, #64]
+ stp x29, x30, [x0, #80]
+ mov x9, sp
+ str x9, [x0, #96]
+ stp d8, d9, [x0, #104]
+ stp d10, d11, [x0, #120]
+ stp d12, d13, [x0, #136]
+ stp d14, d15, [x0, #152]
+ ldp d8, d9, [x1, #104]
+ ldp d10, d11, [x1, #120]
+ ldp d12, d13, [x1, #136]
+ ldp d14, d15, [x1, #152]
+ ldp x19, x20, [x1, #0]
+ ldp x21, x22, [x1, #16]
+ ldp x23, x24, [x1, #32]
+ ldp x25, x26, [x1, #48]
+ ldp x27, x28, [x1, #64]
+ ldp x29, x30, [x1, #80]
+ ldr x9, [x1, #96]
+ mov sp, x9
+ mov x0, x2
+ ret
+
+.globl __cfree_coro_trampoline
+__cfree_coro_trampoline:
+ blr x19
+ brk #0
diff --git a/rt/lib/coro/aarch64_macho.s b/rt/lib/coro/aarch64_macho.s
@@ -0,0 +1,71 @@
+.text
+.align 4
+
+.globl _setjmp
+_setjmp:
+ stp x19, x20, [x0, #0]
+ stp x21, x22, [x0, #16]
+ stp x23, x24, [x0, #32]
+ stp x25, x26, [x0, #48]
+ stp x27, x28, [x0, #64]
+ stp x29, x30, [x0, #80]
+ mov x9, sp
+ str x9, [x0, #96]
+ stp d8, d9, [x0, #104]
+ stp d10, d11, [x0, #120]
+ stp d12, d13, [x0, #136]
+ stp d14, d15, [x0, #152]
+ mov x0, #0
+ ret
+
+.globl _longjmp
+_longjmp:
+ ldp d8, d9, [x0, #104]
+ ldp d10, d11, [x0, #120]
+ ldp d12, d13, [x0, #136]
+ ldp d14, d15, [x0, #152]
+ ldp x19, x20, [x0, #0]
+ ldp x21, x22, [x0, #16]
+ ldp x23, x24, [x0, #32]
+ ldp x25, x26, [x0, #48]
+ ldp x27, x28, [x0, #64]
+ ldp x29, x30, [x0, #80]
+ ldr x9, [x0, #96]
+ mov sp, x9
+ cmp x1, #0
+ csinc x0, x1, xzr, ne
+ ret
+
+.globl ___cfree_coro_switch
+___cfree_coro_switch:
+ stp x19, x20, [x0, #0]
+ stp x21, x22, [x0, #16]
+ stp x23, x24, [x0, #32]
+ stp x25, x26, [x0, #48]
+ stp x27, x28, [x0, #64]
+ stp x29, x30, [x0, #80]
+ mov x9, sp
+ str x9, [x0, #96]
+ stp d8, d9, [x0, #104]
+ stp d10, d11, [x0, #120]
+ stp d12, d13, [x0, #136]
+ stp d14, d15, [x0, #152]
+ ldp d8, d9, [x1, #104]
+ ldp d10, d11, [x1, #120]
+ ldp d12, d13, [x1, #136]
+ ldp d14, d15, [x1, #152]
+ ldp x19, x20, [x1, #0]
+ ldp x21, x22, [x1, #16]
+ ldp x23, x24, [x1, #32]
+ ldp x25, x26, [x1, #48]
+ ldp x27, x28, [x1, #64]
+ ldp x29, x30, [x1, #80]
+ ldr x9, [x1, #96]
+ mov sp, x9
+ mov x0, x2
+ ret
+
+.globl ___cfree_coro_trampoline
+___cfree_coro_trampoline:
+ blr x19
+ brk #0
diff --git a/src/api/cg.c b/src/api/cg.c
@@ -3732,6 +3732,22 @@ void cfree_cg_drop(CfreeCg *g) {
api_release(g, &v);
}
+int cfree_cg_top_const_int(CfreeCg *g, int64_t *out_value) {
+ ApiSValue *v;
+ CfreeCgTypeId ty;
+ u32 width;
+ if (!g || !out_value || !g->sp)
+ return 0;
+ v = &g->stack[g->sp - 1u];
+ if (v->kind != SV_OPERAND || v->op.kind != OPK_IMM)
+ return 0;
+ ty = api_sv_type(v);
+ if (!api_foldable_int_like_type(g->c, ty, &width))
+ return 0;
+ *out_value = api_fold_result(g->c, ty, (u64)v->op.v.imm, width);
+ return 1;
+}
+
void cfree_cg_rot3(CfreeCg *g) {
ApiSValue a, b, c;
if (!g || g->sp < 3)
diff --git a/test/parse/CORPUS.md b/test/parse/CORPUS.md
@@ -495,6 +495,10 @@ ordinary calls.
| `builtin_19_atomic_cas_loop` | ★ | lock-free increment via CAS retry loop (ACQ_REL/ACQUIRE pair) | 42 |
| `builtin_20_ctz` | ★ | `__builtin_ctz(1u<<5) + 37` — count trailing zeros, low-bit case | 42 |
| `builtin_21_ctz_high` | ★ | `__builtin_ctz(0x80000000u) - 31 + 42` — high-bit case (31 trailing zeros) | 42 |
+| `builtin_22_ctz_long_widths` | ★ | `__builtin_ctzl` + `__builtin_ctzll`; operand type selects intrinsic width | 42 |
+| `builtin_23_atomic_long_literal_convert` | ★ | store/RMW integer literals converted to unsigned long atomic object type | 42 |
+| `builtin_24_atomic_lock_free` | ★ | target-aware lock-free folding through `if`, `&&`, and `||`; dead 16-byte atomic arms suppress codegen | 42 |
+| `builtin_25_atomic_fetch_nand` | ★ | `__atomic_fetch_nand` lowers to atomic NAND RMW | 42 |
| `builtin_99_syscall0` | (deferred) | `__cfree_syscall0` requires linking against the syscall stub; covered in `test/libc` | — |
## Variadic coverage
diff --git a/test/parse/cases/builtin_22_ctz_long_widths.c b/test/parse/cases/builtin_22_ctz_long_widths.c
@@ -0,0 +1,5 @@
+int test_main(void) {
+ unsigned long a = 1ul << 4;
+ unsigned long long b = 1ull << 6;
+ return __builtin_ctzl(a) + __builtin_ctzll(b) + 32;
+}
diff --git a/test/parse/cases/builtin_22_ctz_long_widths.expected b/test/parse/cases/builtin_22_ctz_long_widths.expected
@@ -0,0 +1 @@
+42
diff --git a/test/parse/cases/builtin_23_atomic_long_literal_convert.c b/test/parse/cases/builtin_23_atomic_long_literal_convert.c
@@ -0,0 +1,6 @@
+int test_main(void) {
+ unsigned long x = 99;
+ __atomic_store_n(&x, 0, __ATOMIC_RELEASE);
+ unsigned long old = __atomic_fetch_add(&x, 1, __ATOMIC_ACQ_REL);
+ return (int)(old + x + 41);
+}
diff --git a/test/parse/cases/builtin_23_atomic_long_literal_convert.expected b/test/parse/cases/builtin_23_atomic_long_literal_convert.expected
@@ -0,0 +1 @@
+42
diff --git a/test/parse/cases/builtin_24_atomic_lock_free.c b/test/parse/cases/builtin_24_atomic_lock_free.c
@@ -0,0 +1,20 @@
+int test_main(void) {
+ int x = 0;
+ long y = 0;
+ int score = 0;
+ if (__atomic_always_lock_free(1, &x)) score += 1;
+ if (__atomic_always_lock_free(2, &x)) score += 2;
+ if (__atomic_always_lock_free(4, &x)) score += 4;
+ if (__atomic_is_lock_free(8, &y)) score += 8;
+ if (!__atomic_always_lock_free(16, &y)) score += 27;
+ if (__atomic_always_lock_free(32, 0)) {
+ unsigned __int128 *wide = (unsigned __int128 *)0;
+ score += 90 + (int)__atomic_load_n(wide, __ATOMIC_SEQ_CST);
+ }
+ if (__atomic_always_lock_free(32, 0) ||
+ (__atomic_always_lock_free(32, 0) && ((unsigned long)&score % 16) == 0)) {
+ unsigned __int128 *wide = (unsigned __int128 *)0;
+ score += 90 + (int)__atomic_load_n(wide, __ATOMIC_SEQ_CST);
+ }
+ return score;
+}
diff --git a/test/parse/cases/builtin_24_atomic_lock_free.expected b/test/parse/cases/builtin_24_atomic_lock_free.expected
@@ -0,0 +1 @@
+42
diff --git a/test/parse/cases/builtin_25_atomic_fetch_nand.c b/test/parse/cases/builtin_25_atomic_fetch_nand.c
@@ -0,0 +1,6 @@
+int test_main(void) {
+ int x = -43;
+ int prior = __atomic_fetch_nand(&x, -1, __ATOMIC_SEQ_CST);
+ (void)prior;
+ return x;
+}
diff --git a/test/parse/cases/builtin_25_atomic_fetch_nand.expected b/test/parse/cases/builtin_25_atomic_fetch_nand.expected
@@ -0,0 +1 @@
+42