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
|
/* Classes for purging state at function_points.
Copyright (C) 2019-2023 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_ANALYZER_STATE_PURGE_H
#define GCC_ANALYZER_STATE_PURGE_H
/* Hash traits for function_point. */
template <> struct default_hash_traits<function_point>
: public pod_hash_traits<function_point>
{
static const bool empty_zero_p = false;
};
template <>
inline hashval_t
pod_hash_traits<function_point>::hash (value_type v)
{
return v.hash ();
}
template <>
inline bool
pod_hash_traits<function_point>::equal (const value_type &existing,
const value_type &candidate)
{
return existing == candidate;
}
template <>
inline void
pod_hash_traits<function_point>::mark_deleted (value_type &v)
{
v = function_point::deleted ();
}
template <>
inline void
pod_hash_traits<function_point>::mark_empty (value_type &v)
{
v = function_point::empty ();
}
template <>
inline bool
pod_hash_traits<function_point>::is_deleted (value_type v)
{
return v.get_kind () == PK_DELETED;
}
template <>
inline bool
pod_hash_traits<function_point>::is_empty (value_type v)
{
return v.get_kind () == PK_EMPTY;
}
namespace ana {
/* The result of analyzing which decls and SSA names can be purged from state at
different points in the program, so that we can simplify program_state
objects, in the hope of reducing state-blowup. */
class state_purge_map : public log_user
{
public:
typedef ordered_hash_map<tree, state_purge_per_ssa_name *> ssa_map_t;
typedef ssa_map_t::iterator ssa_iterator;
typedef ordered_hash_map<tree, state_purge_per_decl *> decl_map_t;
typedef decl_map_t::iterator decl_iterator;
state_purge_map (const supergraph &sg,
region_model_manager *mgr,
logger *logger);
~state_purge_map ();
const state_purge_per_ssa_name &get_data_for_ssa_name (tree name) const
{
gcc_assert (TREE_CODE (name) == SSA_NAME);
if (tree var = SSA_NAME_VAR (name))
if (TREE_CODE (var) == VAR_DECL)
gcc_assert (!VAR_DECL_IS_VIRTUAL_OPERAND (var));
state_purge_per_ssa_name **slot
= const_cast <ssa_map_t&> (m_ssa_map).get (name);
return **slot;
}
const state_purge_per_decl *get_any_data_for_decl (tree decl) const
{
gcc_assert (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL);
if (state_purge_per_decl **slot
= const_cast <decl_map_t&> (m_decl_map).get (decl))
return *slot;
else
return NULL;
}
state_purge_per_decl &get_or_create_data_for_decl (function *fun, tree decl);
const supergraph &get_sg () const { return m_sg; }
ssa_iterator begin_ssas () const { return m_ssa_map.begin (); }
ssa_iterator end_ssas () const { return m_ssa_map.end (); }
decl_iterator begin_decls () const { return m_decl_map.begin (); }
decl_iterator end_decls () const { return m_decl_map.end (); }
private:
DISABLE_COPY_AND_ASSIGN (state_purge_map);
const supergraph &m_sg;
ssa_map_t m_ssa_map;
decl_map_t m_decl_map;
};
/* Base class for state_purge_per_ssa_name and state_purge_per_decl. */
class state_purge_per_tree
{
public:
function *get_function () const { return m_fun; }
tree get_fndecl () const { return m_fun->decl; }
protected:
typedef hash_set<function_point> point_set_t;
state_purge_per_tree (function *fun)
: m_fun (fun)
{
}
private:
function *m_fun;
};
/* The part of a state_purge_map relating to a specific SSA name.
The result of analyzing a given SSA name, recording which
function_points need to retain state information about it to handle
their successor states, so that we can simplify program_state objects,
in the hope of reducing state-blowup. */
class state_purge_per_ssa_name : public state_purge_per_tree
{
public:
state_purge_per_ssa_name (const state_purge_map &map,
tree name,
function *fun);
bool needed_at_point_p (const function_point &point) const;
private:
static function_point before_use_stmt (const state_purge_map &map,
const gimple *use_stmt);
void add_to_worklist (const function_point &point,
auto_vec<function_point> *worklist,
logger *logger);
void process_point (const function_point &point,
auto_vec<function_point> *worklist,
const state_purge_map &map);
point_set_t m_points_needing_name;
tree m_name;
};
/* The part of a state_purge_map relating to a specific decl.
Analogous to state_purge_per_ssa_name, but for local decls.
This is more involved than the SSA name case, because we also need
to handle pointers and components. */
class state_purge_per_decl : public state_purge_per_tree
{
public:
state_purge_per_decl (const state_purge_map &map,
tree decl,
function *fun);
bool needed_at_point_p (const function_point &point) const;
void add_needed_at (const function_point &point);
void add_pointed_to_at (const function_point &point);
void process_worklists (const state_purge_map &map,
region_model_manager *mgr);
private:
static function_point before_use_stmt (const state_purge_map &map,
const gimple *use_stmt);
void add_to_worklist (const function_point &point,
auto_vec<function_point> *worklist,
point_set_t *seen,
logger *logger);
void process_point_backwards (const function_point &point,
auto_vec<function_point> *worklist,
point_set_t *seen,
const state_purge_map &map,
const region_model &model);
void process_point_forwards (const function_point &point,
auto_vec<function_point> *worklist,
point_set_t *seen,
const state_purge_map &map);
point_set_t m_points_needing_decl;
point_set_t m_points_taking_address;
tree m_decl;
};
/* Subclass of dot_annotator for use by -fdump-analyzer-state-purge.
Annotate the .dot output with state-purge information. */
class state_purge_annotator : public dot_annotator
{
public:
state_purge_annotator (const state_purge_map *map) : m_map (map) {}
bool add_node_annotations (graphviz_out *gv, const supernode &n, bool)
const final override;
void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
bool within_row)
const final override;
private:
void print_needed (graphviz_out *gv,
const function_point &point,
bool within_table) const;
const state_purge_map *m_map;
};
} // namespace ana
#endif /* GCC_ANALYZER_STATE_PURGE_H */
|