Skip to content

test/goroot: expand Go 1.26 compiler coverage#2063

Closed
cpunion wants to merge 20 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-go126-coverage
Closed

test/goroot: expand Go 1.26 compiler coverage#2063
cpunion wants to merge 20 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-go126-coverage

Conversation

@cpunion

@cpunion cpunion commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

This draft starts the Go 1.26 GOROOT coverage work tracked in #2062 on top of the merged Stage 5 integration branch.

The current commits establish and verify the merged runtime/compiler baseline, retire stale expectations, and classify the remaining Darwin failures before adding new directive support.

Current Go 1.26 darwin/arm64 inventory:

  • 2,332 active directive cases in total
  • 1,043 currently covered by the CI runner
  • 998 passing, 38 failing but classified, 7 host-skipped in the latest inventory
  • 1,289 cases still outside current coverage:
    • 504 compile
    • 650 errorcheck
    • 135 runnable cases, including all 5 errorcheckandrundir cases

All Go language, runtime, ABI, standard-library, compile-only, diagnostic, and hybrid diagnostic/run cases are in scope. Exclusions must be limited to audited Go-compiler-specific implementation tests and documented explicitly.

Base integration

This branch is based on the local integration of:

Current changes

Planned coverage work

  • Add dedicated compile directive execution without linking or requiring an executable artifact.
  • Add errorcheck diagnostic and source-position matching.
  • Add two-stage errorcheckandrundir coverage: diagnostics first, then link/run/output verification.
  • Cover rundir, runindir, and buildrundir cases.
  • Include the nested runnable cases outside the current direct-directory scan.
  • Add a reproducible, checked-in coverage inventory/report.
  • Audit and explicitly classify only genuine Go-compiler-specific exclusions.
  • Re-run the Go 1.24/1.25/1.26 matrix under bounded resource settings.

Verification completed so far

  • Full Go 1.26 CI-mode sweep across the current default GOROOT directories.
  • Candidate failures repeated on Go 1.24, Go 1.25, and Go 1.26 where present.
  • New xfail/timeout rules verified together in one targeted run.
  • GOROOT matcher unit tests pass.
  • Resource-safe execution used throughout: one LLGo process, GOMAXPROCS=2, -p 1, nice priority, sharding, and per-step timeouts.

Draft: implementation and coverage classification are still in progress.

Refs #2062

cpunion and others added 20 commits July 9, 2026 13:30
Replaces the size-class counters with gc-shaped heap profiling: sampled
allocations are attributed to physical call stacks at exact statement
lines, and records hold RAW sampled counts — consumers (pprof, goroot
heapsampling.go) apply the Poisson correction themselves, exactly as
with gc.

- Sampling mirrors gc's mcache.nextSample: bytes count down to an
  exponentially distributed threshold (mean MemProfileRate), sample once
  on crossing, redraw. The memoryless distribution is load-bearing: with
  any bounded-support threshold a near-periodic allocation pattern
  phase-locks the sample points onto the large sites (observed 1.6x
  per-site skew on heapsampling's interleaved sizes). ln() is a small
  local approximation — the runtime core cannot import math.
- Stacks come from the FP walk at sample time (fpCallers via a hook the
  public runtime registers), bucketed by stack hash; allocator plumbing
  (including __llgo_stub. wrapper frames of the hook) is trimmed at read
  time. A reentrancy flag spans the whole decision path: threshold
  drawing and bucket allocation themselves allocate, and a recursive
  sample overflows the stack.
- Heap allocations get statement anchors in tracked functions, and a
  package that reads the memory profile (runtime.MemProfile /
  MemProfileRate under either the "runtime" or the patched
  lib-runtime spelling) pins all its trackable functions: per-site
  attribution loses sites to inlining otherwise. Profiling packages are
  rare and accuracy beats inlining there; gc gets both via its inline
  tree (P4).

goroot heapsampling.go passes on darwin/arm64 and linux/arm64 (the
latter was a pre-existing platform gap). Depends on --icf=none from the
line-directive PR: heapsampling's three identical wrapper functions must
keep distinct pcs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…upport

Re-expresses the surviving value of xgo-dev#1892 on the xgo-dev#2023 base (its other
~10k lines were an earlier draft of the funcinfo/pclntab machinery,
since superseded by xgo-dev#2012/xgo-dev#2016/xgo-dev#2019):

- //go:nointerface methods no longer satisfy interfaces: the pragma is
  recorded at import (cl/import.go -> Program.SetNoInterfaceMethod) and
  filtered out of abi uncommon method tables; GlobalDCE metadata takes
  the filtered selection list. Retires the typeparam/mdempsky/15
  goroot xfails (validated go1.26 darwin, ci directive mode).
- Type-assertion failure between same-named types from different
  scopes appends '(types from different scopes)' (gc wording).
- MatchesClosure unwraps closure types on both sides, fixing assertions
  between closure and plain function types.
- reflect.(*rtype).Method builds the method func value with the closure
  layout allocation (fixes reflect method-value identity).
- xfail: retire fixedbugs/issue16130, fixedbugs/issue29735 (validated
  go1.24 + go1.26 darwin), and reword typeparam/mdempsky/16 - the
  panic value now implements runtime.Error; the message still lacks
  the source interface type and prints the command-line-arguments
  package path.
Re-expresses xgo-dev#1918 on the xgo-dev#2023 base (its remaining ~11k diff lines were
the pre-xgo-dev#2012 funcinfo draft, superseded by the stage-5 chain):

- recover() only succeeds when called directly by a deferred function
  (gc semantics): the panic node records the owning Defer frame at
  rethrow (panicKey/panicNode + GoDeferData), and Recover checks the
  caller is that frame's direct deferred call. Closure wraps carry
  StartRecoverFrameAlias/EndRecoverFrame so method-value and closure
  adapters stay transparent to the ownership check.
- Rethrow keeps the xgo-dev#2023 PanicTraceback hook on the unrecovered path.
- xfail: retire fixedbugs/issue4066 (2m-timeout entries; now runs in
  ~2.7s), fixedbugs/issue73916 and issue73916b (go1.26 recover
  semantics), validated on darwin/arm64 go1.26.

Supersedes xgo-dev#1918.
…s; recover conformance

Re-expresses the surviving value of xgo-dev#1882 on top of xgo-dev#2033 (its remaining
~11k diff lines were the pre-xgo-dev#2012 funcinfo draft, superseded):

- Failed (non-comma-ok) type assertions panic with a real
  *runtime.TypeAssertionError built at runtime (TypeAssertError +
  missing-method lookup from abi tables) instead of a plain errorString;
  the recovered value implements runtime.Error, matching gc. The
  source-interface abi type is deliberately not materialized at the
  assert site: doing so can reference another package's private
  local-generic symbols (undefined at link); the message's interface
  name is the documented mdempsky/16 residual.
- SIGBUS joins SIGSEGV in the fault-to-panic signal path (per-OS signal
  constants; darwin/linux).
- goroot xfails retired, validated darwin go1.26 + go1.24: recover2.go,
  recover4.go, zerodivide.go, fixedbugs/issue73917.go, issue73920.go.
- recover1.go stays xfailed with an updated reason (recursive-panic
  sub-call recover masking - Defer-node model follow-up); three ported
  tests covering the same class are t.Skip'ed with that pointer.
- Golden CHECK updates: assert-failure sites now emit
  TypeAssertError+Panic; constants renumbered from actual IR.

Supersedes xgo-dev#1882.
Re-expresses xgo-dev#1906 on the xgo-dev#2023 base (its remaining ~11k diff lines were
the pre-xgo-dev#2012 funcinfo draft, superseded by the stage-5 chain):

- cl gains a liveness analysis for stack-allocated objects: allocas
  whose last use has passed are cleared so bdwgc's conservative stack
  scan stops keeping dead stack objects (and what they point to) alive;
  pointer registers are clobbered around the trigger points
  (llgo_clobber_pointer_regs) and dead stack slots holding the target
  are zeroed (llgo_clear_stack_ptr, pthread stack-bounds walk).
- runtime.SetFinalizer paths (mfinal, runtime_gc, bdwgc binding) hook
  the cleared-slot machinery so finalizers for dead stack objects run.
- xfail: retire deferfin.go, stackobj.go, stackobj3.go, validated on
  darwin/arm64 go1.24 + go1.26 (stackobj2 already passed).

Carries the xgo-dev#2035 shared-GOCACHE commit temporarily (same patch-id,
auto-dedups when the chain rebases after xgo-dev#2035 merges).

Supersedes xgo-dev#1906.
With the shared go-build cache the four heavy test packages (cl, ssa,
internal/cabi, test/go) no longer stagger naturally on cold caches;
their compile phases overlap fully, and the burst of concurrent
clang/lld children has been killing ubuntu runners mid-run (three
consecutive 'runner received a shutdown signal' deaths on this branch,
each ~8 minutes into the heavy-package phase) and showing up on mac as
go-list WaitDelay expirations. -p 2 caps the concurrent package count;
wall time is dominated by the cl package either way.
An acceptance regression asserts exact per-line attribution at rate=1
(raw counts are exact there), and a cl unit test covers the
memprofile-package pinning criterion under both runtime spellings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… capture path

The frame-table init allocates; when one of those allocations crossed the
sampling threshold, captureMemProfileStack -> fpCallers re-entered
initRuntimeFuncPCFramesSlow on the thread that already held the Busy
latch and usleep-spun forever. First testing.callerName call of a test
binary triggers the init, so whole test binaries hung at startup —
which packages hit it depends on the deterministic threshold sequence
meeting the binary's pre-init allocation volume: net/rpc and
net/rpc/jsonrpc under go1.24 stdlib, net/http/expvar/cookiejar under
go1.26 (CI shard timeouts on ubuntu, both attempts).

Entering an Uninit latch from the capture path is safe (the whole sample
runs under memProfileInSample, so init's own allocations cannot
re-sample); only Busy must not be waited on. Drop that one sample.
…ks see the panic frames

gc runs deferred functions on top of the panicked stack; LLGo's longjmp
unwinding removes those frames physically, so runtime.Caller /
CallersFrames / debug.Stack from a deferred function (before or after
recover) could not see the panic site. Now:

- Panic() captures the physical pc chain (the existing
  SavePanicCallerFrames hook, empty since the shadow stack left) into a
  per-thread snapshot; Recover() marks the recovering frame so the
  snapshot stays observable exactly while that frame is live.
- Caller-info walks splice the snapshot below the live deferred frames at
  the defer-owner junction, keeping one panic-machinery frame where gc
  has runtime.gopanic (fixed Caller depths count it).
- Hardware faults (SIGSEGV/SIGBUS and previously-fatal SIGFPE) install a
  SA_SIGINFO handler that captures from the interrupted ucontext pc/fp —
  the handler's own chain dead-ends at the signal trampoline — so fault
  tracebacks start at the fault site, through C frames into the Go
  callers. C is compiled with -fno-omit-frame-pointer so x86-64 chains
  hold.
- Defer execution is attributed to the function's closing brace like gc,
  and explicit panic statements get their own statement anchor.

Signal-path robustness (the reflectmake flake, ~7% -> 0 over 300 runs):
- The recover mark reads the frame-pointer chain, which after siglongjmp
  can reach a stale/unmapped slot; the guarded read (msync page probe)
  lives in the public runtime via a RecoverMark hook, and the core just
  calls it — an unguarded read self-faulted and corrupted the value the
  recover was extracting.
- The fault handler does no async-signal-unsafe work: the snapshot buffer
  is preallocated (no bdwgc malloc in signal context) and the page size
  is primed at install (no sysconf). SA_NODEFER + an unblock on capture
  keep a savemask=0 longjmp escape from leaving the fault signal blocked,
  and a re-entered handler restores the default disposition for one clean
  core; fault-context walks probe page readability before dereferencing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove issue14646/issue5856/issue33724 xfails; the C-fault regression
runs three sequential faults (a handler leaving the signal blocked after
the longjmp escape cores on the second) and asserts the fault-site chain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dex/stage5-integrate-current

# Conflicts:
#	runtime/internal/lib/runtime/unwind_llgo.go
… codex/stage5-integrate-current

# Conflicts:
#	runtime/internal/runtime/z_rt.go
#	test/goroot/xfail.yaml
…o codex/stage5-integrate-current

# Conflicts:
#	test/goroot/xfail.yaml
… into codex/stage5-integrate-current

# Conflicts:
#	runtime/internal/lib/runtime/_wrap/runtime.c
#	test/goroot/xfail.yaml
…te-current

# Conflicts:
#	cl/_testgo/abimethod/in.go
#	cl/_testgo/tpinst/main.go
@cpunion

cpunion commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator Author

Closing this premature draft. Coverage implementation and per-case local validation will be completed before the branch is pushed and a replacement draft PR is opened.

@cpunion cpunion closed this Jul 12, 2026
@cpunion
cpunion deleted the codex/goroot-go126-coverage branch July 12, 2026 01:21
@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.51613% with 58 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cl/compile.go 90.31% 28 Missing and 22 partials ⚠️
cl/instr.go 91.78% 3 Missing and 3 partials ⚠️
ssa/expr.go 92.30% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

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