meta: emit and cache package semantic summaries#2061
Open
luoliwoshang wants to merge 81 commits into
Open
Conversation
# Conflicts: # internal/build/build.go # ssa/abitype.go
… format
Introduces internal/meta, a new binary format and read/write layer for
LLGo's whole-program DCE package summary cache (.meta files).
Key design decisions:
- Zero-copy reads via mmap: PackageMeta is a thin view over a mmap'd byte
slice; Edges, TypeChildren, MethodSlots and IfaceMethods all use
unsafe.Slice to reinterpret the on-disk bytes directly, with no alloc.
- CSR layout (Compressed Sparse Row): each per-symbol section stores an
offsets[nsyms+1] prefix array and a flat data array, matching the design
used by Go's own goobj format (BlkRelocIdx + BlkReloc).
- Single allocation on write: Builder.Build() pre-computes all section sizes
and offsets in one pass, allocates one []byte, then writes every section
directly at its target offset — no intermediate buffers, no seek-back.
- Lazy remap in GlobalSummary: merging N PackageMetas only interns the
string tables (O(unique_symbols)); edges and type-children are translated
lazily on query, avoiding the O(total_edges) rewrite cost of the MVP.
- NameRef for method names: method short names are stored as {Off, Len}
pairs referencing the string table, keeping them distinct from module-level
symbols (LocalSymbol) while still enabling cross-package matching via
global Name interning in GlobalSummary.
- Compile-time layout assertions: const expressions verify sizeof(Edge)==12,
sizeof(MethodSlot)==20, sizeof(MethodSig)==12 so any struct drift that
would break the zero-copy reinterpretation fails at build time.
Sections in the .meta wire format:
stringTable | Symbols | Edges (CSR) | TypeChildren (CSR) |
MethodInfo (CSR) | InterfaceInfo (CSR) | ReflectBitmap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the MVP's uvarint-based metadata format with the new mmap-based zero-copy format (internal/meta). All read paths use unsafe reinterpretation of on-disk bytes; write paths use single-allocation serialization. Key changes: - ssa/cl: switch metaBuilder from *metadata.Builder to *meta.Builder; AddIfaceMethod/AddTypeChild now deduplicate internally - internal/build: wire cache miss/hit paths to meta.ReadMeta/Build/WriteMeta - internal/deadcode: analyze.go uses meta.GlobalSummary with GMethodSlot (flat struct, no nested .Sig) - internal/meta: add zero-copy SymbolName/NameString via unsafe.String, add typeChildrenSet map for O(1) dedup, add FormatMeta for golden tests - cl/_testmeta: update 9 golden files to new deterministic sort order Perf (etcd client demo, 334 packages): summary merge: 114.7ms → ~23ms (5x faster via lazy remap + zero-copy strings) DCE analyze: ~34ms unchanged total: ~148ms → ~52ms (2.8x faster) Co-Authored-By: Claude <noreply@anthropic.com>
Remove Phase 2 eager translation — instead of scanning every type in all 334 packages and translating all ~10080 method slots up front, only mark concrete/interface type kinds in Phase 1 (cheap CSR range checks, no slot translation). MethodSlots() and InterfaceMethods() now translate lazily on first access and cache the result. DCE typically reaches only ~500 of ~10000+ types, so lazy translation avoids thousands of unnecessary pm.NameString + internName calls. Perf (etcd client demo, 334 packages): merge: 115ms → ~18ms (6.4x faster) total: ~149ms → ~48ms (3.1x faster) Co-Authored-By: Claude <noreply@anthropic.com>
PackageMeta query methods are now all lowercase (private). External code interacts only through GlobalSummary, Builder, ReadMeta, Bytes, and Close. - Drop IsConcreteType, IsInterface, IsCompositeType — callers use nmethodSlot/ntypeChild/nifaceMethod count methods instead - Add N-prefix count methods (Go goobj convention): nedge, ntypeChild, nmethodSlot, nifaceMethod — single CSR range check, no unsafe pointer - All CSR accessors share csrSlice[T] generic helper, eliminating repeated CSR+unsafe pointer boilerplate - Remove NSyms() method — same-package callers use pm.nsyms field - Remove NewPackageMetaFromBytes — no external callers - Make buildMethodImplKeys fully lazy: concrete type methodImplKeys are computed on-demand in flood's usedInIface path; only methodRefs reverse index is built eagerly - MetaString → String() (fmt.Stringer) — zero additional public API - Drop ConcreteTypes() from GlobalSummary; isConcrete tracking removed from Phase 1 Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts: # chore/gentests/gentests.go # cl/compile_test.go # internal/build/collect_test.go # ssa/expr.go # ssa/interface.go
# Conflicts: # cl/compile.go # internal/build/build.go
# Conflicts: # ssa/abitype.go
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This reverts commit b7d1b79.
luoliwoshang
marked this pull request as ready for review
July 11, 2026 12:22
4 tasks
luoliwoshang
force-pushed
the
wip/meta-emit
branch
from
July 12, 2026 13:51
f93189d to
0a9fb30
Compare
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.
Summary
Introduce package-level semantic metadata production, persistent cache storage, and a whole-program read-only view for future link-time dead-code analysis.
This PR implements the producer and data path:
.metafileGlobalSummaryRelated design proposal: #1853
Scope and data flow
The emitted facts are:
OrdinaryEdges: ordinary calls and symbol references extracted from LLVM IRFuncDemand: interface conversion, interface method call, named reflection method, and dynamic reflection method demandsTypeChildren: direct component types needed for type reachabilityMethodInfo: concrete method name/signature plus IFn and TFn symbolsInterfaceInfo: structural interface method names and signaturesThe existing dead-code-drop configuration currently gates whether these summaries are collected and required in cache entries. It does not make this PR responsible for deciding what is dead or rewriting linked IR.
Cache miss path
main,-a/force rebuild is active, or no package fingerprint is available.cl.NewPackageExWithEmbedMetacreates a package-localmeta.Builderonly when metadata is requested.FinishMetaCollectionextracts ordinary LLVM symbol edges, builds the immutablePackageMeta, and attaches it to the compiled package.PackageMeta.WriteTowrites a temporary.metafile which is atomically published withrename; the manifest is stored separately.Cache hit path
.metafile is mandatory.meta.Openvalidates its header and sections and maps it read-only; a missing or invalid file turns the lookup into a cache miss..metafile is neither required nor opened, and the package'sMetaremains nil. Existing archive-only cache entries therefore remain usable.PackageMeta, then marks the package as a cache hit. Compilation still registers package types, but does not rebuild metadata or regenerate package code.The cache validation helper follows the same rule: archive and manifest are always required, while
.metais opened and closed for validation only when metadata is required.Version 1 wire format
The file uses magic
LLPM, version1, little-endianuint32values, and 4-byte-aligned sections.Header section offsets are absolute byte offsets. CSR offsets are record indexes into that section's flat data array, not byte offsets. Every CSR
nsymsequals the Symbols count. Symbol values index the package-local Symbols section. Name offsets are relative to StringTable, name lengths exclude alignment padding, and the Symbolsreservedfield is written as zero. The final InterfaceInfo section extends to EOF.FuncDemand.kindvalues are:DemandUseIfacetargetis a type symbolDemandIfaceMethodtargetis an interface symbol;extrais its method indexDemandNamedMethodtargetis a StringTable offset;extrais the string lengthDemandReflectMethodGlobalSummary
NewGlobalSummaryinterns every package-local symbol name into one global Symbol namespace and builds alocToGlbtranslation table for each package. Method names use a separateNamenamespace.For duplicate symbols, the summary selects one owner and prefers packages carrying semantic function, method, interface, or type facts over ordinary-edge-only copies. Public queries lazily translate the selected owner's package-local data into global IDs:
OrdinaryEdgesFuncDemandsTypeChildrenMethodSlotsIfaceMethodsGlobalSummaryborrows its inputPackageMetavalues. Mmap-backed package summaries must therefore remain open for the lifetime of the global summary and any aliased query results.Tests
internal/metainternal/build_testmetagolden tests covering emitted package summariesOut of scope
This PR intentionally does not include the metadata consumer or mutate linked output. The following work has been removed and belongs in follow-up PRs:
_testdropsymbol-retention and drop regression cases