Repository: go
Issue: cmd/compile: function argument value escapes no matter the run-time condition
Labels: NeedsInvestigation,compiler/runtime

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.15.5 linux/amd64
</pre>

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/julio/.cache/go-build"
GOENV="/home/julio/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/julio/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/julio/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go-1.15"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.15/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build392370816=/tmp/go-build -gno-record-gcc-switches"
</pre></details>

### What did you do?

While benchmarking a time histogram implementation, I found an unexpected allocation in a `IncrementOrStore()` method implementation, happening directly in the prolog code the compiler generated, where the return value is allocated regardless of whether it's needed or not. 

I could reproduce the issue with the following dummy function:

```go
func foo(cond bool, v int) *int {
    if cond {
        return &v
    }
    return nil
}
```

The compiled function of the previous example will allocate the int value in its function prolog rather than in the true-condition path:

```
TEXT    "".foo(SB), ABIInternal, $24-24
MOVQ    (TLS), CX
CMPQ    SP, 16(CX)
PCDATA  $0, $-2
JLS     102
PCDATA  $0, $-1
SUBQ    $24, SP
MOVQ    BP, 16(SP)
LEAQ    16(SP), BP
FUNCDATA        $0, gclocals·54241e171da8af6ae173d69da0236748(SB)
FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
LEAQ    type.int(SB), AX
MOVQ    AX, (SP)
PCDATA  $1, $0
CALL    runtime.newobject(SB) // <--- The int value is allocated in the function prolog, no matter the condition
MOVQ    8(SP), AX
MOVQ    "".v+40(SP), CX
MOVQ    CX, (AX)
MOVBLZX "".cond+32(SP), CX
NOP
TESTB   CL, CL
JEQ     83
MOVQ    AX, "".~r2+48(SP)
MOVQ    16(SP), BP
ADDQ    $24, SP
RET
MOVQ    $0, "".~r2+48(SP)
MOVQ    16(SP), BP
ADDQ    $24, SP
RET
NOP
PCDATA  $1, $-1
PCDATA  $0, $-2
CALL    runtime.morestack_noctxt(SB)
PCDATA  $0, $-1
JMP     0
```

The workaround I use for now is to rather return the value from the condition block scope:

```diff
func foo(cond bool, v int) *int {
    if cond {
+       v := v // new declaration in this scope to avoid using function scope
        return &v
    }
    return nil
}
```

Resulting in:

```
TEXT    "".foo(SB), ABIInternal, $24-24
MOVQ    (TLS), CX
CMPQ    SP, 16(CX)
PCDATA  $0, $-2
JLS     101
PCDATA  $0, $-1
SUBQ    $24, SP
MOVQ    BP, 16(SP)
LEAQ    16(SP), BP
FUNCDATA        $0, gclocals·54241e171da8af6ae173d69da0236748(SB)
FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
MOVBLZX "".cond+32(SP), AX
TESTB   AL, AL
JEQ     82
LEAQ    type.int(SB), AX
MOVQ    AX, (SP)
PCDATA  $1, $0
CALL    runtime.newobject(SB)  // <--- The int value is no longer allocated by the function prolog, and according to the condition
MOVQ    8(SP), AX
MOVQ    "".v+40(SP), CX
MOVQ    CX, (AX)
MOVQ    AX, "".~r2+48(SP)
MOVQ    16(SP), BP
ADDQ    $24, SP
RET
MOVQ    $0, "".~r2+48(SP)
MOVQ    16(SP), BP
ADDQ    $24, SP
RET
NOP
PCDATA  $1, $-1
PCDATA  $0, $-2
CALL    runtime.morestack_noctxt(SB)
PCDATA  $0, $-1
JMP     0
```