Skip to content

Commit 9afcd6e

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 golang#15520 from 8s to 1.5s Fixes golang#6289 Fixes golang#15528 Change-Id: I317112da17aadb180c958ea328ab380f83e640b4
1 parent f3d5478 commit 9afcd6e

File tree

2 files changed

+207
-15
lines changed

2 files changed

+207
-15
lines changed

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

Lines changed: 91 additions & 15 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 (
@@ -489,6 +487,68 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
489487
} else {
490488
closuredebugruntimecheck(r)
491489
}
490+
491+
case OCONVIFACE:
492+
// This logic is mirrored in isStaticCompositeLiteral.
493+
// If you change something here, change it there, and vice versa.
494+
495+
// Determine the underlying concrete type and value we are converting from.
496+
val := r
497+
for val.Op == OCONVIFACE {
498+
val = val.Left
499+
}
500+
if Isconst(val, CTNIL) && r.Left.Type.IsInterface() {
501+
// The whole thing is nil, so both words
502+
// are zero, so there's nothing to do.
503+
return true
504+
}
505+
506+
var itab *Node
507+
if l.Type.IsEmptyInterface() {
508+
itab = typename(val.Type)
509+
} else {
510+
if val.Type.IsInterface() {
511+
// No concrete type, and we won't be able to staticly initialize its value. Bail.
512+
return false
513+
}
514+
itab = itabname(val.Type, l.Type)
515+
}
516+
517+
// Create a copy of l to modify while we emit data.
518+
n := *l
519+
520+
// Emit itab, advance offset.
521+
gdata(&n, itab, Widthptr)
522+
n.Xoffset += int64(Widthptr)
523+
524+
// Emit data.
525+
if isdirectiface(val.Type) {
526+
if Isconst(val, CTNIL) {
527+
// Nil is zero, nothing to do.
528+
return true
529+
}
530+
// Copy val directly into n.
531+
n.Type = val.Type
532+
setlineno(val)
533+
a := Nod(OXXX, nil, nil)
534+
*a = n
535+
a.Orig = a
536+
if !staticassign(a, val, out) {
537+
*out = append(*out, Nod(OAS, a, val))
538+
}
539+
} else {
540+
// Construct temp to hold val, write pointer to temp into n.
541+
a := staticname(val.Type, 1)
542+
inittemps[val] = a
543+
if !staticassign(a, val, out) {
544+
*out = append(*out, Nod(OAS, a, val))
545+
}
546+
ptr := Nod(OADDR, a, nil)
547+
n.Type = Ptrto(val.Type)
548+
gdata(&n, ptr, Widthptr)
549+
}
550+
551+
return true
492552
}
493553

494554
//dump("not static", r);
@@ -568,26 +628,42 @@ func isStaticCompositeLiteral(n *Node) bool {
568628
if n.Type.IsSlice() {
569629
return false
570630
}
631+
fallthrough
571632
case OSTRUCTLIT:
633+
for _, r := range n.List.Slice() {
634+
if r.Op != OKEY {
635+
Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
636+
}
637+
index := r.Left
638+
if n.Op == OARRAYLIT && index.Op != OLITERAL {
639+
return false
640+
}
641+
value := r.Right
642+
if !isStaticCompositeLiteral(value) {
643+
return false
644+
}
645+
}
646+
return true
572647
case OLITERAL:
573648
return true
574-
default:
575-
return false
576-
}
577-
for _, r := range n.List.Slice() {
578-
if r.Op != OKEY {
579-
Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
649+
case OCONVIFACE:
650+
// See staticassign's OCONVIFACE case for comments.
651+
val := n
652+
for val.Op == OCONVIFACE {
653+
val = val.Left
580654
}
581-
index := r.Left
582-
if n.Op == OARRAYLIT && index.Op != OLITERAL {
583-
return false
655+
if Isconst(val, CTNIL) && n.Left.Type.IsInterface() {
656+
return true
584657
}
585-
value := r.Right
586-
if !isStaticCompositeLiteral(value) {
658+
if !n.Type.IsEmptyInterface() && val.Type.IsInterface() {
587659
return false
588660
}
661+
if Isconst(val, CTNIL) {
662+
return true
663+
}
664+
return isStaticCompositeLiteral(val)
589665
}
590-
return true
666+
return false
591667
}
592668

593669
func structlit(ctxt int, pass int, n *Node, var_ *Node, init *Nodes) {

test/fixedbugs/issue15528.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
23+
// Test correct construction of static empty interface values
24+
var efaces = [...]struct {
25+
x interface{}
26+
s string
27+
}{
28+
{nil, "<nil> <nil>"},
29+
{1, "int 1"},
30+
{[1]int{2}, "[1]int [2]"},
31+
{io.Reader(io.ReadWriter(io.ReadWriteSeeker(nil))), "<nil> <nil>"},
32+
{io.Reader(io.ReadWriter(io.ReadWriteSeeker(&RWS{}))), "*main.RWS &{}"},
33+
{map[string]string{"here": "there"}, "map[string]string map[here:there]"},
34+
{chan bool(nil), "chan bool <nil>"},
35+
{unsafe.Pointer(uintptr(0)), "unsafe.Pointer <nil>"},
36+
}
37+
38+
type Int int
39+
40+
func (i Int) String() string { return fmt.Sprintf("Int=%d", i) }
41+
func (i Int) Strung() {}
42+
43+
type Strunger interface {
44+
fmt.Stringer
45+
Strung()
46+
}
47+
48+
// Test correct construction of static non-empty interface values
49+
var ifaces = [...]struct {
50+
x fmt.Stringer
51+
s string
52+
}{
53+
{nil, "<nil> <nil> %!s(<nil>)"},
54+
{Int(3), "main.Int 3 Int=3"},
55+
{Int(int(Int(4))), "main.Int 4 Int=4"},
56+
{Strunger(Int(5)), "main.Int 5 Int=5"},
57+
}
58+
59+
// Test correct handling of direct interface values
60+
var (
61+
one int = 1
62+
iptr interface{} = &one
63+
clos int
64+
f interface{} = func() { clos++ }
65+
deep interface{} = [1]struct{ a *[2]byte }{{a: &[2]byte{'z', 'w'}}}
66+
ch interface{} = make(chan bool, 1)
67+
)
68+
69+
func main() {
70+
var fail bool
71+
for i, test := range efaces {
72+
s := fmt.Sprintf("%[1]T %[1]v", test.x)
73+
if s != test.s {
74+
fmt.Printf("eface(%d)=%q want %q\n", i, s, test.s)
75+
fail = true
76+
}
77+
}
78+
79+
for i, test := range ifaces {
80+
s := fmt.Sprintf("%[1]T %#[1]v %[1]s", test.x)
81+
if s != test.s {
82+
fmt.Printf("iface(%d)=%q want %q\n", i, s, test.s)
83+
fail = true
84+
}
85+
}
86+
87+
if got := *(iptr.(*int)); got != 1 {
88+
fmt.Printf("bad int ptr %d\n", got)
89+
fail = true
90+
}
91+
92+
f.(func())()
93+
f.(func())()
94+
f.(func())()
95+
if clos != 3 {
96+
fmt.Printf("bad closure exec %d\n", clos)
97+
fail = true
98+
}
99+
100+
if !reflect.DeepEqual(*(deep.([1]struct{ a *[2]byte })[0].a), [2]byte{'z', 'w'}) {
101+
fmt.Printf("bad deep directiface\n")
102+
fail = true
103+
}
104+
105+
cc := ch.(chan bool)
106+
cc <- true
107+
if got := <-cc; !got {
108+
fmt.Printf("bad chan\n")
109+
fail = true
110+
}
111+
112+
if fail {
113+
fmt.Println("BUG")
114+
os.Exit(1)
115+
}
116+
}

0 commit comments

Comments
 (0)