Skip to content

Suspected bug in escape analysis causing unnecessary heap allocs #5378

@digitalentity

Description

@digitalentity

The demo code: https://github.com/digitalentity/tinygo_gotchas/tree/master/src/alloc_woes

There is a heap alloc in main.go at line 12:

func PerformMath() vec3.T {
	a := vec3.T{1, 2, 3}
	b := vec3.T{4, 5, 6} // <<< TinyGo allocates this on heap

	c := b.Scale(0.5)
	return vec3.Cross(&a, c)
}

The vec3.Cross creates and returns a new object, making a, b and c visibility scope only limited to the PerformMath():

func Cross(a, b *T) T {
	return T{
		a[1]*b[2] - a[2]*b[1],
		a[2]*b[0] - a[0]*b[2],
		a[0]*b[1] - a[1]*b[0],
	}
}

Building with -print-allocs=. confirms the unnecessary heap allocation:

$ make build-alloc-woes 
tinygo version 0.41.1 linux/amd64 (using go version go1.25.0 and LLVM version 20.1.1)
toolchain/tinygo/bin/tinygo build -size=short -opt=1 -panic=trap -gc=conservative -scheduler=tasks -print-allocs=. -target=nucleo-f722ze -o build/demo-.hex ./src/alloc_woes
mode: set
/home/ksharlaimov/dev/helvionics/tinygo_gotchas/src/alloc_woes/main.go:12.1,12.54 1 0
/home/ksharlaimov/.cache/tinygo/goroot-4f69c74a8da2c4abfdf60ed68a17fdb99d38c77006ea7da25c73db3862d5a0c8/src/runtime/baremetal.go:44.1,44.24 1 0
/home/ksharlaimov/.cache/tinygo/goroot-4f69c74a8da2c4abfdf60ed68a17fdb99d38c77006ea7da25c73db3862d5a0c8/src/internal/task/task_stack.go:43.1,43.39 1 0
/home/ksharlaimov/.cache/tinygo/goroot-4f69c74a8da2c4abfdf60ed68a17fdb99d38c77006ea7da25c73db3862d5a0c8/src/internal/task/task_stack.go:75.1,75.13 1 0
   code    data     bss |   flash     ram
  13560    1684    4324 |   15244    6008

A test of PerformMath() function with testing.AllocsPerRun says no allocations:

func TestPerformMathAllocations(t *testing.T) {
	allocs := testing.AllocsPerRun(100, func() {
		PerformMath()
	})
	if allocs > 0 {
		t.Errorf("PerformMath allocated %f times, expected 0", allocs)
	}
}

The concern here is

  1. Unnecessary heap allocation and GC pressure, making it hard to write predictable latency and memory usage code.
  2. Inconsistent escape analysis behavior between TinyGo compiler and testing environment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions