commit 651ae85e2d4b2a378ed9567ec16f77c29761e5fb
parent dba8886b09e7bd057a9bcdc43c9588d458cbcf02
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 13 May 2026 09:11:57 -0700
toy-todo.md plan
Diffstat:
| A | doc/toy-todo.md | | | 623 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 623 insertions(+), 0 deletions(-)
diff --git a/doc/toy-todo.md b/doc/toy-todo.md
@@ -0,0 +1,623 @@
+# Toy CG Coverage TODO
+
+Toy should become the primary way to exercise the public CG API in
+`include/cfree/cg.h`. This file tracks the gaps between what the current toy
+language can express and the full CG surface.
+
+Baseline: `lang/toy/toy.c` currently has core integer functions, locals,
+globals, direct calls, tail calls, pointers to `int`, variadic helpers, inline
+asm helpers, atomics, memory helpers, a hard-coded record field test, and a
+hard-coded type-query test.
+
+## Language Extension Plan
+
+This section is a decision log for source-language extensions that make toy
+able to express the full CG API surface. Items move from `Proposed` to
+`Accepted` or `Rejected` as decisions are made.
+
+### Group 1: Types And Literals
+
+Status: Accepted
+
+Goal: make toy able to name every CG type shape and produce scalar constants
+for those types. Later work on calls, atomics, globals, records, and memory
+operations should build on these type forms instead of adding one-off builtins.
+
+Proposal:
+
+- Builtin scalar type names: `void`, `bool`, `i8`, `u8`, `i16`, `u16`, `i32`,
+ `u32`, `i64`, `u64`, `isize`, `usize`, `f32`, `f64`, and `va_list`.
+- Keep `int` as a compatibility alias for `isize` for existing toy tests.
+- Pointer types remain prefix: `*T`.
+- Arrays use prefix count syntax: `[N]T`, for example `[4]i32`.
+- Function pointer types use `fn(T, U): R` as a type form. Variadic function
+ types use `fn(T, ...): R`.
+- Qualified types use prefix qualifiers: `const T`, `volatile T`, and
+ `restrict T`. Multiple qualifiers are allowed as repeated prefixes.
+- Boolean literals are `true` and `false`.
+- Numeric literals keep their current unsuffixed form and are context-typed by
+ the target declaration, parameter, return, or cast. Add decimal float literals
+ for `f32`/`f64`.
+- Add explicit casts as `expr as T` so tests can force conversion edges without
+ needing C-like cast ambiguity in the grammar.
+- Add type query expressions: `sizeof<T>()`, `alignof<T>()`, and
+ `offsetof<T>(field)`.
+
+Decisions:
+
+- Use `true`/`false` for boolean literals.
+- Use `[N]T` for arrays.
+- Use `expr as T` for casts.
+- Include `offsetof<T>(field)` with the type-query expressions.
+
+### Group 2: Declarations, Linkage, And Attributes
+
+Status: Accepted
+
+Goal: expose function/data declaration modes and symbol attributes without
+turning toy into C. Declarations should stay regular enough that coverage cases
+are easy to read and failures point at CG behavior rather than parser trivia.
+
+Proposal:
+
+- Function and data definitions are static/private by default.
+- Exported definitions use `pub`:
+ `pub fn name(args): ret { ... }`, `pub let x: T = init;`, and
+ `pub var x: T = init;`.
+- Imported function declarations use explicit `extern`:
+ `extern fn name(args): ret;`
+ This emits `cfree_cg_func_decl` without `cfree_cg_func_begin`.
+- Function definitions keep the current non-exported shape:
+ `fn name(args): ret { ... }`.
+- Global definitions keep `let`/`var`:
+ `let x: T = init;`, `var x: T = init;`, and zero-init `var x: T;`.
+- Imported data declarations use `extern`:
+ `extern let name: T;`, `extern var name: T;`.
+ These emit `cfree_cg_data_decl` without a definition.
+- Attributes use a prefix bracket list before `pub`, `extern`, `fn`, `let`, or
+ `var`:
+ `#[bind(local), visibility(hidden), section(".foo"), align(16), used]`
+- Supported attributes:
+ `bind(global|local|weak)`, `visibility(default|hidden|protected)`,
+ `section("name")`, `align(N)`, `readonly`, `tls`, `tls_model(default|local_exec|initial_exec|local_dynamic|general_dynamic|tlvp)`,
+ `common`, `used`, and `noreturn`.
+- `pub` maps to global binding unless an explicit `bind(...)` attribute
+ overrides it for an error-case test. Non-`pub` definitions map to local
+ binding by default.
+- `let` implies readonly for defined data; `readonly` remains accepted on `var`
+ to exercise the declaration flag directly.
+
+Decisions:
+
+- Use `#[...]` attributes.
+- Default definitions are static/private.
+- Use `pub` to mark exported definitions.
+- Use explicit `extern` for outside functions and data.
+
+### Group 3: Composite Types And Initializers
+
+Status: Accepted
+
+Goal: let toy source define records, enums, aliases, arrays, and aggregate
+initializers directly, so CG type construction, layout, data emission, field
+access, and ABI aggregate behavior are all covered without hard-coded helper
+types.
+
+Proposal:
+
+- Type aliases:
+ `type Word = u64;`
+- Record declarations:
+ `record Pair { a: i32; b: i32; }`
+- Anonymous record fields use `_`:
+ `record Tuple2 { _: i32; _: i32; }`
+- Field alignment uses field attributes:
+ `record Padded { #[align(16)] x: i32; y: i32; }`
+- Packed records use a record attribute:
+ `#[packed] record Header { tag: u8; len: u32; }`
+- Enum declarations:
+ `enum Color: i32 { red = 1; green = 2; blue = 3; }`
+ The base type is required so enum layout is explicit.
+- Record literals use named fields:
+ `Pair { a: 1, b: 2 }`
+- Array literals use bracket values:
+ `[1, 2, 3, 4]`
+ The expected type supplies the element type and count.
+- Zero aggregate literal:
+ `zero<T>()`
+ This gives tests a concise way to produce zeroed arrays/records/globals.
+- Global initializers may use scalar literals, bool/float literals, string/byte
+ literals, array literals, record literals, `zero<T>()`, and symbol addresses.
+- String literals have type `*[N]u8` or `*u8` only through decay contexts; exact
+ behavior can be narrowed during implementation.
+- Symbol address initializers use `&name` initially; symbol addends and
+ non-`ADDR` reference kinds are handled in a later symbol-reference group.
+
+Decisions:
+
+- Use `record`; do not add `struct` as an alias initially.
+- Support `Color.red` and `.red` enum value references. `.red` requires an
+ expected enum type from context.
+- Use `zero<T>()` for zero values and zero aggregate initializers.
+
+### Group 4: Lvalues, Field Access, Indexing, And Memory
+
+Status: Accepted
+
+Goal: make normal toy expressions produce the lvalue shapes CG needs for
+`addr`, `indirect`, `load`, `store`, `index`, `field`, `memcpy`, and `memset`.
+The language should express memory operations through ordinary source forms
+first, with explicit builtins kept for exact CG knob coverage.
+
+Proposal:
+
+- Field access:
+ `expr.field`
+ If `expr` has record type, this selects the field directly. If `expr` has
+ pointer-to-record type, field access implicitly dereferences before selecting
+ the field. There is no `->` operator.
+- Array and pointer indexing:
+ `expr[index]`
+ This lowers through `cfree_cg_index`; arrays use array lvalues, pointers use
+ pointer rvalues converted to element lvalues.
+- Address-of accepts any lvalue:
+ `&x`, `&*p`, `&arr[i]`, `&rec.field`, and `&ptr.field`.
+- Assignment accepts any assignable lvalue:
+ `x = value;`, `*p = value;`, `arr[i] = value;`, `rec.field = value;`,
+ `ptr.field = value;`.
+- Aggregate copy is expressed by assignment when source and destination have
+ the same array or record type.
+- Explicit memory builtins remain for exact size/alignment coverage:
+ `memcpy(dst, src, size, align)`, `memset(dst, value, size, align)`.
+ They return `void`.
+
+Decisions:
+
+- Do not add `->`; use `.` and let type checking decide whether the base is
+ direct or indirect.
+- Allow aggregate assignment for both records and arrays.
+- `memcpy` and `memset` return `void`.
+- Do not add explicit offset indexing syntax or a helper for now.
+- Do not add compound assignment or inc/dec syntax for now.
+
+### Group 5: Operators, Casts, And Control Values
+
+Status: Accepted
+
+Goal: exercise every scalar binop, cmp, unop, and conversion path while keeping
+the expression grammar close to the CG stack model.
+
+Proposal:
+
+- Arithmetic operators are type-directed:
+ signed integer types use signed division/remainder and signed comparisons;
+ unsigned integer types use unsigned division/remainder and unsigned
+ comparisons; floats use float arithmetic/comparisons when supported by CG.
+- Shift operators:
+ `<<` always maps to `CFREE_CG_SHL`; `>>` maps to signed or unsigned shift
+ based on the left operand type.
+- Bitwise operators apply to integer types: `&`, `|`, `^`, and unary `~`.
+- Logical operators apply to `bool`: `!`, `&&`, `||`.
+- Comparisons produce `bool`, not `int`.
+- Conditions and logical operators accept integer values using C-style truth:
+ zero is false, any non-zero value is true. The operation result is `bool`.
+- Casts use the accepted spelling `expr as T`.
+- Enum values cast explicitly to/from their base integer type through `as`.
+- Pointer casts are allowed through `as *T` for CG conversion coverage.
+- Pointer comparisons are allowed for `==`, `!=`, `<`, `<=`, `>`, and `>=`.
+ Ordered pointer comparisons are address-order comparisons using the target
+ pointer representation.
+- Add bit helpers as explicit builtins instead of syntax:
+ `bitget(value, lo, width)` and `bitset(dst, src, lo, width)`.
+ These map to the inline CG composites and require integer operands.
+- Keep no compound assignment and no inc/dec syntax.
+
+Decisions:
+
+- Integer truth uses zero for false and any non-zero value for true.
+- Add `bitget` and `bitset` builtins.
+- Allow all pointer comparisons, including ordered address comparisons.
+
+### Group 6: Control Flow And Expression Scopes
+
+Status: Accepted
+
+Goal: expose the structured scope operations, labels/branches through normal
+control flow, and unreachable paths without turning toy into an assembly-like
+language.
+
+Proposal:
+
+- Keep statement control flow:
+ `if cond { ... } else { ... }`, `while cond { ... }`, `break;`,
+ `continue;`, and `return`.
+- Add expression `if`:
+ `if cond { expr } else { expr }`
+ This produces a value and exercises expression-valued scopes.
+- Add result-typed `while`:
+ `while<T> cond { ... }`
+ The loop body may use `break expr;` to exit with a value of `T`. This maps to
+ `cfree_cg_scope_begin` with a non-`NONE` result type.
+- Conditional break/continue stays source-level structured:
+ `if cond { break; }` and `if cond { continue; }`
+ The compiler may lower these through the conditional CG helpers where useful,
+ but toy does not add `break if` or `continue if` syntax.
+- Add `unreachable();` and `trap();` as intrinsics in the intrinsic group, but
+ control-flow tests should also use them to cover dead-end blocks.
+- Do not add source-level labels/goto initially. Short-circuiting, `if`, and
+ loops should cover labels and branches at the CG API level.
+
+Decisions:
+
+- Expression `if` uses braces.
+- Use `while<T> cond { ... }` for value-producing loops.
+- Keep source as `if cond { break; }` and `if cond { continue; }`; do not add
+ conditional break/continue syntax.
+
+### Group 7: Calls, Function Pointers, ABI, And Variadics
+
+Status: Accepted
+
+Goal: make toy able to exercise direct calls, indirect calls, tail calls,
+scalar and aggregate ABI paths, and variadic argument handling using ordinary
+typed source.
+
+Proposal:
+
+- Direct calls keep the existing spelling:
+ `callee(arg0, arg1)`.
+- Function values are addressable with `&name` and have type `*fn(...): R`.
+- Bare function names are also allowed in value position and produce function
+ values, so `let fp: *fn(i32): i32 = f;` is valid.
+- Indirect calls use normal call syntax when the callee expression has function
+ or pointer-to-function type:
+ `fp(arg0, arg1)`.
+- Tail calls keep explicit source spelling:
+ `return tail callee(args);`
+ The callee may be direct or indirect. Variadic tail calls remain rejected.
+- Function parameters and returns may use every scalar type, pointers, enums,
+ aliases, records, and arrays where CG supports the shape.
+- Records can be passed and returned by value. Tests should include small
+ records, large records, mixed integer/float records, and homogeneous float
+ aggregates on targets where the ABI distinguishes them.
+- Arrays act like records: they are first-class values and pass by value. There
+ is no implicit array-to-pointer decay.
+- `&arr` is equivalent to `&arr[0]`, producing a pointer to the first element
+ rather than a pointer to the whole array.
+- Variadic functions keep `...` in the parameter list:
+ `fn sum(count: i32, ...): i32 { ... }`
+- `va_start`, `va_arg<T>`, `va_copy`, and `va_end` become typed builtins:
+ `va_start(ap);`, `va_arg<T>(ap)`, `va_copy(dst, src);`, `va_end(ap);`
+- Variadic call sites accept any expression type. Default argument promotion
+ is explicit through `as` instead of hidden C-like promotion.
+- Function type queries from Group 1 should be used in tests to validate
+ function pointer and variadic type shapes.
+
+Decisions:
+
+- Allow bare function names in value position.
+- Arrays are first-class by-value aggregates, like records.
+- Do not add implicit array decay.
+- `&arr` is the same as `&arr[0]`.
+- Variadic call-site promotions are explicit through `as`.
+
+### Group 8: Intrinsics And Atomics
+
+Status: Accepted
+
+Goal: expose the CG intrinsic and atomic APIs with typed, explicit source forms
+that make memory order, result shape, and multi-result behavior visible in toy
+tests.
+
+Proposal:
+
+- Intrinsics use named builtin functions:
+ `trap()`, `unreachable()`, `clz(x)`, `ctz(x)`, `popcount(x)`, `bswap(x)`,
+ `prefetch(ptr)`, `expect(value, expected)`, and
+ `assume_aligned(ptr, align)`.
+- `setjmp`/`longjmp` use typed builtins:
+ `setjmp(buf)` returns `i32`; `longjmp(buf, value)` returns `void` and is
+ noreturn in control-flow analysis.
+- Overflow intrinsics return a small record shape:
+ `add_overflow(a, b)`, `sub_overflow(a, b)`, and `mul_overflow(a, b)`.
+ The result is an anonymous record with fields `{ value: T; ok: bool; }`.
+- Atomic memory order names are enum-like builtin constants:
+ `.relaxed`, `.consume`, `.acquire`, `.release`, `.acq_rel`, `.seq_cst`.
+- Atomic builtins take memory order explicitly:
+ `atomic_load<T>(ptr, order)`, `atomic_store<T>(ptr, value, order)`,
+ `atomic_rmw<T>(op, ptr, value, order)`,
+ `atomic_cmpxchg<T>(ptr, expected, desired, success_order, failure_order)`,
+ and `atomic_fence(order)`.
+- Atomic RMW op names are enum-like constants:
+ `.xchg`, `.add`, `.sub`, `.and`, `.or`, `.xor`, `.nand`.
+- `atomic_cmpxchg<T>` returns a builtin record:
+ an anonymous record with fields `{ prior: T; ok: bool; }`
+ so tests can inspect both CG results.
+- The parser/type checker should reject invalid compare-exchange order pairs
+ when it can do so locally.
+- Keep compatibility aliases for the current simple builtins only during test
+ migration if needed: `atomic_add`, `atomic_sub`, `atomic_cas_ok`, and
+ `fence`.
+
+Decisions:
+
+- Overflow and compare-exchange builtins return anonymous records.
+- Use dot constants for memory orders and RMW ops.
+- Remove compatibility aliases after test migration.
+
+### Group 9: Symbol References, Relocations, And TLS
+
+Status: Accepted
+
+Goal: support thread-local storage and normal imported/exported symbol access
+as language features. Do not add a use-site relocation escape hatch unless the
+CG API grows a clear language-level semantic for it.
+
+Proposal:
+
+- Keep ordinary symbol address syntax:
+ `&name`
+ The declaration attributes on `name`, plus target and code-model context,
+ select the ordinary reference kind. For example, TLS attributes select the
+ default TLS access path, and imported functions/data can use the platform's
+ normal PLT/GOT behavior where appropriate.
+- Global data symbol initializers also use ordinary address syntax:
+ `let p: *i32 = &name;`
+ This maps to a normal `cfree_cg_data_symbol` emission selected from the same
+ declaration/context information.
+- TLS variables are declared with Group 2 attributes:
+ `#[tls, tls_model(local_exec)] var tls_counter: i32;`
+- TLS imports use `extern` plus the same attributes:
+ `#[tls, tls_model(initial_exec)] extern var errno: i32;`
+- Normal TLS variable access and `&tls_counter` use the declaration's TLS
+ attributes and model. This is the language-level path for exercising TLS
+ codegen.
+- Supported source TLS models are:
+ `default`, `local_exec`, `initial_exec`, `local_dynamic`,
+ `general_dynamic`, and `tlvp`.
+- Target-incompatible TLS models should produce diagnostics rather than silently
+ falling back.
+- For non-TLS imports, ordinary calls, bare function values, variable loads, and
+ `&name` should choose direct, PLT, GOT, or PC-relative references according
+ to declaration binding, visibility, target, and output mode.
+
+Rationale:
+
+- Thread-local storage is a real source-language feature. It belongs in toy
+ because normal loads, stores, address-taking, imports, and TLS models all
+ exercise meaningful codegen behavior.
+- Use-site relocation forms such as explicit GOT/PLT/PCREL selection are not
+ currently accepted as toy language extensions. If the only way to exercise a
+ `CfreeCgSymbolRefKind` is an artificial escape hatch, that is evidence that
+ the CG API may need narrowing or re-shaping around declaration attributes,
+ target mode, and semantic operations instead.
+- Low-level relocation record tests can remain in object/linker-specific
+ harnesses until there is a clear language-level operation that needs them.
+
+Follow-up CG API questions:
+
+- Should the public CG API keep `CfreeCgSymbolRefKind` on `push_symbol` and
+ `data_symbol`, or should reference kind be inferred from declaration attrs
+ and compile/output mode?
+- For source TLS models, should `default` always choose the platform default,
+ or should toy require an explicit model in tests that assert a specific
+ relocation sequence?
+
+Decisions:
+
+- Support TLS as a normal source-language feature through declaration
+ attributes and normal variable/address access.
+- Do not add explicit use-site relocation syntax to toy for now.
+
+### Group 10: Inline Assembly
+
+Status: Accepted
+
+Goal: expose `cfree_cg_inline_asm` through a source form that is explicit
+enough for CG tests but still reads like a language feature rather than a C API
+dump.
+
+Proposal:
+
+- Use an expression form:
+ `asm<T>(template, outputs, inputs, clobbers, flags)`
+ It returns `T`, or `void` for statement-only asm.
+- Template is a string literal or existing `arch(...)` selector.
+- Inputs are typed expressions with constraints:
+ `in("r", expr)`, `in("m", lvalue)`, `in("i", const_expr)`.
+- Outputs declare constraints and result names:
+ `out("=r", name: T)`, `out("=&r", name: T)`.
+- Inout operands consume and produce one value:
+ `inout("+r", expr)`.
+- Multiple outputs return an anonymous record with fields named from the output
+ operands. A single output returns the output value directly.
+- Named operands use the output/input names when provided, giving tests a path
+ to symbolic operand names.
+- Clobbers are string literals:
+ `clobbers("memory", "cc")`.
+- Flags are dot constants:
+ `.volatile`
+ Omitted flags mean non-volatile asm.
+- Example:
+ `asm<i32>("add %0, %1, %2", out("=r", value: i32), inputs(in("r", a), in("r", b)), clobbers(), .volatile)`
+
+Decisions:
+
+- Use a single `asm<T>(...)` form for statement and expression asm.
+- Use wrapper groups such as `inputs(...)` and `clobbers(...)`.
+- Multi-output asm returns an anonymous record.
+
+## Type System
+
+- Add syntax for all builtin scalar types, not just `int`/`isize`, implicit
+ `void` returns, and `va_list`: `bool`, `i8`, `u8`, `i16`, `u16`, `i32`,
+ `u32`, `i64`, `u64`, `usize`, `f32`, and `f64`.
+- Add float literals and operations so toy exercises `cfree_cg_push_float`,
+ floating arithmetic, floating calls/returns, and float ABI paths.
+- Add array type syntax and declaration support. `typecheck()` currently
+ constructs one array type internally, but toy programs cannot declare array
+ locals, globals, parameters, or return values.
+- Add named alias syntax. `typecheck()` constructs an alias, but aliases cannot
+ be introduced or used by toy source.
+- Add qualified type syntax for `const`, `volatile`, and `restrict`.
+ `typecheck()` only constructs a `const int` and does not exercise qualified
+ values in declarations or memory operations.
+- Add record/struct declarations with arbitrary field names, field counts,
+ field types, anonymous fields, packed alignment, and explicit field alignment.
+ Toy currently has only one internal `Pair { int a; int b; }`.
+- Add enum declarations and enum constants as source-level values. The current
+ enum coverage is only an internal constructor call inside `typecheck()`.
+- Add function pointer type syntax. Toy can form direct function types for
+ declared functions, but source cannot declare function pointer variables,
+ parameters, globals, or indirect calls.
+- Exercise `cfree_cg_type_record_field`. The current type-query builtin checks
+ record-ness and field count, but does not query field metadata.
+- Exercise type layout through source constructs, not only hard-coded helper
+ calls: `sizeof(type)`, `alignof(type)`, and possibly `offsetof(record, field)`.
+
+## Declarations And Linkage
+
+- Add function declarations without definitions, including external functions.
+ Toy calls `cfree_cg_func_decl`, but only immediately before defining the same
+ function.
+- Add global data declarations without definitions to exercise
+ `cfree_cg_data_decl`.
+- Add declaration attributes: non-default binding, hidden/protected visibility,
+ custom sections, explicit alignment, readonly, TLS, common, used, and noreturn.
+- Add TLS globals and source-level selection of TLS models.
+- Add support for undefined external data/function references so link-time
+ symbol handling is exercised from toy.
+
+## Global Data Initializers
+
+- Add byte/string data initializers. Toy has a hard-coded `byteconst()` helper
+ for `cfree_cg_push_bytes`, but no source-level string or byte literal data.
+- Add aggregate initializers for arrays and records.
+- Add explicit zero-fill ranges inside aggregate initializers, not only whole
+ object zero initialization.
+- Add symbol initializer addends and non-pointer-width symbol records.
+- Add source coverage for non-`ADDR` data symbol reference kinds: `PCREL`,
+ `GOT`, `PLT`, `TLS_LE`, `TLS_IE`, `TLS_LD`, `TLS_GD`, and `TLVP`.
+
+## Symbol References
+
+- Add source-level control over `cfree_cg_push_symbol` reference kind and addend.
+ Current code references use `CFREE_CG_SYMREF_ADDR` with addend `0`.
+- Add GOT/PLT and PC-relative code reference tests.
+- Add TLS code reference tests for all TLS symbol reference kinds.
+- Add TLVP coverage for Mach-O-style thread-local references.
+
+## Values And Lvalues
+
+- Add string literals as expression values.
+- Generalize address-of beyond identifiers. Source cannot write `&*p`,
+ `&index(p, i)`, or `&record.field`.
+- Generalize assignment targets beyond variables and unary `*p`. Source cannot
+ assign through indexed or field lvalues directly.
+- Add first-class array and record values so `load`, `store`, `addr`,
+ `indirect`, `field`, `index`, `memcpy`, and ABI aggregate paths can interact
+ naturally.
+- Add pre/post increment and decrement to exercise `cfree_cg_inc_dec`.
+
+## Operators And Conversions
+
+- Add unsigned arithmetic and comparisons. Missing source coverage includes
+ `CFREE_CG_UDIV`, `CFREE_CG_UREM`, `CFREE_CG_SHR_U`, `CFREE_CG_LT_U`,
+ `CFREE_CG_LE_U`, `CFREE_CG_GT_U`, and `CFREE_CG_GE_U`.
+- Add explicit casts to exercise `cfree_cg_convert` across integer widths,
+ signedness, pointers, booleans, and floats. Current conversion coverage is
+ essentially comparison `i1 -> int`.
+- Add a real `bool` type instead of representing truth values as `int`.
+- Add bitfield-like extraction/insertion helpers or syntax to exercise
+ `cfree_cg_bitget` and `cfree_cg_bitset`.
+- Check whether toy should allow chained comparisons or keep one comparison per
+ expression; the current parser accepts only one comparison operator at that
+ precedence level.
+
+## Stack Operations
+
+- Keep direct stack manipulation out of normal toy syntax if possible, but add
+ small targeted builtins when needed to exercise stack-sensitive CG behavior.
+- `cfree_cg_dup`, `cfree_cg_swap`, `cfree_cg_drop`, and `cfree_cg_rot3` are used
+ incidentally today. Add explicit regression cases for stack order if bugs
+ appear around multi-result operations, inc/dec, atomics, or inline asm.
+
+## Control Flow
+
+- Add expression-valued scopes so `cfree_cg_scope_begin` with a non-`NONE`
+ result type, `cfree_cg_break`, `cfree_cg_break_true`, `cfree_cg_break_false`,
+ and `cfree_cg_scope_end` are exercised with carried values.
+- Add conditional loop continuation syntax or builtins for
+ `cfree_cg_continue_true` and `cfree_cg_continue_false`.
+- Add direct tests for `cfree_cg_break_true`; current loops use
+ `cfree_cg_break_false` for while conditions.
+- Keep label/jump/branch coverage through short-circuiting and `if`, but add
+ targeted cases for awkward CFG shapes: empty blocks, nested conditionals,
+ early returns inside loops, and branches after unreachable paths.
+
+## Calls And ABI Coverage
+
+- Add indirect calls through function pointers.
+- Add calls and returns for every scalar builtin type.
+- Add by-value record parameters and record returns, including large records and
+ ABI-specific aggregate classifications.
+- Add HFA/HVA-style float aggregate coverage where the target ABI supports it.
+- Add variadic arguments beyond integer values: pointers, floats, and records.
+- Decide whether to support variadic tail calls or document that toy should
+ reject them permanently.
+
+## Intrinsics
+
+- Add source forms for `trap()` and `unreachable()`.
+- Add `setjmp`/`longjmp` coverage, including buffer storage.
+- Add overflow intrinsics: `add_overflow`, `sub_overflow`, and `mul_overflow`.
+ These require a way to consume the two-value `(result, ok)` CG result.
+- Add `prefetch(addr)`.
+- Add `assume_aligned(ptr, align)` or an equivalent helper.
+- Add result-type checks for existing intrinsics across more than `int` once
+ more scalar types exist.
+
+## Atomics
+
+- Add source-level memory order selection for relaxed, consume, acquire,
+ release, acquire-release, and sequentially consistent operations.
+- Add remaining RMW operations: exchange, and, or, xor, and nand.
+- Add atomic operations over widths other than toy `int`.
+- Add compare-exchange forms that expose both returned values: the prior value
+ and the success flag. `atomic_cas_ok` currently drops the prior value.
+- Add invalid-order diagnostics for compare-exchange success/failure pairs.
+
+## Inline Assembly
+
+- Add symbolic operand names.
+- Add multiple outputs and multiple inout operands.
+- Add asm operands for non-`int` types once those types exist.
+- Add non-volatile asm support. Current helpers always set
+ `CFREE_CG_ASM_VOLATILE`.
+- Add richer clobber lists instead of only `memory` or one arch-selected string.
+- Add target-specific tests for unsupported constraints and diagnostics.
+
+## Memory Operations
+
+- Add configurable alignment operands for `memcpy` and `memset`; current toy
+ always emits alignment `1`.
+- Add configurable `index` offset; current toy always passes offset `0`.
+- Add direct field access syntax for arbitrary records. `fieldtest()` only
+ stores and loads field index `1` of the internal `Pair`.
+- Add array indexing over array lvalues, not just pointer indexing.
+- Add record and array copies that lower through normal source constructs.
+
+## Debug Location Coverage
+
+- Add tests that verify `cfree_cg_set_loc` flows through functions, scopes,
+ locals, params, instructions, and data definitions.
+- Add multi-line expressions and initializers so sticky source locations are
+ exercised across nested parse/codegen calls.
+
+## Test Harness Work
+
+- Keep each new feature paired with a small toy corpus case under
+ `test/toy/cases`.
+- Prefer one feature family per case so CG regressions point at a small surface.
+- Run targeted toy cases across representative architectures when the feature
+ touches ABI, relocation, TLS, or inline asm behavior.