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

125 lines
6.0 KiB
Markdown

# `_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:
```powershell
& "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:
```asm
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.
## Recommended admission gate
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.