Files
Cesarus85--mjolnir-vr/research/data_decode_memory_codegen/REPORT.md
T

6.0 KiB

_data_decode_memory legal-C codegen research

Result

The residual is closed by removing the fallback assignment memory_size = count; after system_exit(-1) in the invalid-element-size switch arm. With that one source change, XDK 3911 emits the January section exactly:

  • padded size: 304 / 304 bytes;
  • relocations: 19 / 19, including identity and order;
  • normalized SHA-256: eb39377415e6c3e07f763e8f0f42f6ecafaf61acc0fab30fbc06062b9dc465d8;
  • normalized byte differences: zero;
  • all 15 sibling functions remain strict exact.

This was independently reproduced from a generated copy of the complete translation unit under this research directory. Production source was not edited by this research lane; the one-line candidate was already present as another lane's uncommitted edit when the matrix ran.

Reproduction environment and command

  • Compiler: XDK 3911 CL.Exe, banner version 13.00.9254.1 for x86.
  • Flags: /nologo /c /O2 /Oy- /DDEBUG /Dxbox plus the same repository include roots as the generated campaign build.
  • Target: build/split/source/memory/data_encoding.obj from the January 14, 2002 executable.
  • Comparator: the current hardened tools/coff_compare.py implementation, using section_infos_equal for size, relocation count/identity, normalized bytes, and section ownership.

Run from the closure-wave worktree:

& "C:\Users\isabe\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe" `
  research\data_decode_memory_codegen\run_matrix.py

The script creates only ignored disposable sources, objects, and results.json below research/data_decode_memory_codegen. It compiles each candidate as the complete translation unit and checks every one of the 16 code owners, so a locally improved decoder cannot hide a sibling regression.

Why the source shape matters

The fallback assignment keeps memory_size semantically live on the default edge. VC7 then allocates state, memory, and memory_size to the wrong callee-saved register permutation for the January object. With the assignment absent, the allocator coalesces the invalid-path value with the count parameter and produces the January ESI/EDI/EBX allocation, including the partial-BX switch and the post-system_exit reload:

call _system_exit
mov  ebx, [ebp+0Ch]
add  esp, 14h

That is a general codegen lesson: a statement on a nominally unreachable error edge can change live ranges and register allocation across an entire function even though normal inputs never execute it.

Original-bug note

system_exit is intended to terminate, but its current C declaration is an ordinary returning void function. If it were ever to return for an invalid element_size, the exact source leaves memory_size indeterminate and its subsequent use is undefined behavior. The January machine code itself reloads count on this path, but C does not guarantee that result.

The matching source should therefore carry a plain-English bug comment. The safe, nonmatching fix is to restore memory_size = count; before break; (or to repair the termination contract project-wide); restoring it currently produces the old nonexact hash and must not be mistaken for an exact source.

Bounded experiment matrix

All variants used the complete TU and the campaign compiler/flags. Diffs counts normalized bytes in _data_decode_memory; every row retained 19 relocations.

ID Source shape Size Diffs / first Nonexact functions Verdict
v00 no default fallback assignment 304 0 / none 0 strict exact
v01 byte *memory 304 0 / none 0 code-neutral exact spelling
v02 char *memory 304 0 / none 0 code-neutral exact spelling
v03 buffer pointer born before switch 288 163 / +0x5 4 reject; regresses three siblings
v04 current pointer born before switch 304 211 / +0x8 4 reject; regresses three siblings
v05 offset born before switch 304 209 / +0x8 3 reject; regresses two siblings
v06 split result base-load and offset-add 304 0 / none 0 code-neutral exact spelling
v07 failure-first final gate 304 0 / none 0 code-neutral exact spelling
v08 named final-gate boolean 304 0 / none 0 code-neutral exact spelling
v09 32-bit count alias live across switch 288 176 / +0x5 1 reject
v10 initialize memory_size = 0 288 114 / +0x8 1 safe but nonexact
v11 initialize memory_size = count 320 279 / +0x8 1 safe but nonexact
v12 assign zero after system_exit 304 94 / +0x5 1 safe but nonexact
v13 return NULL after system_exit 304 166 / +0x8 1 safe but nonexact
v14 restore memory_size = count after system_exit 304 114 / +0x4 1 old baseline; nonexact

The exact spellings v01/v02/v06/v07/v08 are not recommended changes: they add no evidence beyond showing that those local spellings are optimizer-neutral. The least invasive exact source is v00.

Do not repeat

In addition to v01-v14 above, the prior closeout log already exhausts E01-E17: declaration order, split/redundant initializers, register hints on locals and parameters, short aliases, multiplication/shift spellings, predecessor-TU perturbations, const, and a state alias. It also rules out volatile, pragmas, flag changes, inline assembly, aliasing violations, and byte forcing. See docs/object_matching_logs/data_encoding_obj.md for the measured hashes and sibling regressions.

Rebuild the real TU, compare all 16 code owners plus all 14 January-owned non-code sections with tools/coff_compare.py, run the whole-TU regression gate and consolidated build, add the bug/fix comment, then mark the object Matching only if all strict checks remain green. No fuzzy acceptance is permitted: the candidate is genuinely byte- and relocation-exact. Because objdiff represents the local jump-table labels differently, the progress ledger still needs a fail-closed semantic bookkeeping entry that re-runs the hardened comparison on every progress build.