1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package escape
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/compile/internal/ir"
10	"fmt"
11)
12
13// stmt evaluates a single Go statement.
14func (e *escape) stmt(n ir.Node) {
15	if n == nil {
16		return
17	}
18
19	lno := ir.SetPos(n)
20	defer func() {
21		base.Pos = lno
22	}()
23
24	if base.Flag.LowerM > 2 {
25		fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n)
26	}
27
28	e.stmts(n.Init())
29
30	switch n.Op() {
31	default:
32		base.Fatalf("unexpected stmt: %v", n)
33
34	case ir.OFALL, ir.OINLMARK:
35		// nop
36
37	case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
38		// TODO(mdempsky): Handle dead code?
39
40	case ir.OBLOCK:
41		n := n.(*ir.BlockStmt)
42		e.stmts(n.List)
43
44	case ir.ODCL:
45		// Record loop depth at declaration.
46		n := n.(*ir.Decl)
47		if !ir.IsBlank(n.X) {
48			e.dcl(n.X)
49		}
50
51	case ir.OLABEL:
52		n := n.(*ir.LabelStmt)
53		if n.Label.IsBlank() {
54			break
55		}
56		switch e.labels[n.Label] {
57		case nonlooping:
58			if base.Flag.LowerM > 2 {
59				fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
60			}
61		case looping:
62			if base.Flag.LowerM > 2 {
63				fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n)
64			}
65			e.loopDepth++
66		default:
67			base.Fatalf("label %v missing tag", n.Label)
68		}
69		delete(e.labels, n.Label)
70
71	case ir.OIF:
72		n := n.(*ir.IfStmt)
73		e.discard(n.Cond)
74		e.block(n.Body)
75		e.block(n.Else)
76
77	case ir.OCHECKNIL:
78		n := n.(*ir.UnaryExpr)
79		e.discard(n.X)
80
81	case ir.OFOR:
82		n := n.(*ir.ForStmt)
83		base.Assert(!n.DistinctVars) // Should all be rewritten before escape analysis
84		e.loopDepth++
85		e.discard(n.Cond)
86		e.stmt(n.Post)
87		e.block(n.Body)
88		e.loopDepth--
89
90	case ir.ORANGE:
91		// for Key, Value = range X { Body }
92		n := n.(*ir.RangeStmt)
93		base.Assert(!n.DistinctVars) // Should all be rewritten before escape analysis
94
95		// X is evaluated outside the loop and persists until the loop
96		// terminates.
97		tmp := e.newLoc(nil, true)
98		e.expr(tmp.asHole(), n.X)
99
100		e.loopDepth++
101		ks := e.addrs([]ir.Node{n.Key, n.Value})
102		if n.X.Type().IsArray() {
103			e.flow(ks[1].note(n, "range"), tmp)
104		} else {
105			e.flow(ks[1].deref(n, "range-deref"), tmp)
106		}
107		e.reassigned(ks, n)
108
109		e.block(n.Body)
110		e.loopDepth--
111
112	case ir.OSWITCH:
113		n := n.(*ir.SwitchStmt)
114
115		if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
116			var ks []hole
117			if guard.Tag != nil {
118				for _, cas := range n.Cases {
119					cv := cas.Var
120					k := e.dcl(cv) // type switch variables have no ODCL.
121					if cv.Type().HasPointers() {
122						ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
123					}
124				}
125			}
126			e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
127		} else {
128			e.discard(n.Tag)
129		}
130
131		for _, cas := range n.Cases {
132			e.discards(cas.List)
133			e.block(cas.Body)
134		}
135
136	case ir.OSELECT:
137		n := n.(*ir.SelectStmt)
138		for _, cas := range n.Cases {
139			e.stmt(cas.Comm)
140			e.block(cas.Body)
141		}
142	case ir.ORECV:
143		// TODO(mdempsky): Consider e.discard(n.Left).
144		n := n.(*ir.UnaryExpr)
145		e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
146	case ir.OSEND:
147		n := n.(*ir.SendStmt)
148		e.discard(n.Chan)
149		e.assignHeap(n.Value, "send", n)
150
151	case ir.OAS:
152		n := n.(*ir.AssignStmt)
153		e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
154	case ir.OASOP:
155		n := n.(*ir.AssignOpStmt)
156		// TODO(mdempsky): Worry about OLSH/ORSH?
157		e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
158	case ir.OAS2:
159		n := n.(*ir.AssignListStmt)
160		e.assignList(n.Lhs, n.Rhs, "assign-pair", n)
161
162	case ir.OAS2DOTTYPE: // v, ok = x.(type)
163		n := n.(*ir.AssignListStmt)
164		e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
165	case ir.OAS2MAPR: // v, ok = m[k]
166		n := n.(*ir.AssignListStmt)
167		e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
168	case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch
169		n := n.(*ir.AssignListStmt)
170		e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)
171
172	case ir.OAS2FUNC:
173		n := n.(*ir.AssignListStmt)
174		e.stmts(n.Rhs[0].Init())
175		ks := e.addrs(n.Lhs)
176		e.call(ks, n.Rhs[0])
177		e.reassigned(ks, n)
178	case ir.ORETURN:
179		n := n.(*ir.ReturnStmt)
180		results := e.curfn.Type().Results()
181		dsts := make([]ir.Node, len(results))
182		for i, res := range results {
183			dsts[i] = res.Nname.(*ir.Name)
184		}
185		e.assignList(dsts, n.Results, "return", n)
186	case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
187		e.call(nil, n)
188	case ir.OGO, ir.ODEFER:
189		n := n.(*ir.GoDeferStmt)
190		e.goDeferStmt(n)
191
192	case ir.OTAILCALL:
193		n := n.(*ir.TailCallStmt)
194		e.call(nil, n.Call)
195	}
196}
197
198func (e *escape) stmts(l ir.Nodes) {
199	for _, n := range l {
200		e.stmt(n)
201	}
202}
203
204// block is like stmts, but preserves loopDepth.
205func (e *escape) block(l ir.Nodes) {
206	old := e.loopDepth
207	e.stmts(l)
208	e.loopDepth = old
209}
210
211func (e *escape) dcl(n *ir.Name) hole {
212	if n.Curfn != e.curfn || n.IsClosureVar() {
213		base.Fatalf("bad declaration of %v", n)
214	}
215	loc := e.oldLoc(n)
216	loc.loopDepth = e.loopDepth
217	return loc.asHole()
218}
219