Skip to content

Commit dfc56a4

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent b8eb5b5 commit dfc56a4

File tree

3 files changed

+208
-5
lines changed

3 files changed

+208
-5
lines changed

src/cmd/compile/internal/gc/reflect.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,8 @@ func typename(t *Type) *Node {
978978
}
979979

980980
func itabname(t, itype *Type) *Node {
981-
if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
982-
Fatalf("itabname %v", t)
981+
if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
982+
Fatalf("itabname(%v, %v)", t, itype)
983983
}
984984
s := Pkglookup(fmt.Sprintf("%-v,%-v", t, itype), itabpkg)
985985
if s.Def == nil {

src/cmd/compile/internal/gc/sinit.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
package gc
66

7-
import (
8-
"fmt"
9-
)
7+
import "fmt"
108

119
// static initialization
1210
const (
@@ -479,6 +477,67 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
479477
} else {
480478
closuredebugruntimecheck(r)
481479
}
480+
481+
case OCONVIFACE:
482+
// This logic is mirrored in isStaticCompositeLiteral.
483+
// If you change something here, change it there, and vice versa.
484+
485+
// Determine the underlying concrete type and value we are converting from.
486+
val := r
487+
for val.Op == OCONVIFACE {
488+
val = val.Left
489+
}
490+
if val.Type.IsInterface() {
491+
// val is an interface type.
492+
// If val is nil, we can statically initialize l;
493+
// both words are zero and so there no work to do, so report success.
494+
// If val is non-nil, we have no concrete type to record,
495+
// and we won't be able to statically initialize its value, so report failure.
496+
return Isconst(val, CTNIL)
497+
}
498+
499+
var itab *Node
500+
if l.Type.IsEmptyInterface() {
501+
itab = typename(val.Type)
502+
} else {
503+
itab = itabname(val.Type, l.Type)
504+
}
505+
506+
// Create a copy of l to modify while we emit data.
507+
n := *l
508+
509+
// Emit itab, advance offset.
510+
gdata(&n, itab, Widthptr)
511+
n.Xoffset += int64(Widthptr)
512+
513+
// Emit data.
514+
if isdirectiface(val.Type) {
515+
if Isconst(val, CTNIL) {
516+
// Nil is zero, nothing to do.
517+
return true
518+
}
519+
// Copy val directly into n.
520+
n.Type = val.Type
521+
setlineno(val)
522+
a := Nod(OXXX, nil, nil)
523+
*a = n
524+
a.Orig = a
525+
if !staticassign(a, val, out) {
526+
*out = append(*out, Nod(OAS, a, val))
527+
}
528+
} else {
529+
// Construct temp to hold val, write pointer to temp into n.
530+
a := staticname(val.Type)
531+
inittemps[val] = a
532+
if !staticassign(a, val, out) {
533+
*out = append(*out, Nod(OAS, a, val))
534+
}
535+
ptr := Nod(OADDR, a, nil)
536+
n.Type = Ptrto(val.Type)
537+
gdata(&n, ptr, Widthptr)
538+
}
539+
540+
return true
482541
}
483542

484543
//dump("not static", r);
@@ -593,6 +652,19 @@ func isStaticCompositeLiteral(n *Node) bool {
593652
return true
594653
case OLITERAL:
595654
return true
655+
case OCONVIFACE:
656+
// See staticassign's OCONVIFACE case for comments.
657+
val := n
658+
for val.Op == OCONVIFACE {
659+
val = val.Left
660+
}
661+
if val.Type.IsInterface() {
662+
return Isconst(val, CTNIL)
663+
}
664+
if isdirectiface(val.Type) && Isconst(val, CTNIL) {
665+
return true
666+
}
667+
return isStaticCompositeLiteral(val)
596668
}
597669
return false
598670
}

test/fixedbugs/issue15528.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// run
2+
3+
// Copyright 2016 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
"io"
12+
"os"
13+
"reflect"
14+
"unsafe"
15+
)
16+
17+
type RWS struct{}
18+
19+
func (x *RWS) Read(p []byte) (n int, err error) { return }
20+
func (x *RWS) Write(p []byte) (n int, err error) { return }
21+
func (x *RWS) Seek(offset int64, whence int) (n int64, err error) { return }
22+
func (x *RWS) String() string { return "rws" }
23+
24+
func makeRWS() io.ReadWriteSeeker { return &RWS{} }
25+
func makeStringer() fmt.Stringer { return &RWS{} }
26+
27+
// Test correct construction of static empty interface values
28+
var efaces = [...]struct {
29+
x interface{}
30+
s string
31+
}{
32+
{nil, "<nil> <nil>"},
33+
{1, "int 1"},
34+
{int(1), "int 1"},
35+
{Int(int(2)), "main.Int Int=2"},
36+
{int(Int(3)), "int 3"},
37+
{[1]int{2}, "[1]int [2]"},
38+
{io.Reader(io.ReadWriter(io.ReadWriteSeeker(nil))), "<nil> <nil>"},
39+
{io.Reader(io.ReadWriter(io.ReadWriteSeeker(&RWS{}))), "*main.RWS rws"},
40+
{makeRWS(), "*main.RWS rws"},
41+
{map[string]string{"here": "there"}, "map[string]string map[here:there]"},
42+
{chan bool(nil), "chan bool <nil>"},
43+
{unsafe.Pointer(uintptr(0)), "unsafe.Pointer <nil>"},
44+
{(*byte)(nil), "*uint8 <nil>"},
45+
{io.Writer((*os.File)(nil)), "*os.File <nil>"},
46+
{(interface{})(io.Writer((*os.File)(nil))), "*os.File <nil>"},
47+
{fmt.Stringer(Strunger(((*Int)(nil)))), "*main.Int <nil>"},
48+
}
49+
50+
type Int int
51+
52+
func (i Int) String() string { return fmt.Sprintf("Int=%d", i) }
53+
func (i Int) Strung() {}
54+
55+
type Strunger interface {
56+
fmt.Stringer
57+
Strung()
58+
}
59+
60+
// Test correct construction of static non-empty interface values
61+
var ifaces = [...]struct {
62+
x fmt.Stringer
63+
s string
64+
}{
65+
{nil, "<nil> <nil> %!s(<nil>)"},
66+
{Int(3), "main.Int 3 Int=3"},
67+
{Int(int(Int(4))), "main.Int 4 Int=4"},
68+
{Strunger(Int(5)), "main.Int 5 Int=5"},
69+
{makeStringer(), "*main.RWS &main.RWS{} rws"},
70+
{fmt.Stringer(nil), "<nil> <nil> %!s(<nil>)"},
71+
{(*RWS)(nil), "*main.RWS (*main.RWS)(nil) rws"},
72+
}
73+
74+
// Test correct handling of direct interface values
75+
var (
76+
one int = 1
77+
iptr interface{} = &one
78+
clos int
79+
f interface{} = func() { clos++ }
80+
deep interface{} = [1]struct{ a *[2]byte }{{a: &[2]byte{'z', 'w'}}}
81+
ch interface{} = make(chan bool, 1)
82+
)
83+
84+
func main() {
85+
var fail bool
86+
for i, test := range efaces {
87+
s := fmt.Sprintf("%[1]T %[1]v", test.x)
88+
if s != test.s {
89+
fmt.Printf("eface(%d)=%q want %q\n", i, s, test.s)
90+
fail = true
91+
}
92+
}
93+
94+
for i, test := range ifaces {
95+
s := fmt.Sprintf("%[1]T %#[1]v %[1]s", test.x)
96+
if s != test.s {
97+
fmt.Printf("iface(%d)=%q want %q\n", i, s, test.s)
98+
fail = true
99+
}
100+
}
101+
102+
if got := *(iptr.(*int)); got != 1 {
103+
fmt.Printf("bad int ptr %d\n", got)
104+
fail = true
105+
}
106+
107+
f.(func())()
108+
f.(func())()
109+
f.(func())()
110+
if clos != 3 {
111+
fmt.Printf("bad closure exec %d\n", clos)
112+
fail = true
113+
}
114+
115+
if !reflect.DeepEqual(*(deep.([1]struct{ a *[2]byte })[0].a), [2]byte{'z', 'w'}) {
116+
fmt.Printf("bad deep directiface\n")
117+
fail = true
118+
}
119+
120+
cc := ch.(chan bool)
121+
cc <- true
122+
if got := <-cc; !got {
123+
fmt.Printf("bad chan\n")
124+
fail = true
125+
}
126+
127+
if fail {
128+
fmt.Println("BUG")
129+
os.Exit(1)
130+
}
131+
}

0 commit comments

Comments
 (0)