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)
11
12// addr evaluates an addressable expression n and returns a hole
13// that represents storing into the represented location.
14func (e *escape) addr(n ir.Node) hole {
15	if n == nil || ir.IsBlank(n) {
16		// Can happen in select case, range, maybe others.
17		return e.discardHole()
18	}
19
20	k := e.heapHole()
21
22	switch n.Op() {
23	default:
24		base.Fatalf("unexpected addr: %v", n)
25	case ir.ONAME:
26		n := n.(*ir.Name)
27		if n.Class == ir.PEXTERN {
28			break
29		}
30		k = e.oldLoc(n).asHole()
31	case ir.OLINKSYMOFFSET:
32		break
33	case ir.ODOT:
34		n := n.(*ir.SelectorExpr)
35		k = e.addr(n.X)
36	case ir.OINDEX:
37		n := n.(*ir.IndexExpr)
38		e.discard(n.Index)
39		if n.X.Type().IsArray() {
40			k = e.addr(n.X)
41		} else {
42			e.mutate(n.X)
43		}
44	case ir.ODEREF:
45		n := n.(*ir.StarExpr)
46		e.mutate(n.X)
47	case ir.ODOTPTR:
48		n := n.(*ir.SelectorExpr)
49		e.mutate(n.X)
50	case ir.OINDEXMAP:
51		n := n.(*ir.IndexExpr)
52		e.discard(n.X)
53		e.assignHeap(n.Index, "key of map put", n)
54	}
55
56	return k
57}
58
59func (e *escape) mutate(n ir.Node) {
60	e.expr(e.mutatorHole(), n)
61}
62
63func (e *escape) addrs(l ir.Nodes) []hole {
64	var ks []hole
65	for _, n := range l {
66		ks = append(ks, e.addr(n))
67	}
68	return ks
69}
70
71func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) {
72	e.expr(e.heapHole().note(where, why), src)
73}
74
75// assignList evaluates the assignment dsts... = srcs....
76func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) {
77	ks := e.addrs(dsts)
78	for i, k := range ks {
79		var src ir.Node
80		if i < len(srcs) {
81			src = srcs[i]
82		}
83
84		if dst := dsts[i]; dst != nil {
85			// Detect implicit conversion of uintptr to unsafe.Pointer when
86			// storing into reflect.{Slice,String}Header.
87			if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) {
88				e.unsafeValue(e.heapHole().note(where, why), src)
89				continue
90			}
91
92			// Filter out some no-op assignments for escape analysis.
93			if src != nil && isSelfAssign(dst, src) {
94				if base.Flag.LowerM != 0 {
95					base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where)
96				}
97				k = e.discardHole()
98			}
99		}
100
101		e.expr(k.note(where, why), src)
102	}
103
104	e.reassigned(ks, where)
105}
106
107// reassigned marks the locations associated with the given holes as
108// reassigned, unless the location represents a variable declared and
109// assigned exactly once by where.
110func (e *escape) reassigned(ks []hole, where ir.Node) {
111	if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil {
112		if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil {
113			// Zero-value assignment for variable declared without an
114			// explicit initial value. Assume this is its initialization
115			// statement.
116			return
117		}
118	}
119
120	for _, k := range ks {
121		loc := k.dst
122		// Variables declared by range statements are assigned on every iteration.
123		if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE {
124			continue
125		}
126		loc.reassigned = true
127	}
128}
129