fix(z80): calls preserve live values and return all results correctly (P7) - #62
Merged
Merged
Conversation
Save callers before argument setup, preserve reused arguments and implicit callee clobbers, and pick up results before restoring overlapping registers. Resolve saturated byte permutations and word-register cycles safely. Add exhaustive and sampled production CPU judges for calls and loops. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrwo36SzxKRZYTgpDo7hzo
Use caller AF saves instead of persistent call scratch overrides. Pick up all returned values around pair restoration without writable inline code. Fix related accumulator, multiply and index-half preservation failures. Reject indirect calls with live caller values until their ABI is reliable. Add independent seeded CPU regressions and require zero mismatches for the now-correct L0 call judges. All four gates pass; critic and worker fuzzers and the per-assert corpus show no new main-pass failures. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrwo36SzxKRZYTgpDo7hzo
Keep MIR2 assertions and intermediate/C output available for modules with unsupported live values across indirect calls. Skip incidental Z80 emission while retaining the explicit error for Z80 output and assertion products. Route the C backend through the existing MIR2 C emitter. Add CLI and sandbox regressions with demonstrated red/green runs. All four gates pass. All 978 native and forced assertions have zero new failures; the 521-source compile sweep only rejects func_ptr.c for Z80 emission. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrwo36SzxKRZYTgpDo7hzo
Emit default assembly for MIR2-only assertions, falling back with a warning only for unsupported indirect-call ABI validation. Use .generated.c for implicit C output and refuse replacement unless --force is requested. Load stack-snapshot byte results into IY halves through A. Add demonstrated red/green CLI and forced-allocation regressions. All four gates pass, 978 assertions have no new failures in either mode, and 600 new call seeds pass. The paired 521-source sweep has no unexpected regressions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrwo36SzxKRZYTgpDo7hzo
PR tests: 🟢 passCommit:
The external CZECH and full Zork I stories are not part of this PR gate. |
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.
P7: function calls return wrong values on the default Z80 path
Bug. On the default PBQP path, calls corrupted values. With the MIR2 VM correct in every case, main produced:
fc(two calls)h_c2(argument reused after the call)h_c4(word arg, word live across the call)Fixes (
pkg/mir2/z80codegen.go, pipeline/CLI)computeClobbersconservatively includes every allocatable register and register half.JP …; DS ntemporary is gone. Moves, a local scratch register or a reentrant stack snapshot replace it, so it is ROM-safe.saveAccAcrossCallis removed. Its scratch override outlived the call and was clobbered.__mul8argument setup uses parallel copies.examples/c89/func_ptr.cstill runs its MIR2 asserts. It prints a warning and produces no.a80.-b cnow really uses the C emitter. It used to fall through to Z80 output.-oit writes<stem>.generated.c, refuses to overwrite an existing file and accepts--force..a80output is unchanged.Evidence
.a80and no overwritten file, except the documentedfunc_ptr.c.Size. +2,914 bytes on 128 shared files (+4.2%). The critic attributes it as:
@externcallees now treated as clobbering everythingThe
@externshare is the cost of a previously unsafe assumption; main assumed externs clobber nothing.Known debt (follow-ups)
; clobbers:annotations now list every register, which makesmzd --verify-abiclobber checks meaningless.@extern.genBinOplhs != "A"costs a few leaf instructions (ADD A,H→LD A,H / ADD A,B).Process. Code by codex
gpt-6.1-sol, with cross-family Claude critics:.a80,-b coverwrite).🤖 Generated with Claude Code
https://claude.ai/code/session_01Wrwo36SzxKRZYTgpDo7hzo