Skip to content

compiler: cache complete escape analysis facts by SCC - #3023

Open
cpunion wants to merge 22 commits into
xgo-dev:mainfrom
cpunion:feat/escape-summary-cache-3012
Open

cpunion wants to merge 22 commits into
xgo-dev:mainfrom
cpunion:feat/escape-summary-cache-3012

Conversation

@cpunion

@cpunion cpunion commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Warm builds currently repeat whole-program escape solving, even when dependency archives are reused:

LLGO_BUILD_CACHE=1 LLGO_ESCAPE_CACHE=1 llgo build .  # solve and persist complete SCC facts
LLGO_BUILD_CACHE=1 LLGO_ESCAPE_CACHE=1 llgo build .  # validate and restore effects + placements

Persist generic effects together with all object placements, stack budgets and capture layouts. Validate source/SSA, queried types, target/layout policies and actual dependencies; corrupt, incomplete or ambiguous records fall back to analysis. Atomic package shards batch I/O. Ordinary source bodies reuse archive-input identities; generic instantiations retain exact SSA fingerprints.

Persistence is opt-in: validation can cost more than solving small programs, so default builds keep the original analysis path.

Dependency SSA is still needed for RTA, caller tracking and LLVM body import. This skips repeated escape solving; it does not yet implement export-only dependency loading or claim a universal build-time speedup.

Depends on #3022 (which includes #3021); #2997 is merged. Incremental diff: cpunion/llgo@8b11bfa...feat/escape-summary-cache-3012

Validation: race tests; fresh-process cold/hot cache and caller-rebuild checks with 1/8 compiler workers; stale source/type/policy/dependency invalidation; corrupt and incomplete records; oversized-shard pruning and preservation of the last valid file; same-name wrappers, including different callees in one recursive SCC. Full compiler, SSA, escape, C ABI and compile-command suites pass; fresh-process cache and environment checks pass with explicit opt-in. Go runtime tests pass on native macOS/arm64 and Linux/amd64 and actual J32/J64/W32, including two-worker J32 and the complete J32/J64 browser-debug matrix.

Part 3 of #3012 (section 10).

@cpunion
cpunion marked this pull request as ready for review November 8, 2026 11:22

@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 is a large, carefully-reasoned compiler change: a persistent per-SCC cache of escape-analysis facts plus the supporting escape/placement infrastructure. Most of the diff is regenerated test expectations; I focused on the production code (internal/goescape/*, internal/build/escape_cache.go, cl/escape.go, cl/compile.go, cl/instr*.go, ssa/*, internal/goflags/*, runtime helpers) and the new docs.

The cache correctness/trust model is sound. The component cache key transitively incorporates each dependency's own component key, and loadCache applies strong defense-in-depth (envelope digest, schema/version/key equality, per-function ID re-check, summary/object/capture count validation, MaxStackBytes bound, and the ReadOnly-only-on-non-heap-*ssa.Convert re-validation at cache.go:327-331). Every validation failure falls back to full analysis rather than emitting partial facts. Path construction from cache keys is hex-only and fixed-length, so no traversal is possible, and reads are size-bounded. I found no correctness defect that would cause miscompilation.

Findings below are performance and maintainability, not correctness. A few inline; one cross-file note:

Stack-buffer sizing policy is duplicated across three locations. The small-slice/string stack-buffer eligibility and capacity computation is expressed independently in cl/escape.go (escapeStackLayout and forceEscapeHeap) and in ssa/datastruct.go (makeSlice, using SmallStackBufferSize/elemSize capacity and the MaxImplicitStackVarSize bound). These must agree exactly: if the analyzer promotes a slice/string that lowering then cannot place within its buffer (or vice-versa), placement desyncs. They are consistent today, but the triplication means a future change to one constant/branch can silently diverge. Consider a single shared helper consulted by both the analyzer callbacks and the SSA lowering.

I also confirmed the new docs (doc/escape-analysis.md, doc/escape-analysis-comparison.md, benchmark/escape/README.md) are unusually accurate against the implementation (Version/const values, cache fallback invariant, capture-by-value semantics, LLGO_BUILD_CACHE, context limits, -m placement strings all match).

Comment thread internal/goescape/cache.go Resolved
Comment thread internal/build/escape_cache.go Resolved
Comment thread internal/goescape/cache.go Resolved
Comment thread doc/escape-analysis.md Resolved
@codecov

codecov Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 15.53398% with 174 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/build/escape_cache.go 10.52% 85 Missing ⚠️
ssa/gcroot_stack.go 11.66% 53 Missing ⚠️
ssa/gcroot.go 0.00% 34 Missing ⚠️
internal/build/wasm_lto.go 75.00% 1 Missing ⚠️
ssa/transient.go 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@cpunion
cpunion force-pushed the feat/escape-summary-cache-3012 branch 3 times, most recently from 314276b to 329df38 Compare November 8, 2026 15:46
Use main's bootstrap safepoint handling and repeat fresh worker processes
to cover GC during initialization without changing worker startup order.
@cpunion
cpunion force-pushed the feat/escape-summary-cache-3012 branch from 7625c64 to f31a33b Compare November 8, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant