]>
Commit | Line | Data |
---|---|---|
8ba50c2c LCW |
1 | /* This plugin contains an analysis pass that detects and warns about |
2 | self-assignment statements. */ | |
3 | /* { dg-options "-O" } */ | |
4 | ||
3e17e31d | 5 | #include "gcc-plugin.h" |
8ba50c2c LCW |
6 | #include "config.h" |
7 | #include "system.h" | |
8 | #include "coretypes.h" | |
9 | #include "tm.h" | |
4d648807 | 10 | #include "tree.h" |
d8a2d370 | 11 | #include "stringpool.h" |
8ba50c2c LCW |
12 | #include "toplev.h" |
13 | #include "basic-block.h" | |
2fb9a547 AM |
14 | #include "pointer-set.h" |
15 | #include "hash-table.h" | |
16 | #include "vec.h" | |
17 | #include "ggc.h" | |
18 | #include "basic-block.h" | |
19 | #include "tree-ssa-alias.h" | |
20 | #include "internal-fn.h" | |
21 | #include "gimple-fold.h" | |
22 | #include "tree-eh.h" | |
23 | #include "gimple-expr.h" | |
24 | #include "is-a.h" | |
8ba50c2c | 25 | #include "gimple.h" |
5be5c238 | 26 | #include "gimple-iterator.h" |
8ba50c2c LCW |
27 | #include "tree.h" |
28 | #include "tree-pass.h" | |
29 | #include "intl.h" | |
e4d5031c | 30 | #include "plugin-version.h" |
ba2dc63d | 31 | #include "diagnostic.h" |
f7695dbf | 32 | #include "context.h" |
8ba50c2c | 33 | |
fca5bb5c DN |
34 | int plugin_is_GPL_compatible; |
35 | ||
8ba50c2c LCW |
36 | /* Indicate whether to check overloaded operator '=', which is performed by |
37 | default. To disable it, use -fplugin-arg-NAME-no-check-operator-eq. */ | |
38 | bool check_operator_eq = true; | |
39 | ||
40 | /* Given a rhs EXPR of a gimple assign statement, if it is | |
41 | - SSA_NAME : returns its var decl, or, if it is a temp variable, | |
42 | returns the rhs of its SSA def statement. | |
43 | - VAR_DECL, PARM_DECL, FIELD_DECL, or a reference expression : | |
44 | returns EXPR itself. | |
45 | - any other expression : returns NULL_TREE. */ | |
46 | ||
47 | static tree | |
48 | get_real_ref_rhs (tree expr) | |
49 | { | |
50 | switch (TREE_CODE (expr)) | |
51 | { | |
52 | case SSA_NAME: | |
53 | { | |
54 | /* Given a self-assign statement, say foo.x = foo.x, | |
55 | the IR (after SSA) looks like: | |
56 | ||
57 | D.1797_14 = foo.x; | |
58 | foo.x ={v} D.1797_14; | |
59 | ||
60 | So if the rhs EXPR is an SSA_NAME of a temp variable, | |
61 | e.g. D.1797_14, we need to grab the rhs of its SSA def | |
62 | statement (i.e. foo.x). */ | |
63 | tree vdecl = SSA_NAME_VAR (expr); | |
70b5e7dc | 64 | if ((!vdecl || DECL_ARTIFICIAL (vdecl)) |
8ba50c2c LCW |
65 | && !gimple_nop_p (SSA_NAME_DEF_STMT (expr))) |
66 | { | |
67 | gimple def_stmt = SSA_NAME_DEF_STMT (expr); | |
68 | /* We are only interested in an assignment with a single | |
69 | rhs operand because if it is not, the original assignment | |
70 | will not possibly be a self-assignment. */ | |
70f34814 | 71 | if (gimple_assign_single_p (def_stmt)) |
8ba50c2c LCW |
72 | return get_real_ref_rhs (gimple_assign_rhs1 (def_stmt)); |
73 | else | |
74 | return NULL_TREE; | |
75 | } | |
76 | else | |
77 | return vdecl; | |
78 | } | |
79 | case VAR_DECL: | |
80 | case PARM_DECL: | |
81 | case FIELD_DECL: | |
82 | case COMPONENT_REF: | |
70f34814 | 83 | case MEM_REF: |
8ba50c2c LCW |
84 | case ARRAY_REF: |
85 | return expr; | |
86 | default: | |
87 | return NULL_TREE; | |
88 | } | |
89 | } | |
90 | ||
91 | /* Given an expression tree, EXPR, that may contains SSA names, returns an | |
92 | equivalent tree with the SSA names converted to var/parm/field decls | |
93 | so that it can be used with '%E' format modifier when emitting warning | |
94 | messages. | |
95 | ||
96 | This function currently only supports VAR/PARM/FIELD_DECL, reference | |
97 | expressions (COMPONENT_REF, INDIRECT_REF, ARRAY_REF), integer constant, | |
98 | and SSA_NAME. If EXPR contains any other tree nodes (e.g. an arithmetic | |
99 | expression appears in array index), NULL_TREE is returned. */ | |
100 | ||
101 | static tree | |
102 | get_non_ssa_expr (tree expr) | |
103 | { | |
70b5e7dc RG |
104 | if (!expr) |
105 | return NULL_TREE; | |
8ba50c2c LCW |
106 | switch (TREE_CODE (expr)) |
107 | { | |
108 | case VAR_DECL: | |
109 | case PARM_DECL: | |
110 | case FIELD_DECL: | |
111 | { | |
112 | if (DECL_NAME (expr)) | |
113 | return expr; | |
114 | else | |
115 | return NULL_TREE; | |
116 | } | |
117 | case COMPONENT_REF: | |
118 | { | |
119 | tree base, orig_base = TREE_OPERAND (expr, 0); | |
120 | tree component, orig_component = TREE_OPERAND (expr, 1); | |
121 | base = get_non_ssa_expr (orig_base); | |
122 | if (!base) | |
123 | return NULL_TREE; | |
124 | component = get_non_ssa_expr (orig_component); | |
125 | if (!component) | |
126 | return NULL_TREE; | |
127 | /* If either BASE or COMPONENT is converted, build a new | |
128 | component reference tree. */ | |
129 | if (base != orig_base || component != orig_component) | |
130 | return build3 (COMPONENT_REF, TREE_TYPE (component), | |
131 | base, component, NULL_TREE); | |
132 | else | |
133 | return expr; | |
134 | } | |
70f34814 | 135 | case MEM_REF: |
8ba50c2c LCW |
136 | { |
137 | tree orig_base = TREE_OPERAND (expr, 0); | |
70f34814 RG |
138 | if (TREE_CODE (orig_base) == SSA_NAME) |
139 | { | |
140 | tree base = get_non_ssa_expr (orig_base); | |
141 | if (!base) | |
142 | return NULL_TREE; | |
143 | return fold_build2 (MEM_REF, TREE_TYPE (expr), | |
144 | base, TREE_OPERAND (expr, 1)); | |
145 | } | |
146 | return expr; | |
8ba50c2c LCW |
147 | } |
148 | case ARRAY_REF: | |
149 | { | |
150 | tree array, orig_array = TREE_OPERAND (expr, 0); | |
151 | tree index, orig_index = TREE_OPERAND (expr, 1); | |
152 | array = get_non_ssa_expr (orig_array); | |
153 | if (!array) | |
154 | return NULL_TREE; | |
155 | index = get_non_ssa_expr (orig_index); | |
156 | if (!index) | |
157 | return NULL_TREE; | |
158 | /* If either ARRAY or INDEX is converted, build a new array | |
159 | reference tree. */ | |
160 | if (array != orig_array || index != orig_index) | |
161 | return build4 (ARRAY_REF, TREE_TYPE (expr), array, index, | |
162 | TREE_OPERAND (expr, 2), TREE_OPERAND (expr, 3)); | |
163 | else | |
164 | return expr; | |
165 | } | |
166 | case SSA_NAME: | |
167 | { | |
168 | tree vdecl = SSA_NAME_VAR (expr); | |
70b5e7dc | 169 | if ((!vdecl || DECL_ARTIFICIAL (vdecl)) |
8ba50c2c LCW |
170 | && !gimple_nop_p (SSA_NAME_DEF_STMT (expr))) |
171 | { | |
172 | gimple def_stmt = SSA_NAME_DEF_STMT (expr); | |
70f34814 | 173 | if (gimple_assign_single_p (def_stmt)) |
8ba50c2c LCW |
174 | vdecl = gimple_assign_rhs1 (def_stmt); |
175 | } | |
176 | return get_non_ssa_expr (vdecl); | |
177 | } | |
178 | case INTEGER_CST: | |
179 | return expr; | |
180 | default: | |
181 | /* Return NULL_TREE for any other kind of tree nodes. */ | |
182 | return NULL_TREE; | |
183 | } | |
184 | } | |
185 | ||
186 | /* Given the LHS and (real) RHS of a gimple assign statement, STMT, check if | |
187 | they are the same. If so, print a warning message about self-assignment. */ | |
188 | ||
189 | static void | |
190 | compare_and_warn (gimple stmt, tree lhs, tree rhs) | |
191 | { | |
192 | if (operand_equal_p (lhs, rhs, OEP_PURE_SAME)) | |
193 | { | |
194 | location_t location; | |
195 | location = (gimple_has_location (stmt) | |
196 | ? gimple_location (stmt) | |
197 | : (DECL_P (lhs) | |
198 | ? DECL_SOURCE_LOCATION (lhs) | |
199 | : input_location)); | |
200 | /* If LHS contains any tree node not currently supported by | |
201 | get_non_ssa_expr, simply emit a generic warning without | |
202 | specifying LHS in the message. */ | |
203 | lhs = get_non_ssa_expr (lhs); | |
204 | if (lhs) | |
fab922b1 | 205 | warning_at (location, 0, G_("%qE is assigned to itself"), lhs); |
8ba50c2c | 206 | else |
fab922b1 | 207 | warning_at (location, 0, G_("self-assignment detected")); |
8ba50c2c LCW |
208 | } |
209 | } | |
210 | ||
211 | /* Check and warn if STMT is a self-assign statement. */ | |
212 | ||
213 | static void | |
214 | warn_self_assign (gimple stmt) | |
215 | { | |
216 | tree rhs, lhs; | |
217 | ||
218 | /* Check assigment statement. */ | |
70f34814 | 219 | if (gimple_assign_single_p (stmt)) |
8ba50c2c LCW |
220 | { |
221 | rhs = get_real_ref_rhs (gimple_assign_rhs1 (stmt)); | |
222 | if (!rhs) | |
223 | return; | |
224 | ||
225 | lhs = gimple_assign_lhs (stmt); | |
226 | if (TREE_CODE (lhs) == SSA_NAME) | |
227 | { | |
228 | lhs = SSA_NAME_VAR (lhs); | |
70b5e7dc | 229 | if (!lhs || DECL_ARTIFICIAL (lhs)) |
8ba50c2c LCW |
230 | return; |
231 | } | |
232 | ||
233 | compare_and_warn (stmt, lhs, rhs); | |
234 | } | |
235 | /* Check overloaded operator '=' (if enabled). */ | |
236 | else if (check_operator_eq && is_gimple_call (stmt)) | |
237 | { | |
238 | tree fdecl = gimple_call_fndecl (stmt); | |
239 | if (fdecl && (DECL_NAME (fdecl) == maybe_get_identifier ("operator="))) | |
240 | { | |
241 | /* If 'operator=' takes reference operands, the arguments will be | |
242 | ADDR_EXPR trees. In this case, just remove the address-taken | |
243 | operator before we compare the lhs and rhs. */ | |
244 | lhs = gimple_call_arg (stmt, 0); | |
245 | if (TREE_CODE (lhs) == ADDR_EXPR) | |
246 | lhs = TREE_OPERAND (lhs, 0); | |
247 | rhs = gimple_call_arg (stmt, 1); | |
248 | if (TREE_CODE (rhs) == ADDR_EXPR) | |
249 | rhs = TREE_OPERAND (rhs, 0); | |
250 | ||
251 | compare_and_warn (stmt, lhs, rhs); | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | /* Entry point for the self-assignment detection pass. */ | |
257 | ||
258 | static unsigned int | |
259 | execute_warn_self_assign (void) | |
260 | { | |
261 | gimple_stmt_iterator gsi; | |
262 | basic_block bb; | |
263 | ||
264 | FOR_EACH_BB (bb) | |
265 | { | |
266 | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) | |
267 | warn_self_assign (gsi_stmt (gsi)); | |
268 | } | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | /* Pass gate function. Currently always returns true. */ | |
274 | ||
275 | static bool | |
276 | gate_warn_self_assign (void) | |
277 | { | |
278 | return true; | |
279 | } | |
280 | ||
27a4cd48 DM |
281 | namespace { |
282 | ||
283 | const pass_data pass_data_warn_self_assign = | |
8ba50c2c | 284 | { |
27a4cd48 DM |
285 | GIMPLE_PASS, /* type */ |
286 | "warn_self_assign", /* name */ | |
287 | OPTGROUP_NONE, /* optinfo_flags */ | |
288 | true, /* has_gate */ | |
289 | true, /* has_execute */ | |
290 | TV_NONE, /* tv_id */ | |
291 | PROP_ssa, /* properties_required */ | |
292 | 0, /* properties_provided */ | |
293 | 0, /* properties_destroyed */ | |
294 | 0, /* todo_flags_start */ | |
295 | 0, /* todo_flags_finish */ | |
8ba50c2c LCW |
296 | }; |
297 | ||
27a4cd48 DM |
298 | class pass_warn_self_assign : public gimple_opt_pass |
299 | { | |
300 | public: | |
301 | pass_warn_self_assign(gcc::context *ctxt) | |
302 | : gimple_opt_pass(pass_data_warn_self_assign, ctxt) | |
303 | {} | |
304 | ||
305 | /* opt_pass methods: */ | |
306 | bool gate () { return gate_warn_self_assign (); } | |
307 | unsigned int execute () { return execute_warn_self_assign (); } | |
308 | ||
309 | }; // class pass_warn_self_assign | |
310 | ||
311 | } // anon namespace | |
312 | ||
313 | static gimple_opt_pass * | |
314 | make_pass_warn_self_assign (gcc::context *ctxt) | |
315 | { | |
316 | return new pass_warn_self_assign (ctxt); | |
317 | } | |
318 | ||
8ba50c2c LCW |
319 | /* The initialization routine exposed to and called by GCC. The spec of this |
320 | function is defined in gcc/gcc-plugin.h. | |
321 | ||
322 | PLUGIN_NAME - name of the plugin (useful for error reporting) | |
323 | ARGC - the size of the ARGV array | |
324 | ARGV - an array of key-value argument pair | |
325 | ||
326 | Returns 0 if initialization finishes successfully. | |
327 | ||
328 | Note that this function needs to be named exactly "plugin_init". */ | |
329 | ||
330 | int | |
9fefa0aa TG |
331 | plugin_init (struct plugin_name_args *plugin_info, |
332 | struct plugin_gcc_version *version) | |
8ba50c2c | 333 | { |
8fc7e474 | 334 | struct register_pass_info pass_info; |
9fefa0aa TG |
335 | const char *plugin_name = plugin_info->base_name; |
336 | int argc = plugin_info->argc; | |
337 | struct plugin_argument *argv = plugin_info->argv; | |
8ba50c2c LCW |
338 | bool enabled = true; |
339 | int i; | |
340 | ||
e4d5031c | 341 | if (!plugin_default_version_check (version, &gcc_version)) |
8ba50c2c LCW |
342 | return 1; |
343 | ||
344 | /* Self-assign detection should happen after SSA is constructed. */ | |
f7695dbf | 345 | pass_info.pass = make_pass_warn_self_assign (g); |
8ba50c2c LCW |
346 | pass_info.reference_pass_name = "ssa"; |
347 | pass_info.ref_pass_instance_number = 1; | |
348 | pass_info.pos_op = PASS_POS_INSERT_AFTER; | |
349 | ||
350 | /* Process the plugin arguments. This plugin takes the following arguments: | |
351 | check-operator-eq, no-check-operator-eq, enable, and disable. | |
352 | By default, the analysis is enabled with 'operator=' checked. */ | |
353 | for (i = 0; i < argc; ++i) | |
354 | { | |
355 | if (!strcmp (argv[i].key, "check-operator-eq")) | |
356 | { | |
357 | if (argv[i].value) | |
358 | warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'" | |
359 | " ignored (superfluous '=%s')"), | |
360 | plugin_name, argv[i].value, argv[i].value); | |
361 | else | |
362 | check_operator_eq = true; | |
363 | } | |
364 | else if (!strcmp (argv[i].key, "no-check-operator-eq")) | |
365 | { | |
366 | if (argv[i].value) | |
367 | warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'" | |
368 | " ignored (superfluous '=%s')"), | |
369 | plugin_name, argv[i].value, argv[i].value); | |
370 | else | |
371 | check_operator_eq = false; | |
372 | } | |
373 | else if (!strcmp (argv[i].key, "enable")) | |
374 | { | |
375 | if (argv[i].value) | |
376 | warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored" | |
377 | " (superfluous '=%s')"), | |
378 | plugin_name, argv[i].value, argv[i].value); | |
379 | else | |
380 | enabled = true; | |
381 | } | |
382 | else if (!strcmp (argv[i].key, "disable")) | |
383 | { | |
384 | if (argv[i].value) | |
385 | warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored" | |
386 | " (superfluous '=%s')"), | |
387 | plugin_name, argv[i].value, argv[i].value); | |
388 | else | |
389 | enabled = false; | |
390 | } | |
391 | else | |
392 | warning (0, G_("plugin %qs: unrecognized argument %qs ignored"), | |
393 | plugin_name, argv[i].key); | |
394 | } | |
395 | ||
396 | /* Register this new pass with GCC if the analysis is enabled. */ | |
397 | if (enabled) | |
398 | register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, | |
399 | &pass_info); | |
400 | ||
401 | return 0; | |
402 | } |