cl: preserve recover-visible panic source lines - #2293
Conversation
There was a problem hiding this comment.
Review summary
Careful, well-documented rework of LLGo's recover/panic model: a panicNode linked list on g, per-invocation recover-frame binding, transparent-wrapper aliasing, volatile recover slots, and a conservative mayRecover propagation across cl/, ssa/, and the runtime. The PanicTypeAssert signature change is applied uniformly (verified against the _testrt/_testgo CHECK lines), and nil-handling in the new SSA helpers is solid and test-covered (ssa/recover_metadata_test.go). SIGBUS wiring (SIGBUS != 0 guard + per-platform constants, Linux 0x7, Darwin 0xa) is correct.
One correctness divergence and a few compile-time performance notes are inline. No memory-safety defects were found in the moveToDefer/Recover/abortPanics unlink-and-free ordering.
Non-blocking confirmation (pre-existing, not changed here): Panic still stores the panic value in a non-GC c.Malloc'd node (arg any, z_rt.go:~204), same as before this PR. Since deferred functions run arbitrary allocating user code between the longjmp and Recover, the panic value's GC reachability while a panic is in flight is worth confirming — but this property predates the PR and is out of its scope.
LLGo baseline benchmarks
Program measurements
Core language and compiler benchmarks
Compared with |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
58e3ace to
b3fa614
Compare
be8b5fd to
cda09e1
Compare
There was a problem hiding this comment.
Review summary
This PR adds a "recover panic-site" tracking pass: it identifies same-package functions in the synchronous call subtree below a recovering defer and emits exact PC-line anchors at their implicit panic instructions, plus removes the now-passing xfail entries. The design is sound and well-tested (TestRuntimeCallerFuncSetKeepsRecoverObservableCallees, TestCompileRuntimeCallerPanicPCLineMetadata, and the concurrent-precompute tests give good coverage of the new set semantics).
The findings below are mostly coverage/scope and doc-accuracy items — no correctness defect that produces wrong code was found in the resolved-call or signature-matching paths. Inline comments carry the concrete diff-line findings.
Additional notes without a precise diff-line anchor:
-
Cross-package recover helper at the seed (
cl/instr.goseed loop, ~line 1073): a defer target qualifies via(isRuntimeCallerFrameFunc(target) || base[target]) && recover.needsRecoverScope(target), wherebaseis the current package's base set. A defer that calls a helper in another package which itself consumes pcs (e.g.defer otherpkg.RecoverAndLog()where that fn callsruntime.Caller) resolves to a static callee that is neitherisRuntimeCallerFrameFuncnor in this package'sbase, so the seed won't fire. This may be intended (parallel to criterion-2's one-call-level boundary), but it isn't documented — worth confirming as intended scope. -
Order-dependent under-approximation in
fnMayReachRuntimeCaller(cl/instr.go, pre-existing, not modified here): when a function is already on the DFSvisitingstack the recursion returnsfalsewithout memoizing, and a caller can then cache a stale negative. For a recursive cycle whereruntime.Calleris reachable only through the back-edge, the base-set membership becomes dependent on the (nondeterministic)trackablemap iteration order. It under-tracks (a frame may be dropped) rather than over-tracks, so no crash — but it is a latent nondeterminism in the exact feature this PR builds on, and can make the "precomputed matches lazy" invariant order-fragile. Out of this PR's diff scope; flagging for awareness.
|
Handled the two additional review-summary notes in/alongside d672e6e:
Validation for the review update:
|
Summary
//go:noinlinefunctions, and recover-visible synchronous calleesGOROOT cases
fixedbugs/bug347.gofixedbugs/bug348.gofixedbugs/issue27201.gofixedbugs/issue29504.gofixedbugs/issue4562.goScope
This is built on the recover-time panic stack snapshot merged in #2034. Frame retention and implicit-panic PC anchoring are deliberately separate: ordinary caller-tracked functions still keep stable frames, but no longer receive a PC-line record at every potentially panicking instruction.
This intentionally excludes the nil-check cases handled by #2256 and unrelated GC/finalizer timing failures.
Validation
go test ./cl -run "TestRuntimeCallerFuncSetKeepsRecoverObservableCallees|TestCompileRuntimeCallerPanicPCLineMetadata|TestRuntimeCallerAnalysisEdgeCases|TestCallerTrackingPrecompute" -count=1go test -race ./cl -run "TestCallerTrackingPrecomputeSupportsConcurrentReads|TestCallerTrackingPrecomputeMatchesLazyAnalysis" -count=1go test -vet=off ./test/go -run "^TestRuntimeStatementLineInfo$" -count=1fmtprintfsize regression check on macOS/arm64:__text: 607,804 bytes on both__llgo_pcl: 7,712 bytes on both