Skip to content

Fix stack object finalizer liveness - #1906

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

Fix stack object finalizer liveness#1906
cpunion wants to merge 53 commits into
xgo-dev:mainfrom
cpunion:codex/goroot-stackobj-coverage

Conversation

@cpunion

@cpunion cpunion commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • implement runtime.SetFinalizer on top of BDWGC finalizers and run queued Go finalizers during runtime.GC
  • add scoped conservative stack/register cleanup for command-line LLGo programs to address stack object/finalizer liveness cases
  • add stable test/go coverage for finalizer behavior and stack-object liveness, and un-xfail the verified darwin/arm64 goroot cases

Tests

  • go install ./...
  • go test ./test/go -run 'TestRuntimeSetFinalizer' -count=1 -v
  • LLGO_ROOT=/Users/lijie/source/goplus/llgo-wt-goroot-stackobj llgo test -run 'TestRuntimeSetFinalizer' -timeout=20m -count=1 ./test/go
  • go test -v ./test/goroot -run '^TestGoRootRunCases$' -goroot $(go env GOROOT) -case '^(deferfin|tinyfin|stackobj|stackobj3)\.go$' -xfail /tmp/llgo-empty-xfail.yaml -count=1 -build-timeout 5m -run-timeout 20s -progress 20s
  • go test ./cl ./ssa ./test/goroot -run 'Test(ParseDirective|XFail)' -count=1
  • go test ./cl ./ssa -count=1
  • git diff --check

Notes

  • The conservative liveness cleanup is intentionally limited to command-line-arguments; the test/go subprocess probe keeps coverage stable even without the full goroot workflow.
  • Linux goroot xfails for these cases are left in place because this pass was verified locally on darwin/arm64 only.

@cpunion

cpunion commented May 22, 2026

Copy link
Copy Markdown
Collaborator Author

Updated with Linux pthread feature macro fix in runtime/internal/lib/runtime/_wrap/runtime.c so pthread_getattr_np is declared on glibc-based CI.

Local verification:

  • go test ./test/go -run 'TestRuntimeSetFinalizer' -count=1 -v
  • go run -tags=dev ./cmd/llgo test -run 'TestRuntimeSetFinalizer' -timeout=20m -count=1 ./test/go
  • go test ./cl ./ssa -count=1
  • git diff --check

@cpunion

cpunion commented May 22, 2026

Copy link
Copy Markdown
Collaborator Author

Updated again after LLGo shard 2 exposed a link failure from compiler-injected stack liveness helpers.

Fixes in 36044708d:

  • emit calls to the C runtime wrapper symbols directly instead of skipped Go helper wrappers
  • only enable conservative stack liveness clearing for command-line-arguments packages that actually call runtime.SetFinalizer, avoiding unrelated test/go programs pulling in the helper symbol

Local verification:

  • LLGO_BUILD_CACHE=off go test ./test/go -run 'TestRuntimeSetFinalizerStackObjectLiveness|TestStringConversionFromWideIntegers' -count=1 -v
  • go test ./cl ./ssa -count=1
  • git diff --check

Note: LLGO_BUILD_CACHE=off go run -tags=dev ./cmd/llgo test ... reached program execution locally but hit a macOS temp-dir cleanup error (operation not permitted) after the target code path; the plain Go test wrapper passed the same LLGo programs.

@codecov-commenter

codecov-commenter commented May 22, 2026

Copy link
Copy Markdown

@cpunion cpunion mentioned this pull request May 24, 2026
@cpunion
cpunion force-pushed the codex/goroot-stackobj-coverage branch from 3604470 to 23c7b8e Compare June 10, 2026 11:30
@cpunion
cpunion force-pushed the codex/goroot-stackobj-coverage branch from 6c251ce to 179c4dc Compare June 27, 2026 13:17
@cpunion
cpunion marked this pull request as ready for review June 28, 2026 06:09
@cpunion
cpunion force-pushed the codex/goroot-stackobj-coverage branch from d70d4b5 to 5e2b697 Compare June 29, 2026 02:26
cpunion and others added 13 commits July 2, 2026 22:01
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Go's pclntab pages are touched by its own runtime (traceback, GC) long
before user code queries it, so its first FuncForPC never pays page-in.
Mirror that: when the prebuilt table is present, init adopts it
(zero-copy, sub-µs), touches the pages the lookup path reads (blob,
funcinfo records, string offsets, strings), runs one synthetic lookup
to warm the code paths, and write-warms the FuncForPC cache pages.

First-in-process FuncForPC: darwin ~17µs -> ~2.8µs, linux ~6.6µs ->
~1.0µs. Startup cost is page-count-bound (tens of µs on stdlib-sized
tables, invisible next to ~3ms process startup; hello-world medians
unchanged). Non-prebuilt binaries stay fully lazy: first-use
construction allocates, which has no place in init, and programs that
never introspect pay nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-depths generates deep_<N> scenarios at configurable call depths;
-bigsizes generates bigfunc scenarios (funcs x statements) whose large
bodies stress statement-level pcline density, mid-function pc
symbolization, and ordinary performance of big method bodies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cpunion
cpunion force-pushed the codex/goroot-stackobj-coverage branch from 5e2b697 to 2ea90eb Compare July 2, 2026 15:03
@cpunion

cpunion commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto #2016 (codex/pclntab-linkphase-p1, which includes #2012) per the review-order plan: #2012#2016 → this line of semantics fixes. Conflicts resolved were additive (context fields, the noinline condition set, and runtime.Panic now calls SavePanicCallerFrames() before the panic-node bookkeeping). Note the PR base is still main, so the diff shows #2012/#2016 commits until those merge.

@cpunion

cpunion commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #2036, which re-expresses the stack-object finalizer liveness (cl liveness analysis + register clobber/stack-slot clearing + SetFinalizer conformance, retiring deferfin/stackobj/stackobj3 xfails) on the stage-5 base; the funcinfo draft carried here was superseded by #2012/#2016/#2019.

@cpunion cpunion closed this Jul 6, 2026
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 8, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 9, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 17, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 23, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 27, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Jul 29, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Aug 1, 2026
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.
cpunion added a commit to cpunion/llgo that referenced this pull request Aug 9, 2026
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.
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.

2 participants