The last line makes the variable y escape. But should it?

142 views
Skip to first unread message

tapi...@gmail.com

unread,
Jun 1, 2021, 9:51:50 AM6/1/21
to golang-nuts

package main

func newIntPtr(n int) *int {
    return &n
}

func main() {
    x := newIntPtr(3)
    y := newIntPtr(5)
    c := make(chan bool)
    go func() {
        *y++
        close(c)
    }()
    <-c
    println(*x, *y)
    println(&x)
    //println(&y) // This line makes y escape.
}

Jan Mercl

unread,
Jun 1, 2021, 10:26:04 AM6/1/21
to tapi...@gmail.com, golang-nuts
On Tue, Jun 1, 2021 at 3:52 PM tapi...@gmail.com <tapi...@gmail.com> wrote:

By default, any local variable that has its address taken and that
address can outlive the function execution forces the variable to
escape, quite naturally as the stack frame with the variable is
destroyed upon returning from the function.

Then there are, or could be, some special cases, where the compiler
can prove it is not necessary. It's possible the compiler cannot prove
much about a special function like 'println` that may, for example,
never exists in SSA form etc.

The last statement in main can be somewhat special wrt escape
analysis, but that depends on implementation details, so in the
general case the answer to the topic question is IMO 'yes'.

tapi...@gmail.com

unread,
Jun 1, 2021, 10:52:51 AM6/1/21
to golang-nuts
Thanks for the explanations. I agree on them mostly.
But the println call doesn't make x escape, so I think println is not the root cause making y escape.
In fact, I'm surprised that y doesn't escape without the last println call.
It looks gc is so smart that it translates "*y" to the value referenced by y at compile time.
However, with the last println call, gc becomes less smart by disabling the translation.
The following is the instructions generated for "*y++" with and without the last println call.

// Without the last println call.
    0x001d 00029 (main.go:12)    MOVQ    "".y+24(SP), AX
    0x0022 00034 (main.go:12)    INCQ    (AX)

// With the last println call.
    0x001d 00029 (main.go:12)    MOVQ    "".&y+24(SP), AX
    0x0022 00034 (main.go:12)    MOVQ    (AX), AX
    0x0025 00037 (main.go:12)    INCQ    (AX)

Axel Wagner

unread,
Jun 1, 2021, 11:18:06 AM6/1/21
to golang-nuts
FWIW, I believe the crux here is that `y` itself escapes, not the pointee of `y`. The pointee escapes as well, but it's printed as `n moved to heap`. The close then closes over `y` (not over `*y`), meaning it needs to store a pointer to `y` as well. That is, I assume, why `y` escapes. However, I'm not sure - I don't understand all the details of how escape analysis works (and it is constantly changing, so it doesn't seem worthwhile to learn it).

I think to prove that `y` does not escape, you'd likely have to prove that the Go routine stops using `y` before the `println(&y)` is executed. I don't think it's reasonable to expect the compiler to prove that.

In other words: There might be an argument that `y` doesn't escape, but I don't believe it's reasonable to expect the compiler to prove so. There will always be such cases. I can't imagine a convincing argument that this one is a critical one to solve.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/81f0e194-5e74-44e8-978c-f02749da6f59n%40googlegroups.com.

Axel Wagner

unread,
Jun 1, 2021, 11:19:34 AM6/1/21
to golang-nuts
"The closure then closes…". Weird typo.

Robert Glonek

unread,
Jun 1, 2021, 12:39:48 PM6/1/21
to golang-nuts
What do you mean by escape? It prints the ptr to y, like the previous prints the ptr to x. Y is the same pointer throughout, as it should be.

Axel Wagner

unread,
Jun 1, 2021, 12:55:58 PM6/1/21
to golang-nuts
"escape" as in "the compiler's escape analysis decides to put it on the heap, instead of the stack". You can compile using `-gcflags=-m` to test that yourself.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Robert Engels

unread,
Jun 1, 2021, 1:30:49 PM6/1/21
to Axel Wagner, golang-nuts
Whenever you take the address of something the compiler is going to have a hard time with escape analysis due to aliasing. Especially with a complex function like println with varadic args. 

On Jun 1, 2021, at 11:55 AM, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:



cheng dong

unread,
Jun 2, 2021, 2:48:31 AM6/2/21
to golang-nuts

`y` escape because the println use interface{} as its parameter, golang can't know in compiling time what this parameter could be use (to be copy to global or send to other goroutine). so it is the interface self make golang hard to keep variable in stack

Jan Mercl

unread,
Jun 2, 2021, 3:09:50 AM6/2/21
to cheng dong, golang-nuts
On Wed, Jun 2, 2021 at 8:48 AM cheng dong <qq451...@gmail.com> wrote:

> `y` escape because the println use interface{} as its parameter,

println is a bootstrap function that the compiler knows about by
itself. It's formal/pseudo signature does not use 'interface{}'. I
haven't checked the actual implementation, but I guess, especially in
the early days of the compiler, implementing println without using
`interface{}' would be simpler and make more sense. I would just
special case at compile time the few supported types to produce calls
of different runtime implementations.

In any case, using println when studying the escape analyzer is an
unfortunate choice because println is kind of a special black box that
can differ substantially between compilers and/or versions.

tapi...@gmail.com

unread,
Jun 2, 2021, 5:13:09 AM6/2/21
to golang-nuts
I think I got the reason.
It is because the compiler is not aware of the println will not change the value of y.
And it is hard or expensive for the compiler to understand the channel synchronization.

If the println function changes y, then y must be allocated on heap.
Reply all
Reply to author
Forward
0 new messages