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	"math"
10	"strings"
11)
12
13// A leaks represents a set of assignment flows from a parameter to
14// the heap, mutator, callee, or to any of its function's (first
15// numEscResults) result parameters.
16type leaks [8]uint8
17
18const (
19	leakHeap = iota
20	leakMutator
21	leakCallee
22	leakResult0
23)
24
25const numEscResults = len(leaks{}) - leakResult0
26
27// Heap returns the minimum deref count of any assignment flow from l
28// to the heap. If no such flows exist, Heap returns -1.
29func (l leaks) Heap() int { return l.get(leakHeap) }
30
31// Mutator returns the minimum deref count of any assignment flow from
32// l to the pointer operand of an indirect assignment statement. If no
33// such flows exist, Mutator returns -1.
34func (l leaks) Mutator() int { return l.get(leakMutator) }
35
36// Callee returns the minimum deref count of any assignment flow from
37// l to the callee operand of call expression. If no such flows exist,
38// Callee returns -1.
39func (l leaks) Callee() int { return l.get(leakCallee) }
40
41// Result returns the minimum deref count of any assignment flow from
42// l to its function's i'th result parameter. If no such flows exist,
43// Result returns -1.
44func (l leaks) Result(i int) int { return l.get(leakResult0 + i) }
45
46// AddHeap adds an assignment flow from l to the heap.
47func (l *leaks) AddHeap(derefs int) { l.add(leakHeap, derefs) }
48
49// AddMutator adds a flow from l to the mutator (i.e., a pointer
50// operand of an indirect assignment statement).
51func (l *leaks) AddMutator(derefs int) { l.add(leakMutator, derefs) }
52
53// AddCallee adds an assignment flow from l to the callee operand of a
54// call expression.
55func (l *leaks) AddCallee(derefs int) { l.add(leakCallee, derefs) }
56
57// AddResult adds an assignment flow from l to its function's i'th
58// result parameter.
59func (l *leaks) AddResult(i, derefs int) { l.add(leakResult0+i, derefs) }
60
61func (l leaks) get(i int) int { return int(l[i]) - 1 }
62
63func (l *leaks) add(i, derefs int) {
64	if old := l.get(i); old < 0 || derefs < old {
65		l.set(i, derefs)
66	}
67}
68
69func (l *leaks) set(i, derefs int) {
70	v := derefs + 1
71	if v < 0 {
72		base.Fatalf("invalid derefs count: %v", derefs)
73	}
74	if v > math.MaxUint8 {
75		v = math.MaxUint8
76	}
77
78	l[i] = uint8(v)
79}
80
81// Optimize removes result flow paths that are equal in length or
82// longer than the shortest heap flow path.
83func (l *leaks) Optimize() {
84	// If we have a path to the heap, then there's no use in
85	// keeping equal or longer paths elsewhere.
86	if x := l.Heap(); x >= 0 {
87		for i := 1; i < len(*l); i++ {
88			if l.get(i) >= x {
89				l.set(i, -1)
90			}
91		}
92	}
93}
94
95var leakTagCache = map[leaks]string{}
96
97// Encode converts l into a binary string for export data.
98func (l leaks) Encode() string {
99	if l.Heap() == 0 {
100		// Space optimization: empty string encodes more
101		// efficiently in export data.
102		return ""
103	}
104	if s, ok := leakTagCache[l]; ok {
105		return s
106	}
107
108	n := len(l)
109	for n > 0 && l[n-1] == 0 {
110		n--
111	}
112	s := "esc:" + string(l[:n])
113	leakTagCache[l] = s
114	return s
115}
116
117// parseLeaks parses a binary string representing a leaks.
118func parseLeaks(s string) leaks {
119	var l leaks
120	if !strings.HasPrefix(s, "esc:") {
121		l.AddHeap(0)
122		return l
123	}
124	copy(l[:], s[4:])
125	return l
126}
127