Skip to content

Commit a9ea36a

Browse files
committed
cmd/compile: export inlined function bodies
Completed implementation for exporting inlined functions using the new binary export format. This change passes (export GO_GCFLAGS=-newexport; make all.bash) but for gc's builtin_test.go which we need to adjust before enabling this code by default. For a high-level description of the export format see the comment at the top of bexport.go. Major changes: 1) The export format for the platform independent export data changed: When we export inlined function bodies, additional objects (other functions, types, etc.) that are referred to by the function bodies will need to be exported. While this doesn't affect the platform-independent portion directly, it adds more objects to the exportlist while we are exporting. Instead of trying to sort the objects into groups, just export objects as they appear in the export list. This is slightly less compact (one extra byte per object), but it is simpler and much more flexible. 2) The export format contains now three sections: 1) The plat- form independent objects, 2) the objects pulled in for export via inlined function bodies, and 3) the inlined function bodies. 3) Completed the exporting and importing code for inlined function bodies. The format is completely compiler-specific and easily changeable w/o affecting other tools. There is still quite a bit of room for denser encoding. This can happen at any time in the future. This change contains also the adjustments for go/internal/gcimporter, necessary because of the export format change 1) mentioned above. For #13241. Change-Id: I86bca0bd984b12ccf13d0d30892e6e25f6d04ed5 Reviewed-on: https://go-review.googlesource.com/21172 Run-TryBot: Robert Griesemer <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 38e11d0 commit a9ea36a

File tree

10 files changed

+1373
-660
lines changed

10 files changed

+1373
-660
lines changed

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

Lines changed: 763 additions & 346 deletions
Large diffs are not rendered by default.

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

Lines changed: 493 additions & 237 deletions
Large diffs are not rendered by default.

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,17 @@ func markdcl() {
7979
block = blockgen
8080
}
8181

82-
func dumpdcl(st string) {
82+
// keep around for debugging
83+
func dumpdclstack() {
8384
i := 0
8485
for d := dclstack; d != nil; d = d.Link {
85-
i++
86-
fmt.Printf(" %.2d %p", i, d)
87-
if d.Name == "" {
88-
fmt.Printf("\n")
89-
continue
86+
fmt.Printf("%6d %p", i, d)
87+
if d.Name != "" {
88+
fmt.Printf(" '%s' %v\n", d.Name, Pkglookup(d.Name, d.Pkg))
89+
} else {
90+
fmt.Printf(" ---\n")
9091
}
91-
92-
fmt.Printf(" '%s'", d.Name)
93-
fmt.Printf(" %v\n", Pkglookup(d.Name, d.Pkg))
92+
i++
9493
}
9594
}
9695

@@ -250,11 +249,8 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
250249
Yyerror("missing expression in var declaration")
251250
break
252251
}
253-
254252
e = el[0]
255253
el = el[1:]
256-
} else {
257-
e = nil
258254
}
259255

260256
v.Op = ONAME
@@ -527,7 +523,7 @@ func ifacedcl(n *Node) {
527523
func funchdr(n *Node) {
528524
// change the declaration context from extern to auto
529525
if Funcdepth == 0 && dclcontext != PEXTERN {
530-
Fatalf("funchdr: dclcontext")
526+
Fatalf("funchdr: dclcontext = %d", dclcontext)
531527
}
532528

533529
if importpkg == nil && n.Func.Nname != nil {
@@ -678,7 +674,7 @@ func funcargs2(t *Type) {
678674
func funcbody(n *Node) {
679675
// change the declaration context from auto to extern
680676
if dclcontext != PAUTO {
681-
Fatalf("funcbody: dclcontext")
677+
Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
682678
}
683679
popdcl()
684680
Funcdepth--

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,8 @@ func dumpexport() {
380380
if forceNewExport || newexport != 0 {
381381
// binary export
382382
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
383-
exportf("\n$$B\n") // indicate binary format
384-
const verifyExport = true // enable to check format changes
385-
if verifyExport {
383+
exportf("\n$$B\n") // indicate binary format
384+
if debugFormat {
386385
// save a copy of the export data
387386
var copy bytes.Buffer
388387
bcopy := obj.Binitw(&copy)
@@ -430,9 +429,8 @@ func dumpexport() {
430429
}
431430

432431
// exportlist grows during iteration - cannot use range
433-
for len(exportlist) > 0 {
434-
n := exportlist[0]
435-
exportlist = exportlist[1:]
432+
for i := 0; i < len(exportlist); i++ {
433+
n := exportlist[i]
436434
lineno = n.Lineno
437435
dumpsym(n.Sym)
438436
}

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ func stmtfmt(n *Node) string {
801801
}
802802

803803
// Don't export "v = <N>" initializing statements, hope they're always
804-
// preceded by the DCL which will be re-parsed and typecheck to reproduce
804+
// preceded by the DCL which will be re-parsed and typechecked to reproduce
805805
// the "v = <N>" again.
806806
case OAS, OASWB:
807807
if fmtmode == FExp && n.Right == nil {
@@ -1146,9 +1146,7 @@ func exprfmt(n *Node, prec int) string {
11461146
if n.Left != nil {
11471147
return fmt.Sprintf("[]%v", n.Left)
11481148
}
1149-
var f string
1150-
f += fmt.Sprintf("[]%v", n.Right)
1151-
return f // happens before typecheck
1149+
return fmt.Sprintf("[]%v", n.Right) // happens before typecheck
11521150

11531151
case OTMAP:
11541152
return fmt.Sprintf("map[%v]%v", n.Left, n.Right)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func typecheckinl(fn *Node) {
6666
return // typecheckinl on local function
6767
}
6868

69-
if Debug['m'] > 2 {
69+
if Debug['m'] > 2 || Debug_export != 0 {
7070
fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, FmtLong), Hconv(fn.Func.Inl, FmtSharp))
7171
}
7272

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,9 @@ func importfile(f *Val, indent []byte) {
815815

816816
case 'B':
817817
// new export format
818+
if Debug_export != 0 {
819+
fmt.Printf("importing %s (%s)\n", path_, file)
820+
}
818821
imp.ReadByte() // skip \n after $$B
819822
Import(imp)
820823

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1588,7 +1588,7 @@ func (p *parser) onew_name() *Node {
15881588
func (p *parser) sym() *Sym {
15891589
switch p.tok {
15901590
case LNAME:
1591-
s := p.sym_
1591+
s := p.sym_ // from localpkg
15921592
p.next()
15931593
// during imports, unqualified non-exported identifiers are from builtinpkg
15941594
if importpkg != nil && !exportname(s.Name) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,12 @@ func compile(fn *Node) {
490490
}
491491
}
492492

493+
type symByName []*Sym
494+
495+
func (a symByName) Len() int { return len(a) }
496+
func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
497+
func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
498+
493499
// genlegacy compiles Curfn using the legacy non-SSA code generator.
494500
func genlegacy(ptxt *obj.Prog, gcargs, gclocals *Sym) {
495501
Genlist(Curfn.Func.Enter)

src/go/internal/gcimporter/bimport.go

Lines changed: 90 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ import (
1515
"unicode/utf8"
1616
)
1717

18+
type importer struct {
19+
imports map[string]*types.Package
20+
data []byte
21+
buf []byte // for reading strings
22+
bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
23+
pkgList []*types.Package
24+
typList []types.Type
25+
26+
debugFormat bool
27+
read int // bytes read
28+
}
29+
1830
// BImportData imports a package from the serialized package data
1931
// and returns the number of bytes consumed and a reference to the package.
2032
// If data is obviously malformed, an error is returned but in
@@ -39,7 +51,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
3951
// --- generic export data ---
4052

4153
if v := p.string(); v != "v0" {
42-
return p.read, nil, fmt.Errorf("unknown version: %s", v)
54+
return p.read, nil, fmt.Errorf("unknown export data version: %s", v)
4355
}
4456

4557
// populate typList with predeclared "known" types
@@ -69,37 +81,20 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
6981
// read compiler-specific flags
7082
p.string() // discard
7183

72-
// read consts
73-
for i := p.int(); i > 0; i-- {
74-
name := p.string()
75-
typ := p.typ(nil)
76-
val := p.value()
77-
p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
78-
}
79-
80-
// read vars
81-
for i := p.int(); i > 0; i-- {
82-
name := p.string()
83-
typ := p.typ(nil)
84-
p.declare(types.NewVar(token.NoPos, pkg, name, typ))
85-
}
86-
87-
// read funcs
88-
for i := p.int(); i > 0; i-- {
89-
name := p.string()
90-
params, isddd := p.paramList()
91-
result, _ := p.paramList()
92-
sig := types.NewSignature(nil, params, result, isddd)
93-
p.int() // read and discard index of inlined function body
94-
p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
84+
// read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go)
85+
objcount := 0
86+
for {
87+
tag := p.tagOrIndex()
88+
if tag == endTag {
89+
break
90+
}
91+
p.obj(tag)
92+
objcount++
9593
}
9694

97-
// read types
98-
for i := p.int(); i > 0; i-- {
99-
// name is parsed as part of named type and the
100-
// type object is added to scope via respective
101-
// named type
102-
_ = p.typ(nil).(*types.Named)
95+
// self-verification
96+
if count := p.int(); count != objcount {
97+
panic(fmt.Sprintf("importer: got %d objects; want %d", objcount, count))
10398
}
10499

105100
// ignore compiler-specific import data
@@ -122,25 +117,6 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
122117
return p.read, pkg, nil
123118
}
124119

125-
type importer struct {
126-
imports map[string]*types.Package
127-
data []byte
128-
buf []byte // for reading strings
129-
bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
130-
pkgList []*types.Package
131-
typList []types.Type
132-
133-
debugFormat bool
134-
read int // bytes read
135-
}
136-
137-
func (p *importer) declare(obj types.Object) {
138-
if alt := p.pkgList[0].Scope().Insert(obj); alt != nil {
139-
// This can only happen if we import a package a second time.
140-
panic(fmt.Sprintf("%s already declared", alt.Name()))
141-
}
142-
}
143-
144120
func (p *importer) pkg() *types.Package {
145121
// if the package was seen before, i is its index (>= 0)
146122
i := p.tagOrIndex()
@@ -178,6 +154,55 @@ func (p *importer) pkg() *types.Package {
178154
return pkg
179155
}
180156

157+
func (p *importer) declare(obj types.Object) {
158+
pkg := obj.Pkg()
159+
if alt := pkg.Scope().Insert(obj); alt != nil {
160+
// This could only trigger if we import a (non-type) object a second time.
161+
// This should never happen because 1) we only import a package once; and
162+
// b) we ignore compiler-specific export data which may contain functions
163+
// whose inlined function bodies refer to other functions that were already
164+
// imported.
165+
// (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj,
166+
// switch case importing functions).
167+
panic(fmt.Sprintf("%s already declared", alt.Name()))
168+
}
169+
}
170+
171+
func (p *importer) obj(tag int) {
172+
switch tag {
173+
case constTag:
174+
pkg, name := p.qualifiedName()
175+
typ := p.typ(nil)
176+
val := p.value()
177+
p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
178+
179+
case typeTag:
180+
_ = p.typ(nil)
181+
182+
case varTag:
183+
pkg, name := p.qualifiedName()
184+
typ := p.typ(nil)
185+
p.declare(types.NewVar(token.NoPos, pkg, name, typ))
186+
187+
case funcTag:
188+
pkg, name := p.qualifiedName()
189+
params, isddd := p.paramList()
190+
result, _ := p.paramList()
191+
sig := types.NewSignature(nil, params, result, isddd)
192+
p.int() // read and discard index of inlined function body
193+
p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
194+
195+
default:
196+
panic("unexpected object tag")
197+
}
198+
}
199+
200+
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
201+
name = p.string()
202+
pkg = p.pkg()
203+
return
204+
}
205+
181206
func (p *importer) record(t types.Type) {
182207
p.typList = append(p.typList, t)
183208
}
@@ -239,11 +264,17 @@ func (p *importer) typ(parent *types.Package) types.Type {
239264

240265
// read associated methods
241266
for i := p.int(); i > 0; i-- {
267+
// TODO(gri) replace this with something closer to fieldName
242268
name := p.string()
269+
if !exported(name) {
270+
p.pkg()
271+
}
272+
243273
recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
244274
params, isddd := p.paramList()
245275
result, _ := p.paramList()
246276
p.int() // read and discard index of inlined function body
277+
247278
sig := types.NewSignature(recv.At(0), params, result, isddd)
248279
t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
249280
}
@@ -432,18 +463,20 @@ func (p *importer) param(named bool) (*types.Var, bool) {
432463
t = types.NewSlice(td.elem)
433464
}
434465

466+
var pkg *types.Package
435467
var name string
436468
if named {
437469
name = p.string()
438470
if name == "" {
439471
panic("expected named parameter")
440472
}
473+
pkg = p.pkg()
441474
}
442475

443476
// read and discard compiler-specific info
444477
p.string()
445478

446-
return types.NewVar(token.NoPos, nil, name, t), isddd
479+
return types.NewVar(token.NoPos, pkg, name, t), isddd
447480
}
448481

449482
func exported(name string) bool {
@@ -617,8 +650,13 @@ func (p *importer) byte() byte {
617650

618651
// Tags. Must be < 0.
619652
const (
620-
// Packages
653+
// Objects
621654
packageTag = -(iota + 1)
655+
constTag
656+
typeTag
657+
varTag
658+
funcTag
659+
endTag
622660

623661
// Types
624662
namedTag
@@ -640,6 +678,7 @@ const (
640678
fractionTag // not used by gc
641679
complexTag
642680
stringTag
681+
unknownTag // not used by gc (only appears in packages with errors)
643682
)
644683

645684
var predeclared = []types.Type{

0 commit comments

Comments
 (0)