BUG REPORT - nd500x: growable PS_ADI DATA loader regressed NC codegen (jumpg -> PC=0)¶
File: E:\Dev\Ronny\NDInsight\SINTRAN\ND500\BUGREPORT-nd500x-growable-DATA-regressed-NC-codegen-2026-07-26.md
Date: 2026-07-26
Repo: ~/repos/nd500x (WSL) Branch: fix/deabf-i1-success-and-load-investigation
Severity: HIGH - the real Norsk Data NC C compiler crashes during code generation; no :NRF is produced.
For: the team that authored the DOM-loader / MMU page-table-format change.
One-line¶
Commit 951237d "fix(dom): realign DOM-loader PTEs to hardware format + growable DATA segments"
changed how a domain's DATA segment is mapped (from PS_ASI + demand-fault to PS_ADI
growable). The new growable work-region mapping delivers different data to the running program
than the old path did, so the NC compiler reads a wrong value, takes a wrong branch, and crashes.
The commit immediately before it (aa5cd5e) compiles cleanly.
This is NOT a config/shell issue and NOT the MMU core (PTE walk, init-page mapping, ENTS/CALL are all correct). It is specifically the growable DATA work-region (grown/demand pages) behaviour.
Repro¶
cd /home/ronny/ND500USERS
printf 'LOGIN GUEST\nCREATE-FILE B:LIST\nCREATE-FILE BOUT:NRF\nNC-A06\nCHECK B,B,B\nGENERATE-CODE B,BOUT\nEXIT\n' \
| ~/repos/nd500x/build/bin/nd500x --monitor --config ~/ND500USERS/nd500x.ini
aa5cd5e (and everything before it): NC runs to a clean exit (~2,313,500 instructions).
- 951237d..HEAD: [STOP] Invalid instruction 0x00 at PC=0x00000000 (~2,312,800 instructions).
- (B.C is a trivial int x; main(){ x = VALUE; }.)
Bisect (definitive)¶
| Commit | DATA-mapping mechanism | Result |
|---|---|---|
f7bb50e (Jul 21) |
PS_ASI + demand-fault | GOOD - clean exit |
aa5cd5e (Jul 25, one before the change) |
PS_ASI + 512-page reserve + demand-fault | GOOD - clean exit |
6b3d4fb/09e3108/dadb73b |
PTE refactor, DOM load broken | untestable (different early crash) |
951237d |
PS_ADI growable DATA | BAD - jumpg -> PC=0 |
Every commit through aa5cd5e - including all the July arithmetic / compare / flag / string
instruction fixes - is GOOD. The regression enters only with the DATA-loader rewrite.
Method: each commit was built from a clean git archive tree (era-matched ndmonlib, plus a 1-line
graft of the ndlib_aout_set_data_base setter that only lands in 951237d), driven with the script
above. Verdict = clean deep exit (GOOD) vs PC=0x00000000 (BAD).
First divergence (instruction-exact)¶
A differential PC trace of aa5cd5e (GOOD) vs 951237d (BAD) is identical for 1,196,939
instructions, then splits at ONE conditional branch inside a number-to-hex-string formatter:
routine 0x080294D9 ; inner loop 0x08029545..0x0802955F = "divide by 16, emit digit, repeat until 0"
0x0802955D w test b.24 ; b.24 = the number being formatted
0x0802955F if><go $230 ; loop while b.24 != 0
b.24 is larger -> loops more -> branch taken (keep looping)
- BAD: b.24 is smaller (~2 hex digits) -> loop exits early -> branch falls through
b.24 is not written by any nearby program store - NC reads it from its DATA/work region. Because
951237d maps that region differently, NC gets a different value there, formats a shorter number, and
falls through the branch into a code path the GOOD run never enters. That wrong path is what later
reaches callee 0x0801D94D, where a stray &local (0x100001A4) is used as a switch index:
0x0801AAE1 call $0x0801D94D, $1, IND(b.148) ; arg addr = 0x100001A4
0x0801D958 w1 := b.20 -> 0x100001A4 ; callee reads it as a small int
0x0801D95A w1 * $5
0x0801DAC8 jumpg $0x08019588(r1) r1=0x100001A4 ; EA overflows -> MEM=0 -> PC=0
What was RULED OUT (with evidence)¶
- DATA init-page mapping: run-time validation - all 61 initialized DATA pages of the domain
translate byte-exact through the live MMU at crash time (
0 mismatch). The initialized data is fine; only the grown/work region differs. - CALL/ENTS by-reference logic:
git difffrom the last-good era shows the arg-copy loop and frame offsets (PREVB/RETA/SP/AUX/N/ARG1 = 0/4/8/12/16/20) are unchanged; matches manual ND-05.009.4 lines 1096-1139. Only env-gated diagnostics + a MON-path pending-state clear were added. - The FORTRAN
DC[0] -> seg1alias: present in BOTH good and bad - not the cause. - CPU instruction semantics: all instruction changes up to and including
aa5cd5eare GOOD. - Config /
--monitorshell /nd500x.ini: identical across good and bad.
Root cause (localized)¶
951237d replaced PS_ASI DATA + demand-fault-on-first-access with PS_ADI growable DATA. The
NC/CAT-500 back-end addresses a large work region (its ~34 MB codegen scratch, e.g. via segment-0 VAs
around 0x02200000). The old path serviced those accesses via an MMU fault that mapped the page; the
new growable path maps them differently, so a value NC reads back from that region differs. The
initialized pages are byte-exact; the defect is in the grown work-region path (grow_on_fault +
the segment-0 growable alias), not in the init adoption.
Suggested fix¶
Two options, in order of confidence:
- Revert the DATA path to
aa5cd5e's PS_ASI + demand-fault approach (proven to reach a clean NC exit), keeping the PTE-format realignment. Lowest risk. - Fix the growable PS_ADI work-region to deliver byte-identical data to the demand-fault path.
Use
aa5cd5eas a regression oracle: run both builds with a read-watch on the work region and diff the value NC reads just before instruction ~1,196,930 (the source ofb.24) to find the exact cell that reads differently.
Files in scope (nd500x): src/ndlib/ndlib_dom_loader.c (DATA-segment build, the seg-0 growable
alias), src/cpu/nd500_segment_alloc.c (nd500_segment_adopt_growable_data,
nd500_segment_register_growable_alias, grow_on_fault, watermark allocator), src/cpu/nd500_mmu.c
(the demand-map / grow-on-fault path).
Notes¶
aa5cd5eis a usable known-good nd500x oracle for this test - no RetroCore needed.- Reproduced against branch HEAD
762058d(regression still present). - All diagnostics used were reverted; the tree is clean apart from the authoring teams' own in-progress work.