diff options
Diffstat (limited to 'src/cmd/compile/internal/ir')
24 files changed, 8136 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ir/bitset.go b/src/cmd/compile/internal/ir/bitset.go new file mode 100644 index 0000000..0c7bd54 --- /dev/null +++ b/src/cmd/compile/internal/ir/bitset.go @@ -0,0 +1,71 @@ +// Copyright 2017 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 ir + +type bitset8 uint8 + +func (f *bitset8) set(mask uint8, b bool) { + if b { + *(*uint8)(f) |= mask + } else { + *(*uint8)(f) &^= mask + } +} + +func (f bitset8) get2(shift uint8) uint8 { + return uint8(f>>shift) & 3 +} + +// set2 sets two bits in f using the bottom two bits of b. +func (f *bitset8) set2(shift uint8, b uint8) { + // Clear old bits. + *(*uint8)(f) &^= 3 << shift + // Set new bits. + *(*uint8)(f) |= uint8(b&3) << shift +} + +type bitset16 uint16 + +func (f *bitset16) set(mask uint16, b bool) { + if b { + *(*uint16)(f) |= mask + } else { + *(*uint16)(f) &^= mask + } +} + +type bitset32 uint32 + +func (f *bitset32) set(mask uint32, b bool) { + if b { + *(*uint32)(f) |= mask + } else { + *(*uint32)(f) &^= mask + } +} + +func (f bitset32) get2(shift uint8) uint8 { + return uint8(f>>shift) & 3 +} + +// set2 sets two bits in f using the bottom two bits of b. +func (f *bitset32) set2(shift uint8, b uint8) { + // Clear old bits. + *(*uint32)(f) &^= 3 << shift + // Set new bits. + *(*uint32)(f) |= uint32(b&3) << shift +} + +func (f bitset32) get3(shift uint8) uint8 { + return uint8(f>>shift) & 7 +} + +// set3 sets three bits in f using the bottom three bits of b. +func (f *bitset32) set3(shift uint8, b uint8) { + // Clear old bits. + *(*uint32)(f) &^= 7 << shift + // Set new bits. + *(*uint32)(f) |= uint32(b&7) << shift +} diff --git a/src/cmd/compile/internal/ir/cfg.go b/src/cmd/compile/internal/ir/cfg.go new file mode 100644 index 0000000..d986ac3 --- /dev/null +++ b/src/cmd/compile/internal/ir/cfg.go @@ -0,0 +1,26 @@ +// 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 ir + +var ( + // maximum size variable which we will allocate on the stack. + // This limit is for explicit variable declarations like "var x T" or "x := ...". + // Note: the flag smallframes can update this value. + MaxStackVarSize = int64(10 * 1024 * 1024) + + // maximum size of implicit variables that we will allocate on the stack. + // p := new(T) allocating T on the stack + // p := &T{} allocating T on the stack + // s := make([]T, n) allocating [n]T on the stack + // s := []byte("...") allocating [n]byte on the stack + // Note: the flag smallframes can update this value. + MaxImplicitStackVarSize = int64(64 * 1024) + + // MaxSmallArraySize is the maximum size of an array which is considered small. + // Small arrays will be initialized directly with a sequence of constant stores. + // Large arrays will be initialized by copying from a static temp. + // 256 bytes was chosen to minimize generated code + statictmp size. + MaxSmallArraySize = int64(256) +) diff --git a/src/cmd/compile/internal/ir/class_string.go b/src/cmd/compile/internal/ir/class_string.go new file mode 100644 index 0000000..11a94c0 --- /dev/null +++ b/src/cmd/compile/internal/ir/class_string.go @@ -0,0 +1,30 @@ +// Code generated by "stringer -type=Class name.go"; DO NOT EDIT. + +package ir + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Pxxx-0] + _ = x[PEXTERN-1] + _ = x[PAUTO-2] + _ = x[PAUTOHEAP-3] + _ = x[PPARAM-4] + _ = x[PPARAMOUT-5] + _ = x[PTYPEPARAM-6] + _ = x[PFUNC-7] +} + +const _Class_name = "PxxxPEXTERNPAUTOPAUTOHEAPPPARAMPPARAMOUTPTYPEPARAMPFUNC" + +var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 50, 55} + +func (i Class) String() string { + if i >= Class(len(_Class_index)-1) { + return "Class(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Class_name[_Class_index[i]:_Class_index[i+1]] +} diff --git a/src/cmd/compile/internal/ir/const.go b/src/cmd/compile/internal/ir/const.go new file mode 100644 index 0000000..eaa4d5b --- /dev/null +++ b/src/cmd/compile/internal/ir/const.go @@ -0,0 +1,99 @@ +// 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 ir + +import ( + "go/constant" + "math" + "math/big" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" +) + +func NewBool(b bool) Node { + return NewLiteral(constant.MakeBool(b)) +} + +func NewInt(v int64) Node { + return NewLiteral(constant.MakeInt64(v)) +} + +func NewString(s string) Node { + return NewLiteral(constant.MakeString(s)) +} + +const ( + // Maximum size in bits for big.Ints before signalling + // overflow and also mantissa precision for big.Floats. + ConstPrec = 512 +) + +func BigFloat(v constant.Value) *big.Float { + f := new(big.Float) + f.SetPrec(ConstPrec) + switch u := constant.Val(v).(type) { + case int64: + f.SetInt64(u) + case *big.Int: + f.SetInt(u) + case *big.Float: + f.Set(u) + case *big.Rat: + f.SetRat(u) + default: + base.Fatalf("unexpected: %v", u) + } + return f +} + +// ConstOverflow reports whether constant value v is too large +// to represent with type t. +func ConstOverflow(v constant.Value, t *types.Type) bool { + switch { + case t.IsInteger(): + bits := uint(8 * t.Size()) + if t.IsUnsigned() { + x, ok := constant.Uint64Val(v) + return !ok || x>>bits != 0 + } + x, ok := constant.Int64Val(v) + if x < 0 { + x = ^x + } + return !ok || x>>(bits-1) != 0 + case t.IsFloat(): + switch t.Size() { + case 4: + f, _ := constant.Float32Val(v) + return math.IsInf(float64(f), 0) + case 8: + f, _ := constant.Float64Val(v) + return math.IsInf(f, 0) + } + case t.IsComplex(): + ft := types.FloatForComplex(t) + return ConstOverflow(constant.Real(v), ft) || ConstOverflow(constant.Imag(v), ft) + } + base.Fatalf("ConstOverflow: %v, %v", v, t) + panic("unreachable") +} + +// IsConstNode reports whether n is a Go language constant (as opposed to a +// compile-time constant). +// +// Expressions derived from nil, like string([]byte(nil)), while they +// may be known at compile time, are not Go language constants. +func IsConstNode(n Node) bool { + return n.Op() == OLITERAL +} + +func IsSmallIntConst(n Node) bool { + if n.Op() == OLITERAL { + v, ok := constant.Int64Val(n.Val()) + return ok && int64(int32(v)) == v + } + return false +} diff --git a/src/cmd/compile/internal/ir/copy.go b/src/cmd/compile/internal/ir/copy.go new file mode 100644 index 0000000..7da9b24 --- /dev/null +++ b/src/cmd/compile/internal/ir/copy.go @@ -0,0 +1,102 @@ +// Copyright 2020 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 ir + +import ( + "cmd/compile/internal/base" + "cmd/internal/src" +) + +// A Node may implement the Orig and SetOrig method to +// maintain a pointer to the "unrewritten" form of a Node. +// If a Node does not implement OrigNode, it is its own Orig. +// +// Note that both SepCopy and Copy have definitions compatible +// with a Node that does not implement OrigNode: such a Node +// is its own Orig, and in that case, that's what both want to return +// anyway (SepCopy unconditionally, and Copy only when the input +// is its own Orig as well, but if the output does not implement +// OrigNode, then neither does the input, making the condition true). +type OrigNode interface { + Node + Orig() Node + SetOrig(Node) +} + +// origNode may be embedded into a Node to make it implement OrigNode. +type origNode struct { + orig Node `mknode:"-"` +} + +func (n *origNode) Orig() Node { return n.orig } +func (n *origNode) SetOrig(o Node) { n.orig = o } + +// Orig returns the “original” node for n. +// If n implements OrigNode, Orig returns n.Orig(). +// Otherwise Orig returns n itself. +func Orig(n Node) Node { + if n, ok := n.(OrigNode); ok { + o := n.Orig() + if o == nil { + Dump("Orig nil", n) + base.Fatalf("Orig returned nil") + } + return o + } + return n +} + +// SepCopy returns a separate shallow copy of n, +// breaking any Orig link to any other nodes. +func SepCopy(n Node) Node { + n = n.copy() + if n, ok := n.(OrigNode); ok { + n.SetOrig(n) + } + return n +} + +// Copy returns a shallow copy of n. +// If Orig(n) == n, then Orig(Copy(n)) == the copy. +// Otherwise the Orig link is preserved as well. +// +// The specific semantics surrounding Orig are subtle but right for most uses. +// See issues #26855 and #27765 for pitfalls. +func Copy(n Node) Node { + c := n.copy() + if n, ok := n.(OrigNode); ok && n.Orig() == n { + c.(OrigNode).SetOrig(c) + } + return c +} + +// DeepCopy returns a “deep” copy of n, with its entire structure copied +// (except for shared nodes like ONAME, ONONAME, OLITERAL, and OTYPE). +// If pos.IsKnown(), it sets the source position of newly allocated Nodes to pos. +func DeepCopy(pos src.XPos, n Node) Node { + var edit func(Node) Node + edit = func(x Node) Node { + switch x.Op() { + case OPACK, ONAME, ONONAME, OLITERAL, ONIL, OTYPE: + return x + } + x = Copy(x) + if pos.IsKnown() { + x.SetPos(pos) + } + EditChildren(x, edit) + return x + } + return edit(n) +} + +// DeepCopyList returns a list of deep copies (using DeepCopy) of the nodes in list. +func DeepCopyList(pos src.XPos, list []Node) []Node { + var out []Node + for _, n := range list { + out = append(out, DeepCopy(pos, n)) + } + return out +} diff --git a/src/cmd/compile/internal/ir/dump.go b/src/cmd/compile/internal/ir/dump.go new file mode 100644 index 0000000..59914ba --- /dev/null +++ b/src/cmd/compile/internal/ir/dump.go @@ -0,0 +1,272 @@ +// Copyright 2018 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. + +// This file implements textual dumping of arbitrary data structures +// for debugging purposes. The code is customized for Node graphs +// and may be used for an alternative view of the node structure. + +package ir + +import ( + "fmt" + "io" + "os" + "reflect" + "regexp" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// DumpAny is like FDumpAny but prints to stderr. +func DumpAny(root interface{}, filter string, depth int) { + FDumpAny(os.Stderr, root, filter, depth) +} + +// FDumpAny prints the structure of a rooted data structure +// to w by depth-first traversal of the data structure. +// +// The filter parameter is a regular expression. If it is +// non-empty, only struct fields whose names match filter +// are printed. +// +// The depth parameter controls how deep traversal recurses +// before it returns (higher value means greater depth). +// If an empty field filter is given, a good depth default value +// is 4. A negative depth means no depth limit, which may be fine +// for small data structures or if there is a non-empty filter. +// +// In the output, Node structs are identified by their Op name +// rather than their type; struct fields with zero values or +// non-matching field names are omitted, and "…" means recursion +// depth has been reached or struct fields have been omitted. +func FDumpAny(w io.Writer, root interface{}, filter string, depth int) { + if root == nil { + fmt.Fprintln(w, "nil") + return + } + + if filter == "" { + filter = ".*" // default + } + + p := dumper{ + output: w, + fieldrx: regexp.MustCompile(filter), + ptrmap: make(map[uintptr]int), + last: '\n', // force printing of line number on first line + } + + p.dump(reflect.ValueOf(root), depth) + p.printf("\n") +} + +type dumper struct { + output io.Writer + fieldrx *regexp.Regexp // field name filter + ptrmap map[uintptr]int // ptr -> dump line number + lastadr string // last address string printed (for shortening) + + // output + indent int // current indentation level + last byte // last byte processed by Write + line int // current line number +} + +var indentBytes = []byte(". ") + +func (p *dumper) Write(data []byte) (n int, err error) { + var m int + for i, b := range data { + // invariant: data[0:n] has been written + if b == '\n' { + m, err = p.output.Write(data[n : i+1]) + n += m + if err != nil { + return + } + } else if p.last == '\n' { + p.line++ + _, err = fmt.Fprintf(p.output, "%6d ", p.line) + if err != nil { + return + } + for j := p.indent; j > 0; j-- { + _, err = p.output.Write(indentBytes) + if err != nil { + return + } + } + } + p.last = b + } + if len(data) > n { + m, err = p.output.Write(data[n:]) + n += m + } + return +} + +// printf is a convenience wrapper. +func (p *dumper) printf(format string, args ...interface{}) { + if _, err := fmt.Fprintf(p, format, args...); err != nil { + panic(err) + } +} + +// addr returns the (hexadecimal) address string of the object +// represented by x (or "?" if x is not addressable), with the +// common prefix between this and the prior address replaced by +// "0x…" to make it easier to visually match addresses. +func (p *dumper) addr(x reflect.Value) string { + if !x.CanAddr() { + return "?" + } + adr := fmt.Sprintf("%p", x.Addr().Interface()) + s := adr + if i := commonPrefixLen(p.lastadr, adr); i > 0 { + s = "0x…" + adr[i:] + } + p.lastadr = adr + return s +} + +// dump prints the contents of x. +func (p *dumper) dump(x reflect.Value, depth int) { + if depth == 0 { + p.printf("…") + return + } + + if pos, ok := x.Interface().(src.XPos); ok { + p.printf("%s", base.FmtPos(pos)) + return + } + + switch x.Kind() { + case reflect.String: + p.printf("%q", x.Interface()) // print strings in quotes + + case reflect.Interface: + if x.IsNil() { + p.printf("nil") + return + } + p.dump(x.Elem(), depth-1) + + case reflect.Ptr: + if x.IsNil() { + p.printf("nil") + return + } + + p.printf("*") + ptr := x.Pointer() + if line, exists := p.ptrmap[ptr]; exists { + p.printf("(@%d)", line) + return + } + p.ptrmap[ptr] = p.line + p.dump(x.Elem(), depth) // don't count pointer indirection towards depth + + case reflect.Slice: + if x.IsNil() { + p.printf("nil") + return + } + p.printf("%s (%d entries) {", x.Type(), x.Len()) + if x.Len() > 0 { + p.indent++ + p.printf("\n") + for i, n := 0, x.Len(); i < n; i++ { + p.printf("%d: ", i) + p.dump(x.Index(i), depth-1) + p.printf("\n") + } + p.indent-- + } + p.printf("}") + + case reflect.Struct: + typ := x.Type() + + isNode := false + if n, ok := x.Interface().(Node); ok { + isNode = true + p.printf("%s %s {", n.Op().String(), p.addr(x)) + } else { + p.printf("%s {", typ) + } + p.indent++ + + first := true + omitted := false + for i, n := 0, typ.NumField(); i < n; i++ { + // Exclude non-exported fields because their + // values cannot be accessed via reflection. + if name := typ.Field(i).Name; types.IsExported(name) { + if !p.fieldrx.MatchString(name) { + omitted = true + continue // field name not selected by filter + } + + // special cases + if isNode && name == "Op" { + omitted = true + continue // Op field already printed for Nodes + } + x := x.Field(i) + if isZeroVal(x) { + omitted = true + continue // exclude zero-valued fields + } + if n, ok := x.Interface().(Nodes); ok && len(n) == 0 { + omitted = true + continue // exclude empty Nodes slices + } + + if first { + p.printf("\n") + first = false + } + p.printf("%s: ", name) + p.dump(x, depth-1) + p.printf("\n") + } + } + if omitted { + p.printf("…\n") + } + + p.indent-- + p.printf("}") + + default: + p.printf("%v", x.Interface()) + } +} + +func isZeroVal(x reflect.Value) bool { + switch x.Kind() { + case reflect.Bool: + return !x.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return x.Uint() == 0 + case reflect.String: + return x.String() == "" + case reflect.Interface, reflect.Ptr, reflect.Slice: + return x.IsNil() + } + return false +} + +func commonPrefixLen(a, b string) (i int) { + for i < len(a) && i < len(b) && a[i] == b[i] { + i++ + } + return +} diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go new file mode 100644 index 0000000..68303c0 --- /dev/null +++ b/src/cmd/compile/internal/ir/expr.go @@ -0,0 +1,1147 @@ +// Copyright 2020 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 ir + +import ( + "bytes" + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/src" + "fmt" + "go/constant" + "go/token" +) + +// An Expr is a Node that can appear as an expression. +type Expr interface { + Node + isExpr() +} + +// A miniExpr is a miniNode with extra fields common to expressions. +// TODO(rsc): Once we are sure about the contents, compact the bools +// into a bit field and leave extra bits available for implementations +// embedding miniExpr. Right now there are ~60 unused bits sitting here. +type miniExpr struct { + miniNode + typ *types.Type + init Nodes // TODO(rsc): Don't require every Node to have an init + flags bitset8 +} + +const ( + miniExprNonNil = 1 << iota + miniExprTransient + miniExprBounded + miniExprImplicit // for use by implementations; not supported by every Expr + miniExprCheckPtr +) + +func (*miniExpr) isExpr() {} + +func (n *miniExpr) Type() *types.Type { return n.typ } +func (n *miniExpr) SetType(x *types.Type) { n.typ = x } +func (n *miniExpr) NonNil() bool { return n.flags&miniExprNonNil != 0 } +func (n *miniExpr) MarkNonNil() { n.flags |= miniExprNonNil } +func (n *miniExpr) Transient() bool { return n.flags&miniExprTransient != 0 } +func (n *miniExpr) SetTransient(b bool) { n.flags.set(miniExprTransient, b) } +func (n *miniExpr) Bounded() bool { return n.flags&miniExprBounded != 0 } +func (n *miniExpr) SetBounded(b bool) { n.flags.set(miniExprBounded, b) } +func (n *miniExpr) Init() Nodes { return n.init } +func (n *miniExpr) PtrInit() *Nodes { return &n.init } +func (n *miniExpr) SetInit(x Nodes) { n.init = x } + +// An AddStringExpr is a string concatenation Expr[0] + Exprs[1] + ... + Expr[len(Expr)-1]. +type AddStringExpr struct { + miniExpr + List Nodes + Prealloc *Name +} + +func NewAddStringExpr(pos src.XPos, list []Node) *AddStringExpr { + n := &AddStringExpr{} + n.pos = pos + n.op = OADDSTR + n.List = list + return n +} + +// An AddrExpr is an address-of expression &X. +// It may end up being a normal address-of or an allocation of a composite literal. +type AddrExpr struct { + miniExpr + X Node + Prealloc *Name // preallocated storage if any +} + +func NewAddrExpr(pos src.XPos, x Node) *AddrExpr { + n := &AddrExpr{X: x} + n.op = OADDR + n.pos = pos + return n +} + +func (n *AddrExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *AddrExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + +func (n *AddrExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OADDR, OPTRLIT: + n.op = op + } +} + +// A BasicLit is a literal of basic type. +type BasicLit struct { + miniExpr + val constant.Value +} + +func NewBasicLit(pos src.XPos, val constant.Value) Node { + n := &BasicLit{val: val} + n.op = OLITERAL + n.pos = pos + if k := val.Kind(); k != constant.Unknown { + n.SetType(idealType(k)) + } + return n +} + +func (n *BasicLit) Val() constant.Value { return n.val } +func (n *BasicLit) SetVal(val constant.Value) { n.val = val } + +// A BinaryExpr is a binary expression X Op Y, +// or Op(X, Y) for builtin functions that do not become calls. +type BinaryExpr struct { + miniExpr + X Node + Y Node +} + +func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr { + n := &BinaryExpr{X: x, Y: y} + n.pos = pos + n.SetOp(op) + return n +} + +func (n *BinaryExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, + OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR, + OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, + OEFACE: + n.op = op + } +} + +// A CallExpr is a function call X(Args). +type CallExpr struct { + miniExpr + origNode + X Node + Args Nodes + KeepAlive []*Name // vars to be kept alive until call returns + IsDDD bool + NoInline bool +} + +func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr { + n := &CallExpr{X: fun} + n.pos = pos + n.orig = n + n.SetOp(op) + n.Args = args + return n +} + +func (*CallExpr) isStmt() {} + +func (n *CallExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OAPPEND, + OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, + ODELETE, + OGETG, OGETCALLERPC, OGETCALLERSP, + OMAKE, OPRINT, OPRINTN, + ORECOVER, ORECOVERFP: + n.op = op + } +} + +// A ClosureExpr is a function literal expression. +type ClosureExpr struct { + miniExpr + Func *Func `mknode:"-"` + Prealloc *Name + IsGoWrap bool // whether this is wrapper closure of a go statement +} + +// Deprecated: Use NewClosureFunc instead. +func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr { + n := &ClosureExpr{Func: fn} + n.op = OCLOSURE + n.pos = pos + return n +} + +// A CompLitExpr is a composite literal Type{Vals}. +// Before type-checking, the type is Ntype. +type CompLitExpr struct { + miniExpr + origNode + Ntype Ntype + List Nodes // initialized values + Prealloc *Name + Len int64 // backing array length for OSLICELIT +} + +func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr { + n := &CompLitExpr{Ntype: typ} + n.pos = pos + n.SetOp(op) + n.List = list + n.orig = n + return n +} + +func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + +func (n *CompLitExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OARRAYLIT, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OSLICELIT: + n.op = op + } +} + +type ConstExpr struct { + miniExpr + origNode + val constant.Value +} + +func NewConstExpr(val constant.Value, orig Node) Node { + n := &ConstExpr{val: val} + n.op = OLITERAL + n.pos = orig.Pos() + n.orig = orig + n.SetType(orig.Type()) + n.SetTypecheck(orig.Typecheck()) + n.SetDiag(orig.Diag()) + return n +} + +func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() } +func (n *ConstExpr) Val() constant.Value { return n.val } + +// A ConvExpr is a conversion Type(X). +// It may end up being a value or a type. +type ConvExpr struct { + miniExpr + X Node + NonEscaping bool // The allocation needed for the conversion to interface is known not to escape +} + +func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr { + n := &ConvExpr{X: x} + n.pos = pos + n.typ = typ + n.SetOp(op) + return n +} + +func (n *ConvExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *ConvExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } +func (n *ConvExpr) CheckPtr() bool { return n.flags&miniExprCheckPtr != 0 } +func (n *ConvExpr) SetCheckPtr(b bool) { n.flags.set(miniExprCheckPtr, b) } + +func (n *ConvExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR: + n.op = op + } +} + +// An IndexExpr is an index expression X[Index]. +type IndexExpr struct { + miniExpr + X Node + Index Node + Assigned bool +} + +func NewIndexExpr(pos src.XPos, x, index Node) *IndexExpr { + n := &IndexExpr{X: x, Index: index} + n.pos = pos + n.op = OINDEX + return n +} + +func (n *IndexExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OINDEX, OINDEXMAP: + n.op = op + } +} + +// A KeyExpr is a Key: Value composite literal key. +type KeyExpr struct { + miniExpr + Key Node + Value Node +} + +func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr { + n := &KeyExpr{Key: key, Value: value} + n.pos = pos + n.op = OKEY + return n +} + +// A StructKeyExpr is an Field: Value composite literal key. +type StructKeyExpr struct { + miniExpr + Field *types.Field + Value Node +} + +func NewStructKeyExpr(pos src.XPos, field *types.Field, value Node) *StructKeyExpr { + n := &StructKeyExpr{Field: field, Value: value} + n.pos = pos + n.op = OSTRUCTKEY + return n +} + +func (n *StructKeyExpr) Sym() *types.Sym { return n.Field.Sym } + +// An InlinedCallExpr is an inlined function call. +type InlinedCallExpr struct { + miniExpr + Body Nodes + ReturnVars Nodes // must be side-effect free +} + +func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr { + n := &InlinedCallExpr{} + n.pos = pos + n.op = OINLCALL + n.Body = body + n.ReturnVars = retvars + return n +} + +func (n *InlinedCallExpr) SingleResult() Node { + if have := len(n.ReturnVars); have != 1 { + base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have) + } + if !n.Type().HasShape() && n.ReturnVars[0].Type().HasShape() { + // If the type of the call is not a shape, but the type of the return value + // is a shape, we need to do an implicit conversion, so the real type + // of n is maintained. + r := NewConvExpr(n.Pos(), OCONVNOP, n.Type(), n.ReturnVars[0]) + r.SetTypecheck(1) + return r + } + return n.ReturnVars[0] +} + +// A LogicalExpr is a expression X Op Y where Op is && or ||. +// It is separate from BinaryExpr to make room for statements +// that must be executed before Y but after X. +type LogicalExpr struct { + miniExpr + X Node + Y Node +} + +func NewLogicalExpr(pos src.XPos, op Op, x, y Node) *LogicalExpr { + n := &LogicalExpr{X: x, Y: y} + n.pos = pos + n.SetOp(op) + return n +} + +func (n *LogicalExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OANDAND, OOROR: + n.op = op + } +} + +// A MakeExpr is a make expression: make(Type[, Len[, Cap]]). +// Op is OMAKECHAN, OMAKEMAP, OMAKESLICE, or OMAKESLICECOPY, +// but *not* OMAKE (that's a pre-typechecking CallExpr). +type MakeExpr struct { + miniExpr + Len Node + Cap Node +} + +func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr { + n := &MakeExpr{Len: len, Cap: cap} + n.pos = pos + n.SetOp(op) + return n +} + +func (n *MakeExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OMAKECHAN, OMAKEMAP, OMAKESLICE, OMAKESLICECOPY: + n.op = op + } +} + +// A NilExpr represents the predefined untyped constant nil. +// (It may be copied and assigned a type, though.) +type NilExpr struct { + miniExpr + Sym_ *types.Sym // TODO: Remove +} + +func NewNilExpr(pos src.XPos) *NilExpr { + n := &NilExpr{} + n.pos = pos + n.op = ONIL + return n +} + +func (n *NilExpr) Sym() *types.Sym { return n.Sym_ } +func (n *NilExpr) SetSym(x *types.Sym) { n.Sym_ = x } + +// A ParenExpr is a parenthesized expression (X). +// It may end up being a value or a type. +type ParenExpr struct { + miniExpr + X Node +} + +func NewParenExpr(pos src.XPos, x Node) *ParenExpr { + n := &ParenExpr{X: x} + n.op = OPAREN + n.pos = pos + return n +} + +func (n *ParenExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *ParenExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + +func (*ParenExpr) CanBeNtype() {} + +// SetOTYPE changes n to be an OTYPE node returning t, +// like all the type nodes in type.go. +func (n *ParenExpr) SetOTYPE(t *types.Type) { + n.op = OTYPE + n.typ = t + t.SetNod(n) +} + +// A RawOrigExpr represents an arbitrary Go expression as a string value. +// When printed in diagnostics, the string value is written out exactly as-is. +type RawOrigExpr struct { + miniExpr + Raw string +} + +func NewRawOrigExpr(pos src.XPos, op Op, raw string) *RawOrigExpr { + n := &RawOrigExpr{Raw: raw} + n.pos = pos + n.op = op + return n +} + +// A ResultExpr represents a direct access to a result. +type ResultExpr struct { + miniExpr + Index int64 // index of the result expr. +} + +func NewResultExpr(pos src.XPos, typ *types.Type, index int64) *ResultExpr { + n := &ResultExpr{Index: index} + n.pos = pos + n.op = ORESULT + n.typ = typ + return n +} + +// A LinksymOffsetExpr refers to an offset within a global variable. +// It is like a SelectorExpr but without the field name. +type LinksymOffsetExpr struct { + miniExpr + Linksym *obj.LSym + Offset_ int64 +} + +func NewLinksymOffsetExpr(pos src.XPos, lsym *obj.LSym, offset int64, typ *types.Type) *LinksymOffsetExpr { + n := &LinksymOffsetExpr{Linksym: lsym, Offset_: offset} + n.typ = typ + n.op = OLINKSYMOFFSET + return n +} + +// NewLinksymExpr is NewLinksymOffsetExpr, but with offset fixed at 0. +func NewLinksymExpr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *LinksymOffsetExpr { + return NewLinksymOffsetExpr(pos, lsym, 0, typ) +} + +// NewNameOffsetExpr is NewLinksymOffsetExpr, but taking a *Name +// representing a global variable instead of an *obj.LSym directly. +func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type) *LinksymOffsetExpr { + if name == nil || IsBlank(name) || !(name.Op() == ONAME && name.Class == PEXTERN) { + base.FatalfAt(pos, "cannot take offset of nil, blank name or non-global variable: %v", name) + } + return NewLinksymOffsetExpr(pos, name.Linksym(), offset, typ) +} + +// A SelectorExpr is a selector expression X.Sel. +type SelectorExpr struct { + miniExpr + X Node + // Sel is the name of the field or method being selected, without (in the + // case of methods) any preceding type specifier. If the field/method is + // exported, than the Sym uses the local package regardless of the package + // of the containing type. + Sel *types.Sym + // The actual selected field - may not be filled in until typechecking. + Selection *types.Field + Prealloc *Name // preallocated storage for OMETHVALUE, if any +} + +func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr { + n := &SelectorExpr{X: x, Sel: sel} + n.pos = pos + n.SetOp(op) + return n +} + +func (n *SelectorExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OMETHVALUE, OMETHEXPR: + n.op = op + } +} + +func (n *SelectorExpr) Sym() *types.Sym { return n.Sel } +func (n *SelectorExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *SelectorExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } +func (n *SelectorExpr) Offset() int64 { return n.Selection.Offset } + +func (n *SelectorExpr) FuncName() *Name { + if n.Op() != OMETHEXPR { + panic(n.no("FuncName")) + } + fn := NewNameAt(n.Selection.Pos, MethodSym(n.X.Type(), n.Sel)) + fn.Class = PFUNC + fn.SetType(n.Type()) + if n.Selection.Nname != nil { + // TODO(austin): Nname is nil for interface method + // expressions (I.M), so we can't attach a Func to + // those here. reflectdata.methodWrapper generates the + // Func. + fn.Func = n.Selection.Nname.(*Name).Func + } + return fn +} + +// Before type-checking, bytes.Buffer is a SelectorExpr. +// After type-checking it becomes a Name. +func (*SelectorExpr) CanBeNtype() {} + +// A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max]. +type SliceExpr struct { + miniExpr + X Node + Low Node + High Node + Max Node +} + +func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr { + n := &SliceExpr{X: x, Low: low, High: high, Max: max} + n.pos = pos + n.op = op + return n +} + +func (n *SliceExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: + n.op = op + } +} + +// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR). +// o must be a slicing op. +func (o Op) IsSlice3() bool { + switch o { + case OSLICE, OSLICEARR, OSLICESTR: + return false + case OSLICE3, OSLICE3ARR: + return true + } + base.Fatalf("IsSlice3 op %v", o) + return false +} + +// A SliceHeader expression constructs a slice header from its parts. +type SliceHeaderExpr struct { + miniExpr + Ptr Node + Len Node + Cap Node +} + +func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *SliceHeaderExpr { + n := &SliceHeaderExpr{Ptr: ptr, Len: len, Cap: cap} + n.pos = pos + n.op = OSLICEHEADER + n.typ = typ + return n +} + +// A StarExpr is a dereference expression *X. +// It may end up being a value or a type. +type StarExpr struct { + miniExpr + X Node +} + +func NewStarExpr(pos src.XPos, x Node) *StarExpr { + n := &StarExpr{X: x} + n.op = ODEREF + n.pos = pos + return n +} + +func (n *StarExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + +func (*StarExpr) CanBeNtype() {} + +// SetOTYPE changes n to be an OTYPE node returning t, +// like all the type nodes in type.go. +func (n *StarExpr) SetOTYPE(t *types.Type) { + n.op = OTYPE + n.X = nil + n.typ = t + t.SetNod(n) +} + +// A TypeAssertionExpr is a selector expression X.(Type). +// Before type-checking, the type is Ntype. +type TypeAssertExpr struct { + miniExpr + X Node + Ntype Ntype + + // Runtime type information provided by walkDotType for + // assertions from non-empty interface to concrete type. + Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type +} + +func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr { + n := &TypeAssertExpr{X: x, Ntype: typ} + n.pos = pos + n.op = ODOTTYPE + return n +} + +func (n *TypeAssertExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case ODOTTYPE, ODOTTYPE2: + n.op = op + } +} + +// A DynamicTypeAssertExpr asserts that X is of dynamic type T. +type DynamicTypeAssertExpr struct { + miniExpr + X Node + // N = not an interface + // E = empty interface + // I = nonempty interface + // For E->N, T is a *runtime.type for N + // For I->N, T is a *runtime.itab for N+I + // For E->I, T is a *runtime.type for I + // For I->I, ditto + // For I->E, T is a *runtime.type for interface{} (unnecessary, but just to fill in the slot) + // For E->E, ditto + T Node +} + +func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, t Node) *DynamicTypeAssertExpr { + n := &DynamicTypeAssertExpr{X: x, T: t} + n.pos = pos + n.op = op + return n +} + +func (n *DynamicTypeAssertExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2: + n.op = op + } +} + +// A UnaryExpr is a unary expression Op X, +// or Op(X) for a builtin function that does not end up being a call. +type UnaryExpr struct { + miniExpr + X Node +} + +func NewUnaryExpr(pos src.XPos, op Op, x Node) *UnaryExpr { + n := &UnaryExpr{X: x} + n.pos = pos + n.SetOp(op) + return n +} + +func (n *UnaryExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OBITNOT, ONEG, ONOT, OPLUS, ORECV, + OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW, + OOFFSETOF, OPANIC, OREAL, OSIZEOF, + OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, OVARDEF, OVARKILL, OVARLIVE: + n.op = op + } +} + +// Probably temporary: using Implicit() flag to mark generic function nodes that +// are called to make getGfInfo analysis easier in one pre-order pass. +func (n *InstExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *InstExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + +// An InstExpr is a generic function or type instantiation. +type InstExpr struct { + miniExpr + X Node + Targs []Node +} + +func NewInstExpr(pos src.XPos, op Op, x Node, targs []Node) *InstExpr { + n := &InstExpr{X: x, Targs: targs} + n.pos = pos + n.op = op + return n +} + +func IsZero(n Node) bool { + switch n.Op() { + case ONIL: + return true + + case OLITERAL: + switch u := n.Val(); u.Kind() { + case constant.String: + return constant.StringVal(u) == "" + case constant.Bool: + return !constant.BoolVal(u) + default: + return constant.Sign(u) == 0 + } + + case OARRAYLIT: + n := n.(*CompLitExpr) + for _, n1 := range n.List { + if n1.Op() == OKEY { + n1 = n1.(*KeyExpr).Value + } + if !IsZero(n1) { + return false + } + } + return true + + case OSTRUCTLIT: + n := n.(*CompLitExpr) + for _, n1 := range n.List { + n1 := n1.(*StructKeyExpr) + if !IsZero(n1.Value) { + return false + } + } + return true + } + + return false +} + +// lvalue etc +func IsAddressable(n Node) bool { + switch n.Op() { + case OINDEX: + n := n.(*IndexExpr) + if n.X.Type() != nil && n.X.Type().IsArray() { + return IsAddressable(n.X) + } + if n.X.Type() != nil && n.X.Type().IsString() { + return false + } + fallthrough + case ODEREF, ODOTPTR: + return true + + case ODOT: + n := n.(*SelectorExpr) + return IsAddressable(n.X) + + case ONAME: + n := n.(*Name) + if n.Class == PFUNC { + return false + } + return true + + case OLINKSYMOFFSET: + return true + } + + return false +} + +func StaticValue(n Node) Node { + for { + if n.Op() == OCONVNOP { + n = n.(*ConvExpr).X + continue + } + + if n.Op() == OINLCALL { + n = n.(*InlinedCallExpr).SingleResult() + continue + } + + n1 := staticValue1(n) + if n1 == nil { + return n + } + n = n1 + } +} + +// staticValue1 implements a simple SSA-like optimization. If n is a local variable +// that is initialized and never reassigned, staticValue1 returns the initializer +// expression. Otherwise, it returns nil. +func staticValue1(nn Node) Node { + if nn.Op() != ONAME { + return nil + } + n := nn.(*Name) + if n.Class != PAUTO { + return nil + } + + defn := n.Defn + if defn == nil { + return nil + } + + var rhs Node +FindRHS: + switch defn.Op() { + case OAS: + defn := defn.(*AssignStmt) + rhs = defn.Y + case OAS2: + defn := defn.(*AssignListStmt) + for i, lhs := range defn.Lhs { + if lhs == n { + rhs = defn.Rhs[i] + break FindRHS + } + } + base.Fatalf("%v missing from LHS of %v", n, defn) + default: + return nil + } + if rhs == nil { + base.Fatalf("RHS is nil: %v", defn) + } + + if reassigned(n) { + return nil + } + + return rhs +} + +// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean +// indicating whether the name has any assignments other than its declaration. +// The second return value is the first such assignment encountered in the walk, if any. It is mostly +// useful for -m output documenting the reason for inhibited optimizations. +// NB: global variables are always considered to be re-assigned. +// TODO: handle initial declaration not including an assignment and followed by a single assignment? +func reassigned(name *Name) bool { + if name.Op() != ONAME { + base.Fatalf("reassigned %v", name) + } + // no way to reliably check for no-reassignment of globals, assume it can be + if name.Curfn == nil { + return true + } + + // TODO(mdempsky): This is inefficient and becoming increasingly + // unwieldy. Figure out a way to generalize escape analysis's + // reassignment detection for use by inlining and devirtualization. + + // isName reports whether n is a reference to name. + isName := func(x Node) bool { + n, ok := x.(*Name) + return ok && n.Canonical() == name + } + + var do func(n Node) bool + do = func(n Node) bool { + switch n.Op() { + case OAS: + n := n.(*AssignStmt) + if isName(n.X) && n != name.Defn { + return true + } + case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2: + n := n.(*AssignListStmt) + for _, p := range n.Lhs { + if isName(p) && n != name.Defn { + return true + } + } + case OADDR: + n := n.(*AddrExpr) + if isName(OuterValue(n.X)) { + return true + } + case OCLOSURE: + n := n.(*ClosureExpr) + if Any(n.Func, do) { + return true + } + } + return false + } + return Any(name.Curfn, do) +} + +// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation. +var IsIntrinsicCall = func(*CallExpr) bool { return false } + +// SameSafeExpr checks whether it is safe to reuse one of l and r +// instead of computing both. SameSafeExpr assumes that l and r are +// used in the same statement or expression. In order for it to be +// safe to reuse l or r, they must: +// * be the same expression +// * not have side-effects (no function calls, no channel ops); +// however, panics are ok +// * not cause inappropriate aliasing; e.g. two string to []byte +// conversions, must result in two distinct slices +// +// The handling of OINDEXMAP is subtle. OINDEXMAP can occur both +// as an lvalue (map assignment) and an rvalue (map access). This is +// currently OK, since the only place SameSafeExpr gets used on an +// lvalue expression is for OSLICE and OAPPEND optimizations, and it +// is correct in those settings. +func SameSafeExpr(l Node, r Node) bool { + if l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) { + return false + } + + switch l.Op() { + case ONAME: + return l == r + + case ODOT, ODOTPTR: + l := l.(*SelectorExpr) + r := r.(*SelectorExpr) + return l.Sel != nil && r.Sel != nil && l.Sel == r.Sel && SameSafeExpr(l.X, r.X) + + case ODEREF: + l := l.(*StarExpr) + r := r.(*StarExpr) + return SameSafeExpr(l.X, r.X) + + case ONOT, OBITNOT, OPLUS, ONEG: + l := l.(*UnaryExpr) + r := r.(*UnaryExpr) + return SameSafeExpr(l.X, r.X) + + case OCONVNOP: + l := l.(*ConvExpr) + r := r.(*ConvExpr) + return SameSafeExpr(l.X, r.X) + + case OCONV: + l := l.(*ConvExpr) + r := r.(*ConvExpr) + // Some conversions can't be reused, such as []byte(str). + // Allow only numeric-ish types. This is a bit conservative. + return types.IsSimple[l.Type().Kind()] && SameSafeExpr(l.X, r.X) + + case OINDEX, OINDEXMAP: + l := l.(*IndexExpr) + r := r.(*IndexExpr) + return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Index, r.Index) + + case OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD: + l := l.(*BinaryExpr) + r := r.(*BinaryExpr) + return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Y, r.Y) + + case OLITERAL: + return constant.Compare(l.Val(), token.EQL, r.Val()) + + case ONIL: + return true + } + + return false +} + +// ShouldCheckPtr reports whether pointer checking should be enabled for +// function fn at a given level. See debugHelpFooter for defined +// levels. +func ShouldCheckPtr(fn *Func, level int) bool { + return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0 +} + +// IsReflectHeaderDataField reports whether l is an expression p.Data +// where p has type reflect.SliceHeader or reflect.StringHeader. +func IsReflectHeaderDataField(l Node) bool { + if l.Type() != types.Types[types.TUINTPTR] { + return false + } + + var tsym *types.Sym + switch l.Op() { + case ODOT: + l := l.(*SelectorExpr) + tsym = l.X.Type().Sym() + case ODOTPTR: + l := l.(*SelectorExpr) + tsym = l.X.Type().Elem().Sym() + default: + return false + } + + if tsym == nil || l.Sym().Name != "Data" || tsym.Pkg.Path != "reflect" { + return false + } + return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader" +} + +func ParamNames(ft *types.Type) []Node { + args := make([]Node, ft.NumParams()) + for i, f := range ft.Params().FieldSlice() { + args[i] = AsNode(f.Nname) + } + return args +} + +// MethodSym returns the method symbol representing a method name +// associated with a specific receiver type. +// +// Method symbols can be used to distinguish the same method appearing +// in different method sets. For example, T.M and (*T).M have distinct +// method symbols. +// +// The returned symbol will be marked as a function. +func MethodSym(recv *types.Type, msym *types.Sym) *types.Sym { + sym := MethodSymSuffix(recv, msym, "") + sym.SetFunc(true) + return sym +} + +// MethodSymSuffix is like methodsym, but allows attaching a +// distinguisher suffix. To avoid collisions, the suffix must not +// start with a letter, number, or period. +func MethodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym { + if msym.IsBlank() { + base.Fatalf("blank method name") + } + + rsym := recv.Sym() + if recv.IsPtr() { + if rsym != nil { + base.Fatalf("declared pointer receiver type: %v", recv) + } + rsym = recv.Elem().Sym() + } + + // Find the package the receiver type appeared in. For + // anonymous receiver types (i.e., anonymous structs with + // embedded fields), use the "go" pseudo-package instead. + rpkg := Pkgs.Go + if rsym != nil { + rpkg = rsym.Pkg + } + + var b bytes.Buffer + if recv.IsPtr() { + // The parentheses aren't really necessary, but + // they're pretty traditional at this point. + fmt.Fprintf(&b, "(%-S)", recv) + } else { + fmt.Fprintf(&b, "%-S", recv) + } + + // A particular receiver type may have multiple non-exported + // methods with the same name. To disambiguate them, include a + // package qualifier for names that came from a different + // package than the receiver type. + if !types.IsExported(msym.Name) && msym.Pkg != rpkg { + b.WriteString(".") + b.WriteString(msym.Pkg.Prefix) + } + + b.WriteString(".") + b.WriteString(msym.Name) + b.WriteString(suffix) + + return rpkg.LookupBytes(b.Bytes()) +} + +// MethodExprName returns the ONAME representing the method +// referenced by expression n, which must be a method selector, +// method expression, or method value. +func MethodExprName(n Node) *Name { + name, _ := MethodExprFunc(n).Nname.(*Name) + return name +} + +// MethodExprFunc is like MethodExprName, but returns the types.Field instead. +func MethodExprFunc(n Node) *types.Field { + switch n.Op() { + case ODOTMETH, OMETHEXPR, OMETHVALUE: + return n.(*SelectorExpr).Selection + } + base.Fatalf("unexpected node: %v (%v)", n, n.Op()) + panic("unreachable") +} diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go new file mode 100644 index 0000000..0331885 --- /dev/null +++ b/src/cmd/compile/internal/ir/fmt.go @@ -0,0 +1,1359 @@ +// 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 ir + +import ( + "bytes" + "fmt" + "go/constant" + "io" + "math" + "os" + "path/filepath" + "reflect" + "strings" + + "unicode/utf8" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// Op + +var OpNames = []string{ + OADDR: "&", + OADD: "+", + OADDSTR: "+", + OALIGNOF: "unsafe.Alignof", + OANDAND: "&&", + OANDNOT: "&^", + OAND: "&", + OAPPEND: "append", + OAS: "=", + OAS2: "=", + OBREAK: "break", + OCALL: "function call", // not actual syntax + OCAP: "cap", + OCASE: "case", + OCLOSE: "close", + OCOMPLEX: "complex", + OBITNOT: "^", + OCONTINUE: "continue", + OCOPY: "copy", + ODELETE: "delete", + ODEFER: "defer", + ODIV: "/", + OEQ: "==", + OFALL: "fallthrough", + OFOR: "for", + OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892 + OGE: ">=", + OGOTO: "goto", + OGT: ">", + OIF: "if", + OIMAG: "imag", + OINLMARK: "inlmark", + ODEREF: "*", + OLEN: "len", + OLE: "<=", + OLSH: "<<", + OLT: "<", + OMAKE: "make", + ONEG: "-", + OMOD: "%", + OMUL: "*", + ONEW: "new", + ONE: "!=", + ONOT: "!", + OOFFSETOF: "unsafe.Offsetof", + OOROR: "||", + OOR: "|", + OPANIC: "panic", + OPLUS: "+", + OPRINTN: "println", + OPRINT: "print", + ORANGE: "range", + OREAL: "real", + ORECV: "<-", + ORECOVER: "recover", + ORETURN: "return", + ORSH: ">>", + OSELECT: "select", + OSEND: "<-", + OSIZEOF: "unsafe.Sizeof", + OSUB: "-", + OSWITCH: "switch", + OUNSAFEADD: "unsafe.Add", + OUNSAFESLICE: "unsafe.Slice", + OXOR: "^", +} + +// GoString returns the Go syntax for the Op, or else its name. +func (o Op) GoString() string { + if int(o) < len(OpNames) && OpNames[o] != "" { + return OpNames[o] + } + return o.String() +} + +// Format implements formatting for an Op. +// The valid formats are: +// +// %v Go syntax ("+", "<-", "print") +// %+v Debug syntax ("ADD", "RECV", "PRINT") +// +func (o Op) Format(s fmt.State, verb rune) { + switch verb { + default: + fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o)) + case 'v': + if s.Flag('+') { + // %+v is OMUL instead of "*" + io.WriteString(s, o.String()) + return + } + io.WriteString(s, o.GoString()) + } +} + +// Node + +// FmtNode implements formatting for a Node n. +// Every Node implementation must define a Format method that calls FmtNode. +// The valid formats are: +// +// %v Go syntax +// %L Go syntax followed by " (type T)" if type is known. +// %+v Debug syntax, as in Dump. +// +func fmtNode(n Node, s fmt.State, verb rune) { + // %+v prints Dump. + // Otherwise we print Go syntax. + if s.Flag('+') && verb == 'v' { + dumpNode(s, n, 1) + return + } + + if verb != 'v' && verb != 'S' && verb != 'L' { + fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n) + return + } + + if n == nil { + fmt.Fprint(s, "<nil>") + return + } + + t := n.Type() + if verb == 'L' && t != nil { + if t.Kind() == types.TNIL { + fmt.Fprint(s, "nil") + } else if n.Op() == ONAME && n.Name().AutoTemp() { + fmt.Fprintf(s, "%v value", t) + } else { + fmt.Fprintf(s, "%v (type %v)", n, t) + } + return + } + + // TODO inlining produces expressions with ninits. we can't print these yet. + + if OpPrec[n.Op()] < 0 { + stmtFmt(n, s) + return + } + + exprFmt(n, s, 0) +} + +var OpPrec = []int{ + OALIGNOF: 8, + OAPPEND: 8, + OBYTES2STR: 8, + OARRAYLIT: 8, + OSLICELIT: 8, + ORUNES2STR: 8, + OCALLFUNC: 8, + OCALLINTER: 8, + OCALLMETH: 8, + OCALL: 8, + OCAP: 8, + OCLOSE: 8, + OCOMPLIT: 8, + OCONVIFACE: 8, + OCONVIDATA: 8, + OCONVNOP: 8, + OCONV: 8, + OCOPY: 8, + ODELETE: 8, + OGETG: 8, + OLEN: 8, + OLITERAL: 8, + OMAKESLICE: 8, + OMAKESLICECOPY: 8, + OMAKE: 8, + OMAPLIT: 8, + ONAME: 8, + ONEW: 8, + ONIL: 8, + ONONAME: 8, + OOFFSETOF: 8, + OPACK: 8, + OPANIC: 8, + OPAREN: 8, + OPRINTN: 8, + OPRINT: 8, + ORUNESTR: 8, + OSIZEOF: 8, + OSLICE2ARRPTR: 8, + OSTR2BYTES: 8, + OSTR2RUNES: 8, + OSTRUCTLIT: 8, + OTARRAY: 8, + OTSLICE: 8, + OTCHAN: 8, + OTFUNC: 8, + OTINTER: 8, + OTMAP: 8, + OTSTRUCT: 8, + OTYPE: 8, + OUNSAFEADD: 8, + OUNSAFESLICE: 8, + OINDEXMAP: 8, + OINDEX: 8, + OSLICE: 8, + OSLICESTR: 8, + OSLICEARR: 8, + OSLICE3: 8, + OSLICE3ARR: 8, + OSLICEHEADER: 8, + ODOTINTER: 8, + ODOTMETH: 8, + ODOTPTR: 8, + ODOTTYPE2: 8, + ODOTTYPE: 8, + ODOT: 8, + OXDOT: 8, + OMETHVALUE: 8, + OMETHEXPR: 8, + OPLUS: 7, + ONOT: 7, + OBITNOT: 7, + ONEG: 7, + OADDR: 7, + ODEREF: 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, + OSEND: 3, + OANDAND: 2, + OOROR: 1, + + // Statements handled by stmtfmt + OAS: -1, + OAS2: -1, + OAS2DOTTYPE: -1, + OAS2FUNC: -1, + OAS2MAPR: -1, + OAS2RECV: -1, + OASOP: -1, + OBLOCK: -1, + OBREAK: -1, + OCASE: -1, + OCONTINUE: -1, + ODCL: -1, + ODEFER: -1, + OFALL: -1, + OFOR: -1, + OFORUNTIL: -1, + OGOTO: -1, + OIF: -1, + OLABEL: -1, + OGO: -1, + ORANGE: -1, + ORETURN: -1, + OSELECT: -1, + OSWITCH: -1, + + OEND: 0, +} + +// StmtWithInit reports whether op is a statement with an explicit init list. +func StmtWithInit(op Op) bool { + switch op { + case OIF, OFOR, OFORUNTIL, OSWITCH: + return true + } + return false +} + +func stmtFmt(n Node, s fmt.State) { + // NOTE(rsc): This code used to support the text-based + // which was more aggressive about printing full Go syntax + // (for example, an actual loop instead of "for loop"). + // The code is preserved for now in case we want to expand + // any of those shortenings later. Or maybe we will delete + // the code. But for now, keep it. + const exportFormat = false + + // 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 := len(n.Init()) == 1 && len(n.Init()[0].Init()) == 0 && StmtWithInit(n.Op()) + + // otherwise, print the inits as separate statements + complexinit := len(n.Init()) != 0 && !simpleinit && exportFormat + + // 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 { + fmt.Fprint(s, "{") + } + + if complexinit { + fmt.Fprintf(s, " %v; ", n.Init()) + } + + switch n.Op() { + case ODCL: + n := n.(*Decl) + fmt.Fprintf(s, "var %v %v", n.X.Sym(), n.X.Type()) + + // Don't export "v = <N>" initializing statements, hope they're always + // preceded by the DCL which will be re-parsed and typechecked to reproduce + // the "v = <N>" again. + case OAS: + n := n.(*AssignStmt) + if n.Def && !complexinit { + fmt.Fprintf(s, "%v := %v", n.X, n.Y) + } else { + fmt.Fprintf(s, "%v = %v", n.X, n.Y) + } + + case OASOP: + n := n.(*AssignOpStmt) + if n.IncDec { + if n.AsOp == OADD { + fmt.Fprintf(s, "%v++", n.X) + } else { + fmt.Fprintf(s, "%v--", n.X) + } + break + } + + fmt.Fprintf(s, "%v %v= %v", n.X, n.AsOp, n.Y) + + case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: + n := n.(*AssignListStmt) + if n.Def && !complexinit { + fmt.Fprintf(s, "%.v := %.v", n.Lhs, n.Rhs) + } else { + fmt.Fprintf(s, "%.v = %.v", n.Lhs, n.Rhs) + } + + case OBLOCK: + n := n.(*BlockStmt) + if len(n.List) != 0 { + fmt.Fprintf(s, "%v", n.List) + } + + case ORETURN: + n := n.(*ReturnStmt) + fmt.Fprintf(s, "return %.v", n.Results) + + case OTAILCALL: + n := n.(*TailCallStmt) + fmt.Fprintf(s, "tailcall %v", n.Call) + + case OINLMARK: + n := n.(*InlineMarkStmt) + fmt.Fprintf(s, "inlmark %d", n.Index) + + case OGO: + n := n.(*GoDeferStmt) + fmt.Fprintf(s, "go %v", n.Call) + + case ODEFER: + n := n.(*GoDeferStmt) + fmt.Fprintf(s, "defer %v", n.Call) + + case OIF: + n := n.(*IfStmt) + if simpleinit { + fmt.Fprintf(s, "if %v; %v { %v }", n.Init()[0], n.Cond, n.Body) + } else { + fmt.Fprintf(s, "if %v { %v }", n.Cond, n.Body) + } + if len(n.Else) != 0 { + fmt.Fprintf(s, " else { %v }", n.Else) + } + + case OFOR, OFORUNTIL: + n := n.(*ForStmt) + opname := "for" + if n.Op() == OFORUNTIL { + opname = "foruntil" + } + if !exportFormat { // TODO maybe only if FmtShort, same below + fmt.Fprintf(s, "%s loop", opname) + break + } + + fmt.Fprint(s, opname) + if simpleinit { + fmt.Fprintf(s, " %v;", n.Init()[0]) + } else if n.Post != nil { + fmt.Fprint(s, " ;") + } + + if n.Cond != nil { + fmt.Fprintf(s, " %v", n.Cond) + } + + if n.Post != nil { + fmt.Fprintf(s, "; %v", n.Post) + } else if simpleinit { + fmt.Fprint(s, ";") + } + + if n.Op() == OFORUNTIL && len(n.Late) != 0 { + fmt.Fprintf(s, "; %v", n.Late) + } + + fmt.Fprintf(s, " { %v }", n.Body) + + case ORANGE: + n := n.(*RangeStmt) + if !exportFormat { + fmt.Fprint(s, "for loop") + break + } + + fmt.Fprint(s, "for") + if n.Key != nil { + fmt.Fprintf(s, " %v", n.Key) + if n.Value != nil { + fmt.Fprintf(s, ", %v", n.Value) + } + fmt.Fprint(s, " =") + } + fmt.Fprintf(s, " range %v { %v }", n.X, n.Body) + + case OSELECT: + n := n.(*SelectStmt) + if !exportFormat { + fmt.Fprintf(s, "%v statement", n.Op()) + break + } + fmt.Fprintf(s, "select { %v }", n.Cases) + + case OSWITCH: + n := n.(*SwitchStmt) + if !exportFormat { + fmt.Fprintf(s, "%v statement", n.Op()) + break + } + fmt.Fprintf(s, "switch") + if simpleinit { + fmt.Fprintf(s, " %v;", n.Init()[0]) + } + if n.Tag != nil { + fmt.Fprintf(s, " %v ", n.Tag) + } + fmt.Fprintf(s, " { %v }", n.Cases) + + case OCASE: + n := n.(*CaseClause) + if len(n.List) != 0 { + fmt.Fprintf(s, "case %.v", n.List) + } else { + fmt.Fprint(s, "default") + } + fmt.Fprintf(s, ": %v", n.Body) + + case OBREAK, OCONTINUE, OGOTO, OFALL: + n := n.(*BranchStmt) + if n.Label != nil { + fmt.Fprintf(s, "%v %v", n.Op(), n.Label) + } else { + fmt.Fprintf(s, "%v", n.Op()) + } + + case OLABEL: + n := n.(*LabelStmt) + fmt.Fprintf(s, "%v: ", n.Label) + } + + if extrablock { + fmt.Fprint(s, "}") + } +} + +func exprFmt(n Node, s fmt.State, prec int) { + // NOTE(rsc): This code used to support the text-based + // which was more aggressive about printing full Go syntax + // (for example, an actual loop instead of "for loop"). + // The code is preserved for now in case we want to expand + // any of those shortenings later. Or maybe we will delete + // the code. But for now, keep it. + const exportFormat = false + + for { + if n == nil { + fmt.Fprint(s, "<nil>") + return + } + + // We always want the original, if any. + if o := Orig(n); o != n { + n = o + continue + } + + // Skip implicit operations introduced during typechecking. + switch nn := n; nn.Op() { + case OADDR: + nn := nn.(*AddrExpr) + if nn.Implicit() { + n = nn.X + continue + } + case ODEREF: + nn := nn.(*StarExpr) + if nn.Implicit() { + n = nn.X + continue + } + case OCONV, OCONVNOP, OCONVIFACE, OCONVIDATA: + nn := nn.(*ConvExpr) + if nn.Implicit() { + n = nn.X + continue + } + } + + break + } + + nprec := OpPrec[n.Op()] + if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() { + nprec = OpPrec[ODEREF] + } + + if prec > nprec { + fmt.Fprintf(s, "(%v)", n) + return + } + + if n, ok := n.(*RawOrigExpr); ok { + fmt.Fprint(s, n.Raw) + return + } + + switch n.Op() { + case OPAREN: + n := n.(*ParenExpr) + fmt.Fprintf(s, "(%v)", n.X) + + case ONIL: + fmt.Fprint(s, "nil") + + case OLITERAL: // this is a bit of a mess + if !exportFormat && n.Sym() != nil { + fmt.Fprint(s, n.Sym()) + return + } + + needUnparen := false + if n.Type() != nil && !n.Type().IsUntyped() { + // 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() == types.Crecv) { + fmt.Fprintf(s, "(%v)(", n.Type()) + } else { + fmt.Fprintf(s, "%v(", n.Type()) + } + needUnparen = true + } + + if n.Type() == types.UntypedRune { + switch x, ok := constant.Uint64Val(n.Val()); { + case !ok: + fallthrough + default: + fmt.Fprintf(s, "('\\x00' + %v)", n.Val()) + + case x < utf8.RuneSelf: + fmt.Fprintf(s, "%q", x) + + case x < 1<<16: + fmt.Fprintf(s, "'\\u%04x'", x) + + case x <= utf8.MaxRune: + fmt.Fprintf(s, "'\\U%08x'", x) + } + } else { + fmt.Fprint(s, types.FmtConst(n.Val(), s.Flag('#'))) + } + + if needUnparen { + fmt.Fprintf(s, ")") + } + + case ODCLFUNC: + n := n.(*Func) + if sym := n.Sym(); sym != nil { + fmt.Fprint(s, sym) + return + } + fmt.Fprintf(s, "<unnamed Func>") + + case ONAME: + n := n.(*Name) + // Special case: name used as local variable in export. + // _ becomes ~b%d internally; print as _ for export + if !exportFormat && n.Sym() != nil && n.Sym().Name[0] == '~' && n.Sym().Name[1] == 'b' { + fmt.Fprint(s, "_") + return + } + fallthrough + case OPACK, ONONAME: + fmt.Fprint(s, n.Sym()) + + case OLINKSYMOFFSET: + n := n.(*LinksymOffsetExpr) + fmt.Fprintf(s, "(%v)(%s@%d)", n.Type(), n.Linksym.Name, n.Offset_) + + case OTYPE: + if n.Type() == nil && n.Sym() != nil { + fmt.Fprint(s, n.Sym()) + return + } + fmt.Fprintf(s, "%v", n.Type()) + + case OTSLICE: + n := n.(*SliceType) + if n.DDD { + fmt.Fprintf(s, "...%v", n.Elem) + } else { + fmt.Fprintf(s, "[]%v", n.Elem) // happens before typecheck + } + + case OTARRAY: + n := n.(*ArrayType) + if n.Len == nil { + fmt.Fprintf(s, "[...]%v", n.Elem) + } else { + fmt.Fprintf(s, "[%v]%v", n.Len, n.Elem) + } + + case OTMAP: + n := n.(*MapType) + fmt.Fprintf(s, "map[%v]%v", n.Key, n.Elem) + + case OTCHAN: + n := n.(*ChanType) + switch n.Dir { + case types.Crecv: + fmt.Fprintf(s, "<-chan %v", n.Elem) + + case types.Csend: + fmt.Fprintf(s, "chan<- %v", n.Elem) + + default: + if n.Elem != nil && n.Elem.Op() == OTCHAN && n.Elem.(*ChanType).Dir == types.Crecv { + fmt.Fprintf(s, "chan (%v)", n.Elem) + } else { + fmt.Fprintf(s, "chan %v", n.Elem) + } + } + + case OTSTRUCT: + fmt.Fprint(s, "<struct>") + + case OTINTER: + fmt.Fprint(s, "<inter>") + + case OTFUNC: + fmt.Fprint(s, "<func>") + + case OCLOSURE: + n := n.(*ClosureExpr) + if !exportFormat { + fmt.Fprint(s, "func literal") + return + } + fmt.Fprintf(s, "%v { %v }", n.Type(), n.Func.Body) + + case OCOMPLIT: + n := n.(*CompLitExpr) + if !exportFormat { + if n.Implicit() { + fmt.Fprintf(s, "... argument") + return + } + if typ := n.Type(); typ != nil { + fmt.Fprintf(s, "%v{%s}", typ, ellipsisIf(len(n.List) != 0)) + return + } + if n.Ntype != nil { + fmt.Fprintf(s, "%v{%s}", n.Ntype, ellipsisIf(len(n.List) != 0)) + return + } + + fmt.Fprint(s, "composite literal") + return + } + fmt.Fprintf(s, "(%v{ %.v })", n.Ntype, n.List) + + case OPTRLIT: + n := n.(*AddrExpr) + fmt.Fprintf(s, "&%v", n.X) + + case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: + n := n.(*CompLitExpr) + if !exportFormat { + fmt.Fprintf(s, "%v{%s}", n.Type(), ellipsisIf(len(n.List) != 0)) + return + } + fmt.Fprintf(s, "(%v{ %.v })", n.Type(), n.List) + + case OKEY: + n := n.(*KeyExpr) + if n.Key != nil && n.Value != nil { + fmt.Fprintf(s, "%v:%v", n.Key, n.Value) + return + } + + if n.Key == nil && n.Value != nil { + fmt.Fprintf(s, ":%v", n.Value) + return + } + if n.Key != nil && n.Value == nil { + fmt.Fprintf(s, "%v:", n.Key) + return + } + fmt.Fprint(s, ":") + + case OSTRUCTKEY: + n := n.(*StructKeyExpr) + fmt.Fprintf(s, "%v:%v", n.Field, n.Value) + + case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OMETHVALUE, OMETHEXPR: + n := n.(*SelectorExpr) + exprFmt(n.X, s, nprec) + if n.Sel == nil { + fmt.Fprint(s, ".<nil>") + return + } + fmt.Fprintf(s, ".%s", n.Sel.Name) + + case ODOTTYPE, ODOTTYPE2: + n := n.(*TypeAssertExpr) + exprFmt(n.X, s, nprec) + if n.Ntype != nil { + fmt.Fprintf(s, ".(%v)", n.Ntype) + return + } + fmt.Fprintf(s, ".(%v)", n.Type()) + + case OINDEX, OINDEXMAP: + n := n.(*IndexExpr) + exprFmt(n.X, s, nprec) + fmt.Fprintf(s, "[%v]", n.Index) + + case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: + n := n.(*SliceExpr) + exprFmt(n.X, s, nprec) + fmt.Fprint(s, "[") + if n.Low != nil { + fmt.Fprint(s, n.Low) + } + fmt.Fprint(s, ":") + if n.High != nil { + fmt.Fprint(s, n.High) + } + if n.Op().IsSlice3() { + fmt.Fprint(s, ":") + if n.Max != nil { + fmt.Fprint(s, n.Max) + } + } + fmt.Fprint(s, "]") + + case OSLICEHEADER: + n := n.(*SliceHeaderExpr) + fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap) + + case OCOMPLEX, OCOPY, OUNSAFEADD, OUNSAFESLICE: + n := n.(*BinaryExpr) + fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y) + + case OCONV, + OCONVIFACE, + OCONVIDATA, + OCONVNOP, + OBYTES2STR, + ORUNES2STR, + OSTR2BYTES, + OSTR2RUNES, + ORUNESTR, + OSLICE2ARRPTR: + n := n.(*ConvExpr) + if n.Type() == nil || n.Type().Sym() == nil { + fmt.Fprintf(s, "(%v)", n.Type()) + } else { + fmt.Fprintf(s, "%v", n.Type()) + } + fmt.Fprintf(s, "(%v)", n.X) + + case OREAL, + OIMAG, + OCAP, + OCLOSE, + OLEN, + ONEW, + OPANIC, + OALIGNOF, + OOFFSETOF, + OSIZEOF: + n := n.(*UnaryExpr) + fmt.Fprintf(s, "%v(%v)", n.Op(), n.X) + + case OAPPEND, + ODELETE, + OMAKE, + ORECOVER, + OPRINT, + OPRINTN: + n := n.(*CallExpr) + if n.IsDDD { + fmt.Fprintf(s, "%v(%.v...)", n.Op(), n.Args) + return + } + fmt.Fprintf(s, "%v(%.v)", n.Op(), n.Args) + + case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: + n := n.(*CallExpr) + exprFmt(n.X, s, nprec) + if n.IsDDD { + fmt.Fprintf(s, "(%.v...)", n.Args) + return + } + fmt.Fprintf(s, "(%.v)", n.Args) + + case OINLCALL: + n := n.(*InlinedCallExpr) + // TODO(mdempsky): Print Init and/or Body? + if len(n.ReturnVars) == 1 { + fmt.Fprintf(s, "%v", n.ReturnVars[0]) + return + } + fmt.Fprintf(s, "(.%v)", n.ReturnVars) + + case OMAKEMAP, OMAKECHAN, OMAKESLICE: + n := n.(*MakeExpr) + if n.Cap != nil { + fmt.Fprintf(s, "make(%v, %v, %v)", n.Type(), n.Len, n.Cap) + return + } + if n.Len != nil && (n.Op() == OMAKESLICE || !n.Len.Type().IsUntyped()) { + fmt.Fprintf(s, "make(%v, %v)", n.Type(), n.Len) + return + } + fmt.Fprintf(s, "make(%v)", n.Type()) + + case OMAKESLICECOPY: + n := n.(*MakeExpr) + fmt.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type(), n.Len, n.Cap) + + case OPLUS, ONEG, OBITNOT, ONOT, ORECV: + // Unary + n := n.(*UnaryExpr) + fmt.Fprintf(s, "%v", n.Op()) + if n.X != nil && n.X.Op() == n.Op() { + fmt.Fprint(s, " ") + } + exprFmt(n.X, s, nprec+1) + + case OADDR: + n := n.(*AddrExpr) + fmt.Fprintf(s, "%v", n.Op()) + if n.X != nil && n.X.Op() == n.Op() { + fmt.Fprint(s, " ") + } + exprFmt(n.X, s, nprec+1) + + case ODEREF: + n := n.(*StarExpr) + fmt.Fprintf(s, "%v", n.Op()) + exprFmt(n.X, s, nprec+1) + + // Binary + case OADD, + OAND, + OANDNOT, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLT, + OLSH, + OMOD, + OMUL, + ONE, + OOR, + ORSH, + OSUB, + OXOR: + n := n.(*BinaryExpr) + exprFmt(n.X, s, nprec) + fmt.Fprintf(s, " %v ", n.Op()) + exprFmt(n.Y, s, nprec+1) + + case OANDAND, + OOROR: + n := n.(*LogicalExpr) + exprFmt(n.X, s, nprec) + fmt.Fprintf(s, " %v ", n.Op()) + exprFmt(n.Y, s, nprec+1) + + case OSEND: + n := n.(*SendStmt) + exprFmt(n.Chan, s, nprec) + fmt.Fprintf(s, " <- ") + exprFmt(n.Value, s, nprec+1) + + case OADDSTR: + n := n.(*AddStringExpr) + for i, n1 := range n.List { + if i != 0 { + fmt.Fprint(s, " + ") + } + exprFmt(n1, s, nprec) + } + default: + fmt.Fprintf(s, "<node %v>", n.Op()) + } +} + +func ellipsisIf(b bool) string { + if b { + return "..." + } + return "" +} + +// Nodes + +// Format implements formatting for a Nodes. +// The valid formats are: +// +// %v Go syntax, semicolon-separated +// %.v Go syntax, comma-separated +// %+v Debug syntax, as in DumpList. +// +func (l Nodes) Format(s fmt.State, verb rune) { + if s.Flag('+') && verb == 'v' { + // %+v is DumpList output + dumpNodes(s, l, 1) + return + } + + if verb != 'v' { + fmt.Fprintf(s, "%%!%c(Nodes)", verb) + return + } + + sep := "; " + if _, ok := s.Precision(); ok { // %.v is expr list + sep = ", " + } + + for i, n := range l { + fmt.Fprint(s, n) + if i+1 < len(l) { + fmt.Fprint(s, sep) + } + } +} + +// Dump + +// Dump prints the message s followed by a debug dump of n. +func Dump(s string, n Node) { + fmt.Printf("%s%+v\n", s, n) +} + +// DumpList prints the message s followed by a debug dump of each node in the list. +func DumpList(s string, list Nodes) { + var buf bytes.Buffer + FDumpList(&buf, s, list) + os.Stdout.Write(buf.Bytes()) +} + +// FDumpList prints to w the message s followed by a debug dump of each node in the list. +func FDumpList(w io.Writer, s string, list Nodes) { + io.WriteString(w, s) + dumpNodes(w, list, 1) + io.WriteString(w, "\n") +} + +// indent prints indentation to w. +func indent(w io.Writer, depth int) { + fmt.Fprint(w, "\n") + for i := 0; i < depth; i++ { + fmt.Fprint(w, ". ") + } +} + +// EscFmt is set by the escape analysis code to add escape analysis details to the node print. +var EscFmt func(n Node) string + +// dumpNodeHeader prints the debug-format node header line to w. +func dumpNodeHeader(w io.Writer, n Node) { + // Useful to see which nodes in an AST printout are actually identical + if base.Debug.DumpPtrs != 0 { + fmt.Fprintf(w, " p(%p)", n) + } + + if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Defn != nil { + // Useful to see where Defn is set and what node it points to + fmt.Fprintf(w, " defn(%p)", n.Name().Defn) + } + + if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Curfn != nil { + // Useful to see where Defn is set and what node it points to + fmt.Fprintf(w, " curfn(%p)", n.Name().Curfn) + } + if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Outer != nil { + // Useful to see where Defn is set and what node it points to + fmt.Fprintf(w, " outer(%p)", n.Name().Outer) + } + + if EscFmt != nil { + if esc := EscFmt(n); esc != "" { + fmt.Fprintf(w, " %s", esc) + } + } + + if n.Sym() != nil && n.Op() != ONAME && n.Op() != ONONAME && n.Op() != OTYPE { + fmt.Fprintf(w, " %+v", n.Sym()) + } + + // Print Node-specific fields of basic type in header line. + v := reflect.ValueOf(n).Elem() + t := v.Type() + nf := t.NumField() + for i := 0; i < nf; i++ { + tf := t.Field(i) + if tf.PkgPath != "" { + // skip unexported field - Interface will fail + continue + } + k := tf.Type.Kind() + if reflect.Bool <= k && k <= reflect.Complex128 { + name := strings.TrimSuffix(tf.Name, "_") + vf := v.Field(i) + vfi := vf.Interface() + if name == "Offset" && vfi == types.BADWIDTH || name != "Offset" && isZero(vf) { + continue + } + if vfi == true { + fmt.Fprintf(w, " %s", name) + } else { + fmt.Fprintf(w, " %s:%+v", name, vf.Interface()) + } + } + } + + // Print Node-specific booleans by looking for methods. + // Different v, t from above - want *Struct not Struct, for methods. + v = reflect.ValueOf(n) + t = v.Type() + nm := t.NumMethod() + for i := 0; i < nm; i++ { + tm := t.Method(i) + if tm.PkgPath != "" { + // skip unexported method - call will fail + continue + } + m := v.Method(i) + mt := m.Type() + if mt.NumIn() == 0 && mt.NumOut() == 1 && mt.Out(0).Kind() == reflect.Bool { + // TODO(rsc): Remove the func/defer/recover wrapping, + // which is guarding against panics in miniExpr, + // once we get down to the simpler state in which + // nodes have no getter methods that aren't allowed to be called. + func() { + defer func() { recover() }() + if m.Call(nil)[0].Bool() { + name := strings.TrimSuffix(tm.Name, "_") + fmt.Fprintf(w, " %s", name) + } + }() + } + } + + if n.Op() == OCLOSURE { + n := n.(*ClosureExpr) + if fn := n.Func; fn != nil && fn.Nname.Sym() != nil { + fmt.Fprintf(w, " fnName(%+v)", fn.Nname.Sym()) + } + } + + if n.Type() != nil { + if n.Op() == OTYPE { + fmt.Fprintf(w, " type") + } + fmt.Fprintf(w, " %+v", n.Type()) + } + if n.Typecheck() != 0 { + fmt.Fprintf(w, " tc(%d)", n.Typecheck()) + } + + if n.Pos().IsKnown() { + fmt.Fprint(w, " # ") + switch n.Pos().IsStmt() { + case src.PosNotStmt: + fmt.Fprint(w, "_") // "-" would be confusing + case src.PosIsStmt: + fmt.Fprint(w, "+") + } + for i, pos := range base.Ctxt.AllPos(n.Pos(), nil) { + if i > 0 { + fmt.Fprint(w, ",") + } + // TODO(mdempsky): Print line pragma details too. + file := filepath.Base(pos.Filename()) + // Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync. + fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col()) + } + } +} + +func dumpNode(w io.Writer, n Node, depth int) { + indent(w, depth) + if depth > 40 { + fmt.Fprint(w, "...") + return + } + + if n == nil { + fmt.Fprint(w, "NilIrNode") + return + } + + if len(n.Init()) != 0 { + fmt.Fprintf(w, "%+v-init", n.Op()) + dumpNodes(w, n.Init(), depth+1) + indent(w, depth) + } + + switch n.Op() { + default: + fmt.Fprintf(w, "%+v", n.Op()) + dumpNodeHeader(w, n) + + case OLITERAL: + fmt.Fprintf(w, "%+v-%v", n.Op(), n.Val()) + dumpNodeHeader(w, n) + return + + case ONAME, ONONAME: + if n.Sym() != nil { + fmt.Fprintf(w, "%+v-%+v", n.Op(), n.Sym()) + } else { + fmt.Fprintf(w, "%+v", n.Op()) + } + dumpNodeHeader(w, n) + if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { + indent(w, depth) + fmt.Fprintf(w, "%+v-ntype", n.Op()) + dumpNode(w, n.Name().Ntype, depth+1) + } + return + + case OASOP: + n := n.(*AssignOpStmt) + fmt.Fprintf(w, "%+v-%+v", n.Op(), n.AsOp) + dumpNodeHeader(w, n) + + case OTYPE: + fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym()) + dumpNodeHeader(w, n) + if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { + indent(w, depth) + fmt.Fprintf(w, "%+v-ntype", n.Op()) + dumpNode(w, n.Name().Ntype, depth+1) + } + return + + case OCLOSURE: + fmt.Fprintf(w, "%+v", n.Op()) + dumpNodeHeader(w, n) + + case ODCLFUNC: + // Func has many fields we don't want to print. + // Bypass reflection and just print what we want. + n := n.(*Func) + fmt.Fprintf(w, "%+v", n.Op()) + dumpNodeHeader(w, n) + fn := n + if len(fn.Dcl) > 0 { + indent(w, depth) + fmt.Fprintf(w, "%+v-Dcl", n.Op()) + for _, dcl := range n.Dcl { + dumpNode(w, dcl, depth+1) + } + } + if len(fn.ClosureVars) > 0 { + indent(w, depth) + fmt.Fprintf(w, "%+v-ClosureVars", n.Op()) + for _, cv := range fn.ClosureVars { + dumpNode(w, cv, depth+1) + } + } + if len(fn.Enter) > 0 { + indent(w, depth) + fmt.Fprintf(w, "%+v-Enter", n.Op()) + dumpNodes(w, fn.Enter, depth+1) + } + if len(fn.Body) > 0 { + indent(w, depth) + fmt.Fprintf(w, "%+v-body", n.Op()) + dumpNodes(w, fn.Body, depth+1) + } + return + } + + v := reflect.ValueOf(n).Elem() + t := reflect.TypeOf(n).Elem() + nf := t.NumField() + for i := 0; i < nf; i++ { + tf := t.Field(i) + vf := v.Field(i) + if tf.PkgPath != "" { + // skip unexported field - Interface will fail + continue + } + switch tf.Type.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Slice: + if vf.IsNil() { + continue + } + } + name := strings.TrimSuffix(tf.Name, "_") + // Do not bother with field name header lines for the + // most common positional arguments: unary, binary expr, + // index expr, send stmt, go and defer call expression. + switch name { + case "X", "Y", "Index", "Chan", "Value", "Call": + name = "" + } + switch val := vf.Interface().(type) { + case Node: + if name != "" { + indent(w, depth) + fmt.Fprintf(w, "%+v-%s", n.Op(), name) + } + dumpNode(w, val, depth+1) + case Nodes: + if len(val) == 0 { + continue + } + if name != "" { + indent(w, depth) + fmt.Fprintf(w, "%+v-%s", n.Op(), name) + } + dumpNodes(w, val, depth+1) + default: + if vf.Kind() == reflect.Slice && vf.Type().Elem().Implements(nodeType) { + if vf.Len() == 0 { + continue + } + if name != "" { + indent(w, depth) + fmt.Fprintf(w, "%+v-%s", n.Op(), name) + } + for i, n := 0, vf.Len(); i < n; i++ { + dumpNode(w, vf.Index(i).Interface().(Node), depth+1) + } + } + } + } +} + +var nodeType = reflect.TypeOf((*Node)(nil)).Elem() + +func dumpNodes(w io.Writer, list Nodes, depth int) { + if len(list) == 0 { + fmt.Fprintf(w, " <nil>") + return + } + + for _, n := range list { + dumpNode(w, n, depth) + } +} + +// reflect.IsZero is not available in Go 1.4 (added in Go 1.13), so we use this copy instead. +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return math.Float64bits(v.Float()) == 0 + case reflect.Complex64, reflect.Complex128: + c := v.Complex() + return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if !isZero(v.Index(i)) { + return false + } + } + return true + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + return v.IsNil() + case reflect.String: + return v.Len() == 0 + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if !isZero(v.Field(i)) { + return false + } + } + return true + default: + return false + } +} diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go new file mode 100644 index 0000000..23d56f7 --- /dev/null +++ b/src/cmd/compile/internal/ir/func.go @@ -0,0 +1,438 @@ +// Copyright 2020 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 ir + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/src" + "fmt" +) + +// A Func corresponds to a single function in a Go program +// (and vice versa: each function is denoted by exactly one *Func). +// +// There are multiple nodes that represent a Func in the IR. +// +// The ONAME node (Func.Nname) is used for plain references to it. +// The ODCLFUNC node (the Func itself) is used for its declaration code. +// The OCLOSURE node (Func.OClosure) is used for a reference to a +// function literal. +// +// An imported function will have an ONAME node which points to a Func +// with an empty body. +// A declared function or method has an ODCLFUNC (the Func itself) and an ONAME. +// A function literal is represented directly by an OCLOSURE, but it also +// has an ODCLFUNC (and a matching ONAME) representing the compiled +// underlying form of the closure, which accesses the captured variables +// using a special data structure passed in a register. +// +// A method declaration is represented like functions, except f.Sym +// will be the qualified method name (e.g., "T.m") and +// f.Func.Shortname is the bare method name (e.g., "m"). +// +// A method expression (T.M) is represented as an OMETHEXPR node, +// in which n.Left and n.Right point to the type and method, respectively. +// Each distinct mention of a method expression in the source code +// constructs a fresh node. +// +// A method value (t.M) is represented by ODOTMETH/ODOTINTER +// when it is called directly and by OMETHVALUE otherwise. +// These are like method expressions, except that for ODOTMETH/ODOTINTER, +// the method name is stored in Sym instead of Right. +// Each OMETHVALUE ends up being implemented as a new +// function, a bit like a closure, with its own ODCLFUNC. +// The OMETHVALUE uses n.Func to record the linkage to +// the generated ODCLFUNC, but there is no +// pointer from the Func back to the OMETHVALUE. +type Func struct { + miniNode + Body Nodes + Iota int64 + + Nname *Name // ONAME node + OClosure *ClosureExpr // OCLOSURE node + + Shortname *types.Sym + + // Extra entry code for the function. For example, allocate and initialize + // memory for escaping parameters. + Enter Nodes + Exit Nodes + + // ONAME nodes for all params/locals for this func/closure, does NOT + // include closurevars until transforming closures during walk. + // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs, + // with PPARAMs and PPARAMOUTs in order corresponding to the function signature. + // However, as anonymous or blank PPARAMs are not actually declared, + // they are omitted from Dcl. + // Anonymous and blank PPARAMOUTs are declared as ~rNN and ~bNN Names, respectively. + Dcl []*Name + + // ClosureVars lists the free variables that are used within a + // function literal, but formally declared in an enclosing + // function. The variables in this slice are the closure function's + // own copy of the variables, which are used within its function + // body. They will also each have IsClosureVar set, and will have + // Byval set if they're captured by value. + ClosureVars []*Name + + // Enclosed functions that need to be compiled. + // Populated during walk. + Closures []*Func + + // Parents records the parent scope of each scope within a + // function. The root scope (0) has no parent, so the i'th + // scope's parent is stored at Parents[i-1]. + Parents []ScopeID + + // Marks records scope boundary changes. + Marks []Mark + + FieldTrack map[*obj.LSym]struct{} + DebugInfo interface{} + LSym *obj.LSym // Linker object in this function's native ABI (Func.ABI) + + Inl *Inline + + // Closgen tracks how many closures have been generated within + // this function. Used by closurename for creating unique + // function names. + Closgen int32 + + Label int32 // largest auto-generated label in this function + + Endlineno src.XPos + WBPos src.XPos // position of first write barrier; see SetWBPos + + Pragma PragmaFlag // go:xxx function annotations + + flags bitset16 + + // ABI is a function's "definition" ABI. This is the ABI that + // this function's generated code is expecting to be called by. + // + // For most functions, this will be obj.ABIInternal. It may be + // a different ABI for functions defined in assembly or ABI wrappers. + // + // This is included in the export data and tracked across packages. + ABI obj.ABI + // ABIRefs is the set of ABIs by which this function is referenced. + // For ABIs other than this function's definition ABI, the + // compiler generates ABI wrapper functions. This is only tracked + // within a package. + ABIRefs obj.ABISet + + NumDefers int32 // number of defer calls in the function + NumReturns int32 // number of explicit returns in the function + + // nwbrCalls records the LSyms of functions called by this + // function for go:nowritebarrierrec analysis. Only filled in + // if nowritebarrierrecCheck != nil. + NWBRCalls *[]SymAndPos + + // For wrapper functions, WrappedFunc point to the original Func. + // Currently only used for go/defer wrappers. + WrappedFunc *Func +} + +func NewFunc(pos src.XPos) *Func { + f := new(Func) + f.pos = pos + f.op = ODCLFUNC + f.Iota = -1 + // Most functions are ABIInternal. The importer or symabis + // pass may override this. + f.ABI = obj.ABIInternal + return f +} + +func (f *Func) isStmt() {} + +func (n *Func) copy() Node { panic(n.no("copy")) } +func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) } +func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) } + +func (f *Func) Type() *types.Type { return f.Nname.Type() } +func (f *Func) Sym() *types.Sym { return f.Nname.Sym() } +func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() } +func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) } + +// An Inline holds fields used for function bodies that can be inlined. +type Inline struct { + Cost int32 // heuristic cost of inlining this function + + // Copies of Func.Dcl and Func.Body for use during inlining. Copies are + // needed because the function's dcl/body may be changed by later compiler + // transformations. These fields are also populated when a function from + // another package is imported. + Dcl []*Name + Body []Node + + // CanDelayResults reports whether it's safe for the inliner to delay + // initializing the result parameters until immediately before the + // "return" statement. + CanDelayResults bool +} + +// A Mark represents a scope boundary. +type Mark struct { + // Pos is the position of the token that marks the scope + // change. + Pos src.XPos + + // Scope identifies the innermost scope to the right of Pos. + Scope ScopeID +} + +// A ScopeID represents a lexical scope within a function. +type ScopeID int32 + +const ( + funcDupok = 1 << iota // duplicate definitions ok + funcWrapper // hide frame from users (elide in tracebacks, don't count as a frame for recover()) + funcABIWrapper // is an ABI wrapper (also set flagWrapper) + funcNeedctxt // function uses context register (has closure variables) + funcReflectMethod // function calls reflect.Type.Method or MethodByName + // true if closure inside a function; false if a simple function or a + // closure in a global variable initialization + funcIsHiddenClosure + funcIsDeadcodeClosure // true if closure is deadcode + funcHasDefer // contains a defer statement + funcNilCheckDisabled // disable nil checks when compiling this function + funcInlinabilityChecked // inliner has already determined whether the function is inlinable + funcExportInline // include inline body in export data + funcInstrumentBody // add race/msan/asan instrumentation during SSA construction + funcOpenCodedDeferDisallowed // can't do open-coded defers + funcClosureCalled // closure is only immediately called; used by escape analysis +) + +type SymAndPos struct { + Sym *obj.LSym // LSym of callee + Pos src.XPos // line of call +} + +func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 } +func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 } +func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper != 0 } +func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 } +func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 } +func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 } +func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 } +func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 } +func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 } +func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 } +func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 } +func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 } +func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 } +func (f *Func) ClosureCalled() bool { return f.flags&funcClosureCalled != 0 } + +func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) } +func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) } +func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, b) } +func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) } +func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) } +func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) } +func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) } +func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) } +func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) } +func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) } +func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) } +func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) } +func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) } +func (f *Func) SetClosureCalled(b bool) { f.flags.set(funcClosureCalled, b) } + +func (f *Func) SetWBPos(pos src.XPos) { + if base.Debug.WB != 0 { + base.WarnfAt(pos, "write barrier") + } + if !f.WBPos.IsKnown() { + f.WBPos = pos + } +} + +// FuncName returns the name (without the package) of the function n. +func FuncName(f *Func) string { + if f == nil || f.Nname == nil { + return "<nil>" + } + return f.Sym().Name +} + +// PkgFuncName returns the name of the function referenced by n, with package prepended. +// This differs from the compiler's internal convention where local functions lack a package +// because the ultimate consumer of this is a human looking at an IDE; package is only empty +// if the compilation package is actually the empty string. +func PkgFuncName(f *Func) string { + if f == nil || f.Nname == nil { + return "<nil>" + } + s := f.Sym() + pkg := s.Pkg + + p := base.Ctxt.Pkgpath + if pkg != nil && pkg.Path != "" { + p = pkg.Path + } + if p == "" { + return s.Name + } + return p + "." + s.Name +} + +var CurFunc *Func + +// WithFunc invokes do with CurFunc and base.Pos set to curfn and +// curfn.Pos(), respectively, and then restores their previous values +// before returning. +func WithFunc(curfn *Func, do func()) { + oldfn, oldpos := CurFunc, base.Pos + defer func() { CurFunc, base.Pos = oldfn, oldpos }() + + CurFunc, base.Pos = curfn, curfn.Pos() + do() +} + +func FuncSymName(s *types.Sym) string { + return s.Name + "·f" +} + +// MarkFunc marks a node as a function. +func MarkFunc(n *Name) { + if n.Op() != ONAME || n.Class != Pxxx { + base.FatalfAt(n.Pos(), "expected ONAME/Pxxx node, got %v (%v/%v)", n, n.Op(), n.Class) + } + + n.Class = PFUNC + n.Sym().SetFunc(true) +} + +// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags +// and compiling runtime +func ClosureDebugRuntimeCheck(clo *ClosureExpr) { + if base.Debug.Closure > 0 { + if clo.Esc() == EscHeap { + base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars) + } else { + base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars) + } + } + if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap { + base.ErrorfAt(clo.Pos(), "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func)) + } +} + +// IsTrivialClosure reports whether closure clo has an +// empty list of captured vars. +func IsTrivialClosure(clo *ClosureExpr) bool { + return len(clo.Func.ClosureVars) == 0 +} + +// globClosgen is like Func.Closgen, but for the global scope. +var globClosgen int32 + +// closureName generates a new unique name for a closure within outerfn. +func closureName(outerfn *Func) *types.Sym { + pkg := types.LocalPkg + outer := "glob." + prefix := "func" + gen := &globClosgen + + if outerfn != nil { + if outerfn.OClosure != nil { + prefix = "" + } + + pkg = outerfn.Sym().Pkg + outer = FuncName(outerfn) + + // There may be multiple functions named "_". In those + // cases, we can't use their individual Closgens as it + // would lead to name clashes. + if !IsBlank(outerfn.Nname) { + gen = &outerfn.Closgen + } + } + + *gen++ + return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) +} + +// NewClosureFunc creates a new Func to represent a function literal. +// If hidden is true, then the closure is marked hidden (i.e., as a +// function literal contained within another function, rather than a +// package-scope variable initialization expression). +func NewClosureFunc(pos src.XPos, hidden bool) *Func { + fn := NewFunc(pos) + fn.SetIsHiddenClosure(hidden) + + fn.Nname = NewNameAt(pos, BlankNode.Sym()) + fn.Nname.Func = fn + fn.Nname.Defn = fn + + fn.OClosure = NewClosureExpr(pos, fn) + + return fn +} + +// NameClosure generates a unique for the given function literal, +// which must have appeared within outerfn. +func NameClosure(clo *ClosureExpr, outerfn *Func) { + fn := clo.Func + if fn.IsHiddenClosure() != (outerfn != nil) { + base.FatalfAt(clo.Pos(), "closure naming inconsistency: hidden %v, but outer %v", fn.IsHiddenClosure(), outerfn) + } + + name := fn.Nname + if !IsBlank(name) { + base.FatalfAt(clo.Pos(), "closure already named: %v", name) + } + + name.SetSym(closureName(outerfn)) + MarkFunc(name) +} + +// UseClosure checks that the ginen function literal has been setup +// correctly, and then returns it as an expression. +// It must be called after clo.Func.ClosureVars has been set. +func UseClosure(clo *ClosureExpr, pkg *Package) Node { + fn := clo.Func + name := fn.Nname + + if IsBlank(name) { + base.FatalfAt(fn.Pos(), "unnamed closure func: %v", fn) + } + // Caution: clo.Typecheck() is still 0 when UseClosure is called by + // tcClosure. + if fn.Typecheck() != 1 || name.Typecheck() != 1 { + base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn) + } + if clo.Type() == nil || name.Type() == nil { + base.FatalfAt(fn.Pos(), "missing types: %v", fn) + } + if !types.Identical(clo.Type(), name.Type()) { + base.FatalfAt(fn.Pos(), "mismatched types: %v", fn) + } + + if base.Flag.W > 1 { + s := fmt.Sprintf("new closure func: %v", fn) + Dump(s, fn) + } + + if pkg != nil { + pkg.Decls = append(pkg.Decls, fn) + } + + if false && IsTrivialClosure(clo) { + // TODO(mdempsky): Investigate if we can/should optimize this + // case. walkClosure already handles it later, but it could be + // useful to recognize earlier (e.g., it might allow multiple + // inlined calls to a function to share a common trivial closure + // func, rather than cloning it for each inlined call). + } + + return clo +} diff --git a/src/cmd/compile/internal/ir/ir.go b/src/cmd/compile/internal/ir/ir.go new file mode 100644 index 0000000..82224ca --- /dev/null +++ b/src/cmd/compile/internal/ir/ir.go @@ -0,0 +1,5 @@ +// 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 ir diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go new file mode 100644 index 0000000..eeb7408 --- /dev/null +++ b/src/cmd/compile/internal/ir/mini.go @@ -0,0 +1,92 @@ +// Copyright 2020 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. + +//go:generate go run -mod=mod mknode.go + +package ir + +import ( + "cmd/compile/internal/types" + "cmd/internal/src" + "fmt" + "go/constant" +) + +// A miniNode is a minimal node implementation, +// meant to be embedded as the first field in a larger node implementation, +// at a cost of 8 bytes. +// +// A miniNode is NOT a valid Node by itself: the embedding struct +// must at the least provide: +// +// func (n *MyNode) String() string { return fmt.Sprint(n) } +// func (n *MyNode) rawCopy() Node { c := *n; return &c } +// func (n *MyNode) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) } +// +// The embedding struct should also fill in n.op in its constructor, +// for more useful panic messages when invalid methods are called, +// instead of implementing Op itself. +// +type miniNode struct { + pos src.XPos // uint32 + op Op // uint8 + bits bitset8 + esc uint16 +} + +// posOr returns pos if known, or else n.pos. +// For use in DeepCopy. +func (n *miniNode) posOr(pos src.XPos) src.XPos { + if pos.IsKnown() { + return pos + } + return n.pos +} + +// op can be read, but not written. +// An embedding implementation can provide a SetOp if desired. +// (The panicking SetOp is with the other panics below.) +func (n *miniNode) Op() Op { return n.op } +func (n *miniNode) Pos() src.XPos { return n.pos } +func (n *miniNode) SetPos(x src.XPos) { n.pos = x } +func (n *miniNode) Esc() uint16 { return n.esc } +func (n *miniNode) SetEsc(x uint16) { n.esc = x } + +const ( + miniWalkdefShift = 0 // TODO(mdempsky): Move to Name.flags. + miniTypecheckShift = 2 + miniDiag = 1 << 4 + miniWalked = 1 << 5 // to prevent/catch re-walking +) + +func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) } +func (n *miniNode) SetTypecheck(x uint8) { + if x > 2 { + panic(fmt.Sprintf("cannot SetTypecheck %d", x)) + } + n.bits.set2(miniTypecheckShift, x) +} + +func (n *miniNode) Diag() bool { return n.bits&miniDiag != 0 } +func (n *miniNode) SetDiag(x bool) { n.bits.set(miniDiag, x) } + +func (n *miniNode) Walked() bool { return n.bits&miniWalked != 0 } +func (n *miniNode) SetWalked(x bool) { n.bits.set(miniWalked, x) } + +// Empty, immutable graph structure. + +func (n *miniNode) Init() Nodes { return Nodes{} } + +// Additional functionality unavailable. + +func (n *miniNode) no(name string) string { return "cannot " + name + " on " + n.op.String() } + +func (n *miniNode) Type() *types.Type { return nil } +func (n *miniNode) SetType(*types.Type) { panic(n.no("SetType")) } +func (n *miniNode) Name() *Name { return nil } +func (n *miniNode) Sym() *types.Sym { return nil } +func (n *miniNode) Val() constant.Value { panic(n.no("Val")) } +func (n *miniNode) SetVal(v constant.Value) { panic(n.no("SetVal")) } +func (n *miniNode) NonNil() bool { return false } +func (n *miniNode) MarkNonNil() { panic(n.no("MarkNonNil")) } diff --git a/src/cmd/compile/internal/ir/mknode.go b/src/cmd/compile/internal/ir/mknode.go new file mode 100644 index 0000000..5a0aaad --- /dev/null +++ b/src/cmd/compile/internal/ir/mknode.go @@ -0,0 +1,229 @@ +// Copyright 2020 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. + +//go:build ignore +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "go/format" + "go/types" + "io/ioutil" + "log" + "reflect" + "sort" + "strings" + + "golang.org/x/tools/go/packages" +) + +var irPkg *types.Package +var buf bytes.Buffer + +func main() { + cfg := &packages.Config{ + Mode: packages.NeedSyntax | packages.NeedTypes, + } + pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir") + if err != nil { + log.Fatal(err) + } + irPkg = pkgs[0].Types + + fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, "package ir") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, `import "fmt"`) + + scope := irPkg.Scope() + for _, name := range scope.Names() { + if strings.HasPrefix(name, "mini") { + continue + } + + obj, ok := scope.Lookup(name).(*types.TypeName) + if !ok { + continue + } + typ := obj.Type().(*types.Named) + if !implementsNode(types.NewPointer(typ)) { + continue + } + + fmt.Fprintf(&buf, "\n") + fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name) + + switch name { + case "Name", "Func": + // Too specialized to automate. + continue + } + + forNodeFields(typ, + "func (n *%[1]s) copy() Node { c := *n\n", + "", + "c.%[1]s = copy%[2]s(c.%[1]s)", + "return &c }\n") + + forNodeFields(typ, + "func (n *%[1]s) doChildren(do func(Node) bool) bool {\n", + "if n.%[1]s != nil && do(n.%[1]s) { return true }", + "if do%[2]s(n.%[1]s, do) { return true }", + "return false }\n") + + forNodeFields(typ, + "func (n *%[1]s) editChildren(edit func(Node) Node) {\n", + "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }", + "edit%[2]s(n.%[1]s, edit)", + "}\n") + } + + makeHelpers() + + out, err := format.Source(buf.Bytes()) + if err != nil { + // write out mangled source so we can see the bug. + out = buf.Bytes() + } + + err = ioutil.WriteFile("node_gen.go", out, 0666) + if err != nil { + log.Fatal(err) + } +} + +// needHelper maps needed slice helpers from their base name to their +// respective slice-element type. +var needHelper = map[string]string{} + +func makeHelpers() { + var names []string + for name := range needHelper { + names = append(names, name) + } + sort.Strings(names) + + for _, name := range names { + fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name]) + } +} + +const sliceHelperTmpl = ` +func copy%[1]s(list []%[2]s) []%[2]s { + if list == nil { + return nil + } + c := make([]%[2]s, len(list)) + copy(c, list) + return c +} +func do%[1]s(list []%[2]s, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func edit%[1]s(list []%[2]s, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(%[2]s) + } + } +} +` + +func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) { + fmt.Fprintf(&buf, prologue, named.Obj().Name()) + + anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool { + if f.Embedded() { + return false + } + name, typ := f.Name(), f.Type() + + slice, _ := typ.Underlying().(*types.Slice) + if slice != nil { + typ = slice.Elem() + } + + tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg)) + if implementsNode(typ) { + if slice != nil { + helper := strings.TrimPrefix(what, "*") + "s" + needHelper[helper] = what + tmpl, what = sliceTmpl, helper + } + } else if what == "*Field" { + // Special case for *Field. + tmpl = sliceTmpl + if slice != nil { + what = "Fields" + } else { + what = "Field" + } + } else { + return false + } + + if tmpl == "" { + return false + } + + // Allow template to not use all arguments without + // upsetting fmt.Printf. + s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what) + fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")]) + return false + }) + + fmt.Fprintf(&buf, epilogue) +} + +func implementsNode(typ types.Type) bool { + if _, ok := typ.Underlying().(*types.Interface); ok { + // TODO(mdempsky): Check the interface implements Node. + // Worst case, node_gen.go will fail to compile if we're wrong. + return true + } + + if ptr, ok := typ.(*types.Pointer); ok { + if str, ok := ptr.Elem().Underlying().(*types.Struct); ok { + return anyField(str, func(f *types.Var) bool { + return f.Embedded() && f.Name() == "miniNode" + }) + } + } + + return false +} + +func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool { + for i, n := 0, typ.NumFields(); i < n; i++ { + if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok { + if value != "-" { + panic(fmt.Sprintf("unexpected tag value: %q", value)) + } + continue + } + + f := typ.Field(i) + if pred(f) { + return true + } + if f.Embedded() { + if typ, ok := f.Type().Underlying().(*types.Struct); ok { + if anyField(typ, pred) { + return true + } + } + } + } + return false +} diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go new file mode 100644 index 0000000..1d4110c --- /dev/null +++ b/src/cmd/compile/internal/ir/name.go @@ -0,0 +1,557 @@ +// Copyright 2020 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 ir + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "fmt" + + "go/constant" +) + +// An Ident is an identifier, possibly qualified. +type Ident struct { + miniExpr + sym *types.Sym +} + +func NewIdent(pos src.XPos, sym *types.Sym) *Ident { + n := new(Ident) + n.op = ONONAME + n.pos = pos + n.sym = sym + return n +} + +func (n *Ident) Sym() *types.Sym { return n.sym } + +func (*Ident) CanBeNtype() {} + +// Name holds Node fields used only by named nodes (ONAME, OTYPE, some OLITERAL). +type Name struct { + miniExpr + BuiltinOp Op // uint8 + Class Class // uint8 + pragma PragmaFlag // int16 + flags bitset16 + DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1 + sym *types.Sym + Func *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem + Offset_ int64 + val constant.Value + Opt interface{} // for use by escape analysis + Embed *[]Embed // list of embedded files, for ONAME var + + PkgName *PkgName // real package for import . names + // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2). + // For a closure var, the ONAME node of the outer captured variable. + // For the case-local variables of a type switch, the type switch guard (OTYPESW). + // For a range variable, the range statement (ORANGE) + // For a recv variable in a case of a select statement, the receive assignment (OSELRECV2) + // For the name of a function, points to corresponding Func node. + Defn Node + + // The function, method, or closure in which local variable or param is declared. + Curfn *Func + + Ntype Ntype + Heapaddr *Name // temp holding heap address of param + + // ONAME closure linkage + // Consider: + // + // func f() { + // x := 1 // x1 + // func() { + // use(x) // x2 + // func() { + // use(x) // x3 + // --- parser is here --- + // }() + // }() + // } + // + // There is an original declaration of x and then a chain of mentions of x + // leading into the current function. Each time x is mentioned in a new closure, + // we create a variable representing x for use in that specific closure, + // since the way you get to x is different in each closure. + // + // Let's number the specific variables as shown in the code: + // x1 is the original x, x2 is when mentioned in the closure, + // and x3 is when mentioned in the closure in the closure. + // + // We keep these linked (assume N > 1): + // + // - x1.Defn = original declaration statement for x (like most variables) + // - x1.Innermost = current innermost closure x (in this case x3), or nil for none + // - x1.IsClosureVar() = false + // + // - xN.Defn = x1, N > 1 + // - xN.IsClosureVar() = true, N > 1 + // - x2.Outer = nil + // - xN.Outer = x(N-1), N > 2 + // + // + // When we look up x in the symbol table, we always get x1. + // Then we can use x1.Innermost (if not nil) to get the x + // for the innermost known closure function, + // but the first reference in a closure will find either no x1.Innermost + // or an x1.Innermost with .Funcdepth < Funcdepth. + // In that case, a new xN must be created, linked in with: + // + // xN.Defn = x1 + // xN.Outer = x1.Innermost + // x1.Innermost = xN + // + // When we finish the function, we'll process its closure variables + // and find xN and pop it off the list using: + // + // x1 := xN.Defn + // x1.Innermost = xN.Outer + // + // We leave x1.Innermost set so that we can still get to the original + // variable quickly. Not shown here, but once we're + // done parsing a function and no longer need xN.Outer for the + // lexical x reference links as described above, funcLit + // recomputes xN.Outer as the semantic x reference link tree, + // even filling in x in intermediate closures that might not + // have mentioned it along the way to inner closures that did. + // See funcLit for details. + // + // During the eventual compilation, then, for closure variables we have: + // + // xN.Defn = original variable + // xN.Outer = variable captured in next outward scope + // to make closure where xN appears + // + // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn + // and x.Innermost/Outer means x.Name.Param.Innermost/Outer. + Innermost *Name + Outer *Name +} + +func (n *Name) isExpr() {} + +func (n *Name) copy() Node { panic(n.no("copy")) } +func (n *Name) doChildren(do func(Node) bool) bool { return false } +func (n *Name) editChildren(edit func(Node) Node) {} + +// TypeDefn returns the type definition for a named OTYPE. +// That is, given "type T Defn", it returns Defn. +// It is used by package types. +func (n *Name) TypeDefn() *types.Type { + if n.Ntype != nil { + return n.Ntype.Type() + } + return n.Type() +} + +// RecordFrameOffset records the frame offset for the name. +// It is used by package types when laying out function arguments. +func (n *Name) RecordFrameOffset(offset int64) { + n.SetFrameOffset(offset) +} + +// NewNameAt returns a new ONAME Node associated with symbol s at position pos. +// The caller is responsible for setting Curfn. +func NewNameAt(pos src.XPos, sym *types.Sym) *Name { + if sym == nil { + base.Fatalf("NewNameAt nil") + } + return newNameAt(pos, ONAME, sym) +} + +// NewIota returns a new OIOTA Node. +func NewIota(pos src.XPos, sym *types.Sym) *Name { + if sym == nil { + base.Fatalf("NewIota nil") + } + return newNameAt(pos, OIOTA, sym) +} + +// NewDeclNameAt returns a new Name associated with symbol s at position pos. +// The caller is responsible for setting Curfn. +func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name { + if sym == nil { + base.Fatalf("NewDeclNameAt nil") + } + switch op { + case ONAME, OTYPE, OLITERAL: + // ok + default: + base.Fatalf("NewDeclNameAt op %v", op) + } + return newNameAt(pos, op, sym) +} + +// NewConstAt returns a new OLITERAL Node associated with symbol s at position pos. +func NewConstAt(pos src.XPos, sym *types.Sym, typ *types.Type, val constant.Value) *Name { + if sym == nil { + base.Fatalf("NewConstAt nil") + } + n := newNameAt(pos, OLITERAL, sym) + n.SetType(typ) + n.SetVal(val) + return n +} + +// newNameAt is like NewNameAt but allows sym == nil. +func newNameAt(pos src.XPos, op Op, sym *types.Sym) *Name { + n := new(Name) + n.op = op + n.pos = pos + n.sym = sym + return n +} + +func (n *Name) Name() *Name { return n } +func (n *Name) Sym() *types.Sym { return n.sym } +func (n *Name) SetSym(x *types.Sym) { n.sym = x } +func (n *Name) SubOp() Op { return n.BuiltinOp } +func (n *Name) SetSubOp(x Op) { n.BuiltinOp = x } +func (n *Name) SetFunc(x *Func) { n.Func = x } +func (n *Name) Offset() int64 { panic("Name.Offset") } +func (n *Name) SetOffset(x int64) { + if x != 0 { + panic("Name.SetOffset") + } +} +func (n *Name) FrameOffset() int64 { return n.Offset_ } +func (n *Name) SetFrameOffset(x int64) { n.Offset_ = x } +func (n *Name) Iota() int64 { return n.Offset_ } +func (n *Name) SetIota(x int64) { n.Offset_ = x } +func (n *Name) Walkdef() uint8 { return n.bits.get2(miniWalkdefShift) } +func (n *Name) SetWalkdef(x uint8) { + if x > 3 { + panic(fmt.Sprintf("cannot SetWalkdef %d", x)) + } + n.bits.set2(miniWalkdefShift, x) +} + +func (n *Name) Linksym() *obj.LSym { return n.sym.Linksym() } +func (n *Name) LinksymABI(abi obj.ABI) *obj.LSym { return n.sym.LinksymABI(abi) } + +func (*Name) CanBeNtype() {} +func (*Name) CanBeAnSSASym() {} +func (*Name) CanBeAnSSAAux() {} + +// Pragma returns the PragmaFlag for p, which must be for an OTYPE. +func (n *Name) Pragma() PragmaFlag { return n.pragma } + +// SetPragma sets the PragmaFlag for p, which must be for an OTYPE. +func (n *Name) SetPragma(flag PragmaFlag) { n.pragma = flag } + +// Alias reports whether p, which must be for an OTYPE, is a type alias. +func (n *Name) Alias() bool { return n.flags&nameAlias != 0 } + +// SetAlias sets whether p, which must be for an OTYPE, is a type alias. +func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) } + +const ( + nameReadonly = 1 << iota + nameByval // is the variable captured by value or by reference + nameNeedzero // if it contains pointers, needs to be zeroed on function entry + nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap) + nameUsed // for variable declared and not used error + nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original (if any) at n.Defn + nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy + nameIsOutputParamInRegisters // output parameter in registers spills as an auto + nameAddrtaken // address taken, even if not moved to heap + nameInlFormal // PAUTO created by inliner, derived from callee formal + nameInlLocal // PAUTO created by inliner, derived from callee local + nameOpenDeferSlot // if temporary var storing info for open-coded defers + nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section + nameAlias // is type name an alias +) + +func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 } +func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 } +func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 } +func (n *Name) Used() bool { return n.flags&nameUsed != 0 } +func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 } +func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 } +func (n *Name) IsOutputParamInRegisters() bool { return n.flags&nameIsOutputParamInRegisters != 0 } +func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 } +func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 } +func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 } +func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 } +func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 } + +func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) } +func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) } +func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) } +func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) } +func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) } +func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) } +func (n *Name) SetIsOutputParamInRegisters(b bool) { n.flags.set(nameIsOutputParamInRegisters, b) } +func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) } +func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) } +func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) } +func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) } +func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) } + +// OnStack reports whether variable n may reside on the stack. +func (n *Name) OnStack() bool { + if n.Op() == ONAME { + switch n.Class { + case PPARAM, PPARAMOUT, PAUTO: + return n.Esc() != EscHeap + case PEXTERN, PAUTOHEAP: + return false + } + } + // Note: fmt.go:dumpNodeHeader calls all "func() bool"-typed + // methods, but it can only recover from panics, not Fatalf. + panic(fmt.Sprintf("%v: not a variable: %v", base.FmtPos(n.Pos()), n)) +} + +// MarkReadonly indicates that n is an ONAME with readonly contents. +func (n *Name) MarkReadonly() { + if n.Op() != ONAME { + base.Fatalf("Node.MarkReadonly %v", n.Op()) + } + n.setReadonly(true) + // Mark the linksym as readonly immediately + // so that the SSA backend can use this information. + // It will be overridden later during dumpglobls. + n.Linksym().Type = objabi.SRODATA +} + +// Val returns the constant.Value for the node. +func (n *Name) Val() constant.Value { + if n.val == nil { + return constant.MakeUnknown() + } + return n.val +} + +// SetVal sets the constant.Value for the node. +func (n *Name) SetVal(v constant.Value) { + if n.op != OLITERAL { + panic(n.no("SetVal")) + } + AssertValidTypeForConst(n.Type(), v) + n.val = v +} + +// Canonical returns the logical declaration that n represents. If n +// is a closure variable, then Canonical returns the original Name as +// it appears in the function that immediately contains the +// declaration. Otherwise, Canonical simply returns n itself. +func (n *Name) Canonical() *Name { + if n.IsClosureVar() && n.Defn != nil { + n = n.Defn.(*Name) + } + return n +} + +func (n *Name) SetByval(b bool) { + if n.Canonical() != n { + base.Fatalf("SetByval called on non-canonical variable: %v", n) + } + n.flags.set(nameByval, b) +} + +func (n *Name) Byval() bool { + // We require byval to be set on the canonical variable, but we + // allow it to be accessed from any instance. + return n.Canonical().flags&nameByval != 0 +} + +// NewClosureVar returns a new closure variable for fn to refer to +// outer variable n. +func NewClosureVar(pos src.XPos, fn *Func, n *Name) *Name { + c := NewNameAt(pos, n.Sym()) + c.Curfn = fn + c.Class = PAUTOHEAP + c.SetIsClosureVar(true) + c.Defn = n.Canonical() + c.Outer = n + + c.SetType(n.Type()) + c.SetTypecheck(n.Typecheck()) + + fn.ClosureVars = append(fn.ClosureVars, c) + + return c +} + +// NewHiddenParam returns a new hidden parameter for fn with the given +// name and type. +func NewHiddenParam(pos src.XPos, fn *Func, sym *types.Sym, typ *types.Type) *Name { + if fn.OClosure != nil { + base.FatalfAt(fn.Pos(), "cannot add hidden parameters to closures") + } + + fn.SetNeedctxt(true) + + // Create a fake parameter, disassociated from any real function, to + // pretend to capture. + fake := NewNameAt(pos, sym) + fake.Class = PPARAM + fake.SetType(typ) + fake.SetByval(true) + + return NewClosureVar(pos, fn, fake) +} + +// CaptureName returns a Name suitable for referring to n from within function +// fn or from the package block if fn is nil. If n is a free variable declared +// within a function that encloses fn, then CaptureName returns the closure +// variable that refers to n within fn, creating it if necessary. +// Otherwise, it simply returns n. +func CaptureName(pos src.XPos, fn *Func, n *Name) *Name { + if n.Op() != ONAME || n.Curfn == nil { + return n // okay to use directly + } + if n.IsClosureVar() { + base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n) + } + + c := n.Innermost + if c == nil { + c = n + } + if c.Curfn == fn { + return c + } + + if fn == nil { + base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn) + } + + // Do not have a closure var for the active closure yet; make one. + c = NewClosureVar(pos, fn, c) + + // Link into list of active closure variables. + // Popped from list in FinishCaptureNames. + n.Innermost = c + + return c +} + +// FinishCaptureNames handles any work leftover from calling CaptureName +// earlier. outerfn should be the function that immediately encloses fn. +func FinishCaptureNames(pos src.XPos, outerfn, fn *Func) { + // closure-specific variables are hanging off the + // ordinary ones; see CaptureName above. + // unhook them. + // make the list of pointers for the closure call. + for _, cv := range fn.ClosureVars { + // Unlink from n; see comment above on type Name for these fields. + n := cv.Defn.(*Name) + n.Innermost = cv.Outer + + // If the closure usage of n is not dense, we need to make it + // dense by recapturing n within the enclosing function. + // + // That is, suppose we just finished parsing the innermost + // closure f4 in this code: + // + // func f() { + // n := 1 + // func() { // f2 + // use(n) + // func() { // f3 + // func() { // f4 + // use(n) + // }() + // }() + // }() + // } + // + // At this point cv.Outer is f2's n; there is no n for f3. To + // construct the closure f4 from within f3, we need to use f3's + // n and in this case we need to create f3's n with CaptureName. + // + // We'll decide later in walk whether to use v directly or &v. + cv.Outer = CaptureName(pos, outerfn, n) + } +} + +// SameSource reports whether two nodes refer to the same source +// element. +// +// It exists to help incrementally migrate the compiler towards +// allowing the introduction of IdentExpr (#42990). Once we have +// IdentExpr, it will no longer be safe to directly compare Node +// values to tell if they refer to the same Name. Instead, code will +// need to explicitly get references to the underlying Name object(s), +// and compare those instead. +// +// It will still be safe to compare Nodes directly for checking if two +// nodes are syntactically the same. The SameSource function exists to +// indicate code that intentionally compares Nodes for syntactic +// equality as opposed to code that has yet to be updated in +// preparation for IdentExpr. +func SameSource(n1, n2 Node) bool { + return n1 == n2 +} + +// Uses reports whether expression x is a (direct) use of the given +// variable. +func Uses(x Node, v *Name) bool { + if v == nil || v.Op() != ONAME { + base.Fatalf("RefersTo bad Name: %v", v) + } + return x.Op() == ONAME && x.Name() == v +} + +// DeclaredBy reports whether expression x refers (directly) to a +// variable that was declared by the given statement. +func DeclaredBy(x, stmt Node) bool { + if stmt == nil { + base.Fatalf("DeclaredBy nil") + } + return x.Op() == ONAME && SameSource(x.Name().Defn, stmt) +} + +// The Class of a variable/function describes the "storage class" +// of a variable or function. During parsing, storage classes are +// called declaration contexts. +type Class uint8 + +//go:generate stringer -type=Class name.go +const ( + Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables + PEXTERN // global variables + PAUTO // local variables + PAUTOHEAP // local variables or parameters moved to heap + PPARAM // input arguments + PPARAMOUT // output results + PTYPEPARAM // type params + PFUNC // global functions + + // Careful: Class is stored in three bits in Node.flags. + _ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3) +) + +type Embed struct { + Pos src.XPos + Patterns []string +} + +// A Pack is an identifier referring to an imported package. +type PkgName struct { + miniNode + sym *types.Sym + Pkg *types.Pkg + Used bool +} + +func (p *PkgName) Sym() *types.Sym { return p.sym } + +func (*PkgName) CanBeNtype() {} + +func NewPkgName(pos src.XPos, sym *types.Sym, pkg *types.Pkg) *PkgName { + p := &PkgName{sym: sym, Pkg: pkg} + p.op = OPACK + p.pos = pos + return p +} diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go new file mode 100644 index 0000000..5fdccf8 --- /dev/null +++ b/src/cmd/compile/internal/ir/node.go @@ -0,0 +1,620 @@ +// 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. + +// “Abstract” syntax representation. + +package ir + +import ( + "fmt" + "go/constant" + "sort" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// A Node is the abstract interface to an IR node. +type Node interface { + // Formatting + Format(s fmt.State, verb rune) + + // Source position. + Pos() src.XPos + SetPos(x src.XPos) + + // For making copies. For Copy and SepCopy. + copy() Node + + doChildren(func(Node) bool) bool + editChildren(func(Node) Node) + + // Abstract graph structure, for generic traversals. + Op() Op + Init() Nodes + + // Fields specific to certain Ops only. + Type() *types.Type + SetType(t *types.Type) + Name() *Name + Sym() *types.Sym + Val() constant.Value + SetVal(v constant.Value) + + // Storage for analysis passes. + Esc() uint16 + SetEsc(x uint16) + Diag() bool + SetDiag(x bool) + + // Typecheck values: + // 0 means the node is not typechecked + // 1 means the node is completely typechecked + // 2 means typechecking of the node is in progress + // 3 means the node has its type from types2, but may need transformation + Typecheck() uint8 + SetTypecheck(x uint8) + NonNil() bool + MarkNonNil() +} + +// Line returns n's position as a string. If n has been inlined, +// it uses the outermost position where n has been inlined. +func Line(n Node) string { + return base.FmtPos(n.Pos()) +} + +func IsSynthetic(n Node) bool { + name := n.Sym().Name + return name[0] == '.' || name[0] == '~' +} + +// IsAutoTmp indicates if n was created by the compiler as a temporary, +// based on the setting of the .AutoTemp flag in n's Name. +func IsAutoTmp(n Node) bool { + if n == nil || n.Op() != ONAME { + return false + } + return n.Name().AutoTemp() +} + +// MayBeShared reports whether n may occur in multiple places in the AST. +// Extra care must be taken when mutating such a node. +func MayBeShared(n Node) bool { + switch n.Op() { + case ONAME, OLITERAL, ONIL, OTYPE: + return true + } + return false +} + +type InitNode interface { + Node + PtrInit() *Nodes + SetInit(x Nodes) +} + +func TakeInit(n Node) Nodes { + init := n.Init() + if len(init) != 0 { + n.(InitNode).SetInit(nil) + } + return init +} + +//go:generate stringer -type=Op -trimprefix=O node.go + +type Op uint8 + +// Node ops. +const ( + OXXX Op = iota + + // names + ONAME // var or func name + // Unnamed arg or return value: f(int, string) (int, error) { etc } + // Also used for a qualified package identifier that hasn't been resolved yet. + ONONAME + OTYPE // type name + OPACK // import + OLITERAL // literal + ONIL // nil + + // expressions + OADD // X + Y + OSUB // X - Y + OOR // X | Y + OXOR // X ^ Y + OADDSTR // +{List} (string addition, list elements are strings) + OADDR // &X + OANDAND // X && Y + OAPPEND // append(Args); after walk, X may contain elem type descriptor + OBYTES2STR // Type(X) (Type is string, X is a []byte) + OBYTES2STRTMP // Type(X) (Type is string, X is a []byte, ephemeral) + ORUNES2STR // Type(X) (Type is string, X is a []rune) + OSTR2BYTES // Type(X) (Type is []byte, X is a string) + OSTR2BYTESTMP // Type(X) (Type is []byte, X is a string, ephemeral) + OSTR2RUNES // Type(X) (Type is []rune, X is a string) + OSLICE2ARRPTR // Type(X) (Type is *[N]T, X is a []T) + // X = Y or (if Def=true) X := Y + // If Def, then Init includes a DCL node for X. + OAS + // Lhs = Rhs (x, y, z = a, b, c) or (if Def=true) Lhs := Rhs + // If Def, then Init includes DCL nodes for Lhs + OAS2 + OAS2DOTTYPE // Lhs = Rhs (x, ok = I.(int)) + OAS2FUNC // Lhs = Rhs (x, y = f()) + OAS2MAPR // Lhs = Rhs (x, ok = m["foo"]) + OAS2RECV // Lhs = Rhs (x, ok = <-c) + OASOP // X AsOp= Y (x += y) + OCALL // X(Args) (function call, method call or type conversion) + + // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure. + // Prior to walk, they are: X(Args), where Args is all regular arguments. + // After walk, if any argument whose evaluation might requires temporary variable, + // that temporary variable will be pushed to Init, Args will contains an updated + // set of arguments. KeepAlive is all OVARLIVE nodes that are attached to OCALLxxx. + OCALLFUNC // X(Args) (function call f(args)) + OCALLMETH // X(Args) (direct method call x.Method(args)) + OCALLINTER // X(Args) (interface method call x.Method(args)) + OCAP // cap(X) + OCLOSE // close(X) + OCLOSURE // func Type { Func.Closure.Body } (func literal) + OCOMPLIT // Type{List} (composite literal, not yet lowered to specific form) + OMAPLIT // Type{List} (composite literal, Type is map) + OSTRUCTLIT // Type{List} (composite literal, Type is struct) + OARRAYLIT // Type{List} (composite literal, Type is array) + OSLICELIT // Type{List} (composite literal, Type is slice), Len is slice length. + OPTRLIT // &X (X is composite literal) + OCONV // Type(X) (type conversion) + OCONVIFACE // Type(X) (type conversion, to interface) + OCONVIDATA // Builds a data word to store X in an interface. Equivalent to IDATA(CONVIFACE(X)). Is an ir.ConvExpr. + OCONVNOP // Type(X) (type conversion, no effect) + OCOPY // copy(X, Y) + ODCL // var X (declares X of type X.Type) + + // Used during parsing but don't last. + ODCLFUNC // func f() or func (r) f() + ODCLCONST // const pi = 3.14 + ODCLTYPE // type Int int or type Int = int + + ODELETE // delete(Args) + ODOT // X.Sel (X is of struct type) + ODOTPTR // X.Sel (X is of pointer to struct type) + ODOTMETH // X.Sel (X is non-interface, Sel is method name) + ODOTINTER // X.Sel (X is interface, Sel is method name) + OXDOT // X.Sel (before rewrite to one of the preceding) + ODOTTYPE // X.Ntype or X.Type (.Ntype during parsing, .Type once resolved); after walk, Itab contains address of interface type descriptor and Itab.X contains address of concrete type descriptor + ODOTTYPE2 // X.Ntype or X.Type (.Ntype during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, Itab contains address of interface type descriptor + OEQ // X == Y + ONE // X != Y + OLT // X < Y + OLE // X <= Y + OGE // X >= Y + OGT // X > Y + ODEREF // *X + OINDEX // X[Index] (index of array or slice) + OINDEXMAP // X[Index] (index of map) + OKEY // Key:Value (key:value in struct/array/map literal) + OSTRUCTKEY // Field:Value (key:value in struct literal, after type checking) + OLEN // len(X) + OMAKE // make(Args) (before type checking converts to one of the following) + OMAKECHAN // make(Type[, Len]) (type is chan) + OMAKEMAP // make(Type[, Len]) (type is map) + OMAKESLICE // make(Type[, Len[, Cap]]) (type is slice) + OMAKESLICECOPY // makeslicecopy(Type, Len, Cap) (type is slice; Len is length and Cap is the copied from slice) + // OMAKESLICECOPY is created by the order pass and corresponds to: + // s = make(Type, Len); copy(s, Cap) + // + // Bounded can be set on the node when Len == len(Cap) is known at compile time. + // + // This node is created so the walk pass can optimize this pattern which would + // otherwise be hard to detect after the order pass. + OMUL // X * Y + ODIV // X / Y + OMOD // X % Y + OLSH // X << Y + ORSH // X >> Y + OAND // X & Y + OANDNOT // X &^ Y + ONEW // new(X); corresponds to calls to new in source code + ONOT // !X + OBITNOT // ^X + OPLUS // +X + ONEG // -X + OOROR // X || Y + OPANIC // panic(X) + OPRINT // print(List) + OPRINTN // println(List) + OPAREN // (X) + OSEND // Chan <- Value + OSLICE // X[Low : High] (X is untypechecked or slice) + OSLICEARR // X[Low : High] (X is pointer to array) + OSLICESTR // X[Low : High] (X is string) + OSLICE3 // X[Low : High : Max] (X is untypedchecked or slice) + OSLICE3ARR // X[Low : High : Max] (X is pointer to array) + OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity) + ORECOVER // recover() + ORECOVERFP // recover(Args) w/ explicit FP argument + ORECV // <-X + ORUNESTR // Type(X) (Type is string, X is rune) + OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE) + OIOTA // iota + OREAL // real(X) + OIMAG // imag(X) + OCOMPLEX // complex(X, Y) + OALIGNOF // unsafe.Alignof(X) + OOFFSETOF // unsafe.Offsetof(X) + OSIZEOF // unsafe.Sizeof(X) + OUNSAFEADD // unsafe.Add(X, Y) + OUNSAFESLICE // unsafe.Slice(X, Y) + OMETHEXPR // X(Args) (method expression T.Method(args), first argument is the method receiver) + OMETHVALUE // X.Sel (method expression t.Method, not called) + + // statements + OBLOCK // { List } (block of code) + OBREAK // break [Label] + // OCASE: case List: Body (List==nil means default) + // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL + // for nil) or an ODYNAMICTYPE indicating a runtime type for generics. + // If a type-switch variable is specified, Var is an + // ONAME for the version of the type-switch variable with the specified + // type. + OCASE + OCONTINUE // continue [Label] + ODEFER // defer Call + OFALL // fallthrough + OFOR // for Init; Cond; Post { Body } + // OFORUNTIL is like OFOR, but the test (Cond) is applied after the body: + // Init + // top: { Body } // Execute the body at least once + // cont: Post + // if Cond { // And then test the loop condition + // List // Before looping to top, execute List + // goto top + // } + // OFORUNTIL is created by walk. There's no way to write this in Go code. + OFORUNTIL + OGOTO // goto Label + OIF // if Init; Cond { Then } else { Else } + OLABEL // Label: + OGO // go Call + ORANGE // for Key, Value = range X { Body } + ORETURN // return Results + OSELECT // select { Cases } + OSWITCH // switch Init; Expr { Cases } + // OTYPESW: X := Y.(type) (appears as .Tag of OSWITCH) + // X is nil if there is no type-switch variable + OTYPESW + OFUNCINST // instantiation of a generic function + + // types + OTCHAN // chan int + OTMAP // map[string]int + OTSTRUCT // struct{} + OTINTER // interface{} + // OTFUNC: func() - Recv is receiver field, Params is list of param fields, Results is + // list of result fields. + OTFUNC + OTARRAY // [8]int or [...]int + OTSLICE // []int + + // misc + // intermediate representation of an inlined call. Uses Init (assignments + // for the captured variables, parameters, retvars, & INLMARK op), + // Body (body of the inlined function), and ReturnVars (list of + // return values) + OINLCALL // intermediary representation of an inlined call. + OEFACE // itable and data words of an empty-interface value. + OITAB // itable word of an interface value. + OIDATA // data word of an interface value in X + OSPTR // base pointer of a slice or string. + OCFUNC // reference to c function pointer (not go func value) + OCHECKNIL // emit code to ensure pointer/interface not nil + OVARDEF // variable is about to be fully initialized + OVARKILL // variable is dead + OVARLIVE // variable is alive + ORESULT // result of a function call; Xoffset is stack offset + OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree. + OLINKSYMOFFSET // offset within a name + + // opcodes for generics + ODYNAMICDOTTYPE // x = i.(T) where T is a type parameter (or derived from a type parameter) + ODYNAMICDOTTYPE2 // x, ok = i.(T) where T is a type parameter (or derived from a type parameter) + ODYNAMICTYPE // a type node for type switches (represents a dynamic target type for a type switch) + + // arch-specific opcodes + OTAILCALL // tail call to another function + OGETG // runtime.getg() (read g pointer) + OGETCALLERPC // runtime.getcallerpc() (continuation PC in caller frame) + OGETCALLERSP // runtime.getcallersp() (stack pointer in caller frame) + + OEND +) + +// IsCmp reports whether op is a comparison operation (==, !=, <, <=, +// >, or >=). +func (op Op) IsCmp() bool { + switch op { + case OEQ, ONE, OLT, OLE, OGT, OGE: + return true + } + return false +} + +// Nodes is a pointer to a slice of *Node. +// For fields that are not used in most nodes, this is used instead of +// a slice to save space. +type Nodes []Node + +// Append appends entries to Nodes. +func (n *Nodes) Append(a ...Node) { + if len(a) == 0 { + return + } + *n = append(*n, a...) +} + +// Prepend prepends entries to Nodes. +// If a slice is passed in, this will take ownership of it. +func (n *Nodes) Prepend(a ...Node) { + if len(a) == 0 { + return + } + *n = append(a, *n...) +} + +// Take clears n, returning its former contents. +func (n *Nodes) Take() []Node { + ret := *n + *n = nil + return ret +} + +// Copy returns a copy of the content of the slice. +func (n Nodes) Copy() Nodes { + if n == nil { + return nil + } + c := make(Nodes, len(n)) + copy(c, n) + return c +} + +// NameQueue is a FIFO queue of *Name. The zero value of NameQueue is +// a ready-to-use empty queue. +type NameQueue struct { + ring []*Name + head, tail int +} + +// Empty reports whether q contains no Names. +func (q *NameQueue) Empty() bool { + return q.head == q.tail +} + +// PushRight appends n to the right of the queue. +func (q *NameQueue) PushRight(n *Name) { + if len(q.ring) == 0 { + q.ring = make([]*Name, 16) + } else if q.head+len(q.ring) == q.tail { + // Grow the ring. + nring := make([]*Name, len(q.ring)*2) + // Copy the old elements. + part := q.ring[q.head%len(q.ring):] + if q.tail-q.head <= len(part) { + part = part[:q.tail-q.head] + copy(nring, part) + } else { + pos := copy(nring, part) + copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) + } + q.ring, q.head, q.tail = nring, 0, q.tail-q.head + } + + q.ring[q.tail%len(q.ring)] = n + q.tail++ +} + +// PopLeft pops a Name from the left of the queue. It panics if q is +// empty. +func (q *NameQueue) PopLeft() *Name { + if q.Empty() { + panic("dequeue empty") + } + n := q.ring[q.head%len(q.ring)] + q.head++ + return n +} + +// NameSet is a set of Names. +type NameSet map[*Name]struct{} + +// Has reports whether s contains n. +func (s NameSet) Has(n *Name) bool { + _, isPresent := s[n] + return isPresent +} + +// Add adds n to s. +func (s *NameSet) Add(n *Name) { + if *s == nil { + *s = make(map[*Name]struct{}) + } + (*s)[n] = struct{}{} +} + +// Sorted returns s sorted according to less. +func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name { + var res []*Name + for n := range s { + res = append(res, n) + } + sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) }) + return res +} + +type PragmaFlag uint16 + +const ( + // Func pragmas. + Nointerface PragmaFlag = 1 << iota + Noescape // func parameters don't escape + Norace // func must not have race detector annotations + Nosplit // func should not execute on separate stack + Noinline // func should not be inlined + NoCheckPtr // func should not be instrumented by checkptr + CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all + UintptrKeepAlive // pointers converted to uintptr must be kept alive (compiler internal only) + UintptrEscapes // pointers converted to uintptr escape + + // Runtime-only func pragmas. + // See ../../../../runtime/HACKING.md for detailed descriptions. + Systemstack // func must run on system stack + Nowritebarrier // emit compiler error instead of write barrier + Nowritebarrierrec // error on write barrier in this or recursive callees + Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees + + // Runtime and cgo type pragmas + NotInHeap // values of this type must not be heap allocated + + // Go command pragmas + GoBuildPragma + + RegisterParams // TODO(register args) remove after register abi is working + +) + +func AsNode(n types.Object) Node { + if n == nil { + return nil + } + return n.(Node) +} + +var BlankNode Node + +func IsConst(n Node, ct constant.Kind) bool { + return ConstType(n) == ct +} + +// IsNil reports whether n represents the universal untyped zero value "nil". +func IsNil(n Node) bool { + // Check n.Orig because constant propagation may produce typed nil constants, + // which don't exist in the Go spec. + return n != nil && Orig(n).Op() == ONIL +} + +func IsBlank(n Node) bool { + if n == nil { + return false + } + return n.Sym().IsBlank() +} + +// IsMethod reports whether n is a method. +// n must be a function or a method. +func IsMethod(n Node) bool { + return n.Type().Recv() != nil +} + +func HasNamedResults(fn *Func) bool { + typ := fn.Type() + return typ.NumResults() > 0 && types.OrigSym(typ.Results().Field(0).Sym) != nil +} + +// HasUniquePos reports whether n has a unique position that can be +// used for reporting error messages. +// +// It's primarily used to distinguish references to named objects, +// whose Pos will point back to their declaration position rather than +// their usage position. +func HasUniquePos(n Node) bool { + switch n.Op() { + case ONAME, OPACK: + return false + case OLITERAL, ONIL, OTYPE: + if n.Sym() != nil { + return false + } + } + + if !n.Pos().IsKnown() { + if base.Flag.K != 0 { + base.Warn("setlineno: unknown position (line 0)") + } + return false + } + + return true +} + +func SetPos(n Node) src.XPos { + lno := base.Pos + if n != nil && HasUniquePos(n) { + base.Pos = n.Pos() + } + return lno +} + +// The result of InitExpr MUST be assigned back to n, e.g. +// n.X = InitExpr(init, n.X) +func InitExpr(init []Node, expr Node) Node { + if len(init) == 0 { + return expr + } + + n, ok := expr.(InitNode) + if !ok || MayBeShared(n) { + // Introduce OCONVNOP to hold init list. + n = NewConvExpr(base.Pos, OCONVNOP, nil, expr) + n.SetType(expr.Type()) + n.SetTypecheck(1) + } + + n.PtrInit().Prepend(init...) + return n +} + +// what's the outer value that a write to n affects? +// outer value means containing struct or array. +func OuterValue(n Node) Node { + for { + switch nn := n; nn.Op() { + case OXDOT: + base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n) + case ODOT: + nn := nn.(*SelectorExpr) + n = nn.X + continue + case OPAREN: + nn := nn.(*ParenExpr) + n = nn.X + continue + case OCONVNOP: + nn := nn.(*ConvExpr) + n = nn.X + continue + case OINDEX: + nn := nn.(*IndexExpr) + if nn.X.Type() == nil { + base.Fatalf("OuterValue needs type for %v", nn.X) + } + if nn.X.Type().IsArray() { + n = nn.X + continue + } + } + + return n + } +} + +const ( + EscUnknown = iota + EscNone // Does not escape to heap, result, or parameters. + EscHeap // Reachable from the heap + EscNever // By construction will not escape. +) diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go new file mode 100644 index 0000000..4498888 --- /dev/null +++ b/src/cmd/compile/internal/ir/node_gen.go @@ -0,0 +1,1524 @@ +// Code generated by mknode.go. DO NOT EDIT. + +package ir + +import "fmt" + +func (n *AddStringExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *AddStringExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.List = copyNodes(c.List) + return &c +} +func (n *AddStringExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doNodes(n.List, do) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *AddStringExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editNodes(n.List, edit) + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *AddrExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *AddrExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *AddrExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *AddrExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *ArrayType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ArrayType) copy() Node { + c := *n + return &c +} +func (n *ArrayType) doChildren(do func(Node) bool) bool { + if n.Len != nil && do(n.Len) { + return true + } + if n.Elem != nil && do(n.Elem) { + return true + } + return false +} +func (n *ArrayType) editChildren(edit func(Node) Node) { + if n.Len != nil { + n.Len = edit(n.Len).(Node) + } + if n.Elem != nil { + n.Elem = edit(n.Elem).(Ntype) + } +} + +func (n *AssignListStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *AssignListStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Lhs = copyNodes(c.Lhs) + c.Rhs = copyNodes(c.Rhs) + return &c +} +func (n *AssignListStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doNodes(n.Lhs, do) { + return true + } + if doNodes(n.Rhs, do) { + return true + } + return false +} +func (n *AssignListStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editNodes(n.Lhs, edit) + editNodes(n.Rhs, edit) +} + +func (n *AssignOpStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *AssignOpStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *AssignOpStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Y != nil && do(n.Y) { + return true + } + return false +} +func (n *AssignOpStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Y != nil { + n.Y = edit(n.Y).(Node) + } +} + +func (n *AssignStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *AssignStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *AssignStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Y != nil && do(n.Y) { + return true + } + return false +} +func (n *AssignStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Y != nil { + n.Y = edit(n.Y).(Node) + } +} + +func (n *BasicLit) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *BasicLit) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *BasicLit) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *BasicLit) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *BinaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *BinaryExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *BinaryExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Y != nil && do(n.Y) { + return true + } + return false +} +func (n *BinaryExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Y != nil { + n.Y = edit(n.Y).(Node) + } +} + +func (n *BlockStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *BlockStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.List = copyNodes(c.List) + return &c +} +func (n *BlockStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doNodes(n.List, do) { + return true + } + return false +} +func (n *BlockStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editNodes(n.List, edit) +} + +func (n *BranchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *BranchStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *BranchStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *BranchStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *CallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *CallExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Args = copyNodes(c.Args) + c.KeepAlive = copyNames(c.KeepAlive) + return &c +} +func (n *CallExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if doNodes(n.Args, do) { + return true + } + if doNames(n.KeepAlive, do) { + return true + } + return false +} +func (n *CallExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + editNodes(n.Args, edit) + editNames(n.KeepAlive, edit) +} + +func (n *CaseClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *CaseClause) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.List = copyNodes(c.List) + c.Body = copyNodes(c.Body) + return &c +} +func (n *CaseClause) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Var != nil && do(n.Var) { + return true + } + if doNodes(n.List, do) { + return true + } + if doNodes(n.Body, do) { + return true + } + return false +} +func (n *CaseClause) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Var != nil { + n.Var = edit(n.Var).(*Name) + } + editNodes(n.List, edit) + editNodes(n.Body, edit) +} + +func (n *ChanType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ChanType) copy() Node { + c := *n + return &c +} +func (n *ChanType) doChildren(do func(Node) bool) bool { + if n.Elem != nil && do(n.Elem) { + return true + } + return false +} +func (n *ChanType) editChildren(edit func(Node) Node) { + if n.Elem != nil { + n.Elem = edit(n.Elem).(Ntype) + } +} + +func (n *ClosureExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ClosureExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *ClosureExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *ClosureExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *CommClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *CommClause) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Body = copyNodes(c.Body) + return &c +} +func (n *CommClause) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Comm != nil && do(n.Comm) { + return true + } + if doNodes(n.Body, do) { + return true + } + return false +} +func (n *CommClause) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Comm != nil { + n.Comm = edit(n.Comm).(Node) + } + editNodes(n.Body, edit) +} + +func (n *CompLitExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *CompLitExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.List = copyNodes(c.List) + return &c +} +func (n *CompLitExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Ntype != nil && do(n.Ntype) { + return true + } + if doNodes(n.List, do) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *CompLitExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Ntype != nil { + n.Ntype = edit(n.Ntype).(Ntype) + } + editNodes(n.List, edit) + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *ConstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ConstExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *ConstExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *ConstExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *ConvExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ConvExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *ConvExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *ConvExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } +} + +func (n *Decl) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *Decl) copy() Node { + c := *n + return &c +} +func (n *Decl) doChildren(do func(Node) bool) bool { + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *Decl) editChildren(edit func(Node) Node) { + if n.X != nil { + n.X = edit(n.X).(*Name) + } +} + +func (n *DynamicType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *DynamicType) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *DynamicType) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.ITab != nil && do(n.ITab) { + return true + } + return false +} +func (n *DynamicType) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.ITab != nil { + n.ITab = edit(n.ITab).(Node) + } +} + +func (n *DynamicTypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *DynamicTypeAssertExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *DynamicTypeAssertExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.T != nil && do(n.T) { + return true + } + return false +} +func (n *DynamicTypeAssertExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.T != nil { + n.T = edit(n.T).(Node) + } +} + +func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ForStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Late = copyNodes(c.Late) + c.Body = copyNodes(c.Body) + return &c +} +func (n *ForStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Cond != nil && do(n.Cond) { + return true + } + if doNodes(n.Late, do) { + return true + } + if n.Post != nil && do(n.Post) { + return true + } + if doNodes(n.Body, do) { + return true + } + return false +} +func (n *ForStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Cond != nil { + n.Cond = edit(n.Cond).(Node) + } + editNodes(n.Late, edit) + if n.Post != nil { + n.Post = edit(n.Post).(Node) + } + editNodes(n.Body, edit) +} + +func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } + +func (n *FuncType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *FuncType) copy() Node { + c := *n + c.Recv = copyField(c.Recv) + c.Params = copyFields(c.Params) + c.Results = copyFields(c.Results) + return &c +} +func (n *FuncType) doChildren(do func(Node) bool) bool { + if doField(n.Recv, do) { + return true + } + if doFields(n.Params, do) { + return true + } + if doFields(n.Results, do) { + return true + } + return false +} +func (n *FuncType) editChildren(edit func(Node) Node) { + editField(n.Recv, edit) + editFields(n.Params, edit) + editFields(n.Results, edit) +} + +func (n *GoDeferStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *GoDeferStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *GoDeferStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Call != nil && do(n.Call) { + return true + } + return false +} +func (n *GoDeferStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Call != nil { + n.Call = edit(n.Call).(Node) + } +} + +func (n *Ident) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *Ident) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *Ident) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *Ident) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *IfStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *IfStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Body = copyNodes(c.Body) + c.Else = copyNodes(c.Else) + return &c +} +func (n *IfStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Cond != nil && do(n.Cond) { + return true + } + if doNodes(n.Body, do) { + return true + } + if doNodes(n.Else, do) { + return true + } + return false +} +func (n *IfStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Cond != nil { + n.Cond = edit(n.Cond).(Node) + } + editNodes(n.Body, edit) + editNodes(n.Else, edit) +} + +func (n *IndexExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *IndexExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *IndexExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Index != nil && do(n.Index) { + return true + } + return false +} +func (n *IndexExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Index != nil { + n.Index = edit(n.Index).(Node) + } +} + +func (n *InlineMarkStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *InlineMarkStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *InlineMarkStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *InlineMarkStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *InlinedCallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *InlinedCallExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Body = copyNodes(c.Body) + c.ReturnVars = copyNodes(c.ReturnVars) + return &c +} +func (n *InlinedCallExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doNodes(n.Body, do) { + return true + } + if doNodes(n.ReturnVars, do) { + return true + } + return false +} +func (n *InlinedCallExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editNodes(n.Body, edit) + editNodes(n.ReturnVars, edit) +} + +func (n *InstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *InstExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Targs = copyNodes(c.Targs) + return &c +} +func (n *InstExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if doNodes(n.Targs, do) { + return true + } + return false +} +func (n *InstExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + editNodes(n.Targs, edit) +} + +func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *InterfaceType) copy() Node { + c := *n + c.Methods = copyFields(c.Methods) + return &c +} +func (n *InterfaceType) doChildren(do func(Node) bool) bool { + if doFields(n.Methods, do) { + return true + } + return false +} +func (n *InterfaceType) editChildren(edit func(Node) Node) { + editFields(n.Methods, edit) +} + +func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *KeyExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *KeyExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Key != nil && do(n.Key) { + return true + } + if n.Value != nil && do(n.Value) { + return true + } + return false +} +func (n *KeyExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Key != nil { + n.Key = edit(n.Key).(Node) + } + if n.Value != nil { + n.Value = edit(n.Value).(Node) + } +} + +func (n *LabelStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *LabelStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *LabelStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *LabelStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *LinksymOffsetExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *LinksymOffsetExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *LinksymOffsetExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *LinksymOffsetExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *LogicalExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *LogicalExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *LogicalExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Y != nil && do(n.Y) { + return true + } + return false +} +func (n *LogicalExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Y != nil { + n.Y = edit(n.Y).(Node) + } +} + +func (n *MakeExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *MakeExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *MakeExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Len != nil && do(n.Len) { + return true + } + if n.Cap != nil && do(n.Cap) { + return true + } + return false +} +func (n *MakeExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Len != nil { + n.Len = edit(n.Len).(Node) + } + if n.Cap != nil { + n.Cap = edit(n.Cap).(Node) + } +} + +func (n *MapType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *MapType) copy() Node { + c := *n + return &c +} +func (n *MapType) doChildren(do func(Node) bool) bool { + if n.Key != nil && do(n.Key) { + return true + } + if n.Elem != nil && do(n.Elem) { + return true + } + return false +} +func (n *MapType) editChildren(edit func(Node) Node) { + if n.Key != nil { + n.Key = edit(n.Key).(Ntype) + } + if n.Elem != nil { + n.Elem = edit(n.Elem).(Ntype) + } +} + +func (n *Name) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } + +func (n *NilExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *NilExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *NilExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *NilExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *ParenExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ParenExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *ParenExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *ParenExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } +} + +func (n *PkgName) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *PkgName) copy() Node { + c := *n + return &c +} +func (n *PkgName) doChildren(do func(Node) bool) bool { + return false +} +func (n *PkgName) editChildren(edit func(Node) Node) { +} + +func (n *RangeStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *RangeStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Body = copyNodes(c.Body) + return &c +} +func (n *RangeStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Key != nil && do(n.Key) { + return true + } + if n.Value != nil && do(n.Value) { + return true + } + if doNodes(n.Body, do) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *RangeStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Key != nil { + n.Key = edit(n.Key).(Node) + } + if n.Value != nil { + n.Value = edit(n.Value).(Node) + } + editNodes(n.Body, edit) + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *RawOrigExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *RawOrigExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *RawOrigExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *RawOrigExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ResultExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *ResultExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *ResultExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + +func (n *ReturnStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ReturnStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Results = copyNodes(c.Results) + return &c +} +func (n *ReturnStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doNodes(n.Results, do) { + return true + } + return false +} +func (n *ReturnStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editNodes(n.Results, edit) +} + +func (n *SelectStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SelectStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Cases = copyCommClauses(c.Cases) + c.Compiled = copyNodes(c.Compiled) + return &c +} +func (n *SelectStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if doCommClauses(n.Cases, do) { + return true + } + if doNodes(n.Compiled, do) { + return true + } + return false +} +func (n *SelectStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + editCommClauses(n.Cases, edit) + editNodes(n.Compiled, edit) +} + +func (n *SelectorExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SelectorExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *SelectorExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Prealloc != nil && do(n.Prealloc) { + return true + } + return false +} +func (n *SelectorExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Prealloc != nil { + n.Prealloc = edit(n.Prealloc).(*Name) + } +} + +func (n *SendStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SendStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *SendStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Chan != nil && do(n.Chan) { + return true + } + if n.Value != nil && do(n.Value) { + return true + } + return false +} +func (n *SendStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Chan != nil { + n.Chan = edit(n.Chan).(Node) + } + if n.Value != nil { + n.Value = edit(n.Value).(Node) + } +} + +func (n *SliceExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SliceExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *SliceExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Low != nil && do(n.Low) { + return true + } + if n.High != nil && do(n.High) { + return true + } + if n.Max != nil && do(n.Max) { + return true + } + return false +} +func (n *SliceExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Low != nil { + n.Low = edit(n.Low).(Node) + } + if n.High != nil { + n.High = edit(n.High).(Node) + } + if n.Max != nil { + n.Max = edit(n.Max).(Node) + } +} + +func (n *SliceHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SliceHeaderExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *SliceHeaderExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Ptr != nil && do(n.Ptr) { + return true + } + if n.Len != nil && do(n.Len) { + return true + } + if n.Cap != nil && do(n.Cap) { + return true + } + return false +} +func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Ptr != nil { + n.Ptr = edit(n.Ptr).(Node) + } + if n.Len != nil { + n.Len = edit(n.Len).(Node) + } + if n.Cap != nil { + n.Cap = edit(n.Cap).(Node) + } +} + +func (n *SliceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SliceType) copy() Node { + c := *n + return &c +} +func (n *SliceType) doChildren(do func(Node) bool) bool { + if n.Elem != nil && do(n.Elem) { + return true + } + return false +} +func (n *SliceType) editChildren(edit func(Node) Node) { + if n.Elem != nil { + n.Elem = edit(n.Elem).(Ntype) + } +} + +func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *StarExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *StarExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *StarExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } +} + +func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *StructKeyExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *StructKeyExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Value != nil && do(n.Value) { + return true + } + return false +} +func (n *StructKeyExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Value != nil { + n.Value = edit(n.Value).(Node) + } +} + +func (n *StructType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *StructType) copy() Node { + c := *n + c.Fields = copyFields(c.Fields) + return &c +} +func (n *StructType) doChildren(do func(Node) bool) bool { + if doFields(n.Fields, do) { + return true + } + return false +} +func (n *StructType) editChildren(edit func(Node) Node) { + editFields(n.Fields, edit) +} + +func (n *SwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *SwitchStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + c.Cases = copyCaseClauses(c.Cases) + c.Compiled = copyNodes(c.Compiled) + return &c +} +func (n *SwitchStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Tag != nil && do(n.Tag) { + return true + } + if doCaseClauses(n.Cases, do) { + return true + } + if doNodes(n.Compiled, do) { + return true + } + return false +} +func (n *SwitchStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Tag != nil { + n.Tag = edit(n.Tag).(Node) + } + editCaseClauses(n.Cases, edit) + editNodes(n.Compiled, edit) +} + +func (n *TailCallStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *TailCallStmt) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *TailCallStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Call != nil && do(n.Call) { + return true + } + return false +} +func (n *TailCallStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Call != nil { + n.Call = edit(n.Call).(*CallExpr) + } +} + +func (n *TypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *TypeAssertExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *TypeAssertExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + if n.Ntype != nil && do(n.Ntype) { + return true + } + return false +} +func (n *TypeAssertExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } + if n.Ntype != nil { + n.Ntype = edit(n.Ntype).(Ntype) + } +} + +func (n *TypeSwitchGuard) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *TypeSwitchGuard) copy() Node { + c := *n + return &c +} +func (n *TypeSwitchGuard) doChildren(do func(Node) bool) bool { + if n.Tag != nil && do(n.Tag) { + return true + } + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) { + if n.Tag != nil { + n.Tag = edit(n.Tag).(*Ident) + } + if n.X != nil { + n.X = edit(n.X).(Node) + } +} + +func (n *UnaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *UnaryExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *UnaryExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { + return true + } + return false +} +func (n *UnaryExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) + } +} + +func (n *typeNode) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *typeNode) copy() Node { + c := *n + return &c +} +func (n *typeNode) doChildren(do func(Node) bool) bool { + return false +} +func (n *typeNode) editChildren(edit func(Node) Node) { +} + +func copyCaseClauses(list []*CaseClause) []*CaseClause { + if list == nil { + return nil + } + c := make([]*CaseClause, len(list)) + copy(c, list) + return c +} +func doCaseClauses(list []*CaseClause, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func editCaseClauses(list []*CaseClause, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(*CaseClause) + } + } +} + +func copyCommClauses(list []*CommClause) []*CommClause { + if list == nil { + return nil + } + c := make([]*CommClause, len(list)) + copy(c, list) + return c +} +func doCommClauses(list []*CommClause, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func editCommClauses(list []*CommClause, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(*CommClause) + } + } +} + +func copyNames(list []*Name) []*Name { + if list == nil { + return nil + } + c := make([]*Name, len(list)) + copy(c, list) + return c +} +func doNames(list []*Name, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func editNames(list []*Name, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(*Name) + } + } +} + +func copyNodes(list []Node) []Node { + if list == nil { + return nil + } + c := make([]Node, len(list)) + copy(c, list) + return c +} +func doNodes(list []Node, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func editNodes(list []Node, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(Node) + } + } +} diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go new file mode 100644 index 0000000..b8cee71 --- /dev/null +++ b/src/cmd/compile/internal/ir/op_string.go @@ -0,0 +1,184 @@ +// Code generated by "stringer -type=Op -trimprefix=O node.go"; DO NOT EDIT. + +package ir + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[OXXX-0] + _ = x[ONAME-1] + _ = x[ONONAME-2] + _ = x[OTYPE-3] + _ = x[OPACK-4] + _ = x[OLITERAL-5] + _ = x[ONIL-6] + _ = x[OADD-7] + _ = x[OSUB-8] + _ = x[OOR-9] + _ = x[OXOR-10] + _ = x[OADDSTR-11] + _ = x[OADDR-12] + _ = x[OANDAND-13] + _ = x[OAPPEND-14] + _ = x[OBYTES2STR-15] + _ = x[OBYTES2STRTMP-16] + _ = x[ORUNES2STR-17] + _ = x[OSTR2BYTES-18] + _ = x[OSTR2BYTESTMP-19] + _ = x[OSTR2RUNES-20] + _ = x[OSLICE2ARRPTR-21] + _ = x[OAS-22] + _ = x[OAS2-23] + _ = x[OAS2DOTTYPE-24] + _ = x[OAS2FUNC-25] + _ = x[OAS2MAPR-26] + _ = x[OAS2RECV-27] + _ = x[OASOP-28] + _ = x[OCALL-29] + _ = x[OCALLFUNC-30] + _ = x[OCALLMETH-31] + _ = x[OCALLINTER-32] + _ = x[OCAP-33] + _ = x[OCLOSE-34] + _ = x[OCLOSURE-35] + _ = x[OCOMPLIT-36] + _ = x[OMAPLIT-37] + _ = x[OSTRUCTLIT-38] + _ = x[OARRAYLIT-39] + _ = x[OSLICELIT-40] + _ = x[OPTRLIT-41] + _ = x[OCONV-42] + _ = x[OCONVIFACE-43] + _ = x[OCONVIDATA-44] + _ = x[OCONVNOP-45] + _ = x[OCOPY-46] + _ = x[ODCL-47] + _ = x[ODCLFUNC-48] + _ = x[ODCLCONST-49] + _ = x[ODCLTYPE-50] + _ = x[ODELETE-51] + _ = x[ODOT-52] + _ = x[ODOTPTR-53] + _ = x[ODOTMETH-54] + _ = x[ODOTINTER-55] + _ = x[OXDOT-56] + _ = x[ODOTTYPE-57] + _ = x[ODOTTYPE2-58] + _ = x[OEQ-59] + _ = x[ONE-60] + _ = x[OLT-61] + _ = x[OLE-62] + _ = x[OGE-63] + _ = x[OGT-64] + _ = x[ODEREF-65] + _ = x[OINDEX-66] + _ = x[OINDEXMAP-67] + _ = x[OKEY-68] + _ = x[OSTRUCTKEY-69] + _ = x[OLEN-70] + _ = x[OMAKE-71] + _ = x[OMAKECHAN-72] + _ = x[OMAKEMAP-73] + _ = x[OMAKESLICE-74] + _ = x[OMAKESLICECOPY-75] + _ = x[OMUL-76] + _ = x[ODIV-77] + _ = x[OMOD-78] + _ = x[OLSH-79] + _ = x[ORSH-80] + _ = x[OAND-81] + _ = x[OANDNOT-82] + _ = x[ONEW-83] + _ = x[ONOT-84] + _ = x[OBITNOT-85] + _ = x[OPLUS-86] + _ = x[ONEG-87] + _ = x[OOROR-88] + _ = x[OPANIC-89] + _ = x[OPRINT-90] + _ = x[OPRINTN-91] + _ = x[OPAREN-92] + _ = x[OSEND-93] + _ = x[OSLICE-94] + _ = x[OSLICEARR-95] + _ = x[OSLICESTR-96] + _ = x[OSLICE3-97] + _ = x[OSLICE3ARR-98] + _ = x[OSLICEHEADER-99] + _ = x[ORECOVER-100] + _ = x[ORECOVERFP-101] + _ = x[ORECV-102] + _ = x[ORUNESTR-103] + _ = x[OSELRECV2-104] + _ = x[OIOTA-105] + _ = x[OREAL-106] + _ = x[OIMAG-107] + _ = x[OCOMPLEX-108] + _ = x[OALIGNOF-109] + _ = x[OOFFSETOF-110] + _ = x[OSIZEOF-111] + _ = x[OUNSAFEADD-112] + _ = x[OUNSAFESLICE-113] + _ = x[OMETHEXPR-114] + _ = x[OMETHVALUE-115] + _ = x[OBLOCK-116] + _ = x[OBREAK-117] + _ = x[OCASE-118] + _ = x[OCONTINUE-119] + _ = x[ODEFER-120] + _ = x[OFALL-121] + _ = x[OFOR-122] + _ = x[OFORUNTIL-123] + _ = x[OGOTO-124] + _ = x[OIF-125] + _ = x[OLABEL-126] + _ = x[OGO-127] + _ = x[ORANGE-128] + _ = x[ORETURN-129] + _ = x[OSELECT-130] + _ = x[OSWITCH-131] + _ = x[OTYPESW-132] + _ = x[OFUNCINST-133] + _ = x[OTCHAN-134] + _ = x[OTMAP-135] + _ = x[OTSTRUCT-136] + _ = x[OTINTER-137] + _ = x[OTFUNC-138] + _ = x[OTARRAY-139] + _ = x[OTSLICE-140] + _ = x[OINLCALL-141] + _ = x[OEFACE-142] + _ = x[OITAB-143] + _ = x[OIDATA-144] + _ = x[OSPTR-145] + _ = x[OCFUNC-146] + _ = x[OCHECKNIL-147] + _ = x[OVARDEF-148] + _ = x[OVARKILL-149] + _ = x[OVARLIVE-150] + _ = x[ORESULT-151] + _ = x[OINLMARK-152] + _ = x[OLINKSYMOFFSET-153] + _ = x[ODYNAMICDOTTYPE-154] + _ = x[ODYNAMICDOTTYPE2-155] + _ = x[ODYNAMICTYPE-156] + _ = x[OTAILCALL-157] + _ = x[OGETG-158] + _ = x[OGETCALLERPC-159] + _ = x[OGETCALLERSP-160] + _ = x[OEND-161] +} + +const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" + +var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 208, 213, 220, 227, 233, 242, 250, 258, 264, 268, 277, 286, 293, 297, 300, 307, 315, 322, 328, 331, 337, 344, 352, 356, 363, 371, 373, 375, 377, 379, 381, 383, 388, 393, 401, 404, 413, 416, 420, 428, 435, 444, 457, 460, 463, 466, 469, 472, 475, 481, 484, 487, 493, 497, 500, 504, 509, 514, 520, 525, 529, 534, 542, 550, 556, 565, 576, 583, 592, 596, 603, 611, 615, 619, 623, 630, 637, 645, 651, 660, 671, 679, 688, 693, 698, 702, 710, 715, 719, 722, 730, 734, 736, 741, 743, 748, 754, 760, 766, 772, 780, 785, 789, 796, 802, 807, 813, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 877, 883, 890, 903, 917, 932, 943, 951, 955, 966, 977, 980} + +func (i Op) String() string { + if i >= Op(len(_Op_index)-1) { + return "Op(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Op_name[_Op_index[i]:_Op_index[i+1]] +} diff --git a/src/cmd/compile/internal/ir/package.go b/src/cmd/compile/internal/ir/package.go new file mode 100644 index 0000000..3896e2b --- /dev/null +++ b/src/cmd/compile/internal/ir/package.go @@ -0,0 +1,35 @@ +// Copyright 2020 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 ir + +import "cmd/compile/internal/types" + +// A Package holds information about the package being compiled. +type Package struct { + // Imports, listed in source order. + // See golang.org/issue/31636. + Imports []*types.Pkg + + // Init functions, listed in source order. + Inits []*Func + + // Top-level declarations. + Decls []Node + + // Extern (package global) declarations. + Externs []Node + + // Assembly function declarations. + Asms []*Name + + // Cgo directives. + CgoPragmas [][]string + + // Variables with //go:embed lines. + Embeds []*Name + + // Exported (or re-exported) symbols. + Exports []*Name +} diff --git a/src/cmd/compile/internal/ir/scc.go b/src/cmd/compile/internal/ir/scc.go new file mode 100644 index 0000000..a42951c --- /dev/null +++ b/src/cmd/compile/internal/ir/scc.go @@ -0,0 +1,131 @@ +// 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 ir + +// Strongly connected components. +// +// Run analysis on minimal sets of mutually recursive functions +// or single non-recursive functions, bottom up. +// +// Finding these sets is finding strongly connected components +// by reverse topological order in the static call graph. +// The algorithm (known as Tarjan's algorithm) for doing that is taken from +// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations. +// +// First, a hidden closure function (n.Func.IsHiddenClosure()) cannot be the +// root of a connected component. Refusing to use it as a root +// forces it into the component of the function in which it appears. +// This is more convenient for escape analysis. +// +// Second, each function becomes two virtual nodes in the graph, +// with numbers n and n+1. We record the function's node number as n +// but search from node n+1. If the search tells us that the component +// number (min) is n+1, we know that this is a trivial component: one function +// plus its closures. If the search tells us that the component number is +// n, then there was a path from node n+1 back to node n, meaning that +// the function set is mutually recursive. The escape analysis can be +// more precise when analyzing a single non-recursive function than +// when analyzing a set of mutually recursive functions. + +type bottomUpVisitor struct { + analyze func([]*Func, bool) + visitgen uint32 + nodeID map[*Func]uint32 + stack []*Func +} + +// VisitFuncsBottomUp invokes analyze on the ODCLFUNC nodes listed in list. +// It calls analyze with successive groups of functions, working from +// the bottom of the call graph upward. Each time analyze is called with +// a list of functions, every function on that list only calls other functions +// on the list or functions that have been passed in previous invocations of +// analyze. Closures appear in the same list as their outer functions. +// The lists are as short as possible while preserving those requirements. +// (In a typical program, many invocations of analyze will be passed just +// a single function.) The boolean argument 'recursive' passed to analyze +// specifies whether the functions on the list are mutually recursive. +// If recursive is false, the list consists of only a single function and its closures. +// If recursive is true, the list may still contain only a single function, +// if that function is itself recursive. +func VisitFuncsBottomUp(list []Node, analyze func(list []*Func, recursive bool)) { + var v bottomUpVisitor + v.analyze = analyze + v.nodeID = make(map[*Func]uint32) + for _, n := range list { + if n.Op() == ODCLFUNC { + n := n.(*Func) + if !n.IsHiddenClosure() { + v.visit(n) + } + } + } +} + +func (v *bottomUpVisitor) visit(n *Func) uint32 { + if id := v.nodeID[n]; id > 0 { + // already visited + return id + } + + v.visitgen++ + id := v.visitgen + v.nodeID[n] = id + v.visitgen++ + min := v.visitgen + v.stack = append(v.stack, n) + + do := func(defn Node) { + if defn != nil { + if m := v.visit(defn.(*Func)); m < min { + min = m + } + } + } + + Visit(n, func(n Node) { + switch n.Op() { + case ONAME: + if n := n.(*Name); n.Class == PFUNC { + do(n.Defn) + } + case ODOTMETH, OMETHVALUE, OMETHEXPR: + if fn := MethodExprName(n); fn != nil { + do(fn.Defn) + } + case OCLOSURE: + n := n.(*ClosureExpr) + do(n.Func) + } + }) + + if (min == id || min == id+1) && !n.IsHiddenClosure() { + // This node is the root of a strongly connected component. + + // The original min passed to visitcodelist was v.nodeID[n]+1. + // If visitcodelist found its way back to v.nodeID[n], then this + // block is a set of mutually recursive functions. + // Otherwise it's just a lone function that does not recurse. + recursive := min == id + + // Remove connected component from stack. + // Mark walkgen so that future visits return a large number + // so as not to affect the caller's min. + + var i int + for i = len(v.stack) - 1; i >= 0; i-- { + x := v.stack[i] + v.nodeID[x] = ^uint32(0) + if x == n { + break + } + } + block := v.stack[i:] + // Run escape analysis on this set of functions. + v.stack = v.stack[:i] + v.analyze(block, recursive) + } + + return min +} diff --git a/src/cmd/compile/internal/ir/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go new file mode 100644 index 0000000..72b6320 --- /dev/null +++ b/src/cmd/compile/internal/ir/sizeof_test.go @@ -0,0 +1,37 @@ +// Copyright 2016 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 ir + +import ( + "reflect" + "testing" + "unsafe" +) + +// Assert that the size of important structures do not change unexpectedly. + +func TestSizeof(t *testing.T) { + const _64bit = unsafe.Sizeof(uintptr(0)) == 8 + + var tests = []struct { + val interface{} // type as a value + _32bit uintptr // size on 32bit platforms + _64bit uintptr // size on 64bit platforms + }{ + {Func{}, 196, 336}, + {Name{}, 112, 200}, + } + + for _, tt := range tests { + want := tt._32bit + if _64bit { + want = tt._64bit + } + got := reflect.TypeOf(tt.val).Size() + if want != got { + t.Errorf("unsafe.Sizeof(%T) = %d, want %d", tt.val, got, want) + } + } +} diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go new file mode 100644 index 0000000..80bd205 --- /dev/null +++ b/src/cmd/compile/internal/ir/stmt.go @@ -0,0 +1,411 @@ +// Copyright 2020 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 ir + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// A Decl is a declaration of a const, type, or var. (A declared func is a Func.) +type Decl struct { + miniNode + X *Name // the thing being declared +} + +func NewDecl(pos src.XPos, op Op, x *Name) *Decl { + n := &Decl{X: x} + n.pos = pos + switch op { + default: + panic("invalid Decl op " + op.String()) + case ODCL, ODCLCONST, ODCLTYPE: + n.op = op + } + return n +} + +func (*Decl) isStmt() {} + +// A Stmt is a Node that can appear as a statement. +// This includes statement-like expressions such as f(). +// +// (It's possible it should include <-c, but that would require +// splitting ORECV out of UnaryExpr, which hasn't yet been +// necessary. Maybe instead we will introduce ExprStmt at +// some point.) +type Stmt interface { + Node + isStmt() +} + +// A miniStmt is a miniNode with extra fields common to statements. +type miniStmt struct { + miniNode + init Nodes +} + +func (*miniStmt) isStmt() {} + +func (n *miniStmt) Init() Nodes { return n.init } +func (n *miniStmt) SetInit(x Nodes) { n.init = x } +func (n *miniStmt) PtrInit() *Nodes { return &n.init } + +// An AssignListStmt is an assignment statement with +// more than one item on at least one side: Lhs = Rhs. +// If Def is true, the assignment is a :=. +type AssignListStmt struct { + miniStmt + Lhs Nodes + Def bool + Rhs Nodes +} + +func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt { + n := &AssignListStmt{} + n.pos = pos + n.SetOp(op) + n.Lhs = lhs + n.Rhs = rhs + return n +} + +func (n *AssignListStmt) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2: + n.op = op + } +} + +// An AssignStmt is a simple assignment statement: X = Y. +// If Def is true, the assignment is a :=. +type AssignStmt struct { + miniStmt + X Node + Def bool + Y Node +} + +func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt { + n := &AssignStmt{X: x, Y: y} + n.pos = pos + n.op = OAS + return n +} + +func (n *AssignStmt) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case OAS: + n.op = op + } +} + +// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y. +type AssignOpStmt struct { + miniStmt + X Node + AsOp Op // OADD etc + Y Node + IncDec bool // actually ++ or -- +} + +func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt { + n := &AssignOpStmt{AsOp: asOp, X: x, Y: y} + n.pos = pos + n.op = OASOP + return n +} + +// A BlockStmt is a block: { List }. +type BlockStmt struct { + miniStmt + List Nodes +} + +func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt { + n := &BlockStmt{} + n.pos = pos + if !pos.IsKnown() { + n.pos = base.Pos + if len(list) > 0 { + n.pos = list[0].Pos() + } + } + n.op = OBLOCK + n.List = list + return n +} + +// A BranchStmt is a break, continue, fallthrough, or goto statement. +type BranchStmt struct { + miniStmt + Label *types.Sym // label if present +} + +func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt { + switch op { + case OBREAK, OCONTINUE, OFALL, OGOTO: + // ok + default: + panic("NewBranch " + op.String()) + } + n := &BranchStmt{Label: label} + n.pos = pos + n.op = op + return n +} + +func (n *BranchStmt) Sym() *types.Sym { return n.Label } + +// A CaseClause is a case statement in a switch or select: case List: Body. +type CaseClause struct { + miniStmt + Var *Name // declared variable for this case in type switch + List Nodes // list of expressions for switch, early select + Body Nodes +} + +func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause { + n := &CaseClause{List: list, Body: body} + n.pos = pos + n.op = OCASE + return n +} + +type CommClause struct { + miniStmt + Comm Node // communication case + Body Nodes +} + +func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause { + n := &CommClause{Comm: comm, Body: body} + n.pos = pos + n.op = OCASE + return n +} + +// A ForStmt is a non-range for loop: for Init; Cond; Post { Body } +// Op can be OFOR or OFORUNTIL (!Cond). +type ForStmt struct { + miniStmt + Label *types.Sym + Cond Node + Late Nodes + Post Node + Body Nodes + HasBreak bool +} + +func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt { + n := &ForStmt{Cond: cond, Post: post} + n.pos = pos + n.op = OFOR + if init != nil { + n.init = []Node{init} + } + n.Body = body + return n +} + +func (n *ForStmt) SetOp(op Op) { + if op != OFOR && op != OFORUNTIL { + panic(n.no("SetOp " + op.String())) + } + n.op = op +} + +// A GoDeferStmt is a go or defer statement: go Call / defer Call. +// +// The two opcodes use a single syntax because the implementations +// are very similar: both are concerned with saving Call and running it +// in a different context (a separate goroutine or a later time). +type GoDeferStmt struct { + miniStmt + Call Node +} + +func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt { + n := &GoDeferStmt{Call: call} + n.pos = pos + switch op { + case ODEFER, OGO: + n.op = op + default: + panic("NewGoDeferStmt " + op.String()) + } + return n +} + +// An IfStmt is a return statement: if Init; Cond { Body } else { Else }. +type IfStmt struct { + miniStmt + Cond Node + Body Nodes + Else Nodes + Likely bool // code layout hint +} + +func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt { + n := &IfStmt{Cond: cond} + n.pos = pos + n.op = OIF + n.Body = body + n.Else = els + return n +} + +// An InlineMarkStmt is a marker placed just before an inlined body. +type InlineMarkStmt struct { + miniStmt + Index int64 +} + +func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt { + n := &InlineMarkStmt{Index: index} + n.pos = pos + n.op = OINLMARK + return n +} + +func (n *InlineMarkStmt) Offset() int64 { return n.Index } +func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x } + +// A LabelStmt is a label statement (just the label, not including the statement it labels). +type LabelStmt struct { + miniStmt + Label *types.Sym // "Label:" +} + +func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt { + n := &LabelStmt{Label: label} + n.pos = pos + n.op = OLABEL + return n +} + +func (n *LabelStmt) Sym() *types.Sym { return n.Label } + +// A RangeStmt is a range loop: for Key, Value = range X { Body } +type RangeStmt struct { + miniStmt + Label *types.Sym + Def bool + X Node + Key Node + Value Node + Body Nodes + HasBreak bool + Prealloc *Name +} + +func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt { + n := &RangeStmt{X: x, Key: key, Value: value} + n.pos = pos + n.op = ORANGE + n.Body = body + return n +} + +// A ReturnStmt is a return statement. +type ReturnStmt struct { + miniStmt + origNode // for typecheckargs rewrite + Results Nodes // return list +} + +func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt { + n := &ReturnStmt{} + n.pos = pos + n.op = ORETURN + n.orig = n + n.Results = results + return n +} + +// A SelectStmt is a block: { Cases }. +type SelectStmt struct { + miniStmt + Label *types.Sym + Cases []*CommClause + HasBreak bool + + // TODO(rsc): Instead of recording here, replace with a block? + Compiled Nodes // compiled form, after walkSelect +} + +func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt { + n := &SelectStmt{Cases: cases} + n.pos = pos + n.op = OSELECT + return n +} + +// A SendStmt is a send statement: X <- Y. +type SendStmt struct { + miniStmt + Chan Node + Value Node +} + +func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt { + n := &SendStmt{Chan: ch, Value: value} + n.pos = pos + n.op = OSEND + return n +} + +// A SwitchStmt is a switch statement: switch Init; Tag { Cases }. +type SwitchStmt struct { + miniStmt + Tag Node + Cases []*CaseClause + Label *types.Sym + HasBreak bool + + // TODO(rsc): Instead of recording here, replace with a block? + Compiled Nodes // compiled form, after walkSwitch +} + +func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt { + n := &SwitchStmt{Tag: tag, Cases: cases} + n.pos = pos + n.op = OSWITCH + return n +} + +// A TailCallStmt is a tail call statement, which is used for back-end +// code generation to jump directly to another function entirely. +type TailCallStmt struct { + miniStmt + Call *CallExpr // the underlying call +} + +func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt { + n := &TailCallStmt{Call: call} + n.pos = pos + n.op = OTAILCALL + return n +} + +// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch. +type TypeSwitchGuard struct { + miniNode + Tag *Ident + X Node + Used bool +} + +func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard { + n := &TypeSwitchGuard{Tag: tag, X: x} + n.pos = pos + n.op = OTYPESW + return n +} diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go new file mode 100644 index 0000000..148edb2 --- /dev/null +++ b/src/cmd/compile/internal/ir/symtab.go @@ -0,0 +1,75 @@ +// 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 ir + +import ( + "cmd/compile/internal/types" + "cmd/internal/obj" +) + +// Syms holds known symbols. +var Syms struct { + AssertE2I *obj.LSym + AssertE2I2 *obj.LSym + AssertI2I *obj.LSym + AssertI2I2 *obj.LSym + Asanread *obj.LSym + Asanwrite *obj.LSym + CheckPtrAlignment *obj.LSym + Deferproc *obj.LSym + DeferprocStack *obj.LSym + Deferreturn *obj.LSym + Duffcopy *obj.LSym + Duffzero *obj.LSym + GCWriteBarrier *obj.LSym + Goschedguarded *obj.LSym + Growslice *obj.LSym + Memmove *obj.LSym + Msanread *obj.LSym + Msanwrite *obj.LSym + Msanmove *obj.LSym + Newobject *obj.LSym + Newproc *obj.LSym + Panicdivide *obj.LSym + Panicshift *obj.LSym + PanicdottypeE *obj.LSym + PanicdottypeI *obj.LSym + Panicnildottype *obj.LSym + Panicoverflow *obj.LSym + Raceread *obj.LSym + Racereadrange *obj.LSym + Racewrite *obj.LSym + Racewriterange *obj.LSym + // Wasm + SigPanic *obj.LSym + Staticuint64s *obj.LSym + Typedmemclr *obj.LSym + Typedmemmove *obj.LSym + Udiv *obj.LSym + WriteBarrier *obj.LSym + Zerobase *obj.LSym + ARM64HasATOMICS *obj.LSym + ARMHasVFPv4 *obj.LSym + X86HasFMA *obj.LSym + X86HasPOPCNT *obj.LSym + X86HasSSE41 *obj.LSym + // Wasm + WasmDiv *obj.LSym + // Wasm + WasmMove *obj.LSym + // Wasm + WasmZero *obj.LSym + // Wasm + WasmTruncS *obj.LSym + // Wasm + WasmTruncU *obj.LSym +} + +// Pkgs holds known packages. +var Pkgs struct { + Go *types.Pkg + Itab *types.Pkg + Runtime *types.Pkg +} diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go new file mode 100644 index 0000000..63dd673 --- /dev/null +++ b/src/cmd/compile/internal/ir/type.go @@ -0,0 +1,335 @@ +// 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 ir + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" + "fmt" +) + +// Nodes that represent the syntax of a type before type-checking. +// After type-checking, they serve only as shells around a *types.Type. +// Calling TypeNode converts a *types.Type to a Node shell. + +// An Ntype is a Node that syntactically looks like a type. +// It can be the raw syntax for a type before typechecking, +// or it can be an OTYPE with Type() set to a *types.Type. +// Note that syntax doesn't guarantee it's a type: an expression +// like *fmt is an Ntype (we don't know whether names are types yet), +// but at least 1+1 is not an Ntype. +type Ntype interface { + Node + CanBeNtype() +} + +// A miniType is a minimal type syntax Node implementation, +// to be embedded as the first field in a larger node implementation. +type miniType struct { + miniNode + typ *types.Type +} + +func (*miniType) CanBeNtype() {} + +func (n *miniType) Type() *types.Type { return n.typ } + +// setOTYPE changes n to be an OTYPE node returning t. +// Rewriting the node in place this way should not be strictly +// necessary (we should be able to update the uses with +// proper OTYPE nodes), but it's mostly harmless and easy +// to keep doing for now. +// +// setOTYPE also records t.Nod = self if t.Nod is not already set. +// (Some types are shared by multiple OTYPE nodes, so only +// the first such node is used as t.Nod.) +func (n *miniType) setOTYPE(t *types.Type, self Ntype) { + if n.typ != nil { + panic(n.op.String() + " SetType: type already set") + } + n.op = OTYPE + n.typ = t + t.SetNod(self) +} + +func (n *miniType) Sym() *types.Sym { return nil } // for Format OTYPE +func (n *miniType) Implicit() bool { return false } // for Format OTYPE + +// A ChanType represents a chan Elem syntax with the direction Dir. +type ChanType struct { + miniType + Elem Ntype + Dir types.ChanDir +} + +func NewChanType(pos src.XPos, elem Ntype, dir types.ChanDir) *ChanType { + n := &ChanType{Elem: elem, Dir: dir} + n.op = OTCHAN + n.pos = pos + return n +} + +func (n *ChanType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Elem = nil +} + +// A MapType represents a map[Key]Value type syntax. +type MapType struct { + miniType + Key Ntype + Elem Ntype +} + +func NewMapType(pos src.XPos, key, elem Ntype) *MapType { + n := &MapType{Key: key, Elem: elem} + n.op = OTMAP + n.pos = pos + return n +} + +func (n *MapType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Key = nil + n.Elem = nil +} + +// A StructType represents a struct { ... } type syntax. +type StructType struct { + miniType + Fields []*Field +} + +func NewStructType(pos src.XPos, fields []*Field) *StructType { + n := &StructType{Fields: fields} + n.op = OTSTRUCT + n.pos = pos + return n +} + +func (n *StructType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Fields = nil +} + +// An InterfaceType represents a struct { ... } type syntax. +type InterfaceType struct { + miniType + Methods []*Field +} + +func NewInterfaceType(pos src.XPos, methods []*Field) *InterfaceType { + n := &InterfaceType{Methods: methods} + n.op = OTINTER + n.pos = pos + return n +} + +func (n *InterfaceType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Methods = nil +} + +// A FuncType represents a func(Args) Results type syntax. +type FuncType struct { + miniType + Recv *Field + Params []*Field + Results []*Field +} + +func NewFuncType(pos src.XPos, rcvr *Field, args, results []*Field) *FuncType { + n := &FuncType{Recv: rcvr, Params: args, Results: results} + n.op = OTFUNC + n.pos = pos + return n +} + +func (n *FuncType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Recv = nil + n.Params = nil + n.Results = nil +} + +// A Field is a declared struct field, interface method, or function argument. +// It is not a Node. +type Field struct { + Pos src.XPos + Sym *types.Sym + Ntype Ntype + Type *types.Type + Embedded bool + IsDDD bool + Note string + Decl *Name +} + +func NewField(pos src.XPos, sym *types.Sym, ntyp Ntype, typ *types.Type) *Field { + return &Field{Pos: pos, Sym: sym, Ntype: ntyp, Type: typ} +} + +func (f *Field) String() string { + var typ string + if f.Type != nil { + typ = fmt.Sprint(f.Type) + } else { + typ = fmt.Sprint(f.Ntype) + } + if f.Sym != nil { + return fmt.Sprintf("%v %v", f.Sym, typ) + } + return typ +} + +// TODO(mdempsky): Make Field a Node again so these can be generated? +// Fields are Nodes in go/ast and cmd/compile/internal/syntax. + +func copyField(f *Field) *Field { + if f == nil { + return nil + } + c := *f + return &c +} +func doField(f *Field, do func(Node) bool) bool { + if f == nil { + return false + } + if f.Decl != nil && do(f.Decl) { + return true + } + if f.Ntype != nil && do(f.Ntype) { + return true + } + return false +} +func editField(f *Field, edit func(Node) Node) { + if f == nil { + return + } + if f.Decl != nil { + f.Decl = edit(f.Decl).(*Name) + } + if f.Ntype != nil { + f.Ntype = edit(f.Ntype).(Ntype) + } +} + +func copyFields(list []*Field) []*Field { + out := make([]*Field, len(list)) + for i, f := range list { + out[i] = copyField(f) + } + return out +} +func doFields(list []*Field, do func(Node) bool) bool { + for _, x := range list { + if doField(x, do) { + return true + } + } + return false +} +func editFields(list []*Field, edit func(Node) Node) { + for _, f := range list { + editField(f, edit) + } +} + +// A SliceType represents a []Elem type syntax. +// If DDD is true, it's the ...Elem at the end of a function list. +type SliceType struct { + miniType + Elem Ntype + DDD bool +} + +func NewSliceType(pos src.XPos, elem Ntype) *SliceType { + n := &SliceType{Elem: elem} + n.op = OTSLICE + n.pos = pos + return n +} + +func (n *SliceType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Elem = nil +} + +// An ArrayType represents a [Len]Elem type syntax. +// If Len is nil, the type is a [...]Elem in an array literal. +type ArrayType struct { + miniType + Len Node + Elem Ntype +} + +func NewArrayType(pos src.XPos, len Node, elem Ntype) *ArrayType { + n := &ArrayType{Len: len, Elem: elem} + n.op = OTARRAY + n.pos = pos + return n +} + +func (n *ArrayType) SetOTYPE(t *types.Type) { + n.setOTYPE(t, n) + n.Len = nil + n.Elem = nil +} + +// A typeNode is a Node wrapper for type t. +type typeNode struct { + miniNode + typ *types.Type +} + +func newTypeNode(pos src.XPos, typ *types.Type) *typeNode { + n := &typeNode{typ: typ} + n.pos = pos + n.op = OTYPE + return n +} + +func (n *typeNode) Type() *types.Type { return n.typ } +func (n *typeNode) Sym() *types.Sym { return n.typ.Sym() } +func (n *typeNode) CanBeNtype() {} + +// TypeNode returns the Node representing the type t. +func TypeNode(t *types.Type) Ntype { + return TypeNodeAt(src.NoXPos, t) +} + +// TypeNodeAt is like TypeNode, but allows specifying the position +// information if a new OTYPE needs to be constructed. +// +// Deprecated: Use TypeNode instead. For typical use, the position for +// an anonymous OTYPE node should not matter. However, TypeNodeAt is +// available for use with toolstash -cmp to refactor existing code +// that is sensitive to OTYPE position. +func TypeNodeAt(pos src.XPos, t *types.Type) Ntype { + if n := t.Obj(); n != nil { + if n.Type() != t { + base.Fatalf("type skew: %v has type %v, but expected %v", n, n.Type(), t) + } + return n.(Ntype) + } + return newTypeNode(pos, t) +} + +// A DynamicType represents the target type in a type switch. +type DynamicType struct { + miniExpr + X Node // a *runtime._type for the targeted type + ITab Node // for type switches from nonempty interfaces to non-interfaces, this is the itab for that pair. +} + +func NewDynamicType(pos src.XPos, x Node) *DynamicType { + n := &DynamicType{X: x} + n.pos = pos + n.op = ODYNAMICTYPE + return n +} diff --git a/src/cmd/compile/internal/ir/val.go b/src/cmd/compile/internal/ir/val.go new file mode 100644 index 0000000..bfe7d2b --- /dev/null +++ b/src/cmd/compile/internal/ir/val.go @@ -0,0 +1,171 @@ +// 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 ir + +import ( + "go/constant" + "math" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" +) + +func ConstType(n Node) constant.Kind { + if n == nil || n.Op() != OLITERAL { + return constant.Unknown + } + return n.Val().Kind() +} + +// ConstValue returns the constant value stored in n as an interface{}. +// It returns int64s for ints and runes, float64s for floats, +// and complex128s for complex values. +func ConstValue(n Node) interface{} { + switch v := n.Val(); v.Kind() { + default: + base.Fatalf("unexpected constant: %v", v) + panic("unreachable") + case constant.Bool: + return constant.BoolVal(v) + case constant.String: + return constant.StringVal(v) + case constant.Int: + return IntVal(n.Type(), v) + case constant.Float: + return Float64Val(v) + case constant.Complex: + return complex(Float64Val(constant.Real(v)), Float64Val(constant.Imag(v))) + } +} + +// IntVal returns v converted to int64. +// Note: if t is uint64, very large values will be converted to negative int64. +func IntVal(t *types.Type, v constant.Value) int64 { + if t.IsUnsigned() { + if x, ok := constant.Uint64Val(v); ok { + return int64(x) + } + } else { + if x, ok := constant.Int64Val(v); ok { + return x + } + } + base.Fatalf("%v out of range for %v", v, t) + panic("unreachable") +} + +func Float64Val(v constant.Value) float64 { + if x, _ := constant.Float64Val(v); !math.IsInf(x, 0) { + return x + 0 // avoid -0 (should not be needed, but be conservative) + } + base.Fatalf("bad float64 value: %v", v) + panic("unreachable") +} + +func AssertValidTypeForConst(t *types.Type, v constant.Value) { + if !ValidTypeForConst(t, v) { + base.Fatalf("%v (%v) does not represent %v (%v)", t, t.Kind(), v, v.Kind()) + } +} + +func ValidTypeForConst(t *types.Type, v constant.Value) bool { + switch v.Kind() { + case constant.Unknown: + return OKForConst[t.Kind()] + case constant.Bool: + return t.IsBoolean() + case constant.String: + return t.IsString() + case constant.Int: + return t.IsInteger() + case constant.Float: + return t.IsFloat() + case constant.Complex: + return t.IsComplex() + } + + base.Fatalf("unexpected constant kind: %v", v) + panic("unreachable") +} + +// NewLiteral returns a new untyped constant with value v. +func NewLiteral(v constant.Value) Node { + return NewBasicLit(base.Pos, v) +} + +func idealType(ct constant.Kind) *types.Type { + switch ct { + case constant.String: + return types.UntypedString + case constant.Bool: + return types.UntypedBool + case constant.Int: + return types.UntypedInt + case constant.Float: + return types.UntypedFloat + case constant.Complex: + return types.UntypedComplex + } + base.Fatalf("unexpected Ctype: %v", ct) + return nil +} + +var OKForConst [types.NTYPE]bool + +// CanInt64 reports whether it is safe to call Int64Val() on n. +func CanInt64(n Node) bool { + if !IsConst(n, constant.Int) { + return false + } + + // if the value inside n cannot be represented as an int64, the + // return value of Int64 is undefined + _, ok := constant.Int64Val(n.Val()) + return ok +} + +// Int64Val returns n as an int64. +// n must be an integer or rune constant. +func Int64Val(n Node) int64 { + if !IsConst(n, constant.Int) { + base.Fatalf("Int64Val(%v)", n) + } + x, ok := constant.Int64Val(n.Val()) + if !ok { + base.Fatalf("Int64Val(%v)", n) + } + return x +} + +// Uint64Val returns n as an uint64. +// n must be an integer or rune constant. +func Uint64Val(n Node) uint64 { + if !IsConst(n, constant.Int) { + base.Fatalf("Uint64Val(%v)", n) + } + x, ok := constant.Uint64Val(n.Val()) + if !ok { + base.Fatalf("Uint64Val(%v)", n) + } + return x +} + +// BoolVal returns n as a bool. +// n must be a boolean constant. +func BoolVal(n Node) bool { + if !IsConst(n, constant.Bool) { + base.Fatalf("BoolVal(%v)", n) + } + return constant.BoolVal(n.Val()) +} + +// StringVal returns the value of a literal string Node as a string. +// n must be a string constant. +func StringVal(n Node) string { + if !IsConst(n, constant.String) { + base.Fatalf("StringVal(%v)", n) + } + return constant.StringVal(n.Val()) +} diff --git a/src/cmd/compile/internal/ir/visit.go b/src/cmd/compile/internal/ir/visit.go new file mode 100644 index 0000000..e4aeae3 --- /dev/null +++ b/src/cmd/compile/internal/ir/visit.go @@ -0,0 +1,186 @@ +// Copyright 2020 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. + +// IR visitors for walking the IR tree. +// +// The lowest level helpers are DoChildren and EditChildren, which +// nodes help implement and provide control over whether and when +// recursion happens during the walk of the IR. +// +// Although these are both useful directly, two simpler patterns +// are fairly common and also provided: Visit and Any. + +package ir + +// DoChildren calls do(x) on each of n's non-nil child nodes x. +// If any call returns true, DoChildren stops and returns true. +// Otherwise, DoChildren returns false. +// +// Note that DoChildren(n, do) only calls do(x) for n's immediate children. +// If x's children should be processed, then do(x) must call DoChildren(x, do). +// +// DoChildren allows constructing general traversals of the IR graph +// that can stop early if needed. The most general usage is: +// +// var do func(ir.Node) bool +// do = func(x ir.Node) bool { +// ... processing BEFORE visiting children ... +// if ... should visit children ... { +// ir.DoChildren(x, do) +// ... processing AFTER visiting children ... +// } +// if ... should stop parent DoChildren call from visiting siblings ... { +// return true +// } +// return false +// } +// do(root) +// +// Since DoChildren does not return true itself, if the do function +// never wants to stop the traversal, it can assume that DoChildren +// itself will always return false, simplifying to: +// +// var do func(ir.Node) bool +// do = func(x ir.Node) bool { +// ... processing BEFORE visiting children ... +// if ... should visit children ... { +// ir.DoChildren(x, do) +// } +// ... processing AFTER visiting children ... +// return false +// } +// do(root) +// +// The Visit function illustrates a further simplification of the pattern, +// only processing before visiting children and never stopping: +// +// func Visit(n ir.Node, visit func(ir.Node)) { +// if n == nil { +// return +// } +// var do func(ir.Node) bool +// do = func(x ir.Node) bool { +// visit(x) +// return ir.DoChildren(x, do) +// } +// do(n) +// } +// +// The Any function illustrates a different simplification of the pattern, +// visiting each node and then its children, recursively, until finding +// a node x for which cond(x) returns true, at which point the entire +// traversal stops and returns true. +// +// func Any(n ir.Node, cond(ir.Node) bool) bool { +// if n == nil { +// return false +// } +// var do func(ir.Node) bool +// do = func(x ir.Node) bool { +// return cond(x) || ir.DoChildren(x, do) +// } +// return do(n) +// } +// +// Visit and Any are presented above as examples of how to use +// DoChildren effectively, but of course, usage that fits within the +// simplifications captured by Visit or Any will be best served +// by directly calling the ones provided by this package. +func DoChildren(n Node, do func(Node) bool) bool { + if n == nil { + return false + } + return n.doChildren(do) +} + +// Visit visits each non-nil node x in the IR tree rooted at n +// in a depth-first preorder traversal, calling visit on each node visited. +func Visit(n Node, visit func(Node)) { + if n == nil { + return + } + var do func(Node) bool + do = func(x Node) bool { + visit(x) + return DoChildren(x, do) + } + do(n) +} + +// VisitList calls Visit(x, visit) for each node x in the list. +func VisitList(list Nodes, visit func(Node)) { + for _, x := range list { + Visit(x, visit) + } +} + +// Any looks for a non-nil node x in the IR tree rooted at n +// for which cond(x) returns true. +// Any considers nodes in a depth-first, preorder traversal. +// When Any finds a node x such that cond(x) is true, +// Any ends the traversal and returns true immediately. +// Otherwise Any returns false after completing the entire traversal. +func Any(n Node, cond func(Node) bool) bool { + if n == nil { + return false + } + var do func(Node) bool + do = func(x Node) bool { + return cond(x) || DoChildren(x, do) + } + return do(n) +} + +// AnyList calls Any(x, cond) for each node x in the list, in order. +// If any call returns true, AnyList stops and returns true. +// Otherwise, AnyList returns false after calling Any(x, cond) +// for every x in the list. +func AnyList(list Nodes, cond func(Node) bool) bool { + for _, x := range list { + if Any(x, cond) { + return true + } + } + return false +} + +// EditChildren edits the child nodes of n, replacing each child x with edit(x). +// +// Note that EditChildren(n, edit) only calls edit(x) for n's immediate children. +// If x's children should be processed, then edit(x) must call EditChildren(x, edit). +// +// EditChildren allows constructing general editing passes of the IR graph. +// The most general usage is: +// +// var edit func(ir.Node) ir.Node +// edit = func(x ir.Node) ir.Node { +// ... processing BEFORE editing children ... +// if ... should edit children ... { +// EditChildren(x, edit) +// ... processing AFTER editing children ... +// } +// ... return x ... +// } +// n = edit(n) +// +// EditChildren edits the node in place. To edit a copy, call Copy first. +// As an example, a simple deep copy implementation would be: +// +// func deepCopy(n ir.Node) ir.Node { +// var edit func(ir.Node) ir.Node +// edit = func(x ir.Node) ir.Node { +// x = ir.Copy(x) +// ir.EditChildren(x, edit) +// return x +// } +// return edit(n) +// } +// +// Of course, in this case it is better to call ir.DeepCopy than to build one anew. +func EditChildren(n Node, edit func(Node) Node) { + if n == nil { + return + } + n.editChildren(edit) +} |