Skip to content

cl: preserve recover-visible panic source lines - #2293

Open
cpunion wants to merge 8 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-panic-pcline-20260811
Open

cl: preserve recover-visible panic source lines#2293
cpunion wants to merge 8 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-panic-pcline-20260811

Conversation

@cpunion

@cpunion cpunion commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • retain physical caller frames for caller consumers, program-unique functions, //go:noinline functions, and recover-visible synchronous callees
  • emit exact implicit-panic PC anchors only in the same-package synchronous call subtree observed by a recovering defer
  • enable 5 unique GOROOT compatibility cases, removing 15 platform/version xfail entries

GOROOT cases

  • fixedbugs/bug347.go
  • fixedbugs/bug348.go
  • fixedbugs/issue27201.go
  • fixedbugs/issue29504.go
  • fixedbugs/issue4562.go

Scope

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=1
  • go test -race ./cl -run "TestCallerTrackingPrecomputeSupportsConcurrentReads|TestCallerTrackingPrecomputeMatchesLazyAnalysis" -count=1
  • go test -vet=off ./test/go -run "^TestRuntimeStatementLineInfo$" -count=1
  • macOS/arm64, Go 1.26.5: all 5 GOROOT cases passed with empty xfail/not-applicable files
  • fmtprintf size regression check on macOS/arm64:
    • base: 1,890,016 bytes
    • PR: 1,890,000 bytes
    • __text: 607,804 bytes on both
    • __llgo_pcl: 7,712 bytes on both
  • Linux/arm64, Go 1.26.5: all 5 GOROOT cases passed with empty xfail/not-applicable files
  • Linux/amd64, Go 1.26.5: PR primary build, both test shards, release artifact test, and coverage passed

@cpunion cpunion added the go-test-compat Go standard-library and GOROOT test compatibility label Aug 11, 2026

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread runtime/internal/runtime/z_baremetal.go
Comment thread cl/compile.go Outdated
Comment thread cl/compile.go Outdated
Comment thread cl/instr.go Outdated
Comment thread cl/instr.go Outdated
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

d672e6e09c43 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Build vs base Run vs base
Linux cprintf 18656 B +0.0% 228.126 ms -5.3% (better) 1.065 ms +1.3% (worse)
Linux fmtprintf 1885664 B -0.0% (better) 2.074 s +1.1% (worse) 2.773 ms -0.6% (better)
Linux println 68480 B +0.0% 238.547 ms +3.1% (worse) 1.328 ms -0.6% (better)
macOS cprintf 84672 B +0.0% 330.879 ms -13.8% (better) 2.789 ms -19.3% (better)
macOS fmtprintf 1890016 B +0.0% 2.693 s +13.5% (worse) 12.738 ms -2.1% (better)
macOS println 121216 B +0.0% 339.550 ms -7.8% (better) 3.510 ms +1.1% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 9.650 ns/op +1.7% (worse)
Linux BenchmarkMergeCompilerFlags 111.900 ns/op +0.0%
Linux BenchmarkMergeLinkerFlags 73.020 ns/op +0.0%
Linux BenchmarkChannelBuffered 28.160 ns/op -0.5% (better)
Linux BenchmarkChannelHandoff 18953 ns/op +1.5% (worse)
Linux BenchmarkDefer 33.870 ns/op -3.5% (better)
Linux BenchmarkDirectCall 1.363 ns/op -0.1% (better)
Linux BenchmarkGlobalRead 1.364 ns/op +0.1% (worse)
Linux BenchmarkGlobalWrite 2.176 ns/op +0.0%
Linux BenchmarkGoroutine 23795 ns/op -1.6% (better)
Linux BenchmarkInterfaceCall 6.550 ns/op +0.0% (worse)
Linux BenchmarkRuntimeGetG 1.365 ns/op -16.7% (better)
macOS BenchmarkLookupPCRandom 11.600 ns/op -7.1% (better)
macOS BenchmarkMergeCompilerFlags 119.400 ns/op -22.3% (better)
macOS BenchmarkMergeLinkerFlags 72.860 ns/op -14.2% (better)
macOS BenchmarkChannelBuffered 23.930 ns/op +5.2% (worse)
macOS BenchmarkChannelHandoff 7135 ns/op +0.4% (worse)
macOS BenchmarkDefer 28.600 ns/op +2.6% (worse)
macOS BenchmarkDirectCall 1.039 ns/op -0.4% (better)
macOS BenchmarkGlobalRead 1.107 ns/op +7.9% (worse)
macOS BenchmarkGlobalWrite 1.029 ns/op +1.0% (worse)
macOS BenchmarkGoroutine 42195 ns/op -3.0% (better)
macOS BenchmarkInterfaceCall 4.536 ns/op -2.8% (better)
macOS BenchmarkRuntimeGetG 2.110 ns/op -0.8% (better)

Compared with 9084d6ec39f9 measured in the same runner job.

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.70199% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cl/instr.go 94.20% 4 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@cpunion
cpunion force-pushed the codex/goroot-panic-pcline-20260811 branch 4 times, most recently from 58e3ace to b3fa614 Compare August 13, 2026 04:04
@cpunion
cpunion marked this pull request as draft August 13, 2026 14:58
@cpunion
cpunion force-pushed the codex/goroot-panic-pcline-20260811 branch from be8b5fd to cda09e1 Compare August 13, 2026 21:39
@cpunion
cpunion marked this pull request as ready for review August 13, 2026 21:51

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.go seed loop, ~line 1073): a defer target qualifies via (isRuntimeCallerFrameFunc(target) || base[target]) && recover.needsRecoverScope(target), where base is 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 calls runtime.Caller) resolves to a static callee that is neither isRuntimeCallerFrameFunc nor in this package's base, 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 DFS visiting stack the recursion returns false without memoizing, and a caller can then cache a stale negative. For a recursive cycle where runtime.Caller is reachable only through the back-edge, the base-set membership becomes dependent on the (nondeterministic) trackable map 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.

Comment thread cl/instr.go Outdated
Comment thread cl/instr.go Outdated
Comment thread cl/instr.go Outdated
@cpunion

cpunion commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Handled the two additional review-summary notes in/alongside d672e6e:

  • Cross-package recovering helpers now seed the caller package through the target package's base caller-consumer set. Both lazy and precomputed analysis paths have regression coverage.
  • The pre-existing recursive-cycle issue in fnMayReachRuntimeCaller is real: a minimal two-function cycle omitted one member in 79/100 repeated local analyses due to map-order-dependent provisional false memoization. It is tracked separately in cl: make recursive runtime.Caller reachability order-independent #2320 so this PR does not grow into an SCC/fixed-point rewrite.

Validation for the review update:

  • targeted caller/recover tests and race tests passed
  • the original statement-line probe and the new deferred-panic recovered-stack probe passed
  • all 5 GOROOT cases covered by this PR passed
  • macOS fmtprintf stayed identical to the parent commit in file size (1,890,016 B), __text (607,804 B), and __llgo_pcl (7,712 B)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go-test-compat Go standard-library and GOROOT test compatibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant