diag: WINEDEBUG=+seh gate crash triage (temp)#5
Draft
Guikingone wants to merge 251 commits into
Draft
Conversation
Auto-free native stream fds (kind 1 → close) and unfinalized HashContext handles (kind 2 → elephc_crypto_free) when their Mixed box is released at scope exit. The resource-kind subtype lives in the high payload word of the Mixed cell; kind 0 resources (synthetic user-wrapper handles) are skipped. Wiring: - __rt_mixed_free_deep: tag-9 dispatch with kind 0/1/2 on AArch64 + x86_64 - __rt_hash_ctx_free: new runtime helper calling elephc_crypto_free - _elephc_crypto_free_fn slot + publish entry - hash_init/hash_copy box with kind=2, fopen boxes with kind=1 Closes the fd leak (unclosed fopen) and HashContext leak (unfinalized hash_init) documented in ROADMAP v0.26.x.
Emit each __rt_* runtime helper into its own text section on Linux (.section .text.<name>) so that --gc-sections can eliminate unreachable helpers at link time. Add -Wl,--gc-sections to the Linux linker for executables (cdylibs are unaffected). macOS dead stripping is deferred: .subsections_via_symbols breaks conditional branches to local labels in the runtime, and per-symbol Mach-O sections require underscore-aware symbol handling that is not trivial to retrofit. The runtime .o remains monolithic on macOS for now; Linux binaries benefit immediately. The runtime cache key (FNV-1a hash of generated assembly) automatically invalidates when the section directives change, so no cache migration is needed. 7 new tests verify that programs using regex, hash, classes, fopen, arrays, and exceptions still run correctly after dead stripping.
…-write # Conflicts: # src/ir/validator.rs
Implements array_is_list, array_key_first/last, array_replace and array_replace_recursive, array_diff_assoc and array_intersect_assoc, array_merge_recursive, array_walk_recursive, array_find/any/all (PHP 8.4), array_udiff/uintersect, and array_multisort through EIR lowering, reusing the shared __rt_* runtime helpers (the legacy direct-AST emitters are not touched). Hash-based set operations accept associative arrays and scalar-element indexed arrays (converted to integer-keyed hashes via __rt_array_to_hash; result keys/values widen to mixed for heterogeneous inputs). The predicate/comparator builtins use the EIR descriptor-callback machinery (string, function, and non-capturing closure callbacks). All are target-aware (ARM64 + x86_64) with codegen and error tests, an examples/array-parity demo, and docs/php/arrays.md, ROADMAP, and CHANGELOG updates.
Add the public serialize()/unserialize() builtins covering scalars, nested arrays, and objects — including the __serialize/__unserialize/ __sleep/__wakeup magic methods and r:/R: object back-references (repeated objects rebuild as one shared instance) — byte-for-byte compatible with PHP's wire format. Persist Phar/PharData state into the archive across all three formats (native PHAR, tar, zip), round-tripping across objects, processes, and the PHP interpreter: - global metadata and stub via setMetadata()/getMetadata()/hasMetadata()/ delMetadata() and setStub()/getStub() - PharFileInfo per-file metadata on $phar["entry"] - whole-archive tar compression for PharData::compress()/decompress() (sibling .tar.gz/.tar.bz2) - signatures for native PHAR, tar, and zip, including Phar::OPENSSL RSA-SHA1 signing with a PEM private key (verifiable by PHP), alongside MD5/SHA1/SHA256/SHA512; tar/zip use a .phar/signature.bin control entry Complete the ZIP phar surface: read entries written with a streaming data descriptor, read and write ZIP64 archives, and read/write traditional-PKWARE (ZipCrypto) encrypted entries via the setZipPassword() compiler extension (entries incl. the stub encrypted, signature.bin in the clear; cipher kept only for legacy compatibility). EIR/checker correctness fixes surfaced along the way: dispatch synthetic SPL methods reached through a mixed receiver or an object-iterator foreach value, decompress compress.zlib:// and compress.bzip2:// fopen wrappers on the EIR backend, infer mixed-receiver method calls as the union of candidate return types instead of int, preserve callee-saved r12-r15 in the x86 __rt_array_grow runtime helper, and route in-dir codegen fixtures through the EIR backend.
… key reuse Follow-up correctness fixes to the foreach Mixed-key array-write path: - __rt_array_set_mixed_key promotes the destination to a hash when an integer key is negative or past the logical end, instead of the packed indexed path dropping negative writes or zero-filling gaps. Sparse and negative integer keys from a rebuild now survive like PHP. Dual-arch (arm64 + x86_64). - Reset foreach_key_locals per function in with_local_storage_context so a foreach key name no longer leaks its boxed-Mixed-key classification into other functions that reuse the name as a genuine string key (which previously miscompiled the later direct string-key read). - Drop the foreach-key marker on a direct reassignment ($k = ...) so a later $dst[$k] is routed by $k's real type, matching the lowering and fixing a spurious "AssocArray -> Array(Mixed)" backend error. Adds 4 regression tests (sparse int, negative int, cross-function name reuse, key reassigned to string). Verified on macOS-aarch64, linux-x86_64, linux-arm64.
…h-key-write fix(eir): keep foreach string keys through Mixed-key array writes
An enum used as a class property type or a promoted-constructor-param type (`private Tag $tag`) failed with "Unknown type: Tag", even though the same enum resolves fine as a value and as a function-parameter type. Class member types are resolved during the class schema pass, which runs before the enum-processing phase populates `enums`. Class and interface names are pre-declared into `declared_classes` before that pass, but enum names were not, so `resolve_type_expr` fell through to "Unknown type". Pre-declare enum names into `declared_classes` after the final assignment (the earlier one is overwritten by the builtin-injection block), mirroring the insert already done later in `schema::enums`. Adds a codegen regression test for an enum as a promoted-constructor-param type.
Parameterize __rt_json_ftoa with the exponent marker char (w0/dil). serialize() now passes 'E' so exponential floats render as d:1.0E+20; like PHP, while json_encode keeps the lowercase 'e' JSON layout. Add regression tests for both.
…rsist # Conflicts: # CHANGELOG.md
…ta-persist feat(streams|zip): Phar metadata/stub + per-file metadata persistence, serialize()/unserialize(), and full ZIP phar support
…ndir, fd reuse) - elephc_crypto_final finalizes a clone, leaving the HashContext owned by its Mixed box so the kind-2 destructor frees it exactly once (no double-free / UAF on explicit-final-then-scope-exit or double-final) - popen pipes (kind 3 -> __rt_pclose, reaps child) and opendir streams (kind 4 -> __rt_closedir) get proper scope-cleanup destructors - explicit fclose/pclose/closedir stamp a -1 sentinel into the Mixed box so an already-released descriptor (whose fd may be reused) is never closed twice - refresh crypto/runtime docstrings, memory-model/runtime docs, ROADMAP - add regression tests for the above
Run scripts/docs/extract_builtins.py --render --force so the generated internals pages track the io.rs line shifts from the new scope-cleanup helpers. Only codegen_line link refs change.
…ope-cleanup fix(core): resource scope-cleanup for Mixed-boxed tag-9 resources
Prepare runtime helpers for macOS per-symbol dead stripping, where .subsections_via_symbols makes the Mach-O assembler reject any conditional branch whose target lies in another atom (another helper). - Rewrite cross-helper conditional branches (feof, fread, fwrite, fd_write, json_encode_array_int, buffer_len) as an inverted conditional skip over an unconditional branch, which may cross atoms. - Make the generator done/epilogue labels local instead of global so each generator helper stays one atom; this also fixes a latent cross-atom fall-through from __rt_gen_send_done into __rt_gen_send_epilogue. No behavior change: the generated code is equivalent on current builds.
Emit the macOS executable runtime object with .alt_entry internal labels and a .subsections_via_symbols footer so each __rt_* helper is a single collectable atom, then link with -dead_strip. Unreferenced runtime helpers are dropped from the binary, the macOS analogue of the Linux per-section --gc-sections path. cdylibs (pic) keep the full runtime. The emitter only marks named identifier labels .alt_entry; numeric (1:/2:) and L-prefixed labels are already assembler-local on Mach-O. Add a guard test that assembles the full all-features runtime under dead stripping so any new cross-atom conditional branch fails at build time.
Add a linking-page section describing the automatic, per-target runtime dead stripping (Linux --gc-sections, macOS -dead_strip) and a CHANGELOG entry for the completed feature.
Conflict resolution: - generators/mod.rs: took main's stackful-coroutine generators (issue illegalstudio#329), which moved the helpers into the `coro` submodule and made my old-generator label changes obsolete. The dead-strip assemble guard confirms the new coroutine helpers are already single-atom safe (no cross-atom conditional branches), so no re-port was needed. - CHANGELOG.md: kept both [Unreleased] sets — the dead-stripping entry plus main's new entries. Re-validated on the merged tree: full all-features runtime assembles under dead stripping, and dead_strip/generators/io/buffer/json tests pass.
The prior macOS approach marked every internal runtime label `.alt_entry`. Older `as` (the CI Xcode) rejects conditional branches to `.alt_entry` labels as "external", failing all macOS jobs. Rename internal labels to Mach-O assembler-local `L`-locals instead: valid conditional-branch targets on every toolchain that still do not start an atom under `.subsections_via_symbols`. The few helpers reached by an unconditional `b`/`bl` from another atom (`__rt_mixed_numeric_common`, `__rt_json_validate_string`/`_number`, `__rt_date_entry`) must stay real symbols so `-dead_strip` keeps their atom alive; emit those via the new `label_shared` (`.alt_entry`), which only unconditional branches ever target. Add a guard test asserting no internal `L__rt_*` label is referenced across atoms, the failure that segfaulted `foreach` over an associative array.
…d-stripping feat(core): runtime dead stripping via per-symbol sections and linker GC
# Conflicts: # docs/internals/builtins/_internal/__elephc_phar_list_entries.md # docs/internals/builtins/_internal/__elephc_phar_set_compression.md # docs/internals/builtins/array/count.md # docs/internals/builtins/class/function_exists.md # docs/internals/builtins/math/pi.md # docs/internals/builtins/misc/define.md # docs/internals/builtins/misc/defined.md # docs/internals/builtins/misc/empty.md # docs/internals/builtins/misc/phpversion.md # docs/internals/builtins/misc/unset.md # docs/internals/builtins/misc/var_dump.md # docs/internals/builtins/pointer/ptr.md # docs/internals/builtins/pointer/ptr_get.md # docs/internals/builtins/pointer/ptr_is_null.md # docs/internals/builtins/pointer/ptr_null.md # docs/internals/builtins/pointer/ptr_offset.md # docs/internals/builtins/pointer/ptr_read16.md # docs/internals/builtins/pointer/ptr_read32.md # docs/internals/builtins/pointer/ptr_read8.md # docs/internals/builtins/pointer/ptr_read_string.md # docs/internals/builtins/pointer/ptr_set.md # docs/internals/builtins/pointer/ptr_sizeof.md # docs/internals/builtins/pointer/ptr_write16.md # docs/internals/builtins/pointer/ptr_write32.md # docs/internals/builtins/pointer/ptr_write8.md # docs/internals/builtins/pointer/ptr_write_string.md # docs/internals/builtins/process/die.md # docs/internals/builtins/process/exec.md # docs/internals/builtins/process/exit.md # docs/internals/builtins/process/passthru.md # docs/internals/builtins/process/pclose.md # docs/internals/builtins/process/popen.md # docs/internals/builtins/process/readline.md # docs/internals/builtins/process/shell_exec.md # docs/internals/builtins/process/sleep.md # docs/internals/builtins/process/system.md # docs/internals/builtins/process/usleep.md # docs/internals/builtins/regex/preg_match.md # docs/internals/builtins/regex/preg_match_all.md # docs/internals/builtins/regex/preg_replace.md # docs/internals/builtins/regex/preg_replace_callback.md # docs/internals/builtins/regex/preg_split.md # docs/internals/builtins/spl/iterator_apply.md # docs/internals/builtins/spl/iterator_count.md # docs/internals/builtins/spl/iterator_to_array.md # docs/internals/builtins/spl/spl_autoload.md # docs/internals/builtins/spl/spl_autoload_call.md # docs/internals/builtins/spl/spl_autoload_extensions.md # docs/internals/builtins/spl/spl_autoload_functions.md # docs/internals/builtins/spl/spl_autoload_register.md # docs/internals/builtins/spl/spl_autoload_unregister.md # docs/internals/builtins/spl/spl_classes.md # docs/internals/builtins/spl/spl_object_hash.md # docs/internals/builtins/spl/spl_object_id.md # docs/internals/builtins/streams/fsockopen.md # docs/internals/builtins/streams/pfsockopen.md # docs/internals/builtins/streams/stream_bucket_append.md # docs/internals/builtins/streams/stream_bucket_prepend.md # docs/internals/builtins/streams/stream_filter_append.md # docs/internals/builtins/streams/stream_filter_prepend.md # docs/internals/builtins/string/addslashes.md # docs/internals/builtins/string/base64_decode.md # docs/internals/builtins/string/base64_encode.md # docs/internals/builtins/string/bin2hex.md # docs/internals/builtins/string/chop.md # docs/internals/builtins/string/chr.md # docs/internals/builtins/string/crc32.md # docs/internals/builtins/string/explode.md # docs/internals/builtins/string/grapheme_strrev.md # docs/internals/builtins/string/gzcompress.md # docs/internals/builtins/string/gzdeflate.md # docs/internals/builtins/string/gzinflate.md # docs/internals/builtins/string/gzuncompress.md # docs/internals/builtins/string/hash.md # docs/internals/builtins/string/hash_algos.md # docs/internals/builtins/string/hash_copy.md # docs/internals/builtins/string/hash_equals.md # docs/internals/builtins/string/hash_final.md # docs/internals/builtins/string/hash_hmac.md # docs/internals/builtins/string/hash_init.md # docs/internals/builtins/string/hash_update.md # docs/internals/builtins/string/hex2bin.md # docs/internals/builtins/string/html_entity_decode.md # docs/internals/builtins/string/htmlentities.md # docs/internals/builtins/string/htmlspecialchars.md # docs/internals/builtins/string/implode.md # docs/internals/builtins/string/inet_ntop.md # docs/internals/builtins/string/inet_pton.md # docs/internals/builtins/string/ip2long.md # docs/internals/builtins/string/lcfirst.md # docs/internals/builtins/string/long2ip.md # docs/internals/builtins/string/ltrim.md # docs/internals/builtins/string/md5.md # docs/internals/builtins/string/nl2br.md # docs/internals/builtins/string/number_format.md # docs/internals/builtins/string/ord.md # docs/internals/builtins/string/printf.md # docs/internals/builtins/string/rawurldecode.md # docs/internals/builtins/string/rawurlencode.md # docs/internals/builtins/string/rtrim.md # docs/internals/builtins/string/sha1.md # docs/internals/builtins/string/sprintf.md # docs/internals/builtins/string/sscanf.md # docs/internals/builtins/string/str_contains.md # docs/internals/builtins/string/str_ends_with.md # docs/internals/builtins/string/str_ireplace.md # docs/internals/builtins/string/str_pad.md # docs/internals/builtins/string/str_repeat.md # docs/internals/builtins/string/str_replace.md # docs/internals/builtins/string/str_split.md # docs/internals/builtins/string/str_starts_with.md # docs/internals/builtins/string/strcasecmp.md # docs/internals/builtins/string/strcmp.md # docs/internals/builtins/string/stripslashes.md # docs/internals/builtins/string/strlen.md # docs/internals/builtins/string/strpos.md # docs/internals/builtins/string/strrev.md # docs/internals/builtins/string/strrpos.md # docs/internals/builtins/string/strstr.md # docs/internals/builtins/string/strtolower.md # docs/internals/builtins/string/strtoupper.md # docs/internals/builtins/string/substr.md # docs/internals/builtins/string/substr_replace.md # docs/internals/builtins/string/trim.md # docs/internals/builtins/string/ucfirst.md # docs/internals/builtins/string/ucwords.md # docs/internals/builtins/string/urldecode.md # docs/internals/builtins/string/urlencode.md # docs/internals/builtins/string/vprintf.md # docs/internals/builtins/string/vsprintf.md # docs/internals/builtins/string/wordwrap.md # docs/internals/builtins/type/boolval.md # docs/internals/builtins/type/ctype_alnum.md # docs/internals/builtins/type/ctype_alpha.md # docs/internals/builtins/type/ctype_digit.md # docs/internals/builtins/type/ctype_space.md # docs/internals/builtins/type/floatval.md # docs/internals/builtins/type/get_resource_id.md # docs/internals/builtins/type/get_resource_type.md # docs/internals/builtins/type/gettype.md # docs/internals/builtins/type/intval.md # docs/internals/builtins/type/is_array.md # docs/internals/builtins/type/is_bool.md # docs/internals/builtins/type/is_callable.md # docs/internals/builtins/type/is_float.md # docs/internals/builtins/type/is_int.md # docs/internals/builtins/type/is_iterable.md # docs/internals/builtins/type/is_null.md # docs/internals/builtins/type/is_numeric.md # docs/internals/builtins/type/is_object.md # docs/internals/builtins/type/is_resource.md # docs/internals/builtins/type/is_scalar.md # docs/internals/builtins/type/is_string.md # docs/internals/builtins/type/settype.md # docs/php/builtins/misc/unset.md # docs/php/builtins/misc/var_dump.md # docs/php/builtins/pointer/ptr.md # docs/php/builtins/pointer/ptr_get.md # docs/php/builtins/pointer/ptr_is_null.md # docs/php/builtins/pointer/ptr_null.md # docs/php/builtins/pointer/ptr_offset.md # docs/php/builtins/pointer/ptr_read16.md # docs/php/builtins/pointer/ptr_read32.md # docs/php/builtins/pointer/ptr_read8.md # docs/php/builtins/pointer/ptr_read_string.md # docs/php/builtins/pointer/ptr_set.md # docs/php/builtins/pointer/ptr_sizeof.md # docs/php/builtins/pointer/ptr_write16.md # docs/php/builtins/pointer/ptr_write32.md # docs/php/builtins/pointer/ptr_write8.md # docs/php/builtins/pointer/ptr_write_string.md # docs/php/builtins/process/die.md # docs/php/builtins/process/exec.md # docs/php/builtins/process/exit.md # docs/php/builtins/process/passthru.md # docs/php/builtins/process/pclose.md # docs/php/builtins/process/popen.md # docs/php/builtins/process/readline.md # docs/php/builtins/process/shell_exec.md # docs/php/builtins/process/sleep.md # docs/php/builtins/process/system.md # docs/php/builtins/process/usleep.md # docs/php/builtins/regex/preg_match.md # docs/php/builtins/regex/preg_match_all.md # docs/php/builtins/regex/preg_replace.md # docs/php/builtins/regex/preg_replace_callback.md # docs/php/builtins/regex/preg_split.md # docs/php/builtins/spl/iterator_apply.md # docs/php/builtins/spl/iterator_count.md # docs/php/builtins/spl/iterator_to_array.md # docs/php/builtins/spl/spl_autoload.md # docs/php/builtins/spl/spl_autoload_call.md # docs/php/builtins/spl/spl_autoload_extensions.md # docs/php/builtins/spl/spl_autoload_functions.md # docs/php/builtins/spl/spl_autoload_register.md # docs/php/builtins/spl/spl_autoload_unregister.md # docs/php/builtins/spl/spl_classes.md # docs/php/builtins/spl/spl_object_hash.md # docs/php/builtins/spl/spl_object_id.md # docs/php/builtins/streams/fsockopen.md # docs/php/builtins/streams/pfsockopen.md # docs/php/builtins/streams/stream_bucket_append.md # docs/php/builtins/streams/stream_bucket_prepend.md # docs/php/builtins/streams/stream_filter_append.md # docs/php/builtins/streams/stream_filter_prepend.md # docs/php/builtins/string/addslashes.md # docs/php/builtins/string/base64_decode.md # docs/php/builtins/string/base64_encode.md # docs/php/builtins/string/bin2hex.md # docs/php/builtins/string/chop.md # docs/php/builtins/string/chr.md # docs/php/builtins/string/crc32.md # docs/php/builtins/string/explode.md # docs/php/builtins/string/grapheme_strrev.md # docs/php/builtins/string/gzcompress.md # docs/php/builtins/string/gzdeflate.md # docs/php/builtins/string/gzinflate.md # docs/php/builtins/string/gzuncompress.md # docs/php/builtins/string/hash.md # docs/php/builtins/string/hash_algos.md # docs/php/builtins/string/hash_copy.md # docs/php/builtins/string/hash_equals.md # docs/php/builtins/string/hash_final.md # docs/php/builtins/string/hash_hmac.md # docs/php/builtins/string/hash_init.md # docs/php/builtins/string/hash_update.md # docs/php/builtins/string/hex2bin.md # docs/php/builtins/string/html_entity_decode.md # docs/php/builtins/string/htmlentities.md # docs/php/builtins/string/htmlspecialchars.md # docs/php/builtins/string/implode.md # docs/php/builtins/string/inet_ntop.md # docs/php/builtins/string/inet_pton.md # docs/php/builtins/string/ip2long.md # docs/php/builtins/string/lcfirst.md # docs/php/builtins/string/long2ip.md # docs/php/builtins/string/ltrim.md # docs/php/builtins/string/md5.md # docs/php/builtins/string/nl2br.md # docs/php/builtins/string/number_format.md # docs/php/builtins/string/ord.md # docs/php/builtins/string/printf.md # docs/php/builtins/string/rawurldecode.md # docs/php/builtins/string/rawurlencode.md # docs/php/builtins/string/rtrim.md # docs/php/builtins/string/sha1.md # docs/php/builtins/string/sprintf.md # docs/php/builtins/string/sscanf.md # docs/php/builtins/string/str_contains.md # docs/php/builtins/string/str_ends_with.md # docs/php/builtins/string/str_ireplace.md # docs/php/builtins/string/str_pad.md # docs/php/builtins/string/str_repeat.md # docs/php/builtins/string/str_replace.md # docs/php/builtins/string/str_split.md # docs/php/builtins/string/str_starts_with.md # docs/php/builtins/string/strcasecmp.md # docs/php/builtins/string/strcmp.md # docs/php/builtins/string/stripslashes.md # docs/php/builtins/string/strlen.md # docs/php/builtins/string/strpos.md # docs/php/builtins/string/strrev.md # docs/php/builtins/string/strrpos.md # docs/php/builtins/string/strstr.md # docs/php/builtins/string/strtolower.md # docs/php/builtins/string/strtoupper.md # docs/php/builtins/string/substr.md # docs/php/builtins/string/substr_replace.md # docs/php/builtins/string/trim.md # docs/php/builtins/string/ucfirst.md # docs/php/builtins/string/ucwords.md # docs/php/builtins/string/urldecode.md # docs/php/builtins/string/urlencode.md # docs/php/builtins/string/vprintf.md # docs/php/builtins/string/vsprintf.md # docs/php/builtins/string/wordwrap.md # docs/php/builtins/type/boolval.md # docs/php/builtins/type/ctype_alnum.md # docs/php/builtins/type/ctype_alpha.md # docs/php/builtins/type/ctype_digit.md # docs/php/builtins/type/ctype_space.md # docs/php/builtins/type/floatval.md # docs/php/builtins/type/get_resource_id.md # docs/php/builtins/type/get_resource_type.md # docs/php/builtins/type/gettype.md # docs/php/builtins/type/intval.md # docs/php/builtins/type/is_array.md # docs/php/builtins/type/is_bool.md # docs/php/builtins/type/is_callable.md # docs/php/builtins/type/is_float.md # docs/php/builtins/type/is_int.md # docs/php/builtins/type/is_iterable.md # docs/php/builtins/type/is_null.md # docs/php/builtins/type/is_numeric.md # docs/php/builtins/type/is_object.md # docs/php/builtins/type/is_resource.md # docs/php/builtins/type/is_scalar.md # docs/php/builtins/type/is_string.md # docs/php/builtins/type/settype.md # scripts/docs/builtin_registry.json # tests/codegen/arrays/mod.rs
The hash-descent path stashed the iterator value low word at [rbp-24], which aliases the pushed callee-saved r14 (callback environment). Walking an associative array therefore corrupted the env register, crashing the generated binary on Linux x86_64. Move the scratch to the hash-path-unused length slot [rbp-40]. ARM64 was unaffected (keeps the value in a register).
…in-parity-eir feat: array builtin parity on EIR (15 builtins)
…ackend-removal feat: remove legacy backend
…rray-strict-arg feat: in_array() accepts the optional strict (3rd) argument
…[skip ci] - Document AI-assisted contributions, planning larger work (.plans), and PR expectations (self-contained, reference the issue) - Move the assembly comment policy into CONTRIBUTING.md (column-81 alignment) and add scripts/check_asm_comments.py to verify it - Point AGENTS.md to CONTRIBUTING.md once up front instead of repeating the reference per section
Drop the 11 finished plans (EIR overview/spec/skeleton/lowering/backend/ switchover, peephole, CSE-LICM-inlining, legacy cleanup, hot-path data type, null-sentinel collision). Keep the unfinished ones: eir-06 register allocator, DESIGN_eir_codegen_bugs, and phpstorm-plugin.
…d-arg-resolution fix: resolve names inside named-argument values (missing NamedArg resolver arm)
…es builtin Reconstruct PR illegalstudio#446's feature set on top of current main. Windows x86_64 PE target: - Platform::Windows with the MSx64 ABI (rcx/rdx/r8/r9, 32-byte shadow, 16-byte alignment) and ~40 Win32 shim wrappers converting SysV->MSx64 (WriteFile/ReadFile, sockets, file/dir ops, VirtualAlloc/Heap, BCryptGenRandom, ...). - Syscall->shim transform wired into the pipeline + runtime cache (Windows-only) so PE binaries contain no raw Linux syscalls; unmapped syscalls route to __rt_unsupported_syscall instead of a silent trap. - Entry wrapper (MSx64->SysV), MinGW-w64 assembler/linker, kernel32/msvcrt/winmm/ ws2_32/bcrypt/shlwapi link set. --target windows-x86_64 (alias x86_64-pc-windows-gnu); .exe / .dll outputs. CI windows-pe-cross-compile job (MinGW + Wine) wired into the gate. Fix the implicit end-of-main exit code on Windows: emit_exit and emit_exit_with_result_reg now load the code into edi and call the __rt_sys_exit (ExitProcess) shim directly, identical to an explicit exit($n), instead of returning through the MinGW CRT. The CRT's exit() reaches the same rdi-consuming shim, so the old return path left rdi holding leftover data and exited with a garbage nonzero code. random_bytes(int $length): string: - Cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a constant length below 1. - Expressed as a single-source registry home file (src/builtins/math/random_bytes.rs) instead of the legacy hand-maintained tables; excluded from the legacy runtime-callable wrapper as an EIR-only builtin. Builtin docs + registry JSON regenerated. Callable-invoker ABI on Windows x86_64: - The descriptor-based runtime callable invoker read its descriptor from a hardcoded rdi, but on Windows the caller passes it in the MSx64 first arg (rcx); read it from int_arg_reg_name(target, 0) instead. - The invoker clone helpers passed arguments to the __rt_array_clone_shallow / __rt_array_to_mixed / __rt_hash_clone_shallow / __rt_hash_to_mixed / __rt_array_new runtime helpers via int_arg_reg_name, which returns the target user-call ABI (rcx/rdx on Windows); those helpers use the System V AMD64 ABI on every x86_64 target and read rdi/rsi. Add runtime_int_arg_reg_name (SysV on all x86_64, platform ABI on AArch64) and route the four helper call sites through it. No-op on Linux and AArch64. # Conflicts: # CHANGELOG.md # CLAUDE.md # docs/internals/builtins/math/mt_rand.md # docs/internals/builtins/math/rand.md # docs/internals/builtins/math/random_int.md # src/codegen/abi/mod.rs # src/codegen/builtins/arrays/call_user_func_array.rs # src/codegen/builtins/system/exit.rs # src/codegen/callable_dispatch.rs # src/codegen/prescan.rs # src/codegen/runtime_callable_invoker.rs # src/codegen_support/abi/bootstrap.rs # src/codegen_support/platform/windows_transform.rs # src/codegen_support/runtime/arrays/random_bytes.rs # src/codegen_support/runtime/emitters.rs # src/pipeline.rs
Run the codegen test suite under ELEPHC_TEST_TARGET=windows-x86_64 via MinGW + Wine in a 16-shard CI matrix, emit a per-shard JUnit report (profile.ci.junit), and compare each shard's failing-test set against a curated allow-list of known-good Windows tests (3137 tests) plus a known -failures list. The gate fails only when a previously-passing (allow-listed) test regresses or a known-failure unexpectedly passes, so the full-suite parity percentage stays informational. Adds scripts/gen_windows_codegen_ allowlist.py to regenerate the lists from a full run, and drops 2 wine-flaky include_paths tests from the allow-list with retry tolerance.
…e path constants Windows x86_64 P0 runtime bundle (stacked on the PE32+ base): * `__rt_winsock_init` (WSAStartup MAKEWORD(2,2)) hooked into the main wrapper before `__elephc_main`, and `__rt_winsock_cleanup` (WSACleanup) into `__rt_sys_exit` before ExitProcess — socket shims previously called msvcrt socket funcs with no Winsock initialization. argc/argv are spilled across the init call (rcx/rdx are volatile on MSx64). * `__rt_sys_access` via GetFileAttributesA (INVALID_FILE_ATTRIBUTES -> -1) plus a C-symbol `access` stub. * `__rt_sys_ftruncate` via SetFilePointerEx + SetEndOfFile (fd spilled across the seek) plus a C-symbol `ftruncate` stub so the existing `call ftruncate` lowering resolves on Windows with no lowering change. * C-symbol `umask` no-op stub (mirrors php-src Windows behavior). * Transform routing for syscall 21 (access), 77 (ftruncate), and 82 (rename) — the `__rt_sys_rename` shim was previously orphaned. Target-aware path constants via the existing `PHP_OS` ConstRef precedent: `PHP_EOL`, `DIRECTORY_SEPARATOR`, `PATH_SEPARATOR` now resolve in `prescan::collect_constants` using new `Platform::php_eol()` / `directory_separator()` / `path_separator()` helpers (Windows: CRLF, "\\", ";"; elsewhere: LF, "/", ":"). macOS/Linux behavior is unchanged. `emit_shim_exit` prologue corrected to `sub rsp, 48` (N % 16 == 0) after `and rsp, -16` so WSACleanup and ExitProcess stay 16-byte aligned; a regression test locks the alignment invariant.
…link search-path Cross-build PCRE2 10.42, zlib 1.3.1, bzip2 1.0.8, and libiconv 1.17 from source for x86_64-w64-mingw32 into an actions/cache'd sysroot on the ubuntu Windows-parity runner, since the apt ELF dev libs cannot link into MinGW PE and no packaged MinGW pcre2/bz2/iconv dev libs exist. Surface the sysroot to both the production link (src/linker.rs) and the test harness (tests/codegen/support/runner.rs) via -L/lib added before the -l args, gated on ELEPHC_MINGW_SYSROOT (set in CI, unset on local non-CI builds so no missing-directory warnings). The external-C libs (-lpcre2-8 -lpcre2-posix -lbz2 -lz) were already pushed unconditionally by runtime_features.rs; they only needed a search path. Add require_windows_builtin_library mirroring the macOS helper, and use it for the convert.iconv.* stream filter so Windows emits -liconv (msvcrt ships no iconv), resolved by the cross-built libiconv. Recovers ~90 Windows codegen tests (regex/preg 67 + phar 15 + iconv 5) pending CI validation.
…dows-gnu Cross-build the elephc-image/pdo/crypto/tz bridge staticlibs for --target x86_64-pc-windows-gnu (PE/COFF) when the test target is windows-x86_64, so MinGW can link them into the .exe. The host (ELF) bridge archives MinGW cannot link into PE were the remaining link-config gap after Tier 1's external-C sysroot. tests/codegen/support/runner.rs: ensure_bridge_staticlibs gains --target x86_64-pc-windows-gnu + CC/AR/RANLIB_x86_64_pc_windows_gnu env (gated on Platform::Windows) so PDO's bundled libsqlite3-sys amalgamation is compiled by x86_64-w64-mingw32-gcc; bridge_staticlib_dir resolves to <target>/x86_64-pc-windows-gnu/debug on Windows so MinGW finds the PE archives. Non-Windows path is byte-identical to the pre-Tier-2 path (cargo_target=None skips the --target/env block; subdir stays 'debug'). Three pure helpers + unit tests cover the gating without env mutation. ci.yml: rustup target add x86_64-pc-windows-gnu in the windows-codegen- parity job; its bridge prebuild uses a new BRIDGE_CRATES_WINDOWS subset (image/pdo/crypto/tz) with --target + CC/AR/RANLIB env. elephc-phar (bzip2-sys system libbz2, ELF-only on the ubuntu host) and elephc-tls (ring asm) are excluded from the Windows prebuild so their C/cc-rs deps cannot block the job; on-demand ensure_bridge_staticlibs still attempts them per-fixture and nextest isolates any failure. macOS/Linux bridge steps keep $BRIDGE_CRATES with no --target. Recovers ~210 Windows codegen tests (image ~164 + pdo 37 + crypto 10) pending CI validation.
Add Windows x86_64 runtime coverage for the P0 time/process primitives: - sleep/usleep C-symbol stubs in emit_shim_c_symbols: delegate to Win32 Sleep (seconds->ms via imul; microseconds->ms via div by 1000), return 0. Resolves the direct 'call sleep'/'call usleep' sites from lower_sleep/ lower_usleep on Windows. - __rt_sys_getrusage syscall shim (route 98): GetProcessTimes with the current-process pseudo-handle (HANDLE)-1, FILETIME->timeval conversion (div by 10_000_000 then 10) into ru_utime/ru_stime; success path zeros all 14 resource fields ru_maxrss..ru_nivcsw (offsets 32..144); non-SELF who zeros the full struct and returns 0; failure returns -1. - WIN32_IMPORTS += Sleep, GetProcessTimes. - 4 win32 unit tests + 2 compile-only codegen tests. poll (syscall 7) deliberately deferred: WSAPOLLFD (16B, 64-bit SOCKET fd) is 2x the Linux pollfd (8B, int fd), so a shim needs a conversion loop -> __rt_poll helper coupled with the stream_select/network surface (Action 2).
…eam_select via ws2_32 select
The pselect6 fd_set conversion loops used `mov rcx, r10d` (64-bit dest, 32-bit source), which GAS rejects with an operand-type mismatch, so the Windows stream_select path failed to assemble under MinGW. Switch to `mov ecx, r10d` (32-bit to 32-bit, zero-extends to rcx); `cl` still holds the shift count b, so semantics are unchanged.
Add the proc_open/proc_close builtins as a cross-target PHP surface (C1a of the proc_open effort). The real fork/pipe/exec runtime lands in C1b (Linux/macOS) and C1c (Windows); this slice ships loud stubs returning -1 so the surface compiles and links on every supported target and boxes as PHP false. - builtin homes: src/builtins/io/proc_open.rs (Union(stream_resource, Bool) check hook) and proc_close.rs (no check hook), registered via pub mod in src/builtins/io/mod.rs. - EIR lowering: lower_proc_open (ABI AArch64 x0=descriptor_spec array ptr, x1=command ptr, x2=command len, x3=pipes array ptr; x86_64 rdi/rsi/rdx/rcx) boxes via box_stream_fd_or_false_result_kind kind 5; lower_proc_close mirrors lower_pclose with __rt_proc_close. - runtime stubs: __rt_proc_open/__rt_proc_close return -1 on every target (AArch64 + x86_64 Linux/Windows), registered in io/mod.rs + emitters.rs. - Mixed resource kind 5 in __rt_mixed_free_deep (both arches) dispatches to __rt_proc_close as the scope-exit destructor. - .comm _proc_pids/_proc_child_fds tables in data/fixed.rs. - runtime_builtin_wrapper_excluded entries in callable_dispatch.rs. - tests: tests/codegen/io/proc.rs (run + compile), windows_pe.rs compile-only, error_tests/io_builtins/streams.rs arity checks; docs in docs/php/builtins/process/.
The MinGW cross-build of PCRE2 10.42 runs `make`, which builds `pcre2posix_test.exe` — a noinst_PROGRAMS entry — and on the new ubuntu-24.04 runner image (20260628.225) this test binary fails to link static-only. pcre2-posix is configured --disable-shared, so the test expects `__imp_pcre2_reg*` dllimport stubs the static archive cannot provide (undefined reference to __imp_pcre2_regcomp/regexec/ regerror/regfree; collect2: ld returned 1; make Error 2; exit 2). This broke all Windows parity shards at the sysroot-build step. PCRE2 10.42 has no configure flag to drop the POSIX wrapper (`--disable-pcre2posix` is unrecognized and silently ignored), and the libpcre2-posix.la library itself links fine — only the test binary breaks. Under our configure flags (no --enable-jit / --enable-fuzz-support / --enable-rebuild-chartables) pcre2posix_test is the only noinst program, so override noinst_PROGRAMS= on both make and make install to skip it. elephc links only -lpcre2-8, so the unused libpcre2-posix.a landing in the sysroot is harmless.
CMake's zlib 1.3.1 installs the static archive as libzlibstatic.a (and, despite -DBUILD_SHARED_LIBS=OFF on the current ubuntu-24.04 image, a shared import lib libzlib.dll.a); neither filename matches the -lz stem the Windows link arm (src/linker.rs) and the test harness (tests/codegen/support/runner.rs) pass the linker, nor the libz.a the MinGW sysroot sanity ls checks. The v1 sysroot cache masked this (it held a usable libz.a from an older build); bumping the cache key to v2 forced a fresh rebuild, exposing the mismatch and failing all 16 Windows Codegen Parity shards at the sanity ls (exit 2). Alias the static archive to the canonical libz.a so -lz resolves to the static lib and the sanity ls finds it, reproducing what the cached sysroot provided. No cache-key bump is needed: actions/cache@v4 only saves on job success, so the broken v2 cache was never persisted and the next run rebuilds with the symlink in place.
Enable WINEDEBUG=+seh on the windows-codegen-parity shards so crashing .exe fixtures emit their fault address and failing instruction pointer into captured stderr (empty under -all), letting us root-cause the 19 segfault regressions on the gate. Diagnostic-only; revert before merge.
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.
Diagnostic PR — enables WINEDEBUG=+seh on the windows-codegen-parity shards to capture fault addresses for the 19 segfault regressions on the gate. Temporary; will be closed and branch deleted once segfault addresses are extracted. Do not merge.