Commit 934c3599 authored by Keith Randall's avatar Keith Randall

cmd/compile: reorder how slicelit initializes a slice

  func f(x, y, z *int) {
    a := []*int{x,y,z}
    ...
  }

We used to use:
  var tmp [3]*int
  a := tmp[:]
  a[0] = x
  a[1] = y
  a[2] = z

Now we do:
  var tmp [3]*int
  tmp[0] = x
  tmp[1] = y
  tmp[2] = z
  a := tmp[:]

Doesn't sound like a big deal, but the compiler has trouble
eliminating write barriers when using the former method because it
doesn't know that the slice points to the stack.  In the latter
method, the compiler knows the array is on the stack and as a result
doesn't emit any write barriers.

This turns out to be extremely common when building ... args, like
for calls fmt.Printf.

Makes go binaries ~1% smaller.

Doesn't have a measurable effect on the go1 fmt benchmarks,
unfortunately.

Fixes #14263
Update #6853

Change-Id: I9074a2788ec9e561a75f3b71c119b69f304d6ba2
Reviewed-on: https://go-review.googlesource.com/22395
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
parent c4c18214
...@@ -745,15 +745,15 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) { ...@@ -745,15 +745,15 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
// var vauto *[...]t = new([...]t) // var vauto *[...]t = new([...]t)
// 4. copy the static array to the auto array // 4. copy the static array to the auto array
// *vauto = vstat // *vauto = vstat
// 5. assign slice of allocated heap to var // 5. for each dynamic part assign to the array
// var = [0:]*auto // vauto[i] = dynamic part
// 6. for each dynamic part assign to the slice // 6. assign slice of allocated heap to var
// var[i] = dynamic part // var = vauto[:]
// //
// an optimization is done if there is no constant part // an optimization is done if there is no constant part
// 3. var vauto *[...]t = new([...]t) // 3. var vauto *[...]t = new([...]t)
// 5. var = [0:]*auto // 5. vauto[i] = dynamic part
// 6. var[i] = dynamic part // 6. var = vauto[:]
// if the literal contains constants, // if the literal contains constants,
// make static initialized array (1),(2) // make static initialized array (1),(2)
...@@ -811,21 +811,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) { ...@@ -811,21 +811,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
init.Append(a) init.Append(a)
} }
// make slice out of heap (5) // put dynamics into array (5)
a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil)))
a = typecheck(a, Etop)
a = orderstmtinplace(a)
a = walkstmt(a)
init.Append(a)
// put dynamics into slice (6)
for _, r := range n.List.Slice() { for _, r := range n.List.Slice() {
if r.Op != OKEY { if r.Op != OKEY {
Fatalf("slicelit: rhs not OKEY: %v", r) Fatalf("slicelit: rhs not OKEY: %v", r)
} }
index := r.Left index := r.Left
value := r.Right value := r.Right
a := Nod(OINDEX, var_, index) a := Nod(OINDEX, vauto, index)
a.Bounded = true a.Bounded = true
// TODO need to check bounds? // TODO need to check bounds?
...@@ -847,7 +840,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) { ...@@ -847,7 +840,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
continue continue
} }
// build list of var[c] = expr // build list of vauto[c] = expr
setlineno(value) setlineno(value)
a = Nod(OAS, a, value) a = Nod(OAS, a, value)
...@@ -856,6 +849,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) { ...@@ -856,6 +849,14 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
a = walkstmt(a) a = walkstmt(a)
init.Append(a) init.Append(a)
} }
// make slice out of heap (6)
a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil)))
a = typecheck(a, Etop)
a = orderstmtinplace(a)
a = walkstmt(a)
init.Append(a)
} }
func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) { func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
......
...@@ -2748,8 +2748,7 @@ func addstr(n *Node, init *Nodes) *Node { ...@@ -2748,8 +2748,7 @@ func addstr(n *Node, init *Nodes) *Node {
prealloc[slice] = prealloc[n] prealloc[slice] = prealloc[n]
} }
slice.List.Set(args[1:]) // skip buf arg slice.List.Set(args[1:]) // skip buf arg
args = []*Node{buf} args = []*Node{buf, slice}
args = append(args, slice)
slice.Esc = EscNone slice.Esc = EscNone
} }
......
...@@ -182,3 +182,17 @@ func f18(p *T18, x *[]int) { ...@@ -182,3 +182,17 @@ func f18(p *T18, x *[]int) {
p.s = p.s[8:9] // ERROR "write barrier" p.s = p.s[8:9] // ERROR "write barrier"
*x = (*x)[3:5] // ERROR "write barrier" *x = (*x)[3:5] // ERROR "write barrier"
} }
func f19(x, y *int, i int) int {
// Constructing a temporary slice on the stack should not
// require any write barriers. See issue 14263.
a := []*int{x, y} // no barrier
return *a[i]
}
func f20(x, y *int, i int) []*int {
// ... but if that temporary slice escapes, then the
// write barriers are necessary.
a := []*int{x, y} // ERROR "write barrier"
return a
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment