2015-02-13 14:40:36 -05:00
// Copyright 2009 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 gc
import (
2016-03-11 13:39:20 -05:00
"bufio"
2015-08-13 19:05:37 -07:00
"bytes"
2015-02-13 14:40:36 -05:00
"cmd/internal/obj"
"fmt"
"sort"
"unicode"
"unicode/utf8"
)
2015-08-13 19:05:37 -07:00
var (
newexport int // if set, use new export format
Debug_export int // if set, print debugging information about export data
exportsize int
)
func exportf ( format string , args ... interface { } ) {
n , _ := fmt . Fprintf ( bout , format , args ... )
exportsize += n
if Debug_export != 0 {
fmt . Printf ( format , args ... )
}
}
2016-03-04 19:04:50 -08:00
var asmlist [ ] * Node
2015-02-13 14:40:36 -05:00
// Mark n's symbol as exported
func exportsym ( n * Node ) {
if n == nil || n . Sym == nil {
return
}
if n . Sym . Flags & ( SymExport | SymPackage ) != 0 {
if n . Sym . Flags & SymPackage != 0 {
2015-04-17 12:03:22 -04:00
Yyerror ( "export/package mismatch: %v" , n . Sym )
2015-02-13 14:40:36 -05:00
}
return
}
n . Sym . Flags |= SymExport
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "export symbol %v\n" , n . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , n )
2015-02-13 14:40:36 -05:00
}
func exportname ( s string ) bool {
2015-08-13 19:05:37 -07:00
if r := s [ 0 ] ; r < utf8 . RuneSelf {
return 'A' <= r && r <= 'Z'
2015-02-13 14:40:36 -05:00
}
r , _ := utf8 . DecodeRuneInString ( s )
return unicode . IsUpper ( r )
}
2015-02-17 22:13:49 -05:00
func initname ( s string ) bool {
return s == "init"
2015-02-13 14:40:36 -05:00
}
// exportedsym reports whether a symbol will be visible
// to files that import our package.
2015-02-17 22:13:49 -05:00
func exportedsym ( sym * Sym ) bool {
2015-02-13 14:40:36 -05:00
// Builtins are visible everywhere.
if sym . Pkg == builtinpkg || sym . Origpkg == builtinpkg {
2015-02-17 22:13:49 -05:00
return true
2015-02-13 14:40:36 -05:00
}
2015-02-17 22:13:49 -05:00
return sym . Pkg == localpkg && exportname ( sym . Name )
2015-02-13 14:40:36 -05:00
}
2015-10-26 14:57:36 -07:00
func autoexport ( n * Node , ctxt Class ) {
2015-02-13 14:40:36 -05:00
if n == nil || n . Sym == nil {
return
}
if ( ctxt != PEXTERN && ctxt != PFUNC ) || dclcontext != PEXTERN {
return
}
2015-05-27 00:44:05 -04:00
if n . Name . Param != nil && n . Name . Param . Ntype != nil && n . Name . Param . Ntype . Op == OTFUNC && n . Name . Param . Ntype . Left != nil { // method
2015-02-13 14:40:36 -05:00
return
}
// -A is for cmd/gc/mkbuiltin script, so export everything
2015-02-17 22:13:49 -05:00
if Debug [ 'A' ] != 0 || exportname ( n . Sym . Name ) || initname ( n . Sym . Name ) {
2015-02-13 14:40:36 -05:00
exportsym ( n )
}
2015-02-17 22:13:49 -05:00
if asmhdr != "" && n . Sym . Pkg == localpkg && n . Sym . Flags & SymAsm == 0 {
2015-02-13 14:40:36 -05:00
n . Sym . Flags |= SymAsm
2016-03-04 19:04:50 -08:00
asmlist = append ( asmlist , n )
2015-02-13 14:40:36 -05:00
}
}
func dumppkg ( p * Pkg ) {
2015-09-08 05:46:31 +02:00
if p == nil || p == localpkg || p . Exported || p == builtinpkg {
2015-02-13 14:40:36 -05:00
return
}
2015-09-08 05:46:31 +02:00
p . Exported = true
2015-02-23 16:07:24 -05:00
suffix := ""
2015-09-08 05:46:31 +02:00
if ! p . Direct {
2015-02-13 14:40:36 -05:00
suffix = " // indirect"
}
2015-08-13 19:05:37 -07:00
exportf ( "\timport %s %q%s\n" , p . Name , p . Path , suffix )
2015-02-13 14:40:36 -05:00
}
// Look for anything we need for the inline body
2016-03-08 10:26:20 -08:00
func reexportdeplist ( ll Nodes ) {
2016-03-08 15:10:26 -08:00
for _ , n := range ll . Slice ( ) {
reexportdep ( n )
2016-02-27 14:31:33 -08:00
}
}
2015-02-13 14:40:36 -05:00
func reexportdep ( n * Node ) {
2015-02-17 22:13:49 -05:00
if n == nil {
2015-02-13 14:40:36 -05:00
return
}
//print("reexportdep %+hN\n", n);
switch n . Op {
case ONAME :
switch n . Class &^ PHEAP {
// methods will be printed along with their type
// nodes for T.Method expressions
case PFUNC :
if n . Left != nil && n . Left . Op == OTYPE {
break
}
// nodes for method calls.
2016-03-17 01:47:16 -07:00
if n . Type == nil || n . Type . Recv ( ) != nil {
2015-02-13 14:40:36 -05:00
break
}
fallthrough
case PEXTERN :
2015-02-17 22:13:49 -05:00
if n . Sym != nil && ! exportedsym ( n . Sym ) {
2015-02-13 14:40:36 -05:00
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "reexport name %v\n" , n . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , n )
2015-02-13 14:40:36 -05:00
}
}
2015-08-13 19:05:37 -07:00
// Local variables in the bodies need their type.
2015-02-13 14:40:36 -05:00
case ODCL :
2015-02-23 16:07:24 -05:00
t := n . Left . Type
2015-02-13 14:40:36 -05:00
if t != Types [ t . Etype ] && t != idealbool && t != idealstring {
2016-03-30 15:09:25 -07:00
if t . IsPtr ( ) {
2016-03-30 10:57:47 -07:00
t = t . Elem ( )
2015-02-13 14:40:36 -05:00
}
2015-02-17 22:13:49 -05:00
if t != nil && t . Sym != nil && t . Sym . Def != nil && ! exportedsym ( t . Sym ) {
2015-02-13 14:40:36 -05:00
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "reexport type %v from declaration\n" , t . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , t . Sym . Def )
2015-02-13 14:40:36 -05:00
}
}
case OLITERAL :
2015-02-23 16:07:24 -05:00
t := n . Type
2015-02-13 14:40:36 -05:00
if t != Types [ n . Type . Etype ] && t != idealbool && t != idealstring {
2016-03-30 15:09:25 -07:00
if t . IsPtr ( ) {
2016-03-30 10:57:47 -07:00
t = t . Elem ( )
2015-02-13 14:40:36 -05:00
}
2015-02-17 22:13:49 -05:00
if t != nil && t . Sym != nil && t . Sym . Def != nil && ! exportedsym ( t . Sym ) {
2015-02-13 14:40:36 -05:00
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "reexport literal type %v\n" , t . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , t . Sym . Def )
2015-02-13 14:40:36 -05:00
}
}
fallthrough
case OTYPE :
2016-03-16 18:44:17 -05:00
if n . Sym != nil && n . Sym . Def != nil && ! exportedsym ( n . Sym ) {
2015-02-13 14:40:36 -05:00
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "reexport literal/type %v\n" , n . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , n )
2015-02-13 14:40:36 -05:00
}
2015-08-13 19:05:37 -07:00
// for operations that need a type when rendered, put the type on the export list.
2015-02-13 14:40:36 -05:00
case OCONV ,
OCONVIFACE ,
OCONVNOP ,
ORUNESTR ,
OARRAYBYTESTR ,
OARRAYRUNESTR ,
OSTRARRAYBYTE ,
OSTRARRAYRUNE ,
ODOTTYPE ,
ODOTTYPE2 ,
OSTRUCTLIT ,
OARRAYLIT ,
OPTRLIT ,
OMAKEMAP ,
OMAKESLICE ,
OMAKECHAN :
2015-02-23 16:07:24 -05:00
t := n . Type
2015-02-13 14:40:36 -05:00
2016-03-13 23:02:38 -07:00
switch t . Etype {
case TARRAY , TCHAN , TPTR32 , TPTR64 :
if t . Sym == nil {
2016-03-30 10:57:47 -07:00
t = t . Elem ( )
2016-03-13 23:02:38 -07:00
}
2015-02-13 14:40:36 -05:00
}
2015-02-17 22:13:49 -05:00
if t != nil && t . Sym != nil && t . Sym . Def != nil && ! exportedsym ( t . Sym ) {
2015-02-13 14:40:36 -05:00
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "reexport type for expression %v\n" , t . Sym )
2015-02-13 14:40:36 -05:00
}
2015-08-12 14:29:50 -07:00
exportlist = append ( exportlist , t . Sym . Def )
2015-02-13 14:40:36 -05:00
}
}
reexportdep ( n . Left )
reexportdep ( n . Right )
reexportdeplist ( n . List )
reexportdeplist ( n . Rlist )
reexportdeplist ( n . Ninit )
2016-03-04 13:16:48 -08:00
reexportdeplist ( n . Nbody )
2015-02-13 14:40:36 -05:00
}
func dumpexportconst ( s * Sym ) {
2016-03-23 08:51:38 -07:00
n := typecheck ( s . Def , Erv )
2015-02-13 14:40:36 -05:00
if n == nil || n . Op != OLITERAL {
2015-08-30 23:10:03 +02:00
Fatalf ( "dumpexportconst: oconst nil: %v" , s )
2015-02-13 14:40:36 -05:00
}
2015-02-23 16:07:24 -05:00
t := n . Type // may or may not be specified
2015-02-13 14:40:36 -05:00
dumpexporttype ( t )
2016-04-01 13:36:24 -07:00
if t != nil && ! t . IsUntyped ( ) {
2016-03-15 13:06:58 -07:00
exportf ( "\tconst %v %v = %v\n" , Sconv ( s , FmtSharp ) , Tconv ( t , FmtSharp ) , Vconv ( n . Val ( ) , FmtSharp ) )
2015-02-13 14:40:36 -05:00
} else {
2016-03-15 13:06:58 -07:00
exportf ( "\tconst %v = %v\n" , Sconv ( s , FmtSharp ) , Vconv ( n . Val ( ) , FmtSharp ) )
2015-02-13 14:40:36 -05:00
}
}
func dumpexportvar ( s * Sym ) {
2015-02-23 16:07:24 -05:00
n := s . Def
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
n = typecheck ( n , Erv | Ecall )
2015-02-13 14:40:36 -05:00
if n == nil || n . Type == nil {
2015-04-17 12:03:22 -04:00
Yyerror ( "variable exported but not defined: %v" , s )
2015-02-13 14:40:36 -05:00
return
}
2015-02-23 16:07:24 -05:00
t := n . Type
2015-02-13 14:40:36 -05:00
dumpexporttype ( t )
if t . Etype == TFUNC && n . Class == PFUNC {
2016-02-27 14:31:33 -08:00
if n . Func != nil && len ( n . Func . Inl . Slice ( ) ) != 0 {
2015-02-13 14:40:36 -05:00
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
if Debug [ 'l' ] < 2 {
typecheckinl ( n )
}
// NOTE: The space after %#S here is necessary for ld's export data parser.
2016-03-15 13:06:58 -07:00
exportf ( "\tfunc %v %v { %v }\n" , Sconv ( s , FmtSharp ) , Tconv ( t , FmtShort | FmtSharp ) , Hconv ( n . Func . Inl , FmtSharp | FmtBody ) )
2015-02-13 14:40:36 -05:00
2016-03-04 13:16:48 -08:00
reexportdeplist ( n . Func . Inl )
2015-02-13 14:40:36 -05:00
} else {
2016-03-15 13:06:58 -07:00
exportf ( "\tfunc %v %v\n" , Sconv ( s , FmtSharp ) , Tconv ( t , FmtShort | FmtSharp ) )
2015-02-13 14:40:36 -05:00
}
} else {
2016-03-15 13:06:58 -07:00
exportf ( "\tvar %v %v\n" , Sconv ( s , FmtSharp ) , Tconv ( t , FmtSharp ) )
2015-02-13 14:40:36 -05:00
}
}
2015-09-14 20:53:44 +02:00
// methodbyname sorts types by symbol name.
2016-03-14 01:20:49 -07:00
type methodbyname [ ] * Field
2015-02-13 14:40:36 -05:00
2015-09-14 20:53:44 +02:00
func ( x methodbyname ) Len ( ) int { return len ( x ) }
func ( x methodbyname ) Swap ( i , j int ) { x [ i ] , x [ j ] = x [ j ] , x [ i ] }
func ( x methodbyname ) Less ( i , j int ) bool { return x [ i ] . Sym . Name < x [ j ] . Sym . Name }
2015-02-13 14:40:36 -05:00
func dumpexporttype ( t * Type ) {
if t == nil {
return
}
2015-09-08 03:51:30 +02:00
if t . Printed || t == Types [ t . Etype ] || t == bytetype || t == runetype || t == errortype {
2015-02-13 14:40:36 -05:00
return
}
2015-09-08 03:51:30 +02:00
t . Printed = true
2015-02-13 14:40:36 -05:00
2016-03-13 23:02:38 -07:00
if t . Sym != nil {
2015-02-13 14:40:36 -05:00
dumppkg ( t . Sym . Pkg )
}
2016-03-13 23:02:38 -07:00
switch t . Etype {
case TSTRUCT , TINTER :
2016-03-17 01:32:18 -07:00
for _ , f := range t . Fields ( ) . Slice ( ) {
2016-03-13 23:02:38 -07:00
dumpexporttype ( f . Type )
}
case TFUNC :
dumpexporttype ( t . Recvs ( ) )
dumpexporttype ( t . Results ( ) )
dumpexporttype ( t . Params ( ) )
case TMAP :
2016-03-30 10:52:13 -07:00
dumpexporttype ( t . Val ( ) )
dumpexporttype ( t . Key ( ) )
2016-03-13 23:02:38 -07:00
case TARRAY , TCHAN , TPTR32 , TPTR64 :
2016-03-30 10:57:47 -07:00
dumpexporttype ( t . Elem ( ) )
2016-03-13 23:02:38 -07:00
}
2015-02-13 14:40:36 -05:00
2016-03-13 23:02:38 -07:00
if t . Sym == nil {
2015-02-13 14:40:36 -05:00
return
}
2016-03-14 01:20:49 -07:00
var m [ ] * Field
2016-03-17 01:32:18 -07:00
for _ , f := range t . Methods ( ) . Slice ( ) {
2016-03-13 23:02:38 -07:00
dumpexporttype ( f . Type )
2015-09-14 20:53:44 +02:00
m = append ( m , f )
2015-02-13 14:40:36 -05:00
}
2015-09-14 20:53:44 +02:00
sort . Sort ( methodbyname ( m ) )
2015-02-13 14:40:36 -05:00
2016-03-15 13:06:58 -07:00
exportf ( "\ttype %v %v\n" , Sconv ( t . Sym , FmtSharp ) , Tconv ( t , FmtSharp | FmtLong ) )
2015-09-14 20:53:44 +02:00
for _ , f := range m {
2015-02-17 22:13:49 -05:00
if f . Nointerface {
2015-08-13 19:05:37 -07:00
exportf ( "\t//go:nointerface\n" )
2015-02-13 14:40:36 -05:00
}
2016-03-30 16:59:53 -07:00
if f . Type . Nname ( ) != nil && len ( f . Type . Nname ( ) . Func . Inl . Slice ( ) ) != 0 { // nname was set by caninl
2015-02-13 14:40:36 -05:00
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
if Debug [ 'l' ] < 2 {
2016-03-30 16:59:53 -07:00
typecheckinl ( f . Type . Nname ( ) )
2015-02-13 14:40:36 -05:00
}
2016-03-30 16:59:53 -07:00
exportf ( "\tfunc %v %v %v { %v }\n" , Tconv ( f . Type . Recvs ( ) , FmtSharp ) , Sconv ( f . Sym , FmtShort | FmtByte | FmtSharp ) , Tconv ( f . Type , FmtShort | FmtSharp ) , Hconv ( f . Type . Nname ( ) . Func . Inl , FmtSharp | FmtBody ) )
reexportdeplist ( f . Type . Nname ( ) . Func . Inl )
2015-02-13 14:40:36 -05:00
} else {
2016-03-15 13:06:58 -07:00
exportf ( "\tfunc %v %v %v\n" , Tconv ( f . Type . Recvs ( ) , FmtSharp ) , Sconv ( f . Sym , FmtShort | FmtByte | FmtSharp ) , Tconv ( f . Type , FmtShort | FmtSharp ) )
2015-02-13 14:40:36 -05:00
}
}
}
func dumpsym ( s * Sym ) {
if s . Flags & SymExported != 0 {
return
}
s . Flags |= SymExported
if s . Def == nil {
2015-04-17 12:03:22 -04:00
Yyerror ( "unknown export symbol: %v" , s )
2015-02-13 14:40:36 -05:00
return
}
// print("dumpsym %O %+S\n", s->def->op, s);
dumppkg ( s . Pkg )
switch s . Def . Op {
default :
2016-03-07 08:23:55 -08:00
Yyerror ( "unexpected export symbol: %v %v" , Oconv ( s . Def . Op , 0 ) , s )
2015-02-13 14:40:36 -05:00
case OLITERAL :
dumpexportconst ( s )
case OTYPE :
if s . Def . Type . Etype == TFORW {
2015-04-17 12:03:22 -04:00
Yyerror ( "export of incomplete type %v" , s )
2015-02-13 14:40:36 -05:00
} else {
dumpexporttype ( s . Def . Type )
}
case ONAME :
dumpexportvar ( s )
}
}
func dumpexport ( ) {
2015-04-19 23:56:30 -04:00
if buildid != "" {
2015-08-13 19:05:37 -07:00
exportf ( "build id %q\n" , buildid )
}
size := 0 // size of export section without enclosing markers
if forceNewExport || newexport != 0 {
// binary export
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
2016-03-18 17:21:32 -07:00
exportf ( "\n$$B\n" ) // indicate binary format
if debugFormat {
2015-08-13 19:05:37 -07:00
// save a copy of the export data
var copy bytes . Buffer
bcopy := obj . Binitw ( & copy )
size = Export ( bcopy , Debug_export != 0 )
bcopy . Flush ( ) // flushing to bytes.Buffer cannot fail
if n , err := bout . Write ( copy . Bytes ( ) ) ; n != size || err != nil {
Fatalf ( "error writing export data: got %d bytes, want %d bytes, err = %v" , n , size , err )
}
2015-10-23 16:01:09 -07:00
// export data must contain no '$' so that we can find the end by searching for "$$"
if bytes . IndexByte ( copy . Bytes ( ) , '$' ) >= 0 {
Fatalf ( "export data contains $" )
2015-08-13 19:05:37 -07:00
}
2015-02-13 14:40:36 -05:00
2015-08-13 19:05:37 -07:00
// verify that we can read the copied export data back in
// (use empty package map to avoid collisions)
savedPkgMap := pkgMap
savedPkgs := pkgs
pkgMap = make ( map [ string ] * Pkg )
pkgs = nil
importpkg = mkpkg ( "" )
2016-03-11 13:39:20 -05:00
Import ( bufio . NewReader ( & copy ) ) // must not die
2015-08-13 19:05:37 -07:00
importpkg = nil
pkgs = savedPkgs
pkgMap = savedPkgMap
} else {
size = Export ( bout , Debug_export != 0 )
}
exportf ( "\n$$\n" )
} else {
// textual export
lno := lineno
exportf ( "\n$$\n" ) // indicate textual format
exportsize = 0
exportf ( "package %s" , localpkg . Name )
if safemode != 0 {
exportf ( " safe" )
}
exportf ( "\n" )
for _ , p := range pkgs {
if p . Direct {
dumppkg ( p )
}
2015-02-13 14:40:36 -05:00
}
2015-08-13 19:05:37 -07:00
// exportlist grows during iteration - cannot use range
2016-03-18 17:21:32 -07:00
for i := 0 ; i < len ( exportlist ) ; i ++ {
n := exportlist [ i ]
2015-08-13 19:05:37 -07:00
lineno = n . Lineno
dumpsym ( n . Sym )
}
size = exportsize
exportf ( "\n$$\n" )
lineno = lno
2015-02-13 14:40:36 -05:00
}
2015-08-13 19:05:37 -07:00
if Debug_export != 0 {
fmt . Printf ( "export data size = %d bytes\n" , size )
}
2015-02-13 14:40:36 -05:00
}
2015-10-22 09:51:12 +09:00
// import
2015-02-13 14:40:36 -05:00
2015-10-22 09:51:12 +09:00
// return the sym for ss, which should match lexical
2015-09-24 23:21:18 +02:00
func importsym ( s * Sym , op Op ) * Sym {
if s . Def != nil && s . Def . Op != op {
2015-03-02 16:03:26 -05:00
pkgstr := fmt . Sprintf ( "during import %q" , importpkg . Path )
2015-02-13 14:40:36 -05:00
redeclare ( s , pkgstr )
}
// mark the symbol so it is not reexported
if s . Def == nil {
2015-12-01 12:05:30 -08:00
if Debug [ 'A' ] != 0 || exportname ( s . Name ) || initname ( s . Name ) {
2015-02-13 14:40:36 -05:00
s . Flags |= SymExport
} else {
s . Flags |= SymPackage // package scope
}
}
return s
}
2015-10-22 09:51:12 +09:00
// return the type pkg.name, forward declaring if needed
2015-02-13 14:40:36 -05:00
func pkgtype ( s * Sym ) * Type {
importsym ( s , OTYPE )
if s . Def == nil || s . Def . Op != OTYPE {
2015-02-23 16:07:24 -05:00
t := typ ( TFORW )
2015-02-13 14:40:36 -05:00
t . Sym = s
s . Def = typenod ( t )
2015-05-26 23:08:39 -04:00
s . Def . Name = new ( Name )
2015-02-13 14:40:36 -05:00
}
if s . Def . Type == nil {
2015-04-17 12:03:22 -04:00
Yyerror ( "pkgtype %v" , s )
2015-02-13 14:40:36 -05:00
}
return s . Def . Type
}
2015-03-02 16:21:15 -05:00
var numImport = make ( map [ string ] int )
2015-03-02 16:03:26 -05:00
func importimport ( s * Sym , path string ) {
2015-02-13 14:40:36 -05:00
// Informational: record package name
// associated with import path, for use in
// human-readable messages.
2015-03-02 16:03:26 -05:00
if isbadimport ( path ) {
2015-02-13 14:40:36 -05:00
errorexit ( )
}
2015-03-02 16:03:26 -05:00
p := mkpkg ( path )
2015-02-13 14:40:36 -05:00
if p . Name == "" {
p . Name = s . Name
2015-03-02 16:21:15 -05:00
numImport [ s . Name ] ++
2015-02-13 14:40:36 -05:00
} else if p . Name != s . Name {
2015-03-02 16:03:26 -05:00
Yyerror ( "conflicting names %s and %s for package %q" , p . Name , s . Name , p . Path )
2015-02-13 14:40:36 -05:00
}
2015-03-02 16:03:26 -05:00
if incannedimport == 0 && myimportpath != "" && path == myimportpath {
Yyerror ( "import %q: package depends on %q (import cycle)" , importpkg . Path , path )
2015-02-13 14:40:36 -05:00
errorexit ( )
}
}
func importconst ( s * Sym , t * Type , n * Node ) {
importsym ( s , OLITERAL )
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
n = convlit ( n , t )
2015-02-13 14:40:36 -05:00
if s . Def != nil { // TODO: check if already the same.
return
}
if n . Op != OLITERAL {
Yyerror ( "expression must be a constant" )
return
}
if n . Sym != nil {
2016-03-23 16:01:15 +11:00
n1 := * n
n = & n1
2015-02-13 14:40:36 -05:00
}
n . Orig = newname ( s )
n . Sym = s
declare ( n , PEXTERN )
if Debug [ 'E' ] != 0 {
2015-04-17 12:03:22 -04:00
fmt . Printf ( "import const %v\n" , s )
2015-02-13 14:40:36 -05:00
}
}
func importvar ( s * Sym , t * Type ) {
importsym ( s , ONAME )
if s . Def != nil && s . Def . Op == ONAME {
if Eqtype ( t , s . Def . Type ) {
return
}
2015-04-17 12:03:22 -04:00
Yyerror ( "inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)" , s , s . Def . Type , s . Importdef . Path , t , importpkg . Path )
2015-02-13 14:40:36 -05:00
}
2015-02-23 16:07:24 -05:00
n := newname ( s )
2015-02-13 14:40:36 -05:00
s . Importdef = importpkg
n . Type = t
declare ( n , PEXTERN )
if Debug [ 'E' ] != 0 {
2016-03-15 13:06:58 -07:00
fmt . Printf ( "import var %v %v\n" , s , Tconv ( t , FmtLong ) )
2015-02-13 14:40:36 -05:00
}
}
func importtype ( pt * Type , t * Type ) {
// override declaration in unsafe.go for Pointer.
// there is no way in Go code to define unsafe.Pointer
// so we have to supply it.
if incannedimport != 0 && importpkg . Name == "unsafe" && pt . Nod . Sym . Name == "Pointer" {
t = Types [ TUNSAFEPTR ]
}
if pt . Etype == TFORW {
2015-02-23 16:07:24 -05:00
n := pt . Nod
2015-02-13 14:40:36 -05:00
copytype ( pt . Nod , t )
pt . Nod = n // unzero nod
pt . Sym . Importdef = importpkg
2016-03-02 11:01:25 -08:00
pt . Sym . Lastlineno = lineno
2015-02-13 14:40:36 -05:00
declare ( n , PEXTERN )
checkwidth ( pt )
} else if ! Eqtype ( pt . Orig , t ) {
2016-03-15 13:06:58 -07:00
Yyerror ( "inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)" , pt . Sym , Tconv ( pt , FmtLong ) , pt . Sym . Importdef . Path , Tconv ( t , FmtLong ) , importpkg . Path )
2015-02-13 14:40:36 -05:00
}
if Debug [ 'E' ] != 0 {
2016-03-15 13:06:58 -07:00
fmt . Printf ( "import type %v %v\n" , pt , Tconv ( t , FmtLong ) )
2015-02-13 14:40:36 -05:00
}
}
func dumpasmhdr ( ) {
b , err := obj . Bopenw ( asmhdr )
if err != nil {
2015-08-30 23:10:03 +02:00
Fatalf ( "%v" , err )
2015-02-13 14:40:36 -05:00
}
2016-03-04 19:04:50 -08:00
fmt . Fprintf ( b , "// generated by compile -asmhdr from package %s\n\n" , localpkg . Name )
for _ , n := range asmlist {
2015-02-13 14:40:36 -05:00
if isblanksym ( n . Sym ) {
continue
}
switch n . Op {
case OLITERAL :
2016-03-15 13:06:58 -07:00
fmt . Fprintf ( b , "#define const_%s %v\n" , n . Sym . Name , Vconv ( n . Val ( ) , FmtSharp ) )
2015-02-13 14:40:36 -05:00
case OTYPE :
2016-03-04 19:04:50 -08:00
t := n . Type
2016-03-30 14:56:08 -07:00
if ! t . IsStruct ( ) || t . Map != nil || t . Funarg {
2015-02-13 14:40:36 -05:00
break
}
fmt . Fprintf ( b , "#define %s__size %d\n" , t . Sym . Name , int ( t . Width ) )
2016-03-17 01:32:18 -07:00
for _ , t := range t . Fields ( ) . Slice ( ) {
2015-02-13 14:40:36 -05:00
if ! isblanksym ( t . Sym ) {
2016-03-28 09:40:53 -07:00
fmt . Fprintf ( b , "#define %s_%s %d\n" , n . Sym . Name , t . Sym . Name , int ( t . Offset ) )
2015-02-13 14:40:36 -05:00
}
}
}
}
obj . Bterm ( b )
}