go: cmd/compile: big array not allocated on heap, which make program panic.

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.8.1 linux/amd64

What did you do?

package main

const N = 1000 * 1000 * 537
var a [N]byte

func main() {
	// this will stack overflow
	for _, v := range a {
	  _ = v
	}

	// edit: blow solo also stack overflow
	var x [N]byte
	var y [N]byte
	_, _ = x, y
}

[edit]: now(Go SDK 1.12.2), the above program doesn’t crash. But below @agnivade shows one case which will still crash.

What did you expect to see?

runs ok

What did you see instead?

crash

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x469cdc, 0xe)
	/sdks/go/src/runtime/panic.go:596 +0x95
runtime.newstack(0x0)
	/sdks/go/src/runtime/stack.go:1089 +0x3f2
runtime.morestack()
	/sdks/go/src/runtime/asm_amd64.s:398 +0x86

goroutine 1 [running]:
main.main()
	/tmp/main.go:6 +0x88 fp=0xc460057f88 sp=0xc460057f80
runtime.main()
	/sdks/go/src/runtime/proc.go:185 +0x20a fp=0xc460057fe0 sp=0xc460057f88
runtime.goexit()
	/sdks/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc460057fe8 sp=0xc460057fe0
exit status 2

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Comments: 23 (12 by maintainers)

Most upvoted comments

Remove the t.AllocsPerRun call and call g(nil, 0) directly still makes stack grow.

g itself uses a huge amount of stack: "".g STEXT size=229 args=0x18 locals=0x5f5e120 funcid=0x0

looking at GOSSAFUNC=g go build what I think could be happening is g(y, n-1) creates a copy of a on stack to then call runtime.convT2Enoptr to create a copy of it to the heap. Even when the branch is not taken go gc always reserves all the stack upfront for all possible branches and does not dynamically grow the stack within a function which would explain the stack growth even if the branch is not taken. Whether the copy of a on stack is needed to begin with is a valid question and this should likely be avoided by the compiler.

This would also explain why moving panic before g(y, n-1) avoids the stack growth. The compiler will not compile in the call for g knowing it is unreachable and therefore no copy of a is made.

00018 (+15) LEAQ ""..autotmp_3-100000000(SP), DI
00019 (15) MOVQ AX, SI
00020 (15) MOVL $12500000, CX
00021 (15) REP
00022 (15) MOVSQ
00023 (15) LEAQ type.[100000000]uint8(SB), AX
00024 (15) LEAQ ""..autotmp_3-100000000(SP), BX
00025 (15) PCDATA $1, $0
00026 (15) CALL runtime.convT2Enoptr(SB)
00027 (15) MOVQ "".n+16(SP), DX
00028 (15) LEAQ -1(DX), CX
00029 (15) CALL "".g(SB)

BTW, maybe the compiler should make an optimization for g(a, a, a, a, a), by rewriting it as

var i interface{} = a
g(i, i, i, i, i)