Skip to content

all: bump golang.org/x/tools to v0.49.0, fold composite literals, and add LTO baseline benchmarks - #2371

Open
cpunion wants to merge 3 commits into
xgo-dev:mainfrom
cpunion:codex/bump-goplus-mod-0.22.0
Open

all: bump golang.org/x/tools to v0.49.0, fold composite literals, and add LTO baseline benchmarks#2371
cpunion wants to merge 3 commits into
xgo-dev:mainfrom
cpunion:codex/bump-goplus-mod-0.22.0

Conversation

@cpunion

@cpunion cpunion commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Bumps github.com/goplus/mod to v0.22.0 (which brings golang.org/x/tools v0.49.0, golang.org/x/mod v0.40.0, golang.org/x/sys v0.47.0, and golang.org/x/sync v0.22.0), adapts compiler lowering to go/ssa v0.49.0, enhances static initializer folding for package-level composite literals, and adds LTO measurements and PR merge-base baseline tracking to the benchmark suite.


Key Changes

  1. Dependency Upgrade:

    • github.com/goplus/mod from v0.21.2 to v0.22.0
    • golang.org/x/tools to v0.49.0
    • golang.org/x/mod to v0.40.0, golang.org/x/sys to v0.47.0, golang.org/x/sync to v0.22.0
  2. Composite Literal Static Folding (cl/static_init.go):

    • In x/tools v0.49.0, go/ssa lowers composite literals in init() using temporary local stack variables (bottom-up *ssa.Alloc + *ssa.Store + *ssa.UnOp(token.MUL)) rather than direct global field stores.
    • Enhanced cl/static_init.go (collectAllocStores) to recursively trace and fold these local temporary alloc stores into static LLVM global constants (avoiding runtime init() stack allocation, memset, and element-wise store instructions, saving ~52.6 KB of binary bloat).
    • Path Isolation: Used appendStaticInitPath to allocate isolated slice paths, preventing slice-aliasing hazards during DAG path collection.
    • Single-Consumer Validation: Added strict validation for *ssa.UnOp(MUL) to ensure dereferenced values are consumed solely by the folded store before instruction suppression.
    • Documentation: Added comprehensive GoDoc comments documenting the recursive DAG traversal and cycle protection.
  3. Zero-Sized Global Sentinel Guard (ssa/decl.go):

    • Prevented packages with zero-sized global variables (such as internal/poll) from overwriting the module's shared sentinel @"__llgo.moduleZeroSizedAlloc$" with non-i8 initializers during package initialization, avoiding LLVM module verification panics.
  4. Benchmark Enhancements (.github/workflows/benchmark.yml, benchmark/baseline/):

    • PR Merge-Base Baseline Tracking: Automatically computes and compares against the true git merge-base commit between base and head, eliminating base drift from concurrent main branch merges.
    • LTO Baseline Measurements: Added -lto=full workload measurements for cprintf_lto, println_lto, and fmtprintf_lto.
  5. Test Fixtures & Coverage (cl/rewrite_internal_test.go, cl/_test*):

    • Refreshed FileCheck LITTEST fixtures in cl/_testgo/ and cl/_testrt/ for x/tools v0.49.0 lowering (explicit store zeroinitializer following AllocZ, stack slot adjustments).
    • Added unit test suites TestStaticGlobalPointerIndirectionLiteralInit and TestCollectAllocStoresFromSSA in cl/rewrite_internal_test.go covering nested composite literals, pointer indirections, and edge cases.

Verification

  • go test -v ./cl -run 'TestStaticGlobal|TestBlankField|TestCollectAllocStores|TestRunAndTestFromTestrt/qsort' (PASS)
  • go test -v ./benchmark/baseline (PASS)
  • go test -v ./internal/packages (PASS)
  • bash test/buildcache/test.sh (18/18 PASS)

@cpunion
cpunion force-pushed the codex/bump-goplus-mod-0.22.0 branch from 2004fb0 to fd12fbe Compare August 20, 2026 03:29

@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: bump goplus/mod to 0.22.0 + loader rewrite

Solid change overall. The dependency bump (goplus/mod 0.22.0, golang.org/x/{mod,sys,tools,sync}) forced dropping the go:linkname hooks into x/tools' internal loader symbols, and the internal/packages/load.go rewrite replaces them with a self-contained implementation over the public packages.Load + packages.Visit. go.sum hashes verify against the checksum database. The ssa/decl.go moduleZeroName guard and the new pointer/nested-alloc handling in cl/static_init.go are reasonable additions.

One issue worth fixing before merge — a slice-aliasing hazard in the new collectAllocStores that can silently miscompile static initializers (inline comment below).

Secondary notes (non-blocking):

  • internal/packages/load.go — serial typecheck. The old loader parsed/typechecked dependencies in parallel goroutines; the new packages.Visit post-order loop runs typecheckPackage serially. This is an acceptable trade-off (correctness/simplicity, and post-order guarantees imported Types are complete before dependents), but it's a real compile-time cost on large dependency graphs — worth noting as known.
  • internal/packages/load.go behavior parity. The rewrite drops IgnoreFuncBodies, the FakeImportC import "C" ignored diagnostic, and the requestedMode field-zeroing that trimmed returned Package fields to the requested mode. Both in-repo callers use the full loadSyntax mode so they're unaffected, but LoadEx/LoadExWithGoVersion are exported — returned fields are no longer trimmed to the requested mode. Consider documenting this in the doc comment.
  • internal/packages/load.go:262targetCompilerAndArch(pkg *Package) never uses its pkg parameter; drop it.
  • internal/packages/load.go:156 — doc-comment nit. The LoadEx comment (inherited from upstream packages.Load) refers to "the PrintErrors function is provided" and "Load returns an error", but this package re-exports neither PrintErrors nor a Load symbol. Minor wording drift.

Comment thread cl/static_init.go Outdated
if !ok || elemStore.Addr != ref {
return false
}
if !handleStoreVal(elemStore, append(basePath, subPath...), out, instrs, visited) {

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.

[P1] Slice-aliasing corrupts static-init paths in collectAllocStores

Slice-aliasing hazard: append(basePath, subPath...) results are stored long-term.

handleStoreVal stores the passed path slice directly into out[].path (staticInitStore.path), which is consumed much later in buildStaticGlobalInitstaticInitNode.add. But append(basePath, subPath...) reuses basePath's backing array whenever cap(basePath) > len(basePath). When an alloc has two or more FieldAddr/IndexAddr referrers (a struct with ≥2 fields, an array with ≥2 indices), the second iteration's append overwrites the tail of the path recorded for the first sibling — the retained path in out is silently corrupted.

basePath carries spare capacity in the common case: the nested-alloc recursion at line 283 passes fullPath (itself an append result, so cap > len), and the top-level call also passes a built-up path when the deref store targets a sub-path of the global. The result is a silent miscompilation (wrong field/element gets the value), not a crash.

Fix by copying per element, e.g.:

full := append(append([]staticInitPathElem(nil), basePath...), subPath...)
if !handleStoreVal(elemStore, full, out, instrs, visited) {

(same at line 253). staticInitStorePath and staticInitStorePathToAlloc build fresh slices along a single linear recursion, so they're safe.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

4fc79b727911 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Build vs base Run vs base
Linux cprintf 19288 B 0 B / +0.0% 327.193 ms -14.54 ms / -4.3% (better) 1.275 ms -21.74 us / -1.7% (better)
Linux cprintf\_lto 19120 B 0 B / +0.0% 335.892 ms +528.8 us / +0.2% (worse) 1.255 ms -143 us / -10.2% (better)
Linux fmtprintf 1877440 B +17288 B / +0.9% (worse) 2.822 s +61.22 ms / +2.2% (worse) 3.192 ms +5.129 us / +0.2% (worse)
Linux fmtprintf\_lto 1797016 B +19912 B / +1.1% (worse) 15.104 s +276.7 ms / +1.9% (worse) 3.234 ms +106.7 us / +3.4% (worse)
Linux println 68776 B 0 B / +0.0% 339.929 ms -5.743 ms / -1.7% (better) 1.551 ms -34.02 us / -2.1% (better)
Linux println\_lto 62464 B 0 B / +0.0% 547.561 ms -2.946 ms / -0.5% (better) 1.607 ms -52.74 us / -3.2% (better)
macOS cprintf 84672 B 0 B / +0.0% 336.565 ms -61.34 ms / -15.4% (better) 2.339 ms -399.5 us / -14.6% (better)
macOS cprintf\_lto 100912 B 0 B / +0.0% 344.192 ms -49.66 ms / -12.6% (better) 2.452 ms -546.4 us / -18.2% (better)
macOS fmtprintf 1909104 B +16384 B / +0.9% (worse) 2.235 s -958 ms / -30.0% (better) 11.395 ms -2.666 ms / -19.0% (better)
macOS fmtprintf\_lto 1642928 B +16528 B / +1.0% (worse) 16.783 s -11.99 s / -41.7% (better) 6.280 ms -3.157 ms / -33.5% (better)
macOS println 121360 B 0 B / +0.0% 405.199 ms -48.88 ms / -10.8% (better) 4.130 ms -173.6 us / -4.0% (better)
macOS println\_lto 128528 B 0 B / +0.0% 478.510 ms -179.4 ms / -27.3% (better) 3.593 ms -2.498 ms / -41.0% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 13.270 ns/op +0.05 ns/op / +0.4% (worse)
Linux BenchmarkMergeCompilerFlags 150.400 ns/op -0.3 ns/op / -0.2% (better)
Linux BenchmarkMergeLinkerFlags 94.720 ns/op +0.57 ns/op / +0.6% (worse)
Linux BenchmarkChannelBuffered 35.530 ns/op +1.54 ns/op / +4.5% (worse)
Linux BenchmarkChannelHandoff 28419 ns/op -1402 ns/op / -4.7% (better)
Linux BenchmarkDefer 43.860 ns/op -1.41 ns/op / -3.1% (better)
Linux BenchmarkDirectCall 1.557 ns/op 0 ns/op / +0.0%
Linux BenchmarkGlobalRead 1.557 ns/op +0.001 ns/op / +0.1% (worse)
Linux BenchmarkGlobalWrite 2.487 ns/op +0.007 ns/op / +0.3% (worse)
Linux BenchmarkGoroutine 31358 ns/op -329 ns/op / -1.0% (better)
Linux BenchmarkInterfaceCall 7.783 ns/op -0.314 ns/op / -3.9% (better)
Linux BenchmarkRuntimeGetG 2.179 ns/op -0.001 ns/op / -0.04587% (better)
macOS BenchmarkLookupPCRandom 11.300 ns/op -1.7 ns/op / -13.1% (better)
macOS BenchmarkMergeCompilerFlags 98.300 ns/op -31.9 ns/op / -24.5% (better)
macOS BenchmarkMergeLinkerFlags 63.110 ns/op -14.46 ns/op / -18.6% (better)
macOS BenchmarkChannelBuffered 21.620 ns/op -2.78 ns/op / -11.4% (better)
macOS BenchmarkChannelHandoff 6744 ns/op -1018 ns/op / -13.1% (better)
macOS BenchmarkDefer 28.020 ns/op -1.1 ns/op / -3.8% (better)
macOS BenchmarkDirectCall 1.005 ns/op -0.057 ns/op / -5.4% (better)
macOS BenchmarkGlobalRead 0.995 ns/op -0.0366 ns/op / -3.5% (better)
macOS BenchmarkGlobalWrite 0.997 ns/op -0.0505 ns/op / -4.8% (better)
macOS BenchmarkGoroutine 31818 ns/op +1790 ns/op / +6.0% (worse)
macOS BenchmarkInterfaceCall 5.734 ns/op +1.322 ns/op / +30.0% (worse)
macOS BenchmarkRuntimeGetG 1.995 ns/op -0.028 ns/op / -1.4% (better)

Compared with 0314faeba69d measured in the same runner job.

@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.58824% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cl/static_init.go 69.56% 22 Missing and 13 partials ⚠️

📢 Thoughts on this report? Let us know!

@cpunion cpunion changed the title build(deps): bump github.com/goplus/mod from 0.21.2 to 0.22.0 and x/tools to 0.49.0 all: bump goplus/mod to 0.22.0, decouple loader, and fold composite literal static inits Aug 20, 2026
@cpunion

cpunion commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator Author

All review feedback addressed in the latest commit:

  • Slice-Aliasing Hazard: Switched from direct append(basePath, subPath...) to appendStaticInitPath with explicit slice copy to ensure complete path isolation across siblings.
  • UnOp Single-Consumer Validation: Added strict validation in collectStaticGlobalInits, collectAllocStores, and handleStoreVal to guarantee *ssa.UnOp(MUL) is only consumed by the folded store before instruction suppression.
  • Package Loader Cleanup: Removed unused pkg *Package parameter from targetCompilerAndArch() and updated doc comments on LoadEx.
  • Test Coverage: Added TestStaticGlobalPointerIndirectionLiteralInit and TestCollectAllocStoresFromSSA in cl/rewrite_internal_test.go.

@cpunion
cpunion force-pushed the codex/bump-goplus-mod-0.22.0 branch from 894e64a to b87e8db Compare August 20, 2026 05:33
@cpunion cpunion changed the title all: bump goplus/mod to 0.22.0, decouple loader, and fold composite literal static inits all: bump goplus/mod to 0.22.0, adapt lowering, and fold composite literal static inits Aug 20, 2026
@cpunion cpunion changed the title all: bump goplus/mod to 0.22.0, adapt lowering, and fold composite literal static inits all: bump golang.org/x/tools to v0.49.0 and fold composite literal static inits Aug 20, 2026
@cpunion
cpunion force-pushed the codex/bump-goplus-mod-0.22.0 branch from b87e8db to 4876c52 Compare August 20, 2026 05:35
@cpunion
cpunion force-pushed the codex/bump-goplus-mod-0.22.0 branch from 4876c52 to ab233da Compare August 20, 2026 05:39
@cpunion
cpunion force-pushed the codex/bump-goplus-mod-0.22.0 branch from ab233da to 4fc79b7 Compare August 20, 2026 06:49
@cpunion cpunion changed the title all: bump golang.org/x/tools to v0.49.0 and fold composite literal static inits all: bump golang.org/x/tools to v0.49.0, fold composite literals, and add LTO baseline benchmarks Aug 20, 2026
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