]>
Commit | Line | Data |
---|---|---|
db242b6d | 1 | /* Detect paths through the CFG which can never be executed in a conforming |
2 | program and isolate them. | |
3 | ||
fbd26352 | 4 | Copyright (C) 2013-2019 Free Software Foundation, Inc. |
db242b6d | 5 | |
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3, or (at your option) | |
11 | any later version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GCC; see the file COPYING3. If not see | |
20 | <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #include "config.h" | |
23 | #include "system.h" | |
24 | #include "coretypes.h" | |
9ef16211 | 25 | #include "backend.h" |
db242b6d | 26 | #include "tree.h" |
9ef16211 | 27 | #include "gimple.h" |
7c29e30e | 28 | #include "cfghooks.h" |
29 | #include "tree-pass.h" | |
9ef16211 | 30 | #include "ssa.h" |
7c29e30e | 31 | #include "diagnostic-core.h" |
b20a8bb4 | 32 | #include "fold-const.h" |
dcf1a1ec | 33 | #include "gimple-iterator.h" |
34 | #include "gimple-walk.h" | |
db242b6d | 35 | #include "tree-ssa.h" |
db242b6d | 36 | #include "cfgloop.h" |
b61383dd | 37 | #include "tree-cfg.h" |
2f21b5f4 | 38 | #include "cfganal.h" |
f22a2cb7 | 39 | #include "intl.h" |
db242b6d | 40 | |
41 | ||
42 | static bool cfg_altered; | |
43 | ||
c59258dc | 44 | /* Callback for walk_stmt_load_store_ops. |
30b10261 | 45 | |
c59258dc | 46 | Return TRUE if OP will dereference the tree stored in DATA, FALSE |
47 | otherwise. | |
48 | ||
49 | This routine only makes a superficial check for a dereference. Thus, | |
50 | it must only be used if it is safe to return a false negative. */ | |
51 | static bool | |
42acab1c | 52 | check_loadstore (gimple *stmt, tree op, tree, void *data) |
c59258dc | 53 | { |
54 | if ((TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF) | |
55 | && operand_equal_p (TREE_OPERAND (op, 0), (tree)data, 0)) | |
2073fe3e | 56 | { |
57 | TREE_THIS_VOLATILE (op) = 1; | |
58 | TREE_SIDE_EFFECTS (op) = 1; | |
59 | update_stmt (stmt); | |
60 | return true; | |
61 | } | |
c59258dc | 62 | return false; |
63 | } | |
64 | ||
1a04a3b3 | 65 | /* Insert a trap after SI and split the block after the trap. */ |
db242b6d | 66 | |
67 | static void | |
1a04a3b3 | 68 | insert_trap (gimple_stmt_iterator *si_p, tree op) |
db242b6d | 69 | { |
c59258dc | 70 | /* We want the NULL pointer dereference to actually occur so that |
71 | code that wishes to catch the signal can do so. | |
72 | ||
73 | If the dereference is a load, then there's nothing to do as the | |
2073fe3e | 74 | LHS will be a throw-away SSA_NAME and the RHS is the NULL dereference. |
db242b6d | 75 | |
c59258dc | 76 | If the dereference is a store and we can easily transform the RHS, |
456dc91f | 77 | then simplify the RHS to enable more DCE. Note that we require the |
78 | statement to be a GIMPLE_ASSIGN which filters out calls on the RHS. */ | |
42acab1c | 79 | gimple *stmt = gsi_stmt (*si_p); |
c59258dc | 80 | if (walk_stmt_load_store_ops (stmt, (void *)op, NULL, check_loadstore) |
456dc91f | 81 | && is_gimple_assign (stmt) |
c59258dc | 82 | && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (stmt)))) |
83 | { | |
84 | /* We just need to turn the RHS into zero converted to the proper | |
85 | type. */ | |
86 | tree type = TREE_TYPE (gimple_assign_lhs (stmt)); | |
87 | gimple_assign_set_rhs_code (stmt, INTEGER_CST); | |
88 | gimple_assign_set_rhs1 (stmt, fold_convert (type, integer_zero_node)); | |
89 | update_stmt (stmt); | |
90 | } | |
91 | ||
1a91d914 | 92 | gcall *new_stmt |
c59258dc | 93 | = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); |
94 | gimple_seq seq = NULL; | |
95 | gimple_seq_add_stmt (&seq, new_stmt); | |
96 | ||
97 | /* If we had a NULL pointer dereference, then we want to insert the | |
98 | __builtin_trap after the statement, for the other cases we want | |
99 | to insert before the statement. */ | |
100 | if (walk_stmt_load_store_ops (stmt, (void *)op, | |
101 | check_loadstore, | |
102 | check_loadstore)) | |
947f1744 | 103 | { |
104 | gsi_insert_after (si_p, seq, GSI_NEW_STMT); | |
105 | if (stmt_ends_bb_p (stmt)) | |
106 | { | |
107 | split_block (gimple_bb (stmt), stmt); | |
108 | return; | |
109 | } | |
110 | } | |
c59258dc | 111 | else |
112 | gsi_insert_before (si_p, seq, GSI_NEW_STMT); | |
113 | ||
1a04a3b3 | 114 | split_block (gimple_bb (new_stmt), new_stmt); |
115 | *si_p = gsi_for_stmt (stmt); | |
db242b6d | 116 | } |
117 | ||
67cf9b55 | 118 | /* BB when reached via incoming edge E will exhibit undefined behavior |
db242b6d | 119 | at STMT. Isolate and optimize the path which exhibits undefined |
67cf9b55 | 120 | behavior. |
db242b6d | 121 | |
122 | Isolation is simple. Duplicate BB and redirect E to BB'. | |
123 | ||
124 | Optimization is simple as well. Replace STMT in BB' with an | |
125 | unconditional trap and remove all outgoing edges from BB'. | |
126 | ||
f22a2cb7 | 127 | If RET_ZERO, do not trap, only return NULL. |
128 | ||
db242b6d | 129 | DUPLICATE is a pre-existing duplicate, use it as BB' if it exists. |
130 | ||
131 | Return BB'. */ | |
132 | ||
133 | basic_block | |
c59258dc | 134 | isolate_path (basic_block bb, basic_block duplicate, |
42acab1c | 135 | edge e, gimple *stmt, tree op, bool ret_zero) |
db242b6d | 136 | { |
137 | gimple_stmt_iterator si, si2; | |
138 | edge_iterator ei; | |
139 | edge e2; | |
1e8fd529 | 140 | bool impossible = true; |
18113215 | 141 | profile_count count = e->count (); |
1e8fd529 | 142 | |
143 | for (si = gsi_start_bb (bb); gsi_stmt (si) != stmt; gsi_next (&si)) | |
144 | if (stmt_can_terminate_bb_p (gsi_stmt (si))) | |
145 | { | |
146 | impossible = false; | |
147 | break; | |
148 | } | |
149 | force_edge_cold (e, impossible); | |
db242b6d | 150 | |
151 | /* First duplicate BB if we have not done so already and remove all | |
152 | the duplicate's outgoing edges as duplicate is going to unconditionally | |
153 | trap. Removing the outgoing edges is both an optimization and ensures | |
154 | we don't need to do any PHI node updates. */ | |
155 | if (!duplicate) | |
156 | { | |
157 | duplicate = duplicate_block (bb, NULL, NULL); | |
18113215 | 158 | duplicate->count = profile_count::zero (); |
f22a2cb7 | 159 | if (!ret_zero) |
160 | for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); ) | |
161 | remove_edge (e2); | |
db242b6d | 162 | } |
18113215 | 163 | bb->count -= count; |
db242b6d | 164 | |
165 | /* Complete the isolation step by redirecting E to reach DUPLICATE. */ | |
166 | e2 = redirect_edge_and_branch (e, duplicate); | |
167 | if (e2) | |
52030270 | 168 | { |
169 | flush_pending_stmts (e2); | |
db242b6d | 170 | |
52030270 | 171 | /* Update profile only when redirection is really processed. */ |
205ce1aa | 172 | bb->count += e->count (); |
52030270 | 173 | } |
db242b6d | 174 | |
175 | /* There may be more than one statement in DUPLICATE which exhibits | |
67cf9b55 | 176 | undefined behavior. Ultimately we want the first such statement in |
db242b6d | 177 | DUPLCIATE so that we're able to delete as much code as possible. |
178 | ||
67cf9b55 | 179 | So each time we discover undefined behavior in DUPLICATE, search for |
180 | the statement which triggers undefined behavior. If found, then | |
db242b6d | 181 | transform the statement into a trap and delete everything after the |
182 | statement. If not found, then this particular instance was subsumed by | |
67cf9b55 | 183 | an earlier instance of undefined behavior and there's nothing to do. |
db242b6d | 184 | |
185 | This is made more complicated by the fact that we have STMT, which is in | |
186 | BB rather than in DUPLICATE. So we set up two iterators, one for each | |
187 | block and walk forward looking for STMT in BB, advancing each iterator at | |
188 | each step. | |
189 | ||
190 | When we find STMT the second iterator should point to STMT's equivalent in | |
191 | duplicate. If DUPLICATE ends before STMT is found in BB, then there's | |
30b10261 | 192 | nothing to do. |
db242b6d | 193 | |
194 | Ignore labels and debug statements. */ | |
195 | si = gsi_start_nondebug_after_labels_bb (bb); | |
196 | si2 = gsi_start_nondebug_after_labels_bb (duplicate); | |
197 | while (!gsi_end_p (si) && !gsi_end_p (si2) && gsi_stmt (si) != stmt) | |
198 | { | |
199 | gsi_next_nondebug (&si); | |
200 | gsi_next_nondebug (&si2); | |
201 | } | |
202 | ||
203 | /* This would be an indicator that we never found STMT in BB, which should | |
204 | never happen. */ | |
205 | gcc_assert (!gsi_end_p (si)); | |
206 | ||
207 | /* If we did not run to the end of DUPLICATE, then SI points to STMT and | |
208 | SI2 points to the duplicate of STMT in DUPLICATE. Insert a trap | |
209 | before SI2 and remove SI2 and all trailing statements. */ | |
210 | if (!gsi_end_p (si2)) | |
f22a2cb7 | 211 | { |
212 | if (ret_zero) | |
213 | { | |
1a91d914 | 214 | greturn *ret = as_a <greturn *> (gsi_stmt (si2)); |
f22a2cb7 | 215 | tree zero = build_zero_cst (TREE_TYPE (gimple_return_retval (ret))); |
216 | gimple_return_set_retval (ret, zero); | |
217 | update_stmt (ret); | |
218 | } | |
219 | else | |
1a04a3b3 | 220 | insert_trap (&si2, op); |
f22a2cb7 | 221 | } |
db242b6d | 222 | |
223 | return duplicate; | |
224 | } | |
225 | ||
0d56015c | 226 | /* Return TRUE if STMT is a div/mod operation using DIVISOR as the divisor. |
227 | FALSE otherwise. */ | |
228 | ||
229 | static bool | |
230 | is_divmod_with_given_divisor (gimple *stmt, tree divisor) | |
231 | { | |
232 | /* Only assignments matter. */ | |
233 | if (!is_gimple_assign (stmt)) | |
234 | return false; | |
235 | ||
236 | /* Check for every DIV/MOD expression. */ | |
237 | enum tree_code rhs_code = gimple_assign_rhs_code (stmt); | |
238 | if (rhs_code == TRUNC_DIV_EXPR | |
239 | || rhs_code == FLOOR_DIV_EXPR | |
240 | || rhs_code == CEIL_DIV_EXPR | |
241 | || rhs_code == EXACT_DIV_EXPR | |
242 | || rhs_code == ROUND_DIV_EXPR | |
243 | || rhs_code == TRUNC_MOD_EXPR | |
244 | || rhs_code == FLOOR_MOD_EXPR | |
245 | || rhs_code == CEIL_MOD_EXPR | |
246 | || rhs_code == ROUND_MOD_EXPR) | |
247 | { | |
248 | /* Pointer equality is fine when DIVISOR is an SSA_NAME, but | |
249 | not sufficient for constants which may have different types. */ | |
250 | if (operand_equal_p (gimple_assign_rhs2 (stmt), divisor, 0)) | |
251 | return true; | |
252 | } | |
253 | return false; | |
254 | } | |
255 | ||
256 | /* NAME is an SSA_NAME that we have already determined has the value 0 or NULL. | |
257 | ||
258 | Return TRUE if USE_STMT uses NAME in a way where a 0 or NULL value results | |
259 | in undefined behavior, FALSE otherwise | |
260 | ||
261 | LOC is used for issuing diagnostics. This case represents potential | |
262 | undefined behavior exposed by path splitting and that's reflected in | |
263 | the diagnostic. */ | |
264 | ||
265 | bool | |
266 | stmt_uses_name_in_undefined_way (gimple *use_stmt, tree name, location_t loc) | |
267 | { | |
268 | /* If we are working with a non pointer type, then see | |
269 | if this use is a DIV/MOD operation using NAME as the | |
270 | divisor. */ | |
271 | if (!POINTER_TYPE_P (TREE_TYPE (name))) | |
272 | { | |
7c1cc03c | 273 | if (!cfun->can_throw_non_call_exceptions) |
0d56015c | 274 | return is_divmod_with_given_divisor (use_stmt, name); |
275 | return false; | |
276 | } | |
277 | ||
278 | /* NAME is a pointer, so see if it's used in a context where it must | |
279 | be non-NULL. */ | |
280 | bool by_dereference | |
281 | = infer_nonnull_range_by_dereference (use_stmt, name); | |
282 | ||
283 | if (by_dereference | |
284 | || infer_nonnull_range_by_attribute (use_stmt, name)) | |
285 | { | |
286 | ||
287 | if (by_dereference) | |
288 | { | |
289 | warning_at (loc, OPT_Wnull_dereference, | |
290 | "potential null pointer dereference"); | |
291 | if (!flag_isolate_erroneous_paths_dereference) | |
292 | return false; | |
293 | } | |
294 | else | |
295 | { | |
296 | if (!flag_isolate_erroneous_paths_attribute) | |
297 | return false; | |
298 | } | |
299 | return true; | |
300 | } | |
301 | return false; | |
302 | } | |
303 | ||
304 | /* Return TRUE if USE_STMT uses 0 or NULL in a context which results in | |
305 | undefined behavior, FALSE otherwise. | |
306 | ||
307 | These cases are explicit in the IL. */ | |
308 | ||
309 | bool | |
310 | stmt_uses_0_or_null_in_undefined_way (gimple *stmt) | |
311 | { | |
7c1cc03c | 312 | if (!cfun->can_throw_non_call_exceptions |
0d56015c | 313 | && is_divmod_with_given_divisor (stmt, integer_zero_node)) |
314 | return true; | |
315 | ||
316 | /* By passing null_pointer_node, we can use the | |
317 | infer_nonnull_range functions to detect explicit NULL | |
318 | pointer dereferences and other uses where a non-NULL | |
319 | value is required. */ | |
320 | ||
321 | bool by_dereference | |
322 | = infer_nonnull_range_by_dereference (stmt, null_pointer_node); | |
323 | if (by_dereference | |
324 | || infer_nonnull_range_by_attribute (stmt, null_pointer_node)) | |
325 | { | |
326 | if (by_dereference) | |
327 | { | |
328 | location_t loc = gimple_location (stmt); | |
329 | warning_at (loc, OPT_Wnull_dereference, | |
330 | "null pointer dereference"); | |
331 | if (!flag_isolate_erroneous_paths_dereference) | |
332 | return false; | |
333 | } | |
334 | else | |
335 | { | |
336 | if (!flag_isolate_erroneous_paths_attribute) | |
337 | return false; | |
338 | } | |
339 | return true; | |
340 | } | |
341 | return false; | |
342 | } | |
343 | ||
0a76252a | 344 | /* Look for PHI nodes which feed statements in the same block where |
345 | the value of the PHI node implies the statement is erroneous. | |
db242b6d | 346 | |
0a76252a | 347 | For example, a NULL PHI arg value which then feeds a pointer |
348 | dereference. | |
db242b6d | 349 | |
0a76252a | 350 | When found isolate and optimize the path associated with the PHI |
351 | argument feeding the erroneous statement. */ | |
352 | static void | |
67cf9b55 | 353 | find_implicit_erroneous_behavior (void) |
db242b6d | 354 | { |
355 | basic_block bb; | |
356 | ||
fc00614f | 357 | FOR_EACH_BB_FN (bb, cfun) |
db242b6d | 358 | { |
1a91d914 | 359 | gphi_iterator si; |
db242b6d | 360 | |
b61383dd | 361 | /* Out of an abundance of caution, do not isolate paths to a |
362 | block where the block has any abnormal outgoing edges. | |
363 | ||
364 | We might be able to relax this in the future. We have to detect | |
365 | when we have to split the block with the NULL dereference and | |
366 | the trap we insert. We have to preserve abnormal edges out | |
367 | of the isolated block which in turn means updating PHIs at | |
368 | the targets of those abnormal outgoing edges. */ | |
cb287480 | 369 | if (has_abnormal_or_eh_outgoing_edge_p (bb)) |
b61383dd | 370 | continue; |
2f21b5f4 | 371 | |
372 | ||
373 | /* If BB has an edge to itself, then duplication of BB below | |
374 | could result in reallocation of BB's PHI nodes. If that happens | |
375 | then the loop below over the PHIs would use the old PHI and | |
376 | thus invalid information. We don't have a good way to know | |
377 | if a PHI has been reallocated, so just avoid isolation in | |
378 | this case. */ | |
379 | if (find_edge (bb, bb)) | |
380 | continue; | |
b61383dd | 381 | |
db242b6d | 382 | /* First look for a PHI which sets a pointer to NULL and which |
383 | is then dereferenced within BB. This is somewhat overly | |
384 | conservative, but probably catches most of the interesting | |
385 | cases. */ | |
386 | for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) | |
387 | { | |
1a91d914 | 388 | gphi *phi = si.phi (); |
db242b6d | 389 | tree lhs = gimple_phi_result (phi); |
390 | ||
db242b6d | 391 | /* PHI produces a pointer result. See if any of the PHI's |
30b10261 | 392 | arguments are NULL. |
db242b6d | 393 | |
394 | When we remove an edge, we want to reprocess the current | |
395 | index, hence the ugly way we update I for each iteration. */ | |
396 | basic_block duplicate = NULL; | |
397 | for (unsigned i = 0, next_i = 0; | |
398 | i < gimple_phi_num_args (phi); | |
399 | i = next_i) | |
400 | { | |
401 | tree op = gimple_phi_arg_def (phi, i); | |
f22a2cb7 | 402 | edge e = gimple_phi_arg_edge (phi, i); |
403 | imm_use_iterator iter; | |
42acab1c | 404 | gimple *use_stmt; |
db242b6d | 405 | |
406 | next_i = i + 1; | |
30b10261 | 407 | |
f22a2cb7 | 408 | if (TREE_CODE (op) == ADDR_EXPR) |
409 | { | |
410 | tree valbase = get_base_address (TREE_OPERAND (op, 0)); | |
53e9c5c4 | 411 | if ((VAR_P (valbase) && !is_global_var (valbase)) |
f22a2cb7 | 412 | || TREE_CODE (valbase) == PARM_DECL) |
413 | { | |
414 | FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) | |
415 | { | |
1a91d914 | 416 | greturn *return_stmt |
417 | = dyn_cast <greturn *> (use_stmt); | |
418 | if (!return_stmt) | |
419 | continue; | |
420 | ||
421 | if (gimple_return_retval (return_stmt) != lhs) | |
f22a2cb7 | 422 | continue; |
423 | ||
bc35ef65 | 424 | { |
425 | auto_diagnostic_group d; | |
426 | if (warning_at (gimple_location (use_stmt), | |
427 | OPT_Wreturn_local_addr, | |
428 | "function may return address " | |
429 | "of local variable")) | |
430 | inform (DECL_SOURCE_LOCATION(valbase), | |
431 | "declared here"); | |
432 | } | |
f22a2cb7 | 433 | |
54a5b8c7 | 434 | if ((flag_isolate_erroneous_paths_dereference |
435 | || flag_isolate_erroneous_paths_attribute) | |
436 | && gimple_bb (use_stmt) == bb) | |
f22a2cb7 | 437 | { |
438 | duplicate = isolate_path (bb, duplicate, e, | |
439 | use_stmt, lhs, true); | |
440 | ||
441 | /* When we remove an incoming edge, we need to | |
442 | reprocess the Ith element. */ | |
443 | next_i = i; | |
444 | cfg_altered = true; | |
445 | } | |
446 | } | |
447 | } | |
448 | } | |
449 | ||
db242b6d | 450 | if (!integer_zerop (op)) |
451 | continue; | |
452 | ||
f37822f7 | 453 | location_t phi_arg_loc = gimple_phi_arg_location (phi, i); |
454 | ||
db242b6d | 455 | /* We've got a NULL PHI argument. Now see if the |
456 | PHI's result is dereferenced within BB. */ | |
457 | FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) | |
458 | { | |
459 | /* We only care about uses in BB. Catching cases in | |
460 | in other blocks would require more complex path | |
cb287480 | 461 | isolation code. */ |
462 | if (gimple_bb (use_stmt) != bb) | |
db242b6d | 463 | continue; |
464 | ||
0d56015c | 465 | location_t loc = gimple_location (use_stmt) |
466 | ? gimple_location (use_stmt) | |
f37822f7 | 467 | : phi_arg_loc; |
30b10261 | 468 | |
0d56015c | 469 | if (stmt_uses_name_in_undefined_way (use_stmt, lhs, loc)) |
db242b6d | 470 | { |
f22a2cb7 | 471 | duplicate = isolate_path (bb, duplicate, e, |
472 | use_stmt, lhs, false); | |
db242b6d | 473 | |
474 | /* When we remove an incoming edge, we need to | |
475 | reprocess the Ith element. */ | |
476 | next_i = i; | |
477 | cfg_altered = true; | |
478 | } | |
479 | } | |
480 | } | |
481 | } | |
0a76252a | 482 | } |
483 | } | |
484 | ||
67cf9b55 | 485 | /* Look for statements which exhibit erroneous behavior. For example |
30b10261 | 486 | a NULL pointer dereference. |
0a76252a | 487 | |
67cf9b55 | 488 | When found, optimize the block containing the erroneous behavior. */ |
0a76252a | 489 | static void |
67cf9b55 | 490 | find_explicit_erroneous_behavior (void) |
0a76252a | 491 | { |
492 | basic_block bb; | |
493 | ||
fc00614f | 494 | FOR_EACH_BB_FN (bb, cfun) |
0a76252a | 495 | { |
496 | gimple_stmt_iterator si; | |
db242b6d | 497 | |
b61383dd | 498 | /* Out of an abundance of caution, do not isolate paths to a |
499 | block where the block has any abnormal outgoing edges. | |
500 | ||
501 | We might be able to relax this in the future. We have to detect | |
502 | when we have to split the block with the NULL dereference and | |
503 | the trap we insert. We have to preserve abnormal edges out | |
504 | of the isolated block which in turn means updating PHIs at | |
505 | the targets of those abnormal outgoing edges. */ | |
cb287480 | 506 | if (has_abnormal_or_eh_outgoing_edge_p (bb)) |
b61383dd | 507 | continue; |
508 | ||
db242b6d | 509 | /* Now look at the statements in the block and see if any of |
510 | them explicitly dereference a NULL pointer. This happens | |
511 | because of jump threading and constant propagation. */ | |
512 | for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) | |
513 | { | |
42acab1c | 514 | gimple *stmt = gsi_stmt (si); |
db242b6d | 515 | |
0d56015c | 516 | if (stmt_uses_0_or_null_in_undefined_way (stmt)) |
db242b6d | 517 | { |
1a04a3b3 | 518 | insert_trap (&si, null_pointer_node); |
519 | bb = gimple_bb (gsi_stmt (si)); | |
db242b6d | 520 | |
521 | /* Ignore any more operands on this statement and | |
522 | continue the statement iterator (which should | |
523 | terminate its loop immediately. */ | |
524 | cfg_altered = true; | |
525 | break; | |
526 | } | |
f22a2cb7 | 527 | |
528 | /* Detect returning the address of a local variable. This only | |
529 | becomes undefined behavior if the result is used, so we do not | |
530 | insert a trap and only return NULL instead. */ | |
1a91d914 | 531 | if (greturn *return_stmt = dyn_cast <greturn *> (stmt)) |
f22a2cb7 | 532 | { |
1a91d914 | 533 | tree val = gimple_return_retval (return_stmt); |
f22a2cb7 | 534 | if (val && TREE_CODE (val) == ADDR_EXPR) |
535 | { | |
536 | tree valbase = get_base_address (TREE_OPERAND (val, 0)); | |
53e9c5c4 | 537 | if ((VAR_P (valbase) && !is_global_var (valbase)) |
f22a2cb7 | 538 | || TREE_CODE (valbase) == PARM_DECL) |
539 | { | |
540 | /* We only need it for this particular case. */ | |
541 | calculate_dominance_info (CDI_POST_DOMINATORS); | |
542 | const char* msg; | |
543 | bool always_executed = dominated_by_p | |
544 | (CDI_POST_DOMINATORS, | |
545 | single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb); | |
546 | if (always_executed) | |
547 | msg = N_("function returns address of local variable"); | |
548 | else | |
549 | msg = N_("function may return address of " | |
550 | "local variable"); | |
bc35ef65 | 551 | { |
552 | auto_diagnostic_group d; | |
553 | if (warning_at (gimple_location (stmt), | |
554 | OPT_Wreturn_local_addr, msg)) | |
555 | inform (DECL_SOURCE_LOCATION(valbase), | |
556 | "declared here"); | |
557 | } | |
54a5b8c7 | 558 | |
559 | /* Do not modify code if the user only asked for | |
560 | warnings. */ | |
561 | if (flag_isolate_erroneous_paths_dereference | |
562 | || flag_isolate_erroneous_paths_attribute) | |
563 | { | |
564 | tree zero = build_zero_cst (TREE_TYPE (val)); | |
565 | gimple_return_set_retval (return_stmt, zero); | |
566 | update_stmt (stmt); | |
567 | } | |
f22a2cb7 | 568 | } |
569 | } | |
570 | } | |
db242b6d | 571 | } |
572 | } | |
0a76252a | 573 | } |
f22a2cb7 | 574 | |
0a76252a | 575 | /* Search the function for statements which, if executed, would cause |
576 | the program to fault such as a dereference of a NULL pointer. | |
577 | ||
578 | Such a program can't be valid if such a statement was to execute | |
579 | according to ISO standards. | |
580 | ||
581 | We detect explicit NULL pointer dereferences as well as those implied | |
582 | by a PHI argument having a NULL value which unconditionally flows into | |
583 | a dereference in the same block as the PHI. | |
584 | ||
585 | In the former case we replace the offending statement with an | |
586 | unconditional trap and eliminate the outgoing edges from the statement's | |
587 | basic block. This may expose secondary optimization opportunities. | |
588 | ||
30b10261 | 589 | In the latter case, we isolate the path(s) with the NULL PHI |
0a76252a | 590 | feeding the dereference. We can then replace the offending statement |
591 | and eliminate the outgoing edges in the duplicate. Again, this may | |
592 | expose secondary optimization opportunities. | |
593 | ||
594 | A warning for both cases may be advisable as well. | |
595 | ||
596 | Other statically detectable violations of the ISO standard could be | |
597 | handled in a similar way, such as out-of-bounds array indexing. */ | |
598 | ||
599 | static unsigned int | |
600 | gimple_ssa_isolate_erroneous_paths (void) | |
601 | { | |
602 | initialize_original_copy_tables (); | |
603 | ||
604 | /* Search all the blocks for edges which, if traversed, will | |
67cf9b55 | 605 | result in undefined behavior. */ |
0a76252a | 606 | cfg_altered = false; |
607 | ||
608 | /* First handle cases where traversal of a particular edge | |
67cf9b55 | 609 | triggers undefined behavior. These cases require creating |
0a76252a | 610 | duplicate blocks and thus new SSA_NAMEs. |
611 | ||
612 | We want that process complete prior to the phase where we start | |
613 | removing edges from the CFG. Edge removal may ultimately result in | |
614 | removal of PHI nodes and thus releasing SSA_NAMEs back to the | |
615 | name manager. | |
616 | ||
617 | If the two processes run in parallel we could release an SSA_NAME | |
618 | back to the manager but we could still have dangling references | |
619 | to the released SSA_NAME in unreachable blocks. | |
620 | that any released names not have dangling references in the IL. */ | |
67cf9b55 | 621 | find_implicit_erroneous_behavior (); |
622 | find_explicit_erroneous_behavior (); | |
0a76252a | 623 | |
db242b6d | 624 | free_original_copy_tables (); |
625 | ||
30b10261 | 626 | /* We scramble the CFG and loop structures a bit, clean up |
db242b6d | 627 | appropriately. We really should incrementally update the |
628 | loop structures, in theory it shouldn't be that hard. */ | |
15381b1e | 629 | free_dominance_info (CDI_POST_DOMINATORS); |
db242b6d | 630 | if (cfg_altered) |
631 | { | |
632 | free_dominance_info (CDI_DOMINATORS); | |
db242b6d | 633 | loops_state_set (LOOPS_NEED_FIXUP); |
634 | return TODO_cleanup_cfg | TODO_update_ssa; | |
635 | } | |
636 | return 0; | |
637 | } | |
638 | ||
7620bc82 | 639 | namespace { |
640 | const pass_data pass_data_isolate_erroneous_paths = | |
db242b6d | 641 | { |
642 | GIMPLE_PASS, /* type */ | |
643 | "isolate-paths", /* name */ | |
644 | OPTGROUP_NONE, /* optinfo_flags */ | |
db242b6d | 645 | TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */ |
646 | ( PROP_cfg | PROP_ssa ), /* properties_required */ | |
647 | 0, /* properties_provided */ | |
648 | 0, /* properties_destroyed */ | |
649 | 0, /* todo_flags_start */ | |
8b88439e | 650 | 0, /* todo_flags_finish */ |
db242b6d | 651 | }; |
652 | ||
7620bc82 | 653 | class pass_isolate_erroneous_paths : public gimple_opt_pass |
db242b6d | 654 | { |
655 | public: | |
656 | pass_isolate_erroneous_paths (gcc::context *ctxt) | |
657 | : gimple_opt_pass (pass_data_isolate_erroneous_paths, ctxt) | |
658 | {} | |
659 | ||
660 | /* opt_pass methods: */ | |
661 | opt_pass * clone () { return new pass_isolate_erroneous_paths (m_ctxt); } | |
31315c24 | 662 | virtual bool gate (function *) |
663 | { | |
664 | /* If we do not have a suitable builtin function for the trap statement, | |
665 | then do not perform the optimization. */ | |
666 | return (flag_isolate_erroneous_paths_dereference != 0 | |
254d68a9 | 667 | || flag_isolate_erroneous_paths_attribute != 0 |
668 | || warn_null_dereference); | |
31315c24 | 669 | } |
670 | ||
65b0537f | 671 | virtual unsigned int execute (function *) |
672 | { | |
673 | return gimple_ssa_isolate_erroneous_paths (); | |
674 | } | |
db242b6d | 675 | |
2b5aceaf | 676 | }; // class pass_isolate_erroneous_paths |
7620bc82 | 677 | } |
db242b6d | 678 | |
679 | gimple_opt_pass * | |
680 | make_pass_isolate_erroneous_paths (gcc::context *ctxt) | |
681 | { | |
682 | return new pass_isolate_erroneous_paths (ctxt); | |
683 | } |