]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: statically initialize some interface values
authorJosh Bleecher Snyder <josharian@gmail.com>
Fri, 13 May 2016 00:22:47 +0000 (17:22 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Mon, 12 Sep 2016 14:31:26 +0000 (14:31 +0000)
When possible, emit static data rather than
init functions for interface values.

This:

* cuts 32k off cmd/go
* removes several error values from runtime init
* cuts the size of the image/color/palette compiled package from 103k to 34k
* reduces the time to build the package in #15520 from 8s to 1.5s

Fixes #6289
Fixes #15528

Change-Id: I317112da17aadb180c958ea328ab380f83e640b4
Reviewed-on: https://go-review.googlesource.com/26668
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/gc/reflect.go
src/cmd/compile/internal/gc/sinit.go
test/fixedbugs/issue15528.go [new file with mode: 0644]

index caedbaa0fe6b5cd840cfc1a85240ecb7bbb079b3..337f8ccbe7a1b6d3dda1ed1e3a4176a5316bb72e 100644 (file)
@@ -978,8 +978,8 @@ func typename(t *Type) *Node {
 }
 
 func itabname(t, itype *Type) *Node {
-       if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
-               Fatalf("itabname %v", t)
+       if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
+               Fatalf("itabname(%v, %v)", t, itype)
        }
        s := Pkglookup(fmt.Sprintf("%-v,%-v", t, itype), itabpkg)
        if s.Def == nil {
index 743e508bf1bdd7fe464633e08d4faf1c8bea9ecf..4666433e53144712db5b279b38253b540d48ce1d 100644 (file)
@@ -4,9 +4,7 @@
 
 package gc
 
-import (
-       "fmt"
-)
+import "fmt"
 
 // static initialization
 const (
@@ -479,6 +477,67 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
                } else {
                        closuredebugruntimecheck(r)
                }
+
+       case OCONVIFACE:
+               // This logic is mirrored in isStaticCompositeLiteral.
+               // If you change something here, change it there, and vice versa.
+
+               // Determine the underlying concrete type and value we are converting from.
+               val := r
+               for val.Op == OCONVIFACE {
+                       val = val.Left
+               }
+               if val.Type.IsInterface() {
+                       // val is an interface type.
+                       // If val is nil, we can statically initialize l;
+                       // both words are zero and so there no work to do, so report success.
+                       // If val is non-nil, we have no concrete type to record,
+                       // and we won't be able to statically initialize its value, so report failure.
+                       return Isconst(val, CTNIL)
+               }
+
+               var itab *Node
+               if l.Type.IsEmptyInterface() {
+                       itab = typename(val.Type)
+               } else {
+                       itab = itabname(val.Type, l.Type)
+               }
+
+               // Create a copy of l to modify while we emit data.
+               n := *l
+
+               // Emit itab, advance offset.
+               gdata(&n, itab, Widthptr)
+               n.Xoffset += int64(Widthptr)
+
+               // Emit data.
+               if isdirectiface(val.Type) {
+                       if Isconst(val, CTNIL) {
+                               // Nil is zero, nothing to do.
+                               return true
+                       }
+                       // Copy val directly into n.
+                       n.Type = val.Type
+                       setlineno(val)
+                       a := Nod(OXXX, nil, nil)
+                       *a = n
+                       a.Orig = a
+                       if !staticassign(a, val, out) {
+                               *out = append(*out, Nod(OAS, a, val))
+                       }
+               } else {
+                       // Construct temp to hold val, write pointer to temp into n.
+                       a := staticname(val.Type)
+                       inittemps[val] = a
+                       if !staticassign(a, val, out) {
+                               *out = append(*out, Nod(OAS, a, val))
+                       }
+                       ptr := Nod(OADDR, a, nil)
+                       n.Type = Ptrto(val.Type)
+                       gdata(&n, ptr, Widthptr)
+               }
+
+               return true
        }
 
        //dump("not static", r);
@@ -593,6 +652,19 @@ func isStaticCompositeLiteral(n *Node) bool {
                return true
        case OLITERAL:
                return true
+       case OCONVIFACE:
+               // See staticassign's OCONVIFACE case for comments.
+               val := n
+               for val.Op == OCONVIFACE {
+                       val = val.Left
+               }
+               if val.Type.IsInterface() {
+                       return Isconst(val, CTNIL)
+               }
+               if isdirectiface(val.Type) && Isconst(val, CTNIL) {
+                       return true
+               }
+               return isStaticCompositeLiteral(val)
        }
        return false
 }
diff --git a/test/fixedbugs/issue15528.go b/test/fixedbugs/issue15528.go
new file mode 100644 (file)
index 0000000..b1f9dfb
--- /dev/null
@@ -0,0 +1,131 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "fmt"
+       "io"
+       "os"
+       "reflect"
+       "unsafe"
+)
+
+type RWS struct{}
+
+func (x *RWS) Read(p []byte) (n int, err error)                   { return }
+func (x *RWS) Write(p []byte) (n int, err error)                  { return }
+func (x *RWS) Seek(offset int64, whence int) (n int64, err error) { return }
+func (x *RWS) String() string                                     { return "rws" }
+
+func makeRWS() io.ReadWriteSeeker { return &RWS{} }
+func makeStringer() fmt.Stringer  { return &RWS{} }
+
+// Test correct construction of static empty interface values
+var efaces = [...]struct {
+       x interface{}
+       s string
+}{
+       {nil, "<nil> <nil>"},
+       {1, "int 1"},
+       {int(1), "int 1"},
+       {Int(int(2)), "main.Int Int=2"},
+       {int(Int(3)), "int 3"},
+       {[1]int{2}, "[1]int [2]"},
+       {io.Reader(io.ReadWriter(io.ReadWriteSeeker(nil))), "<nil> <nil>"},
+       {io.Reader(io.ReadWriter(io.ReadWriteSeeker(&RWS{}))), "*main.RWS rws"},
+       {makeRWS(), "*main.RWS rws"},
+       {map[string]string{"here": "there"}, "map[string]string map[here:there]"},
+       {chan bool(nil), "chan bool <nil>"},
+       {unsafe.Pointer(uintptr(0)), "unsafe.Pointer <nil>"},
+       {(*byte)(nil), "*uint8 <nil>"},
+       {io.Writer((*os.File)(nil)), "*os.File <nil>"},
+       {(interface{})(io.Writer((*os.File)(nil))), "*os.File <nil>"},
+       {fmt.Stringer(Strunger(((*Int)(nil)))), "*main.Int <nil>"},
+}
+
+type Int int
+
+func (i Int) String() string { return fmt.Sprintf("Int=%d", i) }
+func (i Int) Strung()        {}
+
+type Strunger interface {
+       fmt.Stringer
+       Strung()
+}
+
+// Test correct construction of static non-empty interface values
+var ifaces = [...]struct {
+       x fmt.Stringer
+       s string
+}{
+       {nil, "<nil> <nil> %!s(<nil>)"},
+       {Int(3), "main.Int 3 Int=3"},
+       {Int(int(Int(4))), "main.Int 4 Int=4"},
+       {Strunger(Int(5)), "main.Int 5 Int=5"},
+       {makeStringer(), "*main.RWS &main.RWS{} rws"},
+       {fmt.Stringer(nil), "<nil> <nil> %!s(<nil>)"},
+       {(*RWS)(nil), "*main.RWS (*main.RWS)(nil) rws"},
+}
+
+// Test correct handling of direct interface values
+var (
+       one  int         = 1
+       iptr interface{} = &one
+       clos int
+       f    interface{} = func() { clos++ }
+       deep interface{} = [1]struct{ a *[2]byte }{{a: &[2]byte{'z', 'w'}}}
+       ch   interface{} = make(chan bool, 1)
+)
+
+func main() {
+       var fail bool
+       for i, test := range efaces {
+               s := fmt.Sprintf("%[1]T %[1]v", test.x)
+               if s != test.s {
+                       fmt.Printf("eface(%d)=%q want %q\n", i, s, test.s)
+                       fail = true
+               }
+       }
+
+       for i, test := range ifaces {
+               s := fmt.Sprintf("%[1]T %#[1]v %[1]s", test.x)
+               if s != test.s {
+                       fmt.Printf("iface(%d)=%q want %q\n", i, s, test.s)
+                       fail = true
+               }
+       }
+
+       if got := *(iptr.(*int)); got != 1 {
+               fmt.Printf("bad int ptr %d\n", got)
+               fail = true
+       }
+
+       f.(func())()
+       f.(func())()
+       f.(func())()
+       if clos != 3 {
+               fmt.Printf("bad closure exec %d\n", clos)
+               fail = true
+       }
+
+       if !reflect.DeepEqual(*(deep.([1]struct{ a *[2]byte })[0].a), [2]byte{'z', 'w'}) {
+               fmt.Printf("bad deep directiface\n")
+               fail = true
+       }
+
+       cc := ch.(chan bool)
+       cc <- true
+       if got := <-cc; !got {
+               fmt.Printf("bad chan\n")
+               fail = true
+       }
+
+       if fail {
+               fmt.Println("BUG")
+               os.Exit(1)
+       }
+}