test(aot): prove native-entry lowering executes as generated Go, and fix the frame defect it found - #729
Conversation
mparrett
left a comment
There was a problem hiding this comment.
Verified independently at 679d9920 against merge-base 23f3ab15.
The frame fix matches the empty-NS lg -c bundle shape: emit rt.RunProgramMainChunk(unit) after LoadProgramNamespaces, with the existing exit-on-error shape. The early return keeps top-level forms once-only when the main chunk is already in NSOrder.
TestNativeEntryASTGate is green locally (four fixtures and the mutant sub-tests). Reverting just the new emit makes toplevel_effect and vm_backed fail with the documented Nil/CachedVarFn panic and the dropped banner, so the gate kills the defect it claims to pin. gofragment 16/16, and the existing TestLgCompileEntryFrameOptIn / TestNativeEntryMatrix CI lane is green on this head.
One non-blocking follow-up: wire TestNativeEntryASTGate (and, if you want the jank direct-ABI claim in the same lane, TestJankSuiteDirectABIGeneratedGo) into .github/workflows/go.yml. The current AOT step still only runs TestLgCompileEntryFrameOptIn|TestNativeEntryMatrix, and the matrix does not cover a VM-backed callee or a top-level side effect on an empty-NS bundle. entry_frame_test.lg protects the emitted call string; the end-to-end runtime claim lives only behind make native-entry-gate. The Makefile already strips ambient GOFLAGS=-short for that target, so extending the existing AOT -run list is enough.
Approving.
|
@nnunley looks good. official review posted. Btw- was thinking about knocking out the |
|
Take it. If it's unclaimed, it's yours, though I think I have a PR touching it in flight |
679d992 to
4c73840
Compare
Bench-ratchet: control-vs-branch at the rebased headRun after rebasing onto Method. Same command, same exclusive local lane, twice: once at the branch head Deterministic metrics (machine-independent)
Raw counters are identical or differ by a single allocation (114254/114255, 151751/151752). This branch is allocation-neutral. The wall-clock rows are not usable in either runThe calibration anchor moved +166.2% on the branch run and +85.9% on the control run — same machine, same lane. The two runs also disagree on which benchmarks qualify as REGRESSION (branch flags 2, control flags 3; Baseline provenanceThe That predates #645, #719, #726 and #681, so the base→current column is measuring four weeks of merged work rather than anything in this PR. Replacing local recapture with CI-timeline seeding is #651; the per-tier release reference is #597; the umbrella is #663. One change this PR makes to measurement coverageThe branch produced 6 NEW records ( Gates green on
|
|
@nnunley This one looks ready to merge. Happy to do it, but didn't want to disrupt anything you have in flight. Also willing to take the non-blocking follow-up in my last comment. |
--entry-framebinaries could silently drop every namespace-level definition, and nothing in the test suite would notice. This adds a gate that proves lowered entries really execute as generated Go, and fixes the frame defect that gate immediately found.The defect
The generated frame ran
BootCore → SetCommandLineArgs → DecodeExecUnit → LoadProgramNamespaces → prog.<Entry>(ec). For a single-file programlg -cemits a bundle with an empty NS table, soLoadProgramNamespacesiterated nothing and no namespace-level var was ever defined.The binary was therefore correct only when every function reachable from the entry lowered to a direct native call. Any VM-backed callee — a variadic
defn, or anything not:direct-callable?— resolved to a Nil var and panicked:Top-level forms in the program namespace were dropped for the same reason.
rt.RunProgramMainChunkalready existed for exactly this VM-fallback role (#425 Finding 2); the frame simply never emitted a call to it.The fix
pkg/rt/core/ir/passes/entry_frame.lgnow emitsrt.RunProgramMainChunk(unit)afterLoadProgramNamespaces, using the existing exit-on-error shape. It is idempotent by construction: it returns early when the main chunk was already replayed as one of the NS chunks, so a bundle that does carry NS entries still executes top-level forms exactly once.The gate
make native-entry-gatediscoverstest/native-entry/*.lg. Each fixture carries an exact.expectstdout and a structural.goexpect.jsoncontract; a missing sidecar fails rather than skips. Per fixture it proves:defnexists as a Go function matching an expected AST shape, exactly once;directfixture has one direct Go call and zero trampolines for that name, avmfixture has the opposite;lg-compile --entry-frame→lg -c→go build→ run produces byte-exact expected stdout, exit 0;go/ast, never by hardcoded identifiers.Inverted semantic mutant
For
directfixtures, mutating the generated Go body must change observed output — evidence the Go body is what runs. Forvmfixtures the same mutation must not change output — evidence the Go body is dead and the trampoline is genuinely what executes. Both directions are positive, falsifiable claims; a surviving mutant on avmrow is required evidence, not a gap.Fixtures
int_arithintparamsclosure_capturevm_backedtoplevel_effecttoplevel_effectandvm_backedare the regression fixtures for the frame defect: without the fix the first drops its banner and the second panics.Validation
TestLgCompileEntryFrameOptInandTestNativeEntryMatrix(AOT native-entry: generate the Go entry frame, self-containgogen, and stop the silent perf-cliff #425/feat(aot): generate native-entry Go frame from main/-main (#425 Item 1) #628 contracts) still greenmake generate+make check-generated—entry_frame.lgis a core source, artifacts regeneratedinternal/gofragmentmatcher: 16/16Relationship to #681
#681 gates strict capability and engine-output parity and explicitly declines to claim that fixture source executed as generated Go. This PR owns that claim, and is independent of #681.
Test infrastructure notes
test/clojure-test-suiteis a git submodule; jj workspaces (and plaingit worktree) do not materialize it.scripts/link-clojure-test-suite.sh <workspace> [primary-worktree]symlinks it in idempotently, refuses to clobber a real directory, and is wired intomake native-entry-gate. If it is missing,TestJankSuiteDirectABIGeneratedGonow fails loudly with both remedies rather than skipping.test/language_test.gowalkstest/**and executes every.lgfile as a bytecode deftest file, skipping onlycompat,clojure-test-suite,benches,gogen. Adding fixtures undertest/native-entry/and the generator undertest/tools/therefore turned the suite red; both directories are now in that skip list, following the existing precedent.t.TempDir()with its owngo.mod; nothing generated is committed.Rebase
Rebased onto
mainat 23f3ab1 (#712).pkg/rt/generated.sumsconflicted and was resolved by regeneration, not by hand:make generate+make check-generatedare clean at the tip.