Weirdness in stack grow implementation

225 views
Skip to first unread message

tapi...@gmail.com

unread,
Jul 16, 2021, 2:19:39 AM7/16/21
to golang-dev

This following program crashes for stack overflow.
However it looks it should not, for the line "x = [N]byte{}" never get executed.
I added a line in the copystack function: println("copy:", ncopy).
It looks 90% stack space is used.
Some weird.

package main

const N = 1024 * 1024 * 10

var x interface{}

func f(n int) {
    println(n)
    if n > 0 {
        f(n-1)
    }
   
    // Make compiler think f need 10M stack size.
    if x != nil {
        x = [N]byte{}
        panic("unreachable")
    }
}

func main() {
    f(52)
}




jake...@gmail.com

unread,
Jul 16, 2021, 9:01:47 AM7/16/21
to golang-dev
On Friday, July 16, 2021 at 2:19:39 AM UTC-4 tapi...@gmail.com wrote:

This following program crashes for stack overflow.
However it looks it should not, for the line "x = [N]byte{}" never get executed.

You know that. I know that. But there is no reasonable way for the compiler to know that, at least in the general case. Since 'x' is a global variable, the compiler would, minimally have to scan the entire package to make sure x is always nil, just to compile this one function. And there may be ways for it to be set that the compiler can not detect, such as reflection or a debugger. So that is an optimization I would not expect to see.

However, if 'x' is local (like this https://play.golang.org/p/mC5hL3CoapR) then the compiler does identify it as dead code, and the program works as expected.

Michael Pratt

unread,
Jul 16, 2021, 9:48:45 AM7/16/21
to tapi...@gmail.com, golang-dev
Go does not adjust the stack in the middle of function bodies, only on entry. i.e., every function has a fixed frame size. Since func f might need 10MB of stack space, every call will reserve the full space. (A similar C program built with most compilers will also cause a stack overflow).

The one oddity here is that the assignment to x escapes the array to the heap, so in theory we could avoid using the stack altogether. However, it seems we are allocating [N]byte{} on the stack, and then copying it to the heap when converting to interface{}.

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-dev/162cec3b-21c7-42fb-a2a2-cc6ea7a83a8dn%40googlegroups.com.

tapi...@gmail.com

unread,
Jul 16, 2021, 11:28:52 AM7/16/21
to golang-dev
On Friday, July 16, 2021 at 9:48:45 AM UTC-4 Michael Pratt wrote:
Go does not adjust the stack in the middle of function bodies, only on entry. i.e., every function has a fixed frame size. Since func f might need 10MB of stack space, every call will reserve the full space. (A similar C program built with most compilers will also cause a stack overflow).

Does it mean the addresses of values allocated on stack have already been determined at compile time?

Michael Pratt

unread,
Jul 16, 2021, 12:04:15 PM7/16/21
to tapi...@gmail.com, golang-dev
Yes, stack slots are determined at compile time. In fact, the GC uses this to find pointers on the stack.

tapi...@gmail.com

unread,
Jul 16, 2021, 12:11:54 PM7/16/21
to golang-dev
Thanks both for the explanations!

Ian Lance Taylor

unread,
Jul 16, 2021, 12:55:05 PM7/16/21
to tapi...@gmail.com, golang-dev
On Fri, Jul 16, 2021 at 8:28 AM tapi...@gmail.com <tapi...@gmail.com> wrote:
>
> On Friday, July 16, 2021 at 9:48:45 AM UTC-4 Michael Pratt wrote:
>>
>> Go does not adjust the stack in the middle of function bodies, only on entry. i.e., every function has a fixed frame size. Since func f might need 10MB of stack space, every call will reserve the full space. (A similar C program built with most compilers will also cause a stack overflow).
>
>
> Does it mean the addresses of values allocated on stack have already been determined at compile time?

The addresses will of course depend on the stack pointer. What is
determined at compile time is the size of the stack frame for each
function and the offset of each local variable within the stack frame.

Ian

tapi...@gmail.com

unread,
Jul 17, 2021, 1:39:30 AM7/17/21
to golang-dev
Yes, this is what I meant. I didn't know how to describe it precisely. ;)
 
Reply all
Reply to author
Forward
0 new messages