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

1813 lines
38 KiB
Go
Raw Normal View History

// Copyright 2011 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 (
"bytes"
"cmd/internal/obj"
"fmt"
"strconv"
"strings"
"unicode/utf8"
)
// A FmtFlag value is a set of flags (or 0).
// They control how the Xconv functions format their values.
// See the respective function's documentation for details.
type FmtFlag int
const (
FmtWidth FmtFlag = 1 << iota
FmtLeft // "-"
FmtSharp // "#"
FmtSign // "+"
FmtUnsigned // "u"
FmtShort // "h"
FmtLong // "l"
FmtComma // ","
FmtByte // "hh"
FmtBody // for printing export bodies
)
//
// Format conversions
// %L int Line numbers
//
// %E int etype values (aka 'Kind')
//
// %O int Node Opcodes
// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
//
// %J Node* Node details
// Flags: "%hJ" suppresses things not relevant until walk.
//
// %V Val* Constant values
//
// %S Sym* Symbols
// Flags: +,- #: mode (see below)
// "%hS" unqualified identifier in any mode
// "%hhS" in export mode: unqualified identifier if exported, qualified if not
//
// %T Type* Types
// Flags: +,- #: mode (see below)
// 'l' definition instead of name.
// 'h' omit "func" and receiver in function types
// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
//
// %N Node* Nodes
// Flags: +,- #: mode (see below)
// 'h' (only in +/debug mode) suppress recursion
// 'l' (only in Error mode) print "foo (type Bar)"
//
// %H Nodes Nodes
// Flags: those of %N
// ',' separate items with ',' instead of ';'
//
// In mparith2.go and mparith3.go:
// %B Mpint* Big integers
// %F Mpflt* Big floats
//
// %S, %T and %N obey use the following flags to set the format mode:
const (
FErr = iota
FDbg
FExp
FTypeId
)
var fmtmode int = FErr
var fmtpkgpfx int // %uT stickyness
var fmtbody bool
//
// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode.
//
// The mode flags +, - and # are sticky, meaning they persist through
// recursions of %N, %T and %S, but not the h and l flags. The u flag is
// sticky only on %T recursions and only used in %-/Sym mode.
//
// Useful format combinations:
//
// %+N %+H multiline recursive debug dump of node/nodelist
// %+hN %+hH non recursive debug dump
//
// %#N %#T export format
// %#lT type definition instead of name
// %#hT omit"func" and receiver in function signature
//
// %lN "foo (type Bar)" for error messages
//
// %-T type identifiers
// %-hT type identifiers without "func" and arg names in type signatures (methodsym)
// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
//
func setfmode(flags *FmtFlag) (fm int, fb bool) {
fm = fmtmode
fb = fmtbody
if *flags&FmtSign != 0 {
fmtmode = FDbg
} else if *flags&FmtSharp != 0 {
fmtmode = FExp
} else if *flags&FmtLeft != 0 {
fmtmode = FTypeId
}
if *flags&FmtBody != 0 {
fmtbody = true
}
*flags &^= (FmtSharp | FmtLeft | FmtSign | FmtBody)
return
}
// Fmt "%L": Linenumbers
var goopnames = []string{
OADDR: "&",
OADD: "+",
OADDSTR: "+",
OANDAND: "&&",
OANDNOT: "&^",
OAND: "&",
OAPPEND: "append",
OAS: "=",
OAS2: "=",
OBREAK: "break",
OCALL: "function call", // not actual syntax
OCAP: "cap",
OCASE: "case",
OCLOSE: "close",
OCOMPLEX: "complex",
OCOM: "^",
OCONTINUE: "continue",
OCOPY: "copy",
ODEC: "--",
ODELETE: "delete",
ODEFER: "defer",
ODIV: "/",
OEQ: "==",
OFALL: "fallthrough",
OFOR: "for",
OGE: ">=",
OGOTO: "goto",
OGT: ">",
OIF: "if",
OIMAG: "imag",
OINC: "++",
OIND: "*",
OLEN: "len",
OLE: "<=",
OLSH: "<<",
OLT: "<",
OMAKE: "make",
OMINUS: "-",
OMOD: "%",
OMUL: "*",
ONEW: "new",
ONE: "!=",
ONOT: "!",
OOROR: "||",
OOR: "|",
OPANIC: "panic",
OPLUS: "+",
OPRINTN: "println",
OPRINT: "print",
ORANGE: "range",
OREAL: "real",
ORECV: "<-",
ORECOVER: "recover",
ORETURN: "return",
ORSH: ">>",
OSELECT: "select",
OSEND: "<-",
OSUB: "-",
OSWITCH: "switch",
OXOR: "^",
OXFALL: "fallthrough",
}
// Fmt "%O": Node opcodes
func Oconv(o Op, flag FmtFlag) string {
if (flag&FmtSharp != 0) || fmtmode != FDbg {
if o >= 0 && int(o) < len(goopnames) && goopnames[o] != "" {
return goopnames[o]
}
}
if o >= 0 && int(o) < len(opnames) && opnames[o] != "" {
return opnames[o]
}
return fmt.Sprintf("O-%d", o)
}
var classnames = []string{
"Pxxx",
"PEXTERN",
"PAUTO",
"PPARAM",
"PPARAMOUT",
"PPARAMREF",
"PFUNC",
}
// Fmt "%J": Node details.
func Jconv(n *Node, flag FmtFlag) string {
var buf bytes.Buffer
c := flag & FmtShort
if c == 0 && n.Ullman != 0 {
fmt.Fprintf(&buf, " u(%d)", n.Ullman)
}
if c == 0 && n.Addable {
fmt.Fprintf(&buf, " a(%v)", n.Addable)
}
if c == 0 && n.Name != nil && n.Name.Vargen != 0 {
fmt.Fprintf(&buf, " g(%d)", n.Name.Vargen)
}
if n.Lineno != 0 {
fmt.Fprintf(&buf, " l(%d)", n.Lineno)
}
if c == 0 && n.Xoffset != BADWIDTH {
fmt.Fprintf(&buf, " x(%d%+d)", n.Xoffset, stkdelta[n])
}
if n.Class != 0 {
s := ""
if n.Class&PHEAP != 0 {
s = ",heap"
}
if int(n.Class&^PHEAP) < len(classnames) {
fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s)
} else {
fmt.Fprintf(&buf, " class(%d?%s)", n.Class&^PHEAP, s)
}
}
if n.Colas {
fmt.Fprintf(&buf, " colas(%v)", n.Colas)
}
if n.Name != nil && n.Name.Funcdepth != 0 {
fmt.Fprintf(&buf, " f(%d)", n.Name.Funcdepth)
}
if n.Func != nil && n.Func.Depth != 0 {
fmt.Fprintf(&buf, " ff(%d)", n.Func.Depth)
}
switch n.Esc {
case EscUnknown:
break
case EscHeap:
buf.WriteString(" esc(h)")
case EscScope:
buf.WriteString(" esc(s)")
case EscNone:
buf.WriteString(" esc(no)")
case EscNever:
if c == 0 {
buf.WriteString(" esc(N)")
}
default:
fmt.Fprintf(&buf, " esc(%d)", n.Esc)
}
if e, ok := n.Opt().(*NodeEscState); ok && e.Escloopdepth != 0 {
fmt.Fprintf(&buf, " ld(%d)", e.Escloopdepth)
}
if c == 0 && n.Typecheck != 0 {
fmt.Fprintf(&buf, " tc(%d)", n.Typecheck)
}
if c == 0 && n.Dodata != 0 {
fmt.Fprintf(&buf, " dd(%d)", n.Dodata)
}
if n.Isddd {
fmt.Fprintf(&buf, " isddd(%v)", n.Isddd)
}
if n.Implicit {
fmt.Fprintf(&buf, " implicit(%v)", n.Implicit)
}
if n.Embedded != 0 {
fmt.Fprintf(&buf, " embedded(%d)", n.Embedded)
}
if n.Addrtaken {
buf.WriteString(" addrtaken")
}
if n.Assigned {
buf.WriteString(" assigned")
}
if n.Bounded {
buf.WriteString(" bounded")
}
if n.NonNil {
buf.WriteString(" nonnil")
}
if c == 0 && n.Used {
fmt.Fprintf(&buf, " used(%v)", n.Used)
}
return buf.String()
}
// Fmt "%V": Values
func Vconv(v Val, flag FmtFlag) string {
switch u := v.U.(type) {
case *Mpint:
if !u.Rune {
if (flag&FmtSharp != 0) || fmtmode == FExp {
return Bconv(u, FmtSharp)
}
return Bconv(u, 0)
}
x := u.Int64()
if ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'' {
return fmt.Sprintf("'%c'", int(x))
}
if 0 <= x && x < 1<<16 {
return fmt.Sprintf("'\\u%04x'", uint(int(x)))
}
if 0 <= x && x <= utf8.MaxRune {
return fmt.Sprintf("'\\U%08x'", uint64(x))
}
return fmt.Sprintf("('\\x00' + %v)", u)
case *Mpflt:
if (flag&FmtSharp != 0) || fmtmode == FExp {
return Fconv(u, 0)
}
return Fconv(u, FmtSharp)
case *Mpcplx:
if (flag&FmtSharp != 0) || fmtmode == FExp {
return fmt.Sprintf("(%v+%vi)", &u.Real, &u.Imag)
}
if v.U.(*Mpcplx).Real.CmpFloat64(0) == 0 {
return fmt.Sprintf("%vi", Fconv(&u.Imag, FmtSharp))
}
if v.U.(*Mpcplx).Imag.CmpFloat64(0) == 0 {
return Fconv(&u.Real, FmtSharp)
}
if v.U.(*Mpcplx).Imag.CmpFloat64(0) < 0 {
return fmt.Sprintf("(%v%vi)", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp))
}
return fmt.Sprintf("(%v+%vi)", Fconv(&u.Real, FmtSharp), Fconv(&u.Imag, FmtSharp))
case string:
return strconv.Quote(u)
case bool:
if u {
return "true"
}
return "false"
case *NilVal:
return "nil"
}
return fmt.Sprintf("<ctype=%d>", v.Ctype())
}
/*
s%,%,\n%g
s%\n+%\n%g
s%^[ ]*T%%g
s%,.*%%g
s%.+% [T&] = "&",%g
s%^ ........*\]%&~%g
s%~ %%g
*/
var etnames = []string{
Txxx: "Txxx",
TINT: "INT",
TUINT: "UINT",
TINT8: "INT8",
TUINT8: "UINT8",
TINT16: "INT16",
TUINT16: "UINT16",
TINT32: "INT32",
TUINT32: "UINT32",
TINT64: "INT64",
TUINT64: "UINT64",
TUINTPTR: "UINTPTR",
TFLOAT32: "FLOAT32",
TFLOAT64: "FLOAT64",
TCOMPLEX64: "COMPLEX64",
TCOMPLEX128: "COMPLEX128",
TBOOL: "BOOL",
TPTR32: "PTR32",
TPTR64: "PTR64",
TFUNC: "FUNC",
TARRAY: "ARRAY",
TSLICE: "SLICE",
TSTRUCT: "STRUCT",
TCHAN: "CHAN",
TMAP: "MAP",
TINTER: "INTER",
TFORW: "FORW",
TSTRING: "STRING",
TUNSAFEPTR: "TUNSAFEPTR",
TANY: "ANY",
TIDEAL: "TIDEAL",
TNIL: "TNIL",
TBLANK: "TBLANK",
TFUNCARGS: "TFUNCARGS",
TCHANARGS: "TCHANARGS",
TINTERMETH: "TINTERMETH",
TDDDFIELD: "TDDDFIELD",
}
// Fmt "%E": etype
func Econv(et EType) string {
if int(et) < len(etnames) && etnames[et] != "" {
return etnames[et]
}
return fmt.Sprintf("E-%d", et)
}
func (e EType) String() string {
return Econv(e)
}
// Fmt "%S": syms
func symfmt(s *Sym, flag FmtFlag) string {
if s.Pkg != nil && flag&FmtShort == 0 {
switch fmtmode {
case FErr: // This is for the user
if s.Pkg == builtinpkg || s.Pkg == localpkg {
return s.Name
}
// If the name was used by multiple packages, display the full path,
if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 {
return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name)
}
return s.Pkg.Name + "." + s.Name
case FDbg:
return s.Pkg.Name + "." + s.Name
case FTypeId:
if flag&FmtUnsigned != 0 {
return s.Pkg.Name + "." + s.Name // dcommontype, typehash
}
return s.Pkg.Prefix + "." + s.Name // (methodsym), typesym, weaksym
case FExp:
if s.Name != "" && s.Name[0] == '.' {
Fatalf("exporting synthetic symbol %s", s.Name)
}
if s.Pkg != builtinpkg {
return fmt.Sprintf("@%q.%s", s.Pkg.Path, s.Name)
}
}
}
if flag&FmtByte != 0 {
// FmtByte (hh) implies FmtShort (h)
// skip leading "type." in method name
p := s.Name
if i := strings.LastIndex(s.Name, "."); i >= 0 {
p = s.Name[i+1:]
}
// exportname needs to see the name without the prefix too.
if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg {
return fmt.Sprintf("@%q.%s", s.Pkg.Path, p)
}
return p
}
return s.Name
}
var basicnames = []string{
TINT: "int",
TUINT: "uint",
TINT8: "int8",
TUINT8: "uint8",
TINT16: "int16",
TUINT16: "uint16",
TINT32: "int32",
TUINT32: "uint32",
TINT64: "int64",
TUINT64: "uint64",
TUINTPTR: "uintptr",
TFLOAT32: "float32",
TFLOAT64: "float64",
TCOMPLEX64: "complex64",
TCOMPLEX128: "complex128",
TBOOL: "bool",
TANY: "any",
TSTRING: "string",
TNIL: "nil",
TIDEAL: "untyped number",
TBLANK: "blank",
}
func typefmt(t *Type, flag FmtFlag) string {
if t == nil {
return "<T>"
}
if t == bytetype || t == runetype {
// in %-T mode collapse rune and byte with their originals.
if fmtmode != FTypeId {
return Sconv(t.Sym, FmtShort)
}
t = Types[t.Etype]
}
if t == errortype {
return "error"
}
// Unless the 'l' flag was specified, if the type has a name, just print that name.
if flag&FmtLong == 0 && t.Sym != nil && t != Types[t.Etype] {
switch fmtmode {
case FTypeId:
if flag&FmtShort != 0 {
if t.Vargen != 0 {
return fmt.Sprintf("%v·%d", Sconv(t.Sym, FmtShort), t.Vargen)
}
return Sconv(t.Sym, FmtShort)
}
if flag&FmtUnsigned != 0 {
return Sconv(t.Sym, FmtUnsigned)
}
fallthrough
case FExp:
if t.Sym.Pkg == localpkg && t.Vargen != 0 {
return fmt.Sprintf("%v·%d", t.Sym, t.Vargen)
}
}
return Sconv(t.Sym, 0)
}
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
prefix := ""
if fmtmode == FErr && (t == idealbool || t == idealstring) {
prefix = "untyped "
}
return prefix + basicnames[t.Etype]
}
if fmtmode == FDbg {
fmtmode = 0
str := Econv(t.Etype) + "-" + typefmt(t, flag)
fmtmode = FDbg
return str
}
switch t.Etype {
case TPTR32, TPTR64:
if fmtmode == FTypeId && (flag&FmtShort != 0) {
return "*" + Tconv(t.Elem(), FmtShort)
}
return "*" + t.Elem().String()
case TARRAY:
if t.isDDDArray() {
return "[...]" + t.Elem().String()
}
return fmt.Sprintf("[%d]%v", t.NumElem(), t.Elem())
case TSLICE:
return "[]" + t.Elem().String()
case TCHAN:
switch t.ChanDir() {
case Crecv:
return "<-chan " + t.Elem().String()
case Csend:
return "chan<- " + t.Elem().String()
}
if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym == nil && t.Elem().ChanDir() == Crecv {
return "chan (" + t.Elem().String() + ")"
}
return "chan " + t.Elem().String()
case TMAP:
return "map[" + t.Key().String() + "]" + t.Val().String()
case TINTER:
var buf bytes.Buffer
buf.WriteString("interface {")
for i, f := range t.Fields().Slice() {
if i != 0 {
buf.WriteString(";")
}
buf.WriteString(" ")
switch {
case f.Sym == nil:
// Check first that a symbol is defined for this type.
// Wrong interface definitions may have types lacking a symbol.
break
case exportname(f.Sym.Name):
buf.WriteString(Sconv(f.Sym, FmtShort))
default:
buf.WriteString(Sconv(f.Sym, FmtUnsigned))
}
buf.WriteString(Tconv(f.Type, FmtShort))
}
if t.NumFields() != 0 {
buf.WriteString(" ")
}
buf.WriteString("}")
return buf.String()
case TFUNC:
var buf bytes.Buffer
if flag&FmtShort != 0 {
// no leading func
} else {
if t.Recv() != nil {
buf.WriteString("method")
buf.WriteString(Tconv(t.Recvs(), 0))
buf.WriteString(" ")
}
buf.WriteString("func")
}
buf.WriteString(Tconv(t.Params(), 0))
switch t.Results().NumFields() {
case 0:
break
case 1:
if fmtmode != FExp {
buf.WriteString(" ")
buf.WriteString(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type
break
}
fallthrough
default:
buf.WriteString(" ")
buf.WriteString(Tconv(t.Results(), 0))
}
return buf.String()
case TSTRUCT:
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
if m := t.StructType().Map; m != nil {
mt := m.MapType()
// Format the bucket struct for map[x]y as map.bucket[x]y.
// This avoids a recursive print that generates very long names.
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
if mt.Bucket == t {
return "map.bucket[" + m.Key().String() + "]" + m.Val().String()
}
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
if mt.Hmap == t {
return "map.hdr[" + m.Key().String() + "]" + m.Val().String()
}
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
if mt.Hiter == t {
return "map.iter[" + m.Key().String() + "]" + m.Val().String()
}
Yyerror("unknown internal map type")
}
var buf bytes.Buffer
if t.IsFuncArgStruct() {
buf.WriteString("(")
var flag1 FmtFlag
if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags
flag1 = FmtShort
}
for i, f := range t.Fields().Slice() {
if i != 0 {
buf.WriteString(", ")
}
buf.WriteString(Fldconv(f, flag1))
}
buf.WriteString(")")
} else {
buf.WriteString("struct {")
for i, f := range t.Fields().Slice() {
if i != 0 {
buf.WriteString(";")
}
buf.WriteString(" ")
buf.WriteString(Fldconv(f, FmtLong))
}
if t.NumFields() != 0 {
buf.WriteString(" ")
}
buf.WriteString("}")
}
return buf.String()
case TFORW:
if t.Sym != nil {
return "undefined " + t.Sym.String()
}
return "undefined"
case TUNSAFEPTR:
if fmtmode == FExp {
return "@\"unsafe\".Pointer"
}
return "unsafe.Pointer"
case TDDDFIELD:
if fmtmode == FExp {
Fatalf("cannot use TDDDFIELD with old exporter")
}
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.DDDField())
case Txxx:
return "Txxx"
}
if fmtmode == FExp {
Fatalf("missing %v case during export", Econv(t.Etype))
}
// Don't know how to handle - fall back to detailed prints.
return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.Elem())
}
// Statements which may be rendered with a simplestmt as init.
func stmtwithinit(op Op) bool {
switch op {
case OIF, OFOR, OSWITCH:
return true
}
return false
}
func stmtfmt(n *Node) string {
var f string
// some statements allow for an init, but at most one,
// but we may have an arbitrary number added, eg by typecheck
// and inlining. If it doesn't fit the syntax, emit an enclosing
// block starting with the init statements.
// if we can just say "for" n->ninit; ... then do so
simpleinit := n.Ninit.Len() == 1 && n.Ninit.First().Ninit.Len() == 0 && stmtwithinit(n.Op)
// otherwise, print the inits as separate statements
complexinit := n.Ninit.Len() != 0 && !simpleinit && (fmtmode != FErr)
// but if it was for if/for/switch, put in an extra surrounding block to limit the scope
extrablock := complexinit && stmtwithinit(n.Op)
if extrablock {
f += "{"
}
if complexinit {
f += fmt.Sprintf(" %v; ", n.Ninit)
}
switch n.Op {
case ODCL:
if fmtmode == FExp {
switch n.Left.Class &^ PHEAP {
case PPARAM, PPARAMOUT, PAUTO:
f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type)
goto ret
}
}
f += fmt.Sprintf("var %v %v", n.Left.Sym, n.Left.Type)
case ODCLFIELD:
if n.Left != nil {
f += fmt.Sprintf("%v %v", n.Left, n.Right)
} else {
f += Nconv(n.Right, 0)
}
// Don't export "v = <N>" initializing statements, hope they're always
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 <gri@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-18 17:21:32 -07:00
// preceded by the DCL which will be re-parsed and typechecked to reproduce
// the "v = <N>" again.
cmd/internal/gc: emit write barriers at lower level This is primarily preparation for inlining, not an optimization by itself, but it still helps some. name old new delta BenchmarkBinaryTree17 18.2s × (0.99,1.01) 17.9s × (0.99,1.01) -1.57% BenchmarkFannkuch11 4.44s × (1.00,1.00) 4.42s × (1.00,1.00) -0.40% BenchmarkFmtFprintfEmpty 119ns × (0.95,1.02) 118ns × (0.96,1.02) ~ BenchmarkFmtFprintfString 501ns × (0.99,1.02) 486ns × (0.99,1.01) -2.89% BenchmarkFmtFprintfInt 474ns × (0.99,1.00) 457ns × (0.99,1.01) -3.59% BenchmarkFmtFprintfIntInt 792ns × (1.00,1.00) 768ns × (1.00,1.01) -3.03% BenchmarkFmtFprintfPrefixedInt 574ns × (1.00,1.01) 584ns × (0.99,1.03) +1.83% BenchmarkFmtFprintfFloat 749ns × (1.00,1.00) 739ns × (0.99,1.00) -1.34% BenchmarkFmtManyArgs 2.94µs × (1.00,1.01) 2.77µs × (1.00,1.00) -5.76% BenchmarkGobDecode 39.5ms × (0.99,1.01) 39.3ms × (0.99,1.01) ~ BenchmarkGobEncode 39.4ms × (1.00,1.01) 39.4ms × (0.99,1.00) ~ BenchmarkGzip 658ms × (1.00,1.01) 661ms × (0.99,1.01) ~ BenchmarkGunzip 142ms × (1.00,1.00) 142ms × (1.00,1.00) +0.22% BenchmarkHTTPClientServer 134µs × (0.99,1.01) 133µs × (0.98,1.01) ~ BenchmarkJSONEncode 57.1ms × (0.99,1.01) 56.5ms × (0.99,1.01) ~ BenchmarkJSONDecode 141ms × (1.00,1.00) 143ms × (1.00,1.00) +1.09% BenchmarkMandelbrot200 6.01ms × (1.00,1.00) 6.01ms × (1.00,1.00) ~ BenchmarkGoParse 10.1ms × (0.91,1.09) 9.6ms × (0.94,1.07) ~ BenchmarkRegexpMatchEasy0_32 207ns × (1.00,1.01) 210ns × (1.00,1.00) +1.45% BenchmarkRegexpMatchEasy0_1K 592ns × (0.99,1.00) 596ns × (0.99,1.01) +0.68% BenchmarkRegexpMatchEasy1_32 184ns × (0.99,1.01) 184ns × (0.99,1.01) ~ BenchmarkRegexpMatchEasy1_1K 1.01µs × (1.00,1.00) 1.01µs × (0.99,1.01) ~ BenchmarkRegexpMatchMedium_32 327ns × (0.99,1.00) 327ns × (1.00,1.01) ~ BenchmarkRegexpMatchMedium_1K 92.5µs × (1.00,1.00) 93.0µs × (1.00,1.02) +0.48% BenchmarkRegexpMatchHard_32 4.79µs × (0.95,1.00) 4.76µs × (0.95,1.01) ~ BenchmarkRegexpMatchHard_1K 136µs × (1.00,1.00) 136µs × (1.00,1.01) ~ BenchmarkRevcomp 900ms × (0.99,1.01) 892ms × (1.00,1.01) ~ BenchmarkTemplate 170ms × (0.99,1.01) 175ms × (0.99,1.00) +2.95% BenchmarkTimeParse 645ns × (1.00,1.00) 638ns × (1.00,1.00) -1.16% BenchmarkTimeFormat 740ns × (1.00,1.00) 772ns × (1.00,1.00) +4.39% Change-Id: I0be905e32791e0cb70ff01f169c4b309a971d981 Reviewed-on: https://go-review.googlesource.com/9159 Reviewed-by: Rick Hudson <rlh@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-04-17 00:25:10 -04:00
case OAS, OASWB:
if fmtmode == FExp && n.Right == nil {
break
}
if n.Colas && !complexinit {
f += fmt.Sprintf("%v := %v", n.Left, n.Right)
} else {
f += fmt.Sprintf("%v = %v", n.Left, n.Right)
}
case OASOP:
if n.Implicit {
if Op(n.Etype) == OADD {
f += fmt.Sprintf("%v++", n.Left)
} else {
f += fmt.Sprintf("%v--", n.Left)
}
break
}
f += fmt.Sprintf("%v %v= %v", n.Left, Oconv(Op(n.Etype), FmtSharp), n.Right)
case OAS2:
if n.Colas && !complexinit {
f += fmt.Sprintf("%v := %v", Hconv(n.List, FmtComma), Hconv(n.Rlist, FmtComma))
break
}
fallthrough
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
f += fmt.Sprintf("%v = %v", Hconv(n.List, FmtComma), Hconv(n.Rlist, FmtComma))
case ORETURN:
f += fmt.Sprintf("return %v", Hconv(n.List, FmtComma))
case ORETJMP:
f += fmt.Sprintf("retjmp %v", n.Sym)
case OPROC:
f += fmt.Sprintf("go %v", n.Left)
case ODEFER:
f += fmt.Sprintf("defer %v", n.Left)
case OIF:
if simpleinit {
f += fmt.Sprintf("if %v; %v { %v }", n.Ninit.First(), n.Left, n.Nbody)
} else {
f += fmt.Sprintf("if %v { %v }", n.Left, n.Nbody)
}
if n.Rlist.Len() != 0 {
f += fmt.Sprintf(" else { %v }", n.Rlist)
}
case OFOR:
if fmtmode == FErr { // TODO maybe only if FmtShort, same below
f += "for loop"
break
}
f += "for"
if simpleinit {
f += fmt.Sprintf(" %v;", n.Ninit.First())
} else if n.Right != nil {
f += " ;"
}
if n.Left != nil {
f += fmt.Sprintf(" %v", n.Left)
}
if n.Right != nil {
f += fmt.Sprintf("; %v", n.Right)
} else if simpleinit {
f += ";"
}
f += fmt.Sprintf(" { %v }", n.Nbody)
case ORANGE:
if fmtmode == FErr {
f += "for loop"
break
}
if n.List.Len() == 0 {
f += fmt.Sprintf("for range %v { %v }", n.Right, n.Nbody)
break
}
f += fmt.Sprintf("for %v = range %v { %v }", Hconv(n.List, FmtComma), n.Right, n.Nbody)
case OSELECT, OSWITCH:
if fmtmode == FErr {
f += fmt.Sprintf("%v statement", Oconv(n.Op, 0))
break
}
f += Oconv(n.Op, FmtSharp)
if simpleinit {
f += fmt.Sprintf(" %v;", n.Ninit.First())
}
if n.Left != nil {
f += fmt.Sprintf(" %s ", Nconv(n.Left, 0))
}
f += fmt.Sprintf(" { %v }", n.List)
case OCASE, OXCASE:
if n.List.Len() != 0 {
f += fmt.Sprintf("case %v: %v", Hconv(n.List, FmtComma), n.Nbody)
} else {
f += fmt.Sprintf("default: %v", n.Nbody)
}
case OBREAK,
OCONTINUE,
OGOTO,
OFALL,
OXFALL:
if n.Left != nil {
f += fmt.Sprintf("%v %v", Oconv(n.Op, FmtSharp), n.Left)
} else {
f += Oconv(n.Op, FmtSharp)
}
case OEMPTY:
break
case OLABEL:
f += fmt.Sprintf("%v: ", n.Left)
}
ret:
if extrablock {
f += "}"
}
return f
}
var opprec = []int{
OAPPEND: 8,
OARRAYBYTESTR: 8,
OARRAYLIT: 8,
OARRAYRUNESTR: 8,
OCALLFUNC: 8,
OCALLINTER: 8,
OCALLMETH: 8,
OCALL: 8,
OCAP: 8,
OCLOSE: 8,
OCONVIFACE: 8,
OCONVNOP: 8,
OCONV: 8,
OCOPY: 8,
ODELETE: 8,
cmd/internal/gc: inline runtime.getg This more closely restores what the old C runtime did. (In C, g was an 'extern register' with the same effective implementation as in this CL.) On a late 2012 MacBookPro10,2, best of 5 old vs best of 5 new: benchmark old ns/op new ns/op delta BenchmarkBinaryTree17 4981312777 4463426605 -10.40% BenchmarkFannkuch11 3046495712 3006819428 -1.30% BenchmarkFmtFprintfEmpty 89.3 79.8 -10.64% BenchmarkFmtFprintfString 284 262 -7.75% BenchmarkFmtFprintfInt 282 262 -7.09% BenchmarkFmtFprintfIntInt 480 448 -6.67% BenchmarkFmtFprintfPrefixedInt 382 358 -6.28% BenchmarkFmtFprintfFloat 529 486 -8.13% BenchmarkFmtManyArgs 1849 1773 -4.11% BenchmarkGobDecode 12835963 11794385 -8.11% BenchmarkGobEncode 10527170 10288422 -2.27% BenchmarkGzip 436109569 438422516 +0.53% BenchmarkGunzip 110121663 109843648 -0.25% BenchmarkHTTPClientServer 81930 85446 +4.29% BenchmarkJSONEncode 24638574 24280603 -1.45% BenchmarkJSONDecode 93022423 85753546 -7.81% BenchmarkMandelbrot200 4703899 4735407 +0.67% BenchmarkGoParse 5319853 5086843 -4.38% BenchmarkRegexpMatchEasy0_32 151 151 +0.00% BenchmarkRegexpMatchEasy0_1K 452 453 +0.22% BenchmarkRegexpMatchEasy1_32 131 132 +0.76% BenchmarkRegexpMatchEasy1_1K 761 722 -5.12% BenchmarkRegexpMatchMedium_32 228 224 -1.75% BenchmarkRegexpMatchMedium_1K 63751 64296 +0.85% BenchmarkRegexpMatchHard_32 3188 3238 +1.57% BenchmarkRegexpMatchHard_1K 95396 96756 +1.43% BenchmarkRevcomp 661587262 687107364 +3.86% BenchmarkTemplate 108312598 104008540 -3.97% BenchmarkTimeParse 453 459 +1.32% BenchmarkTimeFormat 475 441 -7.16% The garbage benchmark from the benchmarks subrepo gets 2.6% faster as well. Change-Id: I320aeda332db81012688b26ffab23f6581c59cfa Reviewed-on: https://go-review.googlesource.com/8460 Reviewed-by: Rick Hudson <rlh@golang.org> Run-TryBot: Rick Hudson <rlh@golang.org> Reviewed-by: Austin Clements <austin@google.com>
2015-04-03 12:23:28 -04:00
OGETG: 8,
OLEN: 8,
OLITERAL: 8,
OMAKESLICE: 8,
OMAKE: 8,
OMAPLIT: 8,
ONAME: 8,
ONEW: 8,
ONONAME: 8,
OPACK: 8,
OPANIC: 8,
OPAREN: 8,
OPRINTN: 8,
OPRINT: 8,
ORUNESTR: 8,
OSTRARRAYBYTE: 8,
OSTRARRAYRUNE: 8,
OSTRUCTLIT: 8,
OTARRAY: 8,
OTCHAN: 8,
OTFUNC: 8,
OTINTER: 8,
OTMAP: 8,
OTSTRUCT: 8,
OINDEXMAP: 8,
OINDEX: 8,
OSLICE: 8,
OSLICESTR: 8,
OSLICEARR: 8,
OSLICE3: 8,
OSLICE3ARR: 8,
ODOTINTER: 8,
ODOTMETH: 8,
ODOTPTR: 8,
ODOTTYPE2: 8,
ODOTTYPE: 8,
ODOT: 8,
OXDOT: 8,
OCALLPART: 8,
OPLUS: 7,
ONOT: 7,
OCOM: 7,
OMINUS: 7,
OADDR: 7,
OIND: 7,
ORECV: 7,
OMUL: 6,
ODIV: 6,
OMOD: 6,
OLSH: 6,
ORSH: 6,
OAND: 6,
OANDNOT: 6,
OADD: 5,
OSUB: 5,
OOR: 5,
OXOR: 5,
OEQ: 4,
OLT: 4,
OLE: 4,
OGE: 4,
OGT: 4,
ONE: 4,
OCMPSTR: 4,
OCMPIFACE: 4,
OSEND: 3,
OANDAND: 2,
OOROR: 1,
// Statements handled by stmtfmt
OAS: -1,
OAS2: -1,
OAS2DOTTYPE: -1,
OAS2FUNC: -1,
OAS2MAPR: -1,
OAS2RECV: -1,
OASOP: -1,
OBREAK: -1,
OCASE: -1,
OCONTINUE: -1,
ODCL: -1,
ODCLFIELD: -1,
ODEFER: -1,
OEMPTY: -1,
OFALL: -1,
OFOR: -1,
OGOTO: -1,
OIF: -1,
OLABEL: -1,
OPROC: -1,
ORANGE: -1,
ORETURN: -1,
OSELECT: -1,
OSWITCH: -1,
OXCASE: -1,
OXFALL: -1,
OEND: 0,
}
func exprfmt(n *Node, prec int) string {
for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) {
n = n.Left
}
if n == nil {
return "<N>"
}
nprec := opprec[n.Op]
if n.Op == OTYPE && n.Sym != nil {
nprec = 8
}
if prec > nprec {
return fmt.Sprintf("(%v)", n)
}
switch n.Op {
case OPAREN:
return fmt.Sprintf("(%v)", n.Left)
case ODDDARG:
return "... argument"
case OREGISTER:
return obj.Rconv(int(n.Reg))
case OLITERAL: // this is a bit of a mess
if fmtmode == FErr {
if n.Orig != nil && n.Orig != n {
return exprfmt(n.Orig, prec)
}
if n.Sym != nil {
return Sconv(n.Sym, 0)
}
}
if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n {
return exprfmt(n.Orig, prec)
}
if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != idealbool && n.Type != idealstring {
// Need parens when type begins with what might
// be misinterpreted as a unary operator: * or <-.
if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == Crecv) {
return fmt.Sprintf("(%v)(%v)", n.Type, Vconv(n.Val(), 0))
} else {
return fmt.Sprintf("%v(%v)", n.Type, Vconv(n.Val(), 0))
}
}
return Vconv(n.Val(), 0)
// Special case: name used as local variable in export.
// _ becomes ~b%d internally; print as _ for export
case ONAME:
if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
return "_"
}
if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 {
return fmt.Sprintf("%v·%d", n.Sym, n.Name.Vargen)
}
// Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
// but for export, this should be rendered as (*pkg.T).meth.
// These nodes have the special property that they are names with a left OTYPE and a right ONAME.
if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME {
if n.Left.Type.IsPtr() {
return fmt.Sprintf("(%v).%v", n.Left.Type, Sconv(n.Right.Sym, FmtShort|FmtByte))
} else {
return fmt.Sprintf("%v.%v", n.Left.Type, Sconv(n.Right.Sym, FmtShort|FmtByte))
}
}
fallthrough
case OPACK, ONONAME:
return Sconv(n.Sym, 0)
case OTYPE:
if n.Type == nil && n.Sym != nil {
return Sconv(n.Sym, 0)
}
return Tconv(n.Type, 0)
case OTARRAY:
if n.Left != nil {
return fmt.Sprintf("[]%v", n.Left)
}
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 <gri@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-18 17:21:32 -07:00
return fmt.Sprintf("[]%v", n.Right) // happens before typecheck
case OTMAP:
return fmt.Sprintf("map[%v]%v", n.Left, n.Right)
case OTCHAN:
switch ChanDir(n.Etype) {
case Crecv:
return fmt.Sprintf("<-chan %v", n.Left)
case Csend:
return fmt.Sprintf("chan<- %v", n.Left)
default:
if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && ChanDir(n.Left.Etype) == Crecv {
return fmt.Sprintf("chan (%v)", n.Left)
} else {
return fmt.Sprintf("chan %v", n.Left)
}
}
case OTSTRUCT:
return "<struct>"
case OTINTER:
return "<inter>"
case OTFUNC:
return "<func>"
case OCLOSURE:
if fmtmode == FErr {
return "func literal"
}
if len(n.Nbody.Slice()) != 0 {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
}
return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody)
case OCOMPLIT:
ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr()
if fmtmode == FErr {
if n.Right != nil && n.Right.Type != nil && !n.Implicit {
if ptrlit {
return fmt.Sprintf("&%v literal", n.Right.Type.Elem())
} else {
return fmt.Sprintf("%v literal", n.Right.Type)
}
}
return "composite literal"
}
if fmtmode == FExp && ptrlit {
// typecheck has overwritten OIND by OTYPE with pointer type.
return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Elem(), Hconv(n.List, FmtComma))
}
return fmt.Sprintf("(%v{ %v })", n.Right, Hconv(n.List, FmtComma))
case OPTRLIT:
if fmtmode == FExp && n.Left.Implicit {
return Nconv(n.Left, 0)
}
return fmt.Sprintf("&%v", n.Left)
case OSTRUCTLIT:
if fmtmode == FExp { // requires special handling of field names
var f string
if n.Implicit {
f += "{"
} else {
f += fmt.Sprintf("(%v{", n.Type)
}
for i1, n1 := range n.List.Slice() {
f += fmt.Sprintf(" %v:%v", Sconv(n1.Left.Sym, FmtShort|FmtByte), n1.Right)
if i1+1 < n.List.Len() {
f += ","
} else {
f += " "
}
}
if !n.Implicit {
f += "})"
return f
}
f += "}"
return f
}
fallthrough
case OARRAYLIT, OMAPLIT:
if fmtmode == FErr {
return fmt.Sprintf("%v literal", n.Type)
}
if fmtmode == FExp && n.Implicit {
return fmt.Sprintf("{ %v }", Hconv(n.List, FmtComma))
}
return fmt.Sprintf("(%v{ %v })", n.Type, Hconv(n.List, FmtComma))
case OKEY:
if n.Left != nil && n.Right != nil {
if fmtmode == FExp && n.Left.Type == structkey {
// requires special handling of field names
return fmt.Sprintf("%v:%v", Sconv(n.Left.Sym, FmtShort|FmtByte), n.Right)
} else {
return fmt.Sprintf("%v:%v", n.Left, n.Right)
}
}
if n.Left == nil && n.Right != nil {
return fmt.Sprintf(":%v", n.Right)
}
if n.Left != nil && n.Right == nil {
return fmt.Sprintf("%v:", n.Left)
}
return ":"
case OCALLPART:
var f string
f += exprfmt(n.Left, nprec)
if n.Right == nil || n.Right.Sym == nil {
f += ".<nil>"
return f
}
f += fmt.Sprintf(".%v", Sconv(n.Right.Sym, FmtShort|FmtByte))
return f
case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
var f string
f += exprfmt(n.Left, nprec)
if n.Sym == nil {
f += ".<nil>"
return f
}
f += fmt.Sprintf(".%v", Sconv(n.Sym, FmtShort|FmtByte))
return f
case ODOTTYPE, ODOTTYPE2:
var f string
f += exprfmt(n.Left, nprec)
if n.Right != nil {
f += fmt.Sprintf(".(%v)", n.Right)
return f
}
f += fmt.Sprintf(".(%v)", n.Type)
return f
case OINDEX,
OINDEXMAP,
OSLICE,
OSLICESTR,
OSLICEARR,
OSLICE3,
OSLICE3ARR:
var f string
f += exprfmt(n.Left, nprec)
f += fmt.Sprintf("[%v]", n.Right)
return f
case OCOPY, OCOMPLEX:
return fmt.Sprintf("%v(%v, %v)", Oconv(n.Op, FmtSharp), n.Left, n.Right)
case OCONV,
OCONVIFACE,
OCONVNOP,
OARRAYBYTESTR,
OARRAYRUNESTR,
OSTRARRAYBYTE,
OSTRARRAYRUNE,
ORUNESTR:
if n.Type == nil || n.Type.Sym == nil {
return fmt.Sprintf("(%v)(%v)", n.Type, n.Left)
}
if n.Left != nil {
return fmt.Sprintf("%v(%v)", n.Type, n.Left)
}
return fmt.Sprintf("%v(%v)", n.Type, Hconv(n.List, FmtComma))
case OREAL,
OIMAG,
OAPPEND,
OCAP,
OCLOSE,
ODELETE,
OLEN,
OMAKE,
ONEW,
OPANIC,
ORECOVER,
OPRINT,
OPRINTN:
if n.Left != nil {
return fmt.Sprintf("%v(%v)", Oconv(n.Op, FmtSharp), n.Left)
}
if n.Isddd {
return fmt.Sprintf("%v(%v...)", Oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
}
return fmt.Sprintf("%v(%v)", Oconv(n.Op, FmtSharp), Hconv(n.List, FmtComma))
cmd/internal/gc: inline runtime.getg This more closely restores what the old C runtime did. (In C, g was an 'extern register' with the same effective implementation as in this CL.) On a late 2012 MacBookPro10,2, best of 5 old vs best of 5 new: benchmark old ns/op new ns/op delta BenchmarkBinaryTree17 4981312777 4463426605 -10.40% BenchmarkFannkuch11 3046495712 3006819428 -1.30% BenchmarkFmtFprintfEmpty 89.3 79.8 -10.64% BenchmarkFmtFprintfString 284 262 -7.75% BenchmarkFmtFprintfInt 282 262 -7.09% BenchmarkFmtFprintfIntInt 480 448 -6.67% BenchmarkFmtFprintfPrefixedInt 382 358 -6.28% BenchmarkFmtFprintfFloat 529 486 -8.13% BenchmarkFmtManyArgs 1849 1773 -4.11% BenchmarkGobDecode 12835963 11794385 -8.11% BenchmarkGobEncode 10527170 10288422 -2.27% BenchmarkGzip 436109569 438422516 +0.53% BenchmarkGunzip 110121663 109843648 -0.25% BenchmarkHTTPClientServer 81930 85446 +4.29% BenchmarkJSONEncode 24638574 24280603 -1.45% BenchmarkJSONDecode 93022423 85753546 -7.81% BenchmarkMandelbrot200 4703899 4735407 +0.67% BenchmarkGoParse 5319853 5086843 -4.38% BenchmarkRegexpMatchEasy0_32 151 151 +0.00% BenchmarkRegexpMatchEasy0_1K 452 453 +0.22% BenchmarkRegexpMatchEasy1_32 131 132 +0.76% BenchmarkRegexpMatchEasy1_1K 761 722 -5.12% BenchmarkRegexpMatchMedium_32 228 224 -1.75% BenchmarkRegexpMatchMedium_1K 63751 64296 +0.85% BenchmarkRegexpMatchHard_32 3188 3238 +1.57% BenchmarkRegexpMatchHard_1K 95396 96756 +1.43% BenchmarkRevcomp 661587262 687107364 +3.86% BenchmarkTemplate 108312598 104008540 -3.97% BenchmarkTimeParse 453 459 +1.32% BenchmarkTimeFormat 475 441 -7.16% The garbage benchmark from the benchmarks subrepo gets 2.6% faster as well. Change-Id: I320aeda332db81012688b26ffab23f6581c59cfa Reviewed-on: https://go-review.googlesource.com/8460 Reviewed-by: Rick Hudson <rlh@golang.org> Run-TryBot: Rick Hudson <rlh@golang.org> Reviewed-by: Austin Clements <austin@google.com>
2015-04-03 12:23:28 -04:00
case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:
var f string
f += exprfmt(n.Left, nprec)
if n.Isddd {
f += fmt.Sprintf("(%v...)", Hconv(n.List, FmtComma))
return f
}
f += fmt.Sprintf("(%v)", Hconv(n.List, FmtComma))
return f
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
if n.List.Len() != 0 { // pre-typecheck
return fmt.Sprintf("make(%v, %v)", n.Type, Hconv(n.List, FmtComma))
}
if n.Right != nil {
return fmt.Sprintf("make(%v, %v, %v)", n.Type, n.Left, n.Right)
}
if n.Left != nil && (n.Op == OMAKESLICE || !n.Left.Type.IsUntyped()) {
return fmt.Sprintf("make(%v, %v)", n.Type, n.Left)
}
return fmt.Sprintf("make(%v)", n.Type)
// Unary
case OPLUS,
OMINUS,
OADDR,
OCOM,
OIND,
ONOT,
ORECV:
var f string
if n.Left.Op == n.Op {
f += fmt.Sprintf("%v ", Oconv(n.Op, FmtSharp))
} else {
f += Oconv(n.Op, FmtSharp)
}
f += exprfmt(n.Left, nprec+1)
return f
// Binary
case OADD,
OAND,
OANDAND,
OANDNOT,
ODIV,
OEQ,
OGE,
OGT,
OLE,
OLT,
OLSH,
OMOD,
OMUL,
ONE,
OOR,
OOROR,
ORSH,
OSEND,
OSUB,
OXOR:
var f string
f += exprfmt(n.Left, nprec)
f += fmt.Sprintf(" %v ", Oconv(n.Op, FmtSharp))
f += exprfmt(n.Right, nprec+1)
return f
case OADDSTR:
var f string
i := 0
for _, n1 := range n.List.Slice() {
if i != 0 {
f += " + "
}
f += exprfmt(n1, nprec)
i++
}
return f
case OCMPSTR, OCMPIFACE:
var f string
f += exprfmt(n.Left, nprec)
// TODO(marvin): Fix Node.EType type union.
f += fmt.Sprintf(" %v ", Oconv(Op(n.Etype), FmtSharp))
f += exprfmt(n.Right, nprec+1)
return f
case ODCLCONST:
// if exporting, DCLCONST should just be removed as its usage
// has already been replaced with literals
if fmtbody {
return ""
}
}
return fmt.Sprintf("<node %v>", Oconv(n.Op, 0))
}
func nodefmt(n *Node, flag FmtFlag) string {
t := n.Type
// we almost always want the original, except in export mode for literals
// this saves the importer some work, and avoids us having to redo some
// special casing for package unsafe
if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil {
n = n.Orig
}
if flag&FmtLong != 0 && t != nil {
if t.Etype == TNIL {
return "nil"
} else {
return fmt.Sprintf("%v (type %v)", n, t)
}
}
// TODO inlining produces expressions with ninits. we can't print these yet.
if opprec[n.Op] < 0 {
return stmtfmt(n)
}
return exprfmt(n, 0)
}
var dumpdepth int
func indent(buf *bytes.Buffer) {
buf.WriteString("\n")
for i := 0; i < dumpdepth; i++ {
buf.WriteString(". ")
}
}
func nodedump(n *Node, flag FmtFlag) string {
if n == nil {
return ""
}
recur := flag&FmtShort == 0
var buf bytes.Buffer
if recur {
indent(&buf)
if dumpdepth > 10 {
buf.WriteString("...")
return buf.String()
}
if n.Ninit.Len() != 0 {
fmt.Fprintf(&buf, "%v-init%v", Oconv(n.Op, 0), n.Ninit)
indent(&buf)
}
}
switch n.Op {
default:
fmt.Fprintf(&buf, "%v%v", Oconv(n.Op, 0), Jconv(n, 0))
case OREGISTER, OINDREG:
fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), obj.Rconv(int(n.Reg)), Jconv(n, 0))
case OLITERAL:
fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), Vconv(n.Val(), 0), Jconv(n, 0))
case ONAME, ONONAME:
if n.Sym != nil {
fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), n.Sym, Jconv(n, 0))
} else {
fmt.Fprintf(&buf, "%v%v", Oconv(n.Op, 0), Jconv(n, 0))
}
if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil {
indent(&buf)
fmt.Fprintf(&buf, "%v-ntype%v", Oconv(n.Op, 0), n.Name.Param.Ntype)
}
case OASOP:
fmt.Fprintf(&buf, "%v-%v%v", Oconv(n.Op, 0), Oconv(Op(n.Etype), 0), Jconv(n, 0))
case OTYPE:
fmt.Fprintf(&buf, "%v %v%v type=%v", Oconv(n.Op, 0), n.Sym, Jconv(n, 0), n.Type)
if recur && n.Type == nil && n.Name.Param.Ntype != nil {
indent(&buf)
fmt.Fprintf(&buf, "%v-ntype%v", Oconv(n.Op, 0), n.Name.Param.Ntype)
}
}
if n.Sym != nil && n.Op != ONAME {
fmt.Fprintf(&buf, " %v", n.Sym)
}
if n.Type != nil {
fmt.Fprintf(&buf, " %v", n.Type)
}
if recur {
if n.Left != nil {
buf.WriteString(Nconv(n.Left, 0))
}
if n.Right != nil {
buf.WriteString(Nconv(n.Right, 0))
}
if n.List.Len() != 0 {
indent(&buf)
fmt.Fprintf(&buf, "%v-list%v", Oconv(n.Op, 0), n.List)
}
if n.Rlist.Len() != 0 {
indent(&buf)
fmt.Fprintf(&buf, "%v-rlist%v", Oconv(n.Op, 0), n.Rlist)
}
if len(n.Nbody.Slice()) != 0 {
indent(&buf)
fmt.Fprintf(&buf, "%v-body%v", Oconv(n.Op, 0), n.Nbody)
}
}
return buf.String()
}
func (s *Sym) String() string {
return Sconv(s, 0)
}
// Fmt "%S": syms
// Flags: "%hS" suppresses qualifying with package
func Sconv(s *Sym, flag FmtFlag) string {
if flag&FmtLong != 0 {
panic("linksymfmt")
}
if s == nil {
return "<S>"
}
if s.Name == "_" {
return "_"
}
sf := flag
sm, sb := setfmode(&flag)
str := symfmt(s, flag)
flag = sf
fmtmode = sm
fmtbody = sb
return str
}
func (t *Type) String() string {
return Tconv(t, 0)
}
func Fldconv(f *Field, flag FmtFlag) string {
if f == nil {
return "<T>"
}
sf := flag
sm, sb := setfmode(&flag)
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
fmtpkgpfx++
}
if fmtpkgpfx != 0 {
flag |= FmtUnsigned
}
var name string
if flag&FmtShort == 0 {
s := f.Sym
// Take the name from the original, lest we substituted it with ~r%d or ~b%d.
// ~r%d is a (formerly) unnamed result.
if (fmtmode == FErr || fmtmode == FExp) && f.Nname != nil {
if f.Nname.Orig != nil {
s = f.Nname.Orig.Sym
if s != nil && s.Name[0] == '~' {
if s.Name[1] == 'r' { // originally an unnamed result
s = nil
} else if s.Name[1] == 'b' { // originally the blank identifier _
s = Lookup("_")
}
}
} else {
s = nil
}
}
if s != nil && f.Embedded == 0 {
if f.Funarg {
name = Nconv(f.Nname, 0)
} else if flag&FmtLong != 0 {
name = Sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg)
} else {
name = Sconv(s, 0)
}
} else if fmtmode == FExp {
if f.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 {
name = fmt.Sprintf("@%q.?", s.Pkg.Path)
} else {
name = "?"
}
}
}
var typ string
if f.Isddd {
typ = "..." + Tconv(f.Type.Elem(), 0)
} else {
typ = Tconv(f.Type, 0)
}
str := typ
if name != "" {
str = name + " " + typ
}
// The fmtbody flag is intended to suppress escape analysis annotations
// when printing a function type used in a function body.
// (The escape analysis tags do not apply to func vars.)
// But it must not suppress struct field tags.
// See golang.org/issue/13777 and golang.org/issue/14331.
if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != nil {
str += " " + strconv.Quote(*f.Note)
}
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
fmtpkgpfx--
}
flag = sf
fmtbody = sb
fmtmode = sm
return str
}
// Fmt "%T": types.
// Flags: 'l' print definition, not name
// 'h' omit 'func' and receiver from function types, short type names
// 'u' package name, not prefix (FTypeId mode, sticky)
func Tconv(t *Type, flag FmtFlag) string {
if t == nil {
return "<T>"
}
if t.Trecur > 4 {
return "<...>"
}
t.Trecur++
sf := flag
sm, sb := setfmode(&flag)
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
fmtpkgpfx++
}
if fmtpkgpfx != 0 {
flag |= FmtUnsigned
}
str := typefmt(t, flag)
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
fmtpkgpfx--
}
flag = sf
fmtbody = sb
fmtmode = sm
t.Trecur--
return str
}
func (n *Node) String() string {
return Nconv(n, 0)
}
// Fmt '%N': Nodes.
// Flags: 'l' suffix with "(type %T)" where possible
// '+h' in debug mode, don't recurse, no multiline output
func Nconv(n *Node, flag FmtFlag) string {
if n == nil {
return "<N>"
}
sf := flag
sm, sb := setfmode(&flag)
var str string
switch fmtmode {
case FErr, FExp:
str = nodefmt(n, flag)
case FDbg:
dumpdepth++
str = nodedump(n, flag)
dumpdepth--
default:
Fatalf("unhandled %%N mode")
}
flag = sf
fmtbody = sb
fmtmode = sm
return str
}
func (n Nodes) String() string {
return Hconv(n, 0)
}
// Fmt '%H': Nodes.
// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
func Hconv(l Nodes, flag FmtFlag) string {
if l.Len() == 0 && fmtmode == FDbg {
return "<nil>"
}
sf := flag
sm, sb := setfmode(&flag)
sep := "; "
if fmtmode == FDbg {
sep = "\n"
} else if flag&FmtComma != 0 {
sep = ", "
}
var buf bytes.Buffer
for i, n := range l.Slice() {
buf.WriteString(Nconv(n, 0))
if i+1 < l.Len() {
buf.WriteString(sep)
}
}
flag = sf
fmtbody = sb
fmtmode = sm
return buf.String()
}
func dumplist(s string, l Nodes) {
fmt.Printf("%s%v\n", s, Hconv(l, FmtSign))
}
func Dump(s string, n *Node) {
fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, FmtSign))
}