1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
// escape.h -- Go frontend escape analysis. -*- C++ -*-
// Copyright 2015 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.
#ifndef GO_ESCAPE_H
#define GO_ESCAPE_H
#include "go-system.h"
#include "string-dump.h"
class Call_node;
class Connection_node;
class Connection_dump_context;
class Gogo;
class Named_object;
// A basic escape analysis implementation for the Go frontend based on the
// algorithm from "Escape Analysis for Java" by Choi et. al in OOPSLA '99.
// This is a simplified version of the flow insensitive analysis with the goal
// of reducing the overhead cost of garbage collection by allocating objects
// on the stack when they do not escape the function they were created in.
//
// A major simplification is that the analysis only implements what Choi refers
// to as "deferred edges" which are used to used model assignments that copy
// references from one variable to another e.g. a := b. It is unnecessary to
// consider assignments to the fields of an object because, in general, if a
// field of an object escapes and must be heap-allocated, there is no way to
// heap-allocate that escaping field without heap-allocating the entire object.
// That is, for some globally escaping object GVAR, if there is an assignment
// of the form GVAR = t.f such that field f of object t escapes, it is likely
// that t must be heap-allocated as well. In the analysis, this assignment
// will be simplified to GVAR = t, which is imprecise but has the same effect.
// This is a general graph node representing a named object used in a call graph
// or connection graph. In a call graph, each named object is either a Function
// or Function_declaration representing a function called during the program
// execution (which isn't necessarily every function declared). In a connection
// graph, there is a node for each node in the call graph, which forms the root
// of that connection graph. Each connection graph root contains nodes whose
// objects are either variables used in the function defintion or are nested
// closures created within the function definition. The connection graph is
// a way of modeling the connectivity between all objects created in a given
// function as well as understanding the relationship between input arguments
// in the caller and the formal parameters in the callee.
class Node
{
public:
enum Node_classification
{
NODE_CALL,
NODE_CONNECTION
};
virtual ~Node();
// Make a call node for FUNCTION.
static Node*
make_call(Named_object* function);
// Make a connection node for OBJECT.
// Note: values in this enum appear in export data, and therefore MUST NOT
// change.
enum Escapement_lattice
{
// ESCAPE_GLOBAL means that the object escapes all functions globally.
ESCAPE_GLOBAL = 0,
// ESCAPE_ARG with respect to a function means that the object escapes that
// function it is created in via the function's arguments or results.
ESCAPE_ARG = 1,
// ESCAPE_NONE means that the object does not escape the function in which
// it was created.
ESCAPE_NONE = 2
};
// A list of states usually corresponding to a list of function parameters.
typedef std::vector<Escapement_lattice> Escape_states;
static Node*
make_connection(Named_object* object, Escapement_lattice e);
// Return the node classification.
Node_classification
classification() const
{ return this->classification_; }
// Return whether this is a call node.
bool
is_call() const
{ return this->classification_ == NODE_CALL; }
// Return whether this is a connection node.
bool
is_connection() const
{ return this->classification_ == NODE_CONNECTION; }
// If this is a connection node, return the Connection_node.
// Otherwise, return NULL.
Connection_node*
connection_node()
{ return this->convert<Connection_node, NODE_CONNECTION>(); }
// Return this node's unique id.
unsigned int
id() const
{ return this->id_; }
// Return this node's generated name for GraphViz.
virtual const std::string&
name() = 0;
// Return this node's generated label for GraphViz.
virtual const std::string&
label();
// Return the object this node represents.
Named_object*
object() const
{ return this->object_; }
void
add_edge(Node* v)
{ this->edges_.insert(v); }
const std::set<Node*>&
edges() const
{ return this->edges_; }
protected:
Node(Node_classification, Named_object* object);
const std::string&
get_name() const
{ return this->name_; }
void
set_name(const std::string& name)
{ this->name_ = name; }
const std::string&
get_label() const
{ return this->label_; }
void
set_label(const std::string& label)
{ this->label_ = label; }
private:
template<typename Node_class,
Node_classification node_classification>
const Node_class*
convert() const
{
return (this->classification_ == node_classification
? static_cast<const Node_class*>(this)
: NULL);
}
template<typename Node_class,
Node_classification node_classification>
Node_class*
convert()
{
return (this->classification_ == node_classification
? static_cast<Node_class*>(this)
: NULL);
}
// The classification of this node.
Node_classification classification_;
// A unique ID for this node.
unsigned int id_;
// The name for this node e.g. "Node<ID>" used as a GraphViz identifier.
std::string name_;
// The label for this node in the GraphViz representation.
std::string label_;
// The object represented by this node.
Named_object* object_;
// A distinct set of nodes that this node has edges to.
std::set<Node*> edges_;
};
// A node representing a function that might be called during program execution.
class Call_node : public Node
{
public:
Call_node(Named_object* function);
const std::string&
name();
};
// A node representing an object in the connection graph built for each function
// in the call graph.
class Connection_node : public Node
{
public:
Connection_node(Named_object* object, Escapement_lattice e)
: Node(NODE_CONNECTION, object),
escape_state_(e)
{ }
// Return this node's generated name for GraphViz.
const std::string&
name();
// Return this node's generated label for GraphViz.
const std::string&
label();
// Return the escape state for this node.
Escapement_lattice
escape_state() const
{ return this->escape_state_; }
// Set the escape state for this node.
void
set_escape_state(Escapement_lattice e)
{ this->escape_state_ = e; }
// Return the objects inside of this connection graph.
// This is empty for all connection nodes that are not the root of a
// connection graph. Each node in the call graph is a root of a connection
// graph.
const std::set<Node*>&
objects() const
{ return this->objects_; }
void
add_object(Node* object)
{ this->objects_.insert(object); }
void
dump_connection(Connection_dump_context*);
private:
// The escapement of this node.
Escapement_lattice escape_state_;
// The set of nodes contained within this connection node. If this node is
// not a root of a connection graph, this will be empty.
std::set<Node*> objects_;
};
// This class implements fgo-dump-calls. The Call graph dump of a Go program.
class Call_dump_context : public String_dump
{
public:
Call_dump_context(std::ostream* out = NULL);
// Initialize the dump context.
void
dump(Gogo*, const char* basename);
// Get dump output stream.
std::ostream&
ostream()
{ return *this->ostream_; }
// Implementation of String_dump interface.
void
write_c_string(const char*);
void
write_string(const std::string&);
private:
// Stream on output dump file.
std::ostream* ostream_;
Gogo* gogo_;
};
// This class implements fgo-dump-conns. The connection graph dump of
// the functions called in a Go program.
class Connection_dump_context : public String_dump
{
public:
Connection_dump_context(std::ostream* out = NULL);
// Initialize the dump context.
void
dump(Gogo*, const char* basename);
// Get dump output stream.
std::ostream&
ostream()
{ return *this->ostream_; }
// Implementation of String_dump interface.
void
write_c_string(const char*);
void
write_string(const std::string&);
private:
// Stream on output dump file.
std::ostream* ostream_;
Gogo* gogo_;
};
#endif // !defined(GO_ESCAPE_H)
|