parent: 17086:2879112bff3d tip, linux/amd64

The program is:

package main

import "sync"

func main() {
    println(foo(0))
}

func foo(x int) int {
    // fast path
    if x != 42 {
        return x
    }
    // slow path
    mu.Lock()
    defer mu.Unlock()
    seq++
    return seq
}

var (
    mu  sync.Mutex
    seq int
)

The generated code for fast path is:

func foo(x int) int {
  400c40:       64 48 8b 0c 25 f0 ff    mov    %fs:0xfffffffffffffff0,%rcx
  400c47:       ff ff 
  400c49:       48 3b 21                cmp    (%rcx),%rsp
  400c4c:       77 05                   ja     400c53 <main.foo+0x13>
  400c4e:       e8 bd 81 01 00          callq  418e10 <runtime.morestack16>
  400c53:       48 83 ec 08             sub    $0x8,%rsp
  400c57:       48 8b 44 24 10          mov    0x10(%rsp),%rax
  400c5c:       48 c7 44 24 18 00 00    movq   $0x0,0x18(%rsp)
  400c63:       00 00 
        if x != 42 {
  400c65:       48 83 f8 2a             cmp    $0x2a,%rax
  400c69:       74 0f                   je     400c7a <main.foo+0x3a>
                return x
  400c6b:       48 89 44 24 18          mov    %rax,0x18(%rsp)
        }
        mu.Lock()
        defer mu.Unlock()
        seq++
        return seq
}
  400c70:       e8 eb b7 00 00          callq  40c460 <runtime.deferreturn>
  400c75:       48 83 c4 08             add    $0x8,%rsp
  400c79:       c3                      retq   


If the compiler performs some CFG analysis, it can figure out that 'callq
runtime.deferreturn' is unnecessary in this case.

Comment From: DanielMorsing

Comment 1:

We have some other issues that could use dataflow analysis to solve them, For example
issue #5364. It would be a good idea to have a general way of doing DFA. Maybe roll in
escape analysis.

Comment From: bradfitz

Comment 2:

Labels changed: removed priority-triage.

Status changed to Accepted.

Comment From: rsc

Comment 3:

The analysis here can be pretty basic. If the return is above the defer and not in a
loop or after a goto'ed label, it doesn't need the deferreturn.

Labels changed: added go1.2maybe.

Comment From: rsc

Comment 4:

Labels changed: added feature.

Comment From: robpike

Comment 5:

Not for 1.2.

Labels changed: removed go1.2maybe.

Comment From: rsc

Comment 6:

Labels changed: added go1.3maybe.

Comment From: rsc

Comment 7:

Labels changed: removed feature.

Comment From: rsc

Comment 8:

Labels changed: added release-none, removed go1.3maybe.

Comment From: rsc

Comment 9:

Labels changed: added repo-main.

Comment From: linsite

building with go 1.24 shows that unnecessray deferreturn is no longer there. can you confirm this can be closed? @dvyukov

$ go version go version go1.24.0 linux/amd64

0x0000000000469847 <+39>: cmp $0x2a,%rax 0x000000000046984b <+43>: jne 0x4698cf ...
0x00000000004698ce <+174>: retq 0x00000000004698cf <+175>: mov %rax,0x10(%rsp) 0x00000000004698d4 <+180>: add $0x30,%rsp 0x00000000004698d8 <+184>: pop %rbp 0x00000000004698d9 <+185>: retq 0x00000000004698da <+186>: callq 0x430100

Comment From: dvyukov

Please confirm on your end. I've lost all context of this.

Comment From: linsite

Please confirm on your end. I've lost all context of this.

OK, I see it as fixed. cc @seankhliao

Comment From: randall77

Yes, this is fixed. Or maybe more correctly, no longer relevant. We don't have a deferreturn call at each return point any more. There is a single one per function.