summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/ir
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/ir')
-rw-r--r--src/cmd/compile/internal/ir/bitset.go71
-rw-r--r--src/cmd/compile/internal/ir/cfg.go26
-rw-r--r--src/cmd/compile/internal/ir/class_string.go30
-rw-r--r--src/cmd/compile/internal/ir/const.go99
-rw-r--r--src/cmd/compile/internal/ir/copy.go102
-rw-r--r--src/cmd/compile/internal/ir/dump.go272
-rw-r--r--src/cmd/compile/internal/ir/expr.go1147
-rw-r--r--src/cmd/compile/internal/ir/fmt.go1359
-rw-r--r--src/cmd/compile/internal/ir/func.go438
-rw-r--r--src/cmd/compile/internal/ir/ir.go5
-rw-r--r--src/cmd/compile/internal/ir/mini.go92
-rw-r--r--src/cmd/compile/internal/ir/mknode.go229
-rw-r--r--src/cmd/compile/internal/ir/name.go557
-rw-r--r--src/cmd/compile/internal/ir/node.go620
-rw-r--r--src/cmd/compile/internal/ir/node_gen.go1524
-rw-r--r--src/cmd/compile/internal/ir/op_string.go184
-rw-r--r--src/cmd/compile/internal/ir/package.go35
-rw-r--r--src/cmd/compile/internal/ir/scc.go131
-rw-r--r--src/cmd/compile/internal/ir/sizeof_test.go37
-rw-r--r--src/cmd/compile/internal/ir/stmt.go411
-rw-r--r--src/cmd/compile/internal/ir/symtab.go75
-rw-r--r--src/cmd/compile/internal/ir/type.go335
-rw-r--r--src/cmd/compile/internal/ir/val.go171
-rw-r--r--src/cmd/compile/internal/ir/visit.go186
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)
+}