1 /* Detect paths through the CFG which can never be executed in a conforming
2 program and isolate them.
5 Free Software Foundation, Inc.
7 This file is part of GCC.
9 GCC is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3, or (at your option)
14 GCC is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with GCC; see the file COPYING3. If not see
21 <http://www.gnu.org/licenses/>. */
25 #include "coretypes.h"
28 #include "basic-block.h"
31 #include "tree-ssanames.h"
32 #include "gimple-ssa.h"
33 #include "tree-ssa-operands.h"
34 #include "tree-phinodes.h"
35 #include "ssa-iterators.h"
37 #include "tree-pass.h"
40 static bool cfg_altered
;
42 /* Callback for walk_stmt_load_store_ops.
44 Return TRUE if OP will dereference the tree stored in DATA, FALSE
47 This routine only makes a superficial check for a dereference. Thus,
48 it must only be used if it is safe to return a false negative. */
50 check_loadstore (gimple stmt ATTRIBUTE_UNUSED
, tree op
, void *data
)
52 if ((TREE_CODE (op
) == MEM_REF
|| TREE_CODE (op
) == TARGET_MEM_REF
)
53 && operand_equal_p (TREE_OPERAND (op
, 0), (tree
)data
, 0))
58 /* Insert a trap after SI and remove SI and all statements after the trap. */
61 insert_trap_and_remove_trailing_statements (gimple_stmt_iterator
*si_p
, tree op
)
63 /* We want the NULL pointer dereference to actually occur so that
64 code that wishes to catch the signal can do so.
66 If the dereference is a load, then there's nothing to do as the
67 LHS will be a throw-away SSA_NAME and the LHS is the NULL dereference.
69 If the dereference is a store and we can easily transform the RHS,
70 then simplify the RHS to enable more DCE. */
71 gimple stmt
= gsi_stmt (*si_p
);
72 if (walk_stmt_load_store_ops (stmt
, (void *)op
, NULL
, check_loadstore
)
73 && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (stmt
))))
75 /* We just need to turn the RHS into zero converted to the proper
77 tree type
= TREE_TYPE (gimple_assign_lhs (stmt
));
78 gimple_assign_set_rhs_code (stmt
, INTEGER_CST
);
79 gimple_assign_set_rhs1 (stmt
, fold_convert (type
, integer_zero_node
));
84 = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP
), 0);
85 gimple_seq seq
= NULL
;
86 gimple_seq_add_stmt (&seq
, new_stmt
);
88 /* If we had a NULL pointer dereference, then we want to insert the
89 __builtin_trap after the statement, for the other cases we want
90 to insert before the statement. */
91 if (walk_stmt_load_store_ops (stmt
, (void *)op
,
94 gsi_insert_after (si_p
, seq
, GSI_NEW_STMT
);
96 gsi_insert_before (si_p
, seq
, GSI_NEW_STMT
);
98 /* The iterator points to the __builtin_trap. Advance the iterator
99 and delete everything else in the block. */
101 for (; !gsi_end_p (*si_p
);)
103 stmt
= gsi_stmt (*si_p
);
104 unlink_stmt_vdef (stmt
);
105 gsi_remove (si_p
, true);
110 /* BB when reached via incoming edge E will exhibit undefined behaviour
111 at STMT. Isolate and optimize the path which exhibits undefined
114 Isolation is simple. Duplicate BB and redirect E to BB'.
116 Optimization is simple as well. Replace STMT in BB' with an
117 unconditional trap and remove all outgoing edges from BB'.
119 DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
124 isolate_path (basic_block bb
, basic_block duplicate
,
125 edge e
, gimple stmt
, tree op
)
127 gimple_stmt_iterator si
, si2
;
132 /* First duplicate BB if we have not done so already and remove all
133 the duplicate's outgoing edges as duplicate is going to unconditionally
134 trap. Removing the outgoing edges is both an optimization and ensures
135 we don't need to do any PHI node updates. */
138 duplicate
= duplicate_block (bb
, NULL
, NULL
);
139 for (ei
= ei_start (duplicate
->succs
); (e2
= ei_safe_edge (ei
)); )
143 /* Complete the isolation step by redirecting E to reach DUPLICATE. */
144 e2
= redirect_edge_and_branch (e
, duplicate
);
146 flush_pending_stmts (e2
);
149 /* There may be more than one statement in DUPLICATE which exhibits
150 undefined behaviour. Ultimately we want the first such statement in
151 DUPLCIATE so that we're able to delete as much code as possible.
153 So each time we discover undefined behaviour in DUPLICATE, search for
154 the statement which triggers undefined behaviour. If found, then
155 transform the statement into a trap and delete everything after the
156 statement. If not found, then this particular instance was subsumed by
157 an earlier instance of undefined behaviour and there's nothing to do.
159 This is made more complicated by the fact that we have STMT, which is in
160 BB rather than in DUPLICATE. So we set up two iterators, one for each
161 block and walk forward looking for STMT in BB, advancing each iterator at
164 When we find STMT the second iterator should point to STMT's equivalent in
165 duplicate. If DUPLICATE ends before STMT is found in BB, then there's
168 Ignore labels and debug statements. */
169 si
= gsi_start_nondebug_after_labels_bb (bb
);
170 si2
= gsi_start_nondebug_after_labels_bb (duplicate
);
171 while (!gsi_end_p (si
) && !gsi_end_p (si2
) && gsi_stmt (si
) != stmt
)
173 gsi_next_nondebug (&si
);
174 gsi_next_nondebug (&si2
);
177 /* This would be an indicator that we never found STMT in BB, which should
179 gcc_assert (!gsi_end_p (si
));
181 /* If we did not run to the end of DUPLICATE, then SI points to STMT and
182 SI2 points to the duplicate of STMT in DUPLICATE. Insert a trap
183 before SI2 and remove SI2 and all trailing statements. */
184 if (!gsi_end_p (si2
))
185 insert_trap_and_remove_trailing_statements (&si2
, op
);
190 /* Search the function for statements which, if executed, would cause
191 the program to fault such as a dereference of a NULL pointer.
193 Such a program can't be valid if such a statement was to execute
194 according to ISO standards.
196 We detect explicit NULL pointer dereferences as well as those implied
197 by a PHI argument having a NULL value which unconditionally flows into
198 a dereference in the same block as the PHI.
200 In the former case we replace the offending statement with an
201 unconditional trap and eliminate the outgoing edges from the statement's
202 basic block. This may expose secondary optimization opportunities.
204 In the latter case, we isolate the path(s) with the NULL PHI
205 feeding the dereference. We can then replace the offending statement
206 and eliminate the outgoing edges in the duplicate. Again, this may
207 expose secondary optimization opportunities.
209 A warning for both cases may be advisable as well.
211 Other statically detectable violations of the ISO standard could be
212 handled in a similar way, such as out-of-bounds array indexing. */
215 gimple_ssa_isolate_erroneous_paths (void)
219 initialize_original_copy_tables ();
221 /* Search all the blocks for edges which, if traversed, will
222 result in undefined behaviour. */
226 gimple_stmt_iterator si
;
228 /* First look for a PHI which sets a pointer to NULL and which
229 is then dereferenced within BB. This is somewhat overly
230 conservative, but probably catches most of the interesting
232 for (si
= gsi_start_phis (bb
); !gsi_end_p (si
); gsi_next (&si
))
234 gimple phi
= gsi_stmt (si
);
235 tree lhs
= gimple_phi_result (phi
);
237 /* If the result is not a pointer, then there is no need to
238 examine the arguments. */
239 if (!POINTER_TYPE_P (TREE_TYPE (lhs
)))
242 /* PHI produces a pointer result. See if any of the PHI's
245 When we remove an edge, we want to reprocess the current
246 index, hence the ugly way we update I for each iteration. */
247 basic_block duplicate
= NULL
;
248 for (unsigned i
= 0, next_i
= 0;
249 i
< gimple_phi_num_args (phi
);
252 tree op
= gimple_phi_arg_def (phi
, i
);
256 if (!integer_zerop (op
))
259 edge e
= gimple_phi_arg_edge (phi
, i
);
260 imm_use_iterator iter
;
263 /* We've got a NULL PHI argument. Now see if the
264 PHI's result is dereferenced within BB. */
265 FOR_EACH_IMM_USE_STMT (use_stmt
, iter
, lhs
)
267 /* We only care about uses in BB. Catching cases in
268 in other blocks would require more complex path
270 if (gimple_bb (use_stmt
) != bb
)
273 if (infer_nonnull_range (use_stmt
, lhs
))
275 duplicate
= isolate_path (bb
, duplicate
,
278 /* When we remove an incoming edge, we need to
279 reprocess the Ith element. */
287 /* Now look at the statements in the block and see if any of
288 them explicitly dereference a NULL pointer. This happens
289 because of jump threading and constant propagation. */
290 for (si
= gsi_start_bb (bb
); !gsi_end_p (si
); gsi_next (&si
))
292 gimple stmt
= gsi_stmt (si
);
294 /* By passing null_pointer_node, we can use infer_nonnull_range
295 to detect explicit NULL pointer dereferences and other uses
296 where a non-NULL value is required. */
297 if (infer_nonnull_range (stmt
, null_pointer_node
))
299 insert_trap_and_remove_trailing_statements (&si
,
302 /* And finally, remove all outgoing edges from BB. */
304 for (edge_iterator ei
= ei_start (bb
->succs
);
305 (e
= ei_safe_edge (ei
)); )
308 /* Ignore any more operands on this statement and
309 continue the statement iterator (which should
310 terminate its loop immediately. */
316 free_original_copy_tables ();
318 /* We scramble the CFG and loop structures a bit, clean up
319 appropriately. We really should incrementally update the
320 loop structures, in theory it shouldn't be that hard. */
323 free_dominance_info (CDI_DOMINATORS
);
324 free_dominance_info (CDI_POST_DOMINATORS
);
325 loops_state_set (LOOPS_NEED_FIXUP
);
326 return TODO_cleanup_cfg
| TODO_update_ssa
;
332 gate_isolate_erroneous_paths (void)
334 /* If we do not have a suitable builtin function for the trap statement,
335 then do not perform the optimization. */
336 return (flag_isolate_erroneous_paths
!= 0);
340 const pass_data pass_data_isolate_erroneous_paths
=
342 GIMPLE_PASS
, /* type */
343 "isolate-paths", /* name */
344 OPTGROUP_NONE
, /* optinfo_flags */
346 true, /* has_execute */
347 TV_ISOLATE_ERRONEOUS_PATHS
, /* tv_id */
348 ( PROP_cfg
| PROP_ssa
), /* properties_required */
349 0, /* properties_provided */
350 0, /* properties_destroyed */
351 0, /* todo_flags_start */
352 TODO_verify_ssa
, /* todo_flags_finish */
355 class pass_isolate_erroneous_paths
: public gimple_opt_pass
358 pass_isolate_erroneous_paths (gcc::context
*ctxt
)
359 : gimple_opt_pass (pass_data_isolate_erroneous_paths
, ctxt
)
362 /* opt_pass methods: */
363 opt_pass
* clone () { return new pass_isolate_erroneous_paths (m_ctxt
); }
364 bool gate () { return gate_isolate_erroneous_paths (); }
365 unsigned int execute () { return gimple_ssa_isolate_erroneous_paths (); }
367 }; // class pass_uncprop
371 make_pass_isolate_erroneous_paths (gcc::context
*ctxt
)
373 return new pass_isolate_erroneous_paths (ctxt
);