Audit for release 0.1.2#346
Merged
Merged
Conversation
dyld resolves an image carrying LC_DYLD_INFO exclusively through the export trie; the executable's dynamic exports (the every-global set a dlopen'd module binds against) only reached the classic symtab, so a flat-namespace lookup from a plugin failed despite the nlist entry. Append each dynamic export (text and data) to the trie at its image-base-relative address, matching the shared-library export path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both backends lower _Thread_local access with the local-exec TLS model, whose TP-relative offsets are valid only in the executable's static TLS block; baked into ET_DYN they silently address another module's TLS. Fail the link until the general-dynamic model lands (TODO in the writer). Mach-O (TLV descriptors) and PE (TLS directory) resolve per-module and stay unaffected. fmt_link_err moves to the native-emit gate so the final-image writer can use it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Directive parsing truncated #define bodies at the first // even inside
a string literal ("http://x.com" recorded as "http:), and the #if
expression pre-pass had the same literal-unaware truncation. Comments
are removed once in translation phase 3 (C99 5.1.1.2) by the
literal-aware strip_c_comments; the redundant naive strips in
parse_directive and expand_for_if are removed, and the post-substitution
strip on #if expressions (which covers driver-predefined macro bodies
that never pass phase 3) now uses strip_c_comments as well. The
literal-unaware strip_comments helper is deleted.
A missing #include emitted a warning and continued with an empty body,
so the compile exited 0 on a miscompiled unit. It is now a hard error
naming the header and the search outcome, matching gcc/clang; the -H
trace still records the '! name (missing)' line first.
Regression tests: define_body_keeps_slashes_inside_string_literal,
if_string_comparison_keeps_slashes_inside_literal,
unknown_include_is_a_hard_error; quoted_include_form_is_recognised and
show_includes_records_resolution_trace adapted to the fatal behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PE/COFF orders the export name pointer table by name so the loader's import binding and GetProcAddress can binary-search it; the writer emitted declaration order, so a DLL whose exports were not declared alphabetically resolved names data-dependently (a miss surfaces as STATUS_ENTRYPOINT_NOT_FOUND). Emit the name pointers, the ordinal table, and the name strings in lexical order; AddressOfFunctions stays in declaration order and each name maps back through its ordinal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Data-family sections were byte-concatenated with sh_addralign ignored and the merged stream placed at an 8-aligned base, so a foreign object's 16-byte constant pool (clang/gcc -O2 `.rodata.cst16`) could land misaligned and legacy-SSE aligned loads fault at runtime. Pad the family blob to each section's alignment when parsing, carry the maximum through NativeObject / MergedNative / Build, align each unit's base in the merged `.data`, and place the data section at that alignment in the writers: ELF rounds `data_off` past `.got` (p_vaddr tracks p_offset), Mach-O rounds `__data`'s in-segment offset and records the section alignment; PE's `.data` RVA is already page-aligned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
repack_struct skipped bitfield members without reserving their storage,
so `struct { int flags:8; char c; } __attribute__((packed))` got
sizeof 1 with both members at offset 0 and a store to `c` corrupted
`flags`. The repack now lays bitfields out at the bit level with no
storage-unit padding -- the gcc/clang packed layout; C99 6.7.2.1p11
leaves the unit implementation-defined -- places the next non-bitfield
member at the following byte boundary, and re-derives each bitfield's
addressable unit as the smallest 1/2/4/8-byte window covering its bits,
slid back from the struct's tail so the read-modify-write span stays in
bounds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sion Two constant-initializer fixes sharing one member-dispatch path: A designator chain naming a char-array member (`.outer.inner = "s"`) stored the literal's address bytes with a mis-sized relocation instead of copying the array (C99 6.7.8p14). The per-member dispatch of fill_struct_fields is factored into fill_member_value and the chain now resolves to the final member's StructField, so a chain takes the same string / array / aggregate / bitfield shapes as a directly named member; the runtime local path gains the matching string-copy branch. An unbraced union member of a struct fell into the scalar path, which wrote the raw integer bits over the whole union with no conversion to the first named member's type (C99 6.7.8p17), and write_init_bytes wrapped the byte shift past 8 bytes, repeating the pattern at offset 8 (a debug build panicked on the shift). The unbraced union now routes through fill_struct_fields, which converts and stores one value into the first named member and stops; struct_flat_init_slots counts a union as its first member's slots; write_init_bytes zero-fills past the value's 8 significant bytes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… Windows Four libc-surface fixes: * stdlib.h declares strtof as returning double (c5 aliases float to double), but Linux bound it to libc's float-returning strtof and Windows to a strtof msvcrt does not export. Bind to strtod on every OS, matching the prototype and the existing macOS binding. * Darwin's ioctl and shm_open are variadic and read the third argument via va_arg -- from the stack on arm64 -- so the fixed-arity prototypes passed it in x2 where the callee never looks (EFAULT for FIONREAD, garbage permission bits for shm_open). Declare ioctl variadic (POSIX XSH; glibc agrees) and shm_open variadic on Darwin only (glibc uses the fixed POSIX shape). Add the Linux FIO* request codes. * msvcrt's _snprintf/_vsnprintf return -1 and omit the NUL on truncation; C99 7.19.6.5p3 / 7.19.6.12p3 require the untruncated length and a NUL for size > 0. The runtime now defines conforming snprintf/vsnprintf over _vsnprintf + _vscprintf, gated on the new __BADC_C5_CRT__ (set for hosted executables and shared libraries; native/EFI and freestanding images stay CRT-import-free), and stdio.h drops the msvcrt bindings for the standard spellings so they resolve against the runtime. The underscored _snprintf and _vsnprintf spellings keep msvcrt semantics. Regression fixtures: strtof_parses_float, ioctl_fionread_pipe, shm_open_mode_arg (all pass on macOS arm64; fixed-arity ioctl counter-check returns 3), snprintf_truncation_c99 (Windows lanes run the runtime shim; POSIX lanes lock the libc contract), plus PE-level tests for the import shape and the CRT-only gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One family of front-end fixes where a width/signedness/rank fact was dropped instead of tracked: * lexer: octal and binary literals route through the shared u/U/l/L suffix scanner instead of silently dropping the suffix. * expr: integer-literal typing at the 2^31 / 2^63 boundaries (C99 6.4.4.1p5); the signed range test was off by one, so 2147483648 stayed int and -2147483648 - 1 wrapped at 32 bits. * const_expr: ConstVal carries the value's C type; one fold (const_int_binop) applies the 6.3.1.8 conversions to every operator, so unsigned 64-bit /, %, >>, and relationals fold unsigned. Division by zero in an evaluated constant expression is diagnosed (6.6p4) instead of folding to 0, unevaluated ?: arms and short-circuited operands stay accepted, and LLONG_MIN / -1 wraps instead of aborting the compiler. * expr: signed >> takes the promoted left-operand type (6.5.7p3), not int, so a 64-bit shift result is not re-narrowed. * expr: a ternary with differing arithmetic integer arms converts both arms to the usual-arithmetic-conversions common type (6.5.15p5) instead of taking the else arm's type. * expr + walk: compound /= and %= select signedness from the common type of both operands (6.5.16.2p3) and mask narrow unsigned operands like the plain-binary lowering. * walk: switch case labels convert to the promoted controlling type (6.8.4.2p5), so case 0x80000000: matches an int INT_MIN. * decl_base + aggregate: base-first specifier orders (int long, int unsigned, char unsigned, double long) fold the trailing modifiers into the base type (6.7.2p2) instead of discarding them; trailing inline / _Noreturn set their pending flags. Regression fixtures (compiled, executed, parity-checked) plus lexer suffix and const-expr diagnostic unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The release-version bump changes OUTPUT_MARKER in every emitted object, so the byte+reloc golden table pins to the new version (the mismatch reproduces at the bump commit with no compiler change). New snapshots cover the ten fixtures added with the front-end and initializer fixes; math_classify / libc_fp_classify pick up the return-narrowing the promoted shift type now requires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dir The fatal missing-include diagnostic surfaced two latent gaps: macOS code includes <TargetConditionals.h> under __APPLE__ (bundle the platform-dispatch macros with macOS values, zeros elsewhere), and the demo's stdin pipeline never could resolve shell.c's quoted sqlite3.h include (pass the demo directory as -I, as any compiler needs for stdin input). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fatal missing-include diagnostic surfaced two more corpus gaps: C99 7.6 fenv (rounding-mode encodings are per-arch on Linux/macOS, UCRT abstract values on Windows; legacy msvcrt predates C99 fenv so the bindings target ucrtbase) and the underscored fd-level io.h Windows sources include directly. fegetenv and the environment object are TODO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de lookup Windows sources reach <direct.h> and <intrin.h> under _MSC_VER, and spell bundled headers in any case (<Windows.h>): resolve embedded headers case-insensitively for Windows targets, matching the platform's filesystems. intrin.h binds the msvcrt byte-swap exports and defines the compiler-reordering fence as an opaque no-op call; the remaining intrinsics stay undeclared so their use fails loudly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…c surfaced X11/Xresource.h routes to Xutil.h, which already carries the Xrm types and prototypes; minwinbase.h / minwindef.h / sdkddkver.h follow the existing winuser.h forward-to-windows.h convention. All were silently empty includes before missing headers became fatal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
curl's Windows RNG includes <bcrypt.h> (the bound CNG surface already lives in the bundled <windows.h>) and defines __MINGW32__, whose internal <_mingw.h> glue the demo resolves with an empty-guard shim. All demo translation units now cross-compile for windows-x64 with no missing includes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The volatile qualifier was parsed and discarded, so every optimization
level violated C99 5.1.2.3p2 / 6.7.3p6: an unused volatile read was
deleted even at -O0, mem2reg promoted volatile locals (reverting a
store made between setjmp and longjmp, 7.13.2.1p3), and store_forward
forwarded across volatile accesses.
The type tag gains VOLATILE_BIT, orthogonal to UNSIGNED_BIT and
stripped by strip_unsigned; the declaration-specifier, declarator,
pointer-level, and cast parses set it. Inst::{Load, Store, LoadLocal,
StoreLocal} carry a volatile flag the walker sets from the accessed
lvalue's tag (locals, globals, TLS, derefs, members, bitfields,
initializers, read-modify-writes). Gated consumers: the builder's
load-local CSE, is_pure_inst / is_dead_pure (an unused volatile load
emits at every level), mem2reg promotion, slot coalescing,
store_forward, index_fold, and struct_return_reg. A volatile
read-modify-write no longer re-reads the object for its result value;
it narrows the stored value in a register. An aggregate copy of a
volatile object stays unmarked (TODO on Inst::Mcpy).
Regression: volatile_setjmp_longjmp.c, volatile_ptr_alias_loop.c,
volatile_unused_read.c on the native and JIT parity lists, plus
SSA-level tests in mem2reg, store_forward, and reg_alloc.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixtures that declare volatile now keep their accesses in memory as 6.7.3 requires; the drift is confined to those fixtures plus the three new volatile regression fixtures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C99 6.5.16.1p1 lets the target add qualifiers; passing a plain struct pointer to a volatile-qualified parameter warned as an incompatible struct type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The walker's parameter-prologue loops masked only UNSIGNED_BIT off the declared tag, so a VOLATILE_BIT-carrying tag never compared equal to its band: a `volatile double` parameter was not marked FP and was seeded as an integer ParamRef reading a stale integer argument register while the caller passed the value in an FP register, a `volatile float` lost its narrow entry copy, and a by-value `volatile struct` lost its entry classification the same way. All five sites now use strip_unsigned, and the mislabeled `is_pointer` UNSIGNED_BIT test is renamed to what it checks: unsigned parameters keep the full-width store/load; pointer tags land in the I64 default arm by band value. Regression: volatile_param_classes.c on the native and JIT parity lists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
splice_multi_block emitted the caller blocks after the splice point in a single pass, so a use of the inlined call's result (mapped only when the callee's Return is spliced) resolved to NO_VALUE and the value was silently dropped at -O. Run the emission to a fixed point, as the flat single-block splice already does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The post-coloring Shl K / Shr K fold reads the Shl source's place when the Shr is emitted, past the source's live range; an intervening definition colored onto the same place (or an inst with fixed emit-level clobbers) invalidates the read. Fold only when the pair shares a block, every intervening inst is clobber-free, and no intervening definition occupies the source's place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A DataReloc target of the &arr[N] address constant equals the next object's start offset, so compaction marked and rebased the wrong object; with bss segregation the neighbor moved and the stored end pointer went stale. Carry the pointed-at object's start offset (target_anchor) on the reloc and resolve intervals through it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The address-taken trampoline for a variadic libc binding re-pushed only the declared prefix plus one register argument, dropping the variadic tail the caller marshalled per the host convention (stack region on AAPCS64-Darwin, al on System V). Route variadic bindings through the frameless tail jump already used for FP-touching bindings; it forwards every register class and the stack unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parse_block_typedef recorded the prototype only for the function-type shape; a `typedef RET (*NAME)(args)` declarator left the typedef symbol without params/is_variadic, so indirect calls through variables of that typedef marshalled a variadic tail as fixed register arguments. Mirror the file-scope branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
emit_atomic_cas / emit_atomic_rmw marshal operands into the borrowed x9..x12 in sequence; an operand the allocator placed in one of those registers could be read after an earlier move overwrote it (a compare-exchange then reported success without exchanging). The values are already saved at [sp..sp+32] before marshalling, so read such operands from their saved slots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The windows-arm64 setjmp lowering is an Inst::Intrinsic, which the allocator's call-crossing tests did not match, so a value live across the setjmp site could sit in a caller-saved register that longjmp does not restore (the jmp_buf covers x19-x28, x29, sp, d8-d15 only), violating C99 7.13.2.1p3. Treat the setjmp site like a call in values_live_across_calls and promote_calls_after_def_to_classes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marshal_args loaded a mixed SSE/INTEGER aggregate's integer eightbyte into its target GPR before the scalar parallel move consumed that GPR as a source (System V AMD64 3.2.3 classifies per eightbyte; the integer targets are argument registers). Route the aggregate's base through its first integer eightbyte register inside the parallel move and load the eightbytes afterwards, base register last; all-SSE aggregates keep the early scratch-based loads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Win64 and System V variadic call arms captured a <=16-byte aggregate return from rax:rdx unconditionally; an SSE eightbyte arrives in xmm0/xmm1 (System V AMD64 3.2.3) exactly as on the non-variadic path. Share one classified store across all call shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The System V va_copy lowering materialized the destination pointer into rcx, which sits in the allocator's caller pool and may hold a value live across the intrinsic (va_copy is not a call). Route both pointers through the reserved r10/r11 scratches and borrow the copy temporary around a push/pop pair, as emit_mcpy already does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five conformance fixes plus one input-handling fix:
* Object-like alias chains (`#define A B` / `#define B C` / `#define
C B x`) paint every intermediate name for the rescan (C99
6.10.3.4p2); the chain walk previously painted only the head, so
the rescan re-fired an intermediate and duplicated tokens. A
revisited name now ends the walk instead of looping to the cap.
* `__has_include("h")` resolves its quoted form against the including
file's directory and `__has_include_next` resumes past the current
file's search-path entry, exactly as the matching directives do
(C99 6.10.2p2); both previously ran a plain angle-include search.
* `#if` character constants decode hexadecimal and octal escape
sequences (C99 6.4.4.4) and sign-extend a single-character constant
on signed-plain-char targets, matching the lexer.
* `#if` ternaries apply the usual arithmetic conversions across both
arms (C99 6.5.15p5), so `(1 ? -1 : 0u) > 0` compares unsigned.
* An `#elif` / `#else` / `#endif` met while joining macro-argument
lines with no locally opened frame belongs to the conditional
enclosing the macro call; it now updates the outer stack (shared
helpers keep both walks identical) instead of being swallowed and
leaving the block unterminated.
* A UTF-8 byte-order mark opening a file is skipped, following gcc
and clang.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A byte outside the token grammar (`$`, `@`, backtick, any non-ASCII byte outside a literal) was silently discarded, so the parse could re-synchronize into a different program (`*$p` compiled as `*p`). Such bytes are now a lex error quoting the offending byte. C99 6.4 white-space (VT, FF) and stray NULs stay ignored, following gcc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* `prologue_ends` is keyed by the function's merged entry offset instead of its bare name: two units' same-named static functions each keep their own post-prologue anchor, so Win-x64 .pdata and the DWARF CFA no longer describe a framed static as a frameless leaf copied from the first unit. * An unresolved STB_WEAK UNDEF with no dylib routing resolves to address 0 (ELF practice) instead of becoming a required import against dylib 0: branches become no-ops (the GNU linkers' AArch64 handling), address materializations produce 0 so the `if (fn) fn();` guard reads null, and `.data` pointer slots take 0 + addend. Unsupported instruction shapes are a diagnostic. * GNU version-name strings appended to `.dynstr` re-pad it to 8, so `.hash` / `.gnu.version` keep their claimed sh_addralign (gABI). * Unit bss bases and the merged file image pad to 16 so each object's compaction-preserved alignment residue survives the link. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* `_Alignas(N)` / `__attribute__((aligned(N)))` / `__declspec (align(N))` on a file-scope or static block-scope object is honored up to 16: the object's data offset is padded, the unit records `data_align = 16` (threaded through `Program` into the writers and the ET_REL `.data` sh_addralign), and the compaction residue rules carry the placement into the final image. Larger requests, automatic objects above the 8-byte slot alignment, and member alignment above 8 are diagnostics -- never a silent drop (C11 6.7.5). * `int x; extern int x;` keeps the tentative definition (C99 6.2.2p4 / 6.9.2p2): an extern redeclaration after a file-scope definition -- tentative, initialized, or the array form -- no longer marks the symbol extern-only, so the TU still defines it and the link succeeds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gma warning * Static-archive members join the link on demand: a member is included iff it defines a symbol the link still leaves undefined, iterated to a fixpoint (matching the documented model and SysV ar practice). Unreferenced members no longer fail a valid link with their unrelated undefined or duplicate symbols. * The auto-include retry no longer overrides a user definition: when a multi-TU link defines a name the retry bound to a bundled-header libc binding, the referencing source is recompiled with the name as a C89 6.3.2.2 implicit `extern int name();` so the call resolves against the user's definition. Names nothing defines keep the auto-include. * `-c` and `--ar` warn when `#pragma subsystem` / `#pragma entrypoint` is dropped: the pragma rides the in-memory program of the invocation that links, and an ET_REL object carries neither, so a GUI TU compiled to an object no longer silently links as a console image. `Program::entry_pragma` carries the literal pragma value, distinct from the resolved `entry_name`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Remove bindings whose target symbol the named library does not export, so a use fails loudly at link instead of aborting at load: macOS clock_nanosleep, fexecve, mremap, and the sched_setparam / sched_getparam / sched_setscheduler / sched_getscheduler / sched_rr_get_interval family (dlsym-verified against libSystem), and glibc ctermid_r. * Windows: CharLowerW / CharUpperW bind to user32.dll and UuidCreate / UuidCreateSequential to rpcrt4.dll (their documented homes; kernel32 fails at load). _ftelli64 and the _byteswap_* family bind to ucrtbase (absent from the legacy msvcrt.dll); ctime_s binds to the 64-bit-time_t export `_ctime64_s` like its localtime_s / gmtime_s neighbors; the `_getpid` spelling is arch-gated to ucrtbase on arm64 like unistd.h's getpid. * Windows x64 binds tzname / timezone / daylight as msvcrt data imports (the `environ` treatment), so a read after `tzset()` sees the CRT's values instead of dead zero slots; the runtime's local slots stay Linux-only (COPY-relocation targets). arm64 keeps safe local slots (empty names, zero offset) with a TODO until the msvcrt.dll surface is verified on an arm64 box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Probing both boxes shows msvcrt.dll exports _tzname/_timezone/_daylight as data on arm64 as well as x64, so the arm64 fallback slots go away (they would now collide with the bindings), and the byte-swap trio lives in the UCRT on both architectures. tzset outputs verified live on kromyrzen and kromarm00. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compiler::new follows the host; the environ binding shape is per-OS (macOS GOT data import, Linux runtime definition, Windows per-arch), so the test compiled differently on a Windows host and failed before reaching the diagnostic under test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fatal missing-include diagnostic surfaced every silently-empty
include across the tcl / cpython / curl lanes. Each is resolved by
providing the platform-faithful surface -- sys/file.h flock,
linux/{auxvec,fs,limits,memfd,sched,wait}.h and sys/{auxv,pidfd}.h
subsets, net/ethernet.h, mach/* and sysctl, the Windows framework
forwards -- never by turning a HAVE_ config claim off, so the demos
exercise exactly what they did before. Adds the macOS deployment-target
predefine and Annex-K memset_s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The octal digit loop used checked multiply where the binary and hex paths already wrap, so the octal spelling of ULLONG_MAX panicked a debug build; wrap so debug and release agree and the type picker types the literal from the bit pattern (C99 6.4.4.1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The recursive-descent parser had no depth limit, so deeply nested parentheses, declarators, initializer braces, or blocks overflowed the native stack and aborted the compiler (SIGABRT at ~250 paren levels in a debug build). Every recursive entry point (statements, expressions, constant expressions, declarators, initializer lists) now runs through a shared counter bounded at 1024 levels -- well above the 63-level minimum of C99 5.2.4.1 -- and reports "<construct> nesting too deep". The CLI driver runs on a thread with a 256 MiB stack reservation so the diagnostic fires before the native stack runs out in debug builds, which spend tens of KiB of stack per nesting level. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The float arm of a conditional expression stored its result through
LocalAddr + Store, which marked the synthetic merge slot address-taken
and excluded it from mem2reg promotion. Both backends lower the fused
StoreLocal / LoadLocal { F32 } in a single instruction (narrowing an
f64 arm per C99 6.3.1.5), so the address-taken form was unnecessary.
Use the fused ops for float as for double; the merge slot is now
promoted and the single-precision value is preserved (C99 6.5.15).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Correct comments that describe behavior the code has moved past:
* x86_64 param_reg_stack_split: register/stack placements need not
be contiguous (System V AMD64 MEMORY-class or Win64 overflow
aggregates interleave them); no assertion gates this.
* x86_64 detect_tail_call: callee-saved use is allowed (the
caller-saved arg window is disjoint from gpr_used), and any
LocalAddr, not only a negative slot, disqualifies the tail call.
* aarch64 move_call_result: an FP scalar result rides d0 (AAPCS64
6.4.2), an integer result x0 (6.4.1); emit_return leaves it in d0.
* reg_alloc last_use: document its live consumers (the call-spanning
interval and coalescing tests), and add the GotoIndirect arm to
the two terminator walks that lacked it -- redundant with the
exit_acc bump but uniform with the liveness and use-count walks.
* elf_reloc: .rela.text is emitted, so cross-TU calls link.
* mach_o: __thread_data carries TLS init bytes once tls_init_size is
non-zero; __DWARF is emitted for executables and dylibs at its own
vmaddr slot.
* pe: .reloc follows any absolute pointer (TLS directory and
address-of-static relocations); the adrp+add pair is PC-relative
and needs no base relocation.
* pe / program / compiler: a _Thread_local initializer raises
tls_init_size and lands in .tdata.
* link / synth_build: multi-object TLS is resolved via per-unit
TPOFF / descriptor rebasing; the Windows/aarch64 TEB path records
a fixup for every access.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ive/freestanding input handling setenv now honors the POSIX overwrite flag via a runtime shim (msvcrt _putenv_s always overwrites); the Windows JIT resolves symbols through the same module-handle set bind_imports uses; the --freestanding entry check runs after inputs are parsed; and an archive-only invocation links its members instead of reporting no files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e shim The runtime-shim setenv definition resolved only on the compiled path; under the interpreter and JIT the unbound symbol errored (a fixture's setenv roundtrip failed on Windows). Define setenv inline in <stdlib.h> so all three paths share one definition: probe getenv, then _putenv_s. Overwrite=0 leaves an existing binding untouched (verified on the Windows box). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erpreter The inline setenv wrapper calls _putenv_s, which the interpreter's libc bridge did not handle; a setenv roundtrip fixture failed under --interp on a Windows host. Dispatch _putenv_s to the host with force overwrite (its msvcrt semantics), matching the setenv arm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n surfaces The macOS CI lanes need three more real system headers the fatal missing-include diagnostic surfaced: sys/cdefs.h (the BSD declaration- decoration macros, tcl's unix port), plus the minimal CoreFoundation and SystemConfiguration surfaces curl's macos.c reaches for (SCDynamicStoreCopyProxies, CFRelease); framework symbols bind under their underscore-prefixed Mach-O names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
emit_load hard-coded offset 0 for the F32/F64 arms while emit_store passed the folded displacement to the immediate-offset encoders. index fold declined the floating kinds, so disp was always 0 there and the drop stayed masked; extend the constant-offset fold to F32/F64 (the scaled-index fold stays integer-only) and route disp through the FP load encoders. The four FP ldr/str encoders now share enc_ldst_scaled, so an out-of-range or misaligned immediate is caught uniformly. Call-site SP adjustments used the raw 12-bit enc_add_imm/enc_sub_imm on plan.scratch_bytes and the 16-byte-stride push total; an outgoing area past 4095 bytes shifted the immediate into the LSL#12 bit and adjusted SP by the wrong multiple. Route every call-site adjustment through emit_sub_sp_imm/emit_add_sp_imm, the 24-bit split the prologue already uses. Two consequences the wide-argument fixtures surfaced: the argument planner's fp_arg_mask shift overflowed for arguments past 31 (the walker only sets the mask for the first 32), and emit_call_indirect blindly fell back to x9 for the target pointer when every scratch held a live arg source. Guard the shift and stage the pointer in a reserved stack cell (host-ABI branch) or capture it after the pushes consume the sources (c5-stack branch). Fixtures fp_load_folded_disp.c, call_sp_adjust_imm12_overflow.c, and indirect_call_target_scratch_exhausted.c registered in the jit, native, and both native_elf lanes; two byte-scan codegen tests lock the folded FP encoding and the shifted-12 SP split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FP loads now fold the displacement in place (disp=N replaces a separate add) and call sites use the 24-bit SP-adjust split, changing the emitted shapes the affected fixtures pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Windows CRT emits \r\n for \n on stdout, so the exact-bytes comparison failed on the Windows lanes; strip CR before comparing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…port symbol, harden silent fallbacks #340: --export-all/--export-data now reach PE executables. `.edata` is emitted whenever the image exports anything (pragma set or an executable's dynamic exports), and the export directory resolves each name to its runtime RVA (text or data). Previously the flags no-op'd for PE executables so GetProcAddress returned NULL for every host symbol. #341: a Mach-O data import (bound through the GOT, no PLT trampoline) no longer gets a local text symbol at code offset 0 (the first function's address), which mislabeled backtraces and breakpoints. `Build`'s per-import trampoline offsets become `Vec<Option<usize>>` so the writers key each local symbol by import index, not by an ordering contract the synth path does not uphold; a `None` slot emits no text symbol. #333: convert five silent fallbacks to diagnostics. * pe.rs pad_to overshoot -> hard error (layout-drift guard, replacing a release-compiled-out debug_assert). * elf.rs export whose ent_pc misses pc_to_native -> error, matching the Mach-O/PE writers instead of dropping it from .dynsym. * mach_o.rs __tlv_bootstrap ordinal via one shared helper for the bind stream and the nlist entry; error when no linked dylib is libSystem rather than guessing ordinal 1. * synth_build.rs import->dylib routing miss defaults to 0 only for a flat-lookup import or a single-dylib image; a miss with several dylibs is an error, not a silent misroute. * link.rs merged import->dylib map rejects a cross-unit routing conflict and a per-unit dylib index out of range instead of first-writer-wins + unwrap_or(0). Regression tests: macho_data_import_gets_no_bogus_local_text_symbol (tests/linker.rs), plus writer/unit tests for each guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tcl's macOS notifier includes <os/lock.h> (os_unfair_lock) and quickjs-libc includes <crt_externs.h> (_NSGetEnviron); both bind to libSystem under the underscore-prefixed Mach-O names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add C99 6.7.6.2 variable-length arrays (block-scope, single dimension), closing the conformance gap where any non-constant array dimension was rejected at parse time. Frame mechanism: reuse the existing per-frame alloca arena. A VLA declaration evaluates its runtime element count, multiplies by the element size, and allocates from the arena via Intrinsic::Alloca; the base pointer and byte count are stored in two hidden frame slots. A function that declares a VLA sets the alloca-top slot, which already disables mem2reg promotion for the whole function (the runtime-sized, address-taken array is never promoted) and blocks inlining. Reclamation: two new intrinsics, AllocaSave and AllocaRestore, read and write the arena top. parse_block_stmt brackets a VLA-declaring block with VlaScopeEnter / VlaScopeExit so the storage is reclaimed on block exit and, for a loop body, on every iteration -- the fixed arena does not grow unboundedly. Type representation: Symbol gains is_vla + the two hidden-slot indices. A VLA identifier decays through Expr::VlaBase (a load of the base pointer); sizeof of a VLA lowers to Expr::VlaSizeof (a load of the byte count) per 6.5.3.4p2. Indexing, deref, pointer arithmetic, and address-of then reuse the existing pointer paths. IN scope: single-dimension block-scope VLA; runtime sizeof; a dimension from a function argument; per-iteration scope reclamation; a VLA parameter (adjusted to a pointer, 6.7.6.3p7). __STDC_NO_VLA__ is no longer predefined. Rejected cleanly (no miscompile): multidimensional VLAs (runtime element stride); file-scope / variably-modified file objects; a VLA with an initializer (6.7.8p3). Regressions: vla_basic_sum, vla_runtime_sizeof, vla_size_from_arg, vla_scope_reclaim_loop, vla_param_decay (native + native_elf + native_elf_x64 + PE fixture tables and a JIT lane in tests/vla.rs); vla_multidim_rejected, vla_file_scope_rejected, vla_initializer_rejected assert the diagnostics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… build The Mach-O TLS path now requires libSystem in the dylib set (a wrong ordinal guess would bind __tlv_bootstrap to the wrong dylib). Emitting a host-compiled program for macOS from a non-macOS host lacks libSystem; compile for each target so its own dylib bindings are present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The vla_*_rejected.c fixtures assert a clean diagnostic for unsupported VLA forms, so the every-fixture-compiles smoke must not expect them to build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes for the 2026-07-01 pre-release audit: all 32 release blockers and all 28 high-severity findings, each with a regression test. Validated per wave on the three local boxes (cargo test debug+release, nine demo smokes) plus the BADC_MAX_GPR=2/3 pressure matrix on x64.
Highlights:
volatilecarried through the IR with pass gating._Alignashonored to 16 or diagnosed.--interp, and--jit: undefined externs are clean errors, the JIT reaches bound host data, atexit and exit-status semantics match libc.Release blockers: closes #271, closes #272, closes #273, closes #274, closes #275, closes #276, closes #277, closes #278, closes #279, closes #280, closes #281, closes #282, closes #283, closes #284, closes #285, closes #286, closes #287, closes #288, closes #289, closes #291, closes #293, closes #295, closes #298, closes #300, closes #302, closes #304, closes #306, closes #308, closes #311, closes #313, closes #315, closes #317.
High severity: closes #290, closes #292, closes #294, closes #296, closes #297, closes #299, closes #301, closes #303, closes #305, closes #307, closes #309, closes #310, closes #312, closes #314, closes #316, closes #318, closes #320, closes #321, closes #322, closes #323, closes #324, closes #325, closes #326, closes #327, closes #328, closes #329, closes #330, closes #331.
Medium severity: closes #332, closes #333, closes #334, closes #335, closes #336, closes #337, closes #338, closes #339, closes #340, closes #341, closes #342, closes #343, closes #344, closes #345.
The medium set adds six further batches, each with a regression test and box-validated per increment:
Not in scope here: #319 (aarch64 non-HFA struct ABI).
🤖 Generated with Claude Code