Escape analysis in Go

· gsg


As I am finally taking the time to learn Rust, TIL about escape analysis. It is used in garbage-collected runtimes to determine what variables can safely be allocated on the stack.

For motivation, consider this snippet:

 1package escape_analysis
 2
 3func CreateValue() int {
 4	value := 10
 5	return value
 6}
 7
 8func CreateReference() *int {
 9	value := 10
10	return &value
11}

If I compile this, I get:

~/src % go tool compile -m -l escape_analysis.go 
/home/gg/src/escape_analysis.go:9:2: moved to heap: value

So value from CreateReference gets moved to the heap, but value from CreateValue safely remains in the stack. Neat!

But this goes against the mental model I had just developed. I thought everything would be allocated on the heap, with stack allocation happening as an optimization. The message from the compiler suggests it's the other way around.

So which one is it? The source code comments 1 say:

// First, we construct a directed weighted graph where vertices
// (termed "locations") represent variables allocated by statements
// and expressions, and edges represent assignments between variables
// (with weights representing addressing/dereference counts).
//
// Next we walk the graph looking for assignment paths that might
// violate the invariants stated above. If a variable v's address is
// stored in the heap or elsewhere that may outlive it, then v is
// marked as requiring heap allocation.

(the invariants referred to are ensuring that i. pointers to stack objects cannot be stored on the heap; pointers to stack objects cannot outlive their referend)

So now the log line in our motivating example make sense: value was initially due for the stack by nature of being a local variable, but had to be moved to the heap because it was found to "escape".

In the future, I want to find out how the Go team verifies or tests these tools.

References #