Skip to content

transform: various escape analysis improvements#5461

Open
jakebailey wants to merge 3 commits into
tinygo-org:devfrom
jakebailey:fix-5378
Open

transform: various escape analysis improvements#5461
jakebailey wants to merge 3 commits into
tinygo-org:devfrom
jakebailey:fix-5378

Conversation

@jakebailey

Copy link
Copy Markdown
Member

Fixes #5378

This began as a fix to #5378, but like most of my PRs, ballooned into a bit more.

Each commit has a description, but the gist is that we are able to stack allocate more stuff that BigGo could also already do.

Unfortunately after #5220 the diff for the output is not helpful, so I have structured the PR such that all new tests are added in the first commit, that way looking at individual later commits show which allocs they remove.

@jakebailey jakebailey marked this pull request as draft June 12, 2026 18:54
Add allocation diagnostics coverage for pointer-returning helpers, aggregate
slice returns, and conditional pointer returns before changing the allocation
optimizer.

The golden files record the current heap-allocation behavior so later commits
show exactly which diagnostics each optimizer improvement removes.
Follow returned pointer aliases when deciding whether runtime.alloc calls can
be lowered to stack allocations. A returned parameter is not the same as
nocapture: it still flows back to the caller and must be checked as an alias
of the original allocation.

Keep the analysis conservative for recursive returned-parameter chains and
unknown operands. The existing golden test now shows that the non-escaping
returned pointer cases no longer require heap allocation.
Track whether an allocation reaches a callee return separately from the
instruction where it escapes. The extra result state is needed because LLVM's
returned parameter attribute only covers scalar returns: a slice helper returns
its data pointer inside a {ptr, len, cap} aggregate, so the alias is only
visible by walking insertvalue and ret uses in the callee.

This lets OptimizeAllocs keep the backing array for returnIntSlice(s) on the
stack when the returned slice is ignored, matching gc's escape decision for the
same pattern. The golden output still keeps the escaping returned-slice case on
the heap.
@jakebailey

Copy link
Copy Markdown
Member Author

Some raw data about the 787 std allocs this removes:

Representative removed variables/sites:

  • runtime/hashmap.go:388 n := *m
  • many new(big.Int) temporaries in crypto/dsa, crypto/elliptic, math/big
  • fixed-size local arrays like tag, expectedTag, iv, mask, S in crypto code
  • many local go/types temporaries in expression/type checking paths.
 archive/tar/common.go:426             blk        var blk block
 archive/zip/reader.go:639             File{}     if readDirectoryHeader(&File{}, rs) == nil
 archive/zip/reader_test.go:1082        f          var f File
 
 context/x_test.go:252                  check      check(o0, "o0", "", "", "")
 context/x_test.go:255                  check      check(o1, "o1", "c1k1", "", "")
 context/x_test.go:261                  check      check(o3, "o3", "", "c2k2", "c3k3")
 
 go/build/constraint/expr_test.go:76    p          p := &exprParser{s: tt.in}
 go/constant/value.go:216               mant       var mant big.Float
 go/constant/value.go:879               t          var t big.Float
 
 image/jpeg/dct_test.go:295-298         f          chained new(big.Float).Mul(...)
 image/jpeg/dct_test.go:352-356         f          chained new(big.Float).Mul/Quo(...)
 
 internal/chacha8rand/rand_test.go:17   s          var s State
 internal/chacha8rand/rand_test.go:68   blocks     var blocks [32]uint64
 internal/chacha8rand/rand_test.go:77   b1, b2     var b1, b2 [32]uint64
 
 slices/slices_test.go:1235             b          b = Insert(b, len(b)-1, 0)
 slices/slices_test.go:1253             b          b = Replace(b, len(b)-2, len(b)-1, 0, 0)
 slices/sort_test.go:237                fs         fs := []float64{...}
 
 time/sleep_test.go:647                 tr         var tr Timer
 time/tick_test.go:105                  tk         tk := &Ticker{C: c}
 time/tick_test.go:664                  tick       tick := &Ticker{C: c}
 
 tinygo/src/os/exec_linux_test.go:22    proc       StartProcess(..., &ProcAttr{})
 tinygo/src/runtime/hashmap.go:388      n          n := *m

@jakebailey jakebailey marked this pull request as ready for review June 12, 2026 22:11
@jakebailey jakebailey requested a review from Copilot June 12, 2026 22:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves TinyGo’s heap-to-stack allocation optimization by enhancing escape analysis to recognize values that flow through returned parameters (including through aggregate operations), addressing unnecessary heap allocations like the one reported in #5378.

Changes:

  • Extend transform/allocs.go escape analysis to track “returned” flows and follow them across calls (including through insertvalue / extractvalue) while guarding against recursion.
  • Add new alloc-focused scenarios to transform/testdata/allocs2.go (returned pointer/slice cases, including a recursive case).
  • Update golden outputs (allocs2.out.reason / allocs2.out.cover) to reflect the reduced/shifted allocation set after the analysis improvements.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
transform/allocs.go Enhances escape analysis to better detect non-escaping returned-pointer flows and aggregate return propagation.
transform/testdata/allocs2.go Adds new test cases covering returned pointer/slice scenarios and recursion behavior.
transform/testdata/allocs2.out.reason Updates expected “reason” output for heap allocations after the analysis change.
transform/testdata/allocs2.out.cover Updates expected coverage-style allocation output after the analysis change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

Suspected bug in escape analysis causing unnecessary heap allocs

2 participants