mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: statically initialize some interface values
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>
This commit is contained in:
parent
b8eb5b53ea
commit
dfc56a4cd3
3 changed files with 208 additions and 5 deletions
|
|
@ -978,8 +978,8 @@ func typename(t *Type) *Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func itabname(t, itype *Type) *Node {
|
func itabname(t, itype *Type) *Node {
|
||||||
if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
|
if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
|
||||||
Fatalf("itabname %v", t)
|
Fatalf("itabname(%v, %v)", t, itype)
|
||||||
}
|
}
|
||||||
s := Pkglookup(fmt.Sprintf("%-v,%-v", t, itype), itabpkg)
|
s := Pkglookup(fmt.Sprintf("%-v,%-v", t, itype), itabpkg)
|
||||||
if s.Def == nil {
|
if s.Def == nil {
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@
|
||||||
|
|
||||||
package gc
|
package gc
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// static initialization
|
// static initialization
|
||||||
const (
|
const (
|
||||||
|
|
@ -479,6 +477,67 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
|
||||||
} else {
|
} else {
|
||||||
closuredebugruntimecheck(r)
|
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);
|
//dump("not static", r);
|
||||||
|
|
@ -593,6 +652,19 @@ func isStaticCompositeLiteral(n *Node) bool {
|
||||||
return true
|
return true
|
||||||
case OLITERAL:
|
case OLITERAL:
|
||||||
return true
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
131
test/fixedbugs/issue15528.go
Normal file
131
test/fixedbugs/issue15528.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue