Skip to content

transform: follow merged pointers during escape analysis - #6236

Open
jakebailey wants to merge 1 commit into
tinygo-org:devfrom
jakebailey:stack-promote-pointer-merges
Open

jakebailey wants to merge 1 commit into
tinygo-org:devfrom
jakebailey:stack-promote-pointer-merges

Conversation

@jakebailey

Copy link
Copy Markdown
Member

Merging a pointer with another value does not itself make the pointer escape. Treating every phi and select as an escape forces temporary reflection storage onto the heap even when its users only read it.

Follow the merged value to its actual users while preserving conservative handling of cycles and pointers that are returned or stored elsewhere.

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.

🟡 Changes recommended

The allocation promotion can collapse overlapping loop-iteration lifetimes into one entry-block alloca, overwriting a still-live previous object and changing loaded values.

1 open issue
What changed in this PR

This PR extends TinyGo’s escape analysis to follow pointers through PHI merges, allowing read-only uses to qualify for stack allocation.

Changes:

  • Handle PHI nodes alongside existing bitcast and select traversal.
  • Add regression cases for read-only merges, returned pointers, and pointer cycles.
  • Update the expected escape diagnostic location.
File Description
transform/​testdata/​allocs2.go Update the expected escape location to the return statement.
transform/​testdata/​allocs.out.ll Record expected stack and heap allocation results.
transform/​testdata/​allocs.ll Add select, PHI, and pointer-cycle test cases.
transform/​allocs.go Follow PHI users when determining pointer escapes.

Review effort: Balanced

Comment thread transform/allocs.go Outdated
}
case llvm.BitCast:
// A bitcast escapes if the casted-to value escapes.
case llvm.BitCast, llvm.PHI, llvm.Select:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Dang, yes

Merging a pointer with another value does not itself make the pointer
escape. Treating every phi as an escape forces temporary reflection
storage onto the heap even when its users only read it.

Follow the merged value to its actual users while preserving
conservative handling of cycles and pointers that are returned or stored
elsewhere. Loop-carried pointers can outlive the next allocation, so
merges in control-flow cycles must remain conservative too.
@jakebailey
jakebailey force-pushed the stack-promote-pointer-merges branch from 17e1cc0 to 21584e2 Compare November 9, 2026 18:00
@davecheney

Copy link
Copy Markdown
Contributor

Thanks @jakebailey. Possible follow-up, not for this PR: blockInCycle rejects every phi inside a loop, including an if/else merge within one iteration, so temporaries in loop bodies stay on the heap. Rejecting only phis that carry a value across the loop's back edge would keep the fix for the loop-carried case and allow those.

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.

3 participants