Repository: go
Issue: runtime: unnecessary heap allocations when `range`ing over an empty array
Labels: Performance,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.14.2 linux/amd64
</pre>

### Does this issue reproduce with the latest release?
Yes (1.15beta1)


### 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/nickgregory/.cache/go-build"
GOENV="/home/nickgregory/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/nickgregory/dev/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go-1.14"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.14/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-build389543628=/tmp/go-build -gno-record-gcc-switches"
</pre></details>

### What did you do?

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
-->

In an example like the following, `thing` is created on the heap even if the passed `arr` has length 0 and the loop body is never run:

```go
package main

type foo struct {
	a uint64
	b uint64
}

var glob *foo

//go:noinline
func f(arr []foo) {
	for _, thing := range arr {
		glob = &thing
	}
}

func main() {
	f([]foo{})
}
```

This is contrived/minimalistic example (inlining is just to make the asm easier to read), but shows the point. This originally came up in a case where `arr` was generated in an event processing critical path, and this iteration over a (usually empty array) was creating millions of objects that escaped to heap (due to storage in another object, not assignment to a global), eating ~10% of run time.

### What did you expect to see?

I would expect that the `runtime.newobject` call would only happen after the array is checked for length > 0.

### What did you see instead?

```
...
  45d528:	e8 d3 e1 fa ff       	call   40b700 <runtime.newobject>
  45d52d:	48 8b 44 24 08       	mov    rax,QWORD PTR [rsp+0x8]
  45d532:	48 8b 4c 24 28       	mov    rcx,QWORD PTR [rsp+0x28]
  45d537:	48 85 c9             	test   rcx,rcx
  45d53a:	7e 35                	jle    45d571 <main.f+0x71>
  45d53c:	48 8b 54 24 20       	mov    rdx,QWORD PTR [rsp+0x20]
  45d541:	31 db                	xor    ebx,ebx
  45d543:	eb 04                	jmp    45d549 <main.f+0x49>
  45d545:	48 83 c2 10          	add    rdx,0x10
...
{loop body}
...
  45d569:	48 ff c3             	inc    rbx
  45d56c:	48 39 d9             	cmp    rcx,rbx
  45d56f:	7f d4                	jg     45d545 <main.f+0x45>
  45d571:	48 8b 6c 24 10       	mov    rbp,QWORD PTR [rsp+0x10]
  45d576:	48 83 c4 18          	add    rsp,0x18
  45d57a:	c3                   	ret    
...
```