gimple-ssa-isolate-paths.c (check_loadstore): New function.
[gcc.git] / gcc / gimple-ssa-isolate-paths.c
1 /* Detect paths through the CFG which can never be executed in a conforming
2 program and isolate them.
3
4 Copyright (C) 2013
5 Free Software Foundation, Inc.
6
7 This file is part of GCC.
8
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)
12 any later version.
13
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.
18
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/>. */
22
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "tree.h"
27 #include "flags.h"
28 #include "basic-block.h"
29 #include "gimple.h"
30 #include "tree-ssa.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"
36 #include "cfgloop.h"
37 #include "tree-pass.h"
38
39
40 static bool cfg_altered;
41
42 /* Callback for walk_stmt_load_store_ops.
43
44 Return TRUE if OP will dereference the tree stored in DATA, FALSE
45 otherwise.
46
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. */
49 static bool
50 check_loadstore (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
51 {
52 if ((TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF)
53 && operand_equal_p (TREE_OPERAND (op, 0), (tree)data, 0))
54 return true;
55 return false;
56 }
57
58 /* Insert a trap after SI and remove SI and all statements after the trap. */
59
60 static void
61 insert_trap_and_remove_trailing_statements (gimple_stmt_iterator *si_p, tree op)
62 {
63 /* We want the NULL pointer dereference to actually occur so that
64 code that wishes to catch the signal can do so.
65
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.
68
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))))
74 {
75 /* We just need to turn the RHS into zero converted to the proper
76 type. */
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));
80 update_stmt (stmt);
81 }
82
83 gimple new_stmt
84 = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
85 gimple_seq seq = NULL;
86 gimple_seq_add_stmt (&seq, new_stmt);
87
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,
92 check_loadstore,
93 check_loadstore))
94 gsi_insert_after (si_p, seq, GSI_NEW_STMT);
95 else
96 gsi_insert_before (si_p, seq, GSI_NEW_STMT);
97
98 /* The iterator points to the __builtin_trap. Advance the iterator
99 and delete everything else in the block. */
100 gsi_next (si_p);
101 for (; !gsi_end_p (*si_p);)
102 {
103 stmt = gsi_stmt (*si_p);
104 unlink_stmt_vdef (stmt);
105 gsi_remove (si_p, true);
106 release_defs (stmt);
107 }
108 }
109
110 /* BB when reached via incoming edge E will exhibit undefined behaviour
111 at STMT. Isolate and optimize the path which exhibits undefined
112 behaviour.
113
114 Isolation is simple. Duplicate BB and redirect E to BB'.
115
116 Optimization is simple as well. Replace STMT in BB' with an
117 unconditional trap and remove all outgoing edges from BB'.
118
119 DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
120
121 Return BB'. */
122
123 basic_block
124 isolate_path (basic_block bb, basic_block duplicate,
125 edge e, gimple stmt, tree op)
126 {
127 gimple_stmt_iterator si, si2;
128 edge_iterator ei;
129 edge e2;
130
131
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. */
136 if (!duplicate)
137 {
138 duplicate = duplicate_block (bb, NULL, NULL);
139 for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
140 remove_edge (e2);
141 }
142
143 /* Complete the isolation step by redirecting E to reach DUPLICATE. */
144 e2 = redirect_edge_and_branch (e, duplicate);
145 if (e2)
146 flush_pending_stmts (e2);
147
148
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.
152
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.
158
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
162 each step.
163
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
166 nothing to do.
167
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)
172 {
173 gsi_next_nondebug (&si);
174 gsi_next_nondebug (&si2);
175 }
176
177 /* This would be an indicator that we never found STMT in BB, which should
178 never happen. */
179 gcc_assert (!gsi_end_p (si));
180
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);
186
187 return duplicate;
188 }
189
190 /* Search the function for statements which, if executed, would cause
191 the program to fault such as a dereference of a NULL pointer.
192
193 Such a program can't be valid if such a statement was to execute
194 according to ISO standards.
195
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.
199
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.
203
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.
208
209 A warning for both cases may be advisable as well.
210
211 Other statically detectable violations of the ISO standard could be
212 handled in a similar way, such as out-of-bounds array indexing. */
213
214 static unsigned int
215 gimple_ssa_isolate_erroneous_paths (void)
216 {
217 basic_block bb;
218
219 initialize_original_copy_tables ();
220
221 /* Search all the blocks for edges which, if traversed, will
222 result in undefined behaviour. */
223 cfg_altered = false;
224 FOR_EACH_BB (bb)
225 {
226 gimple_stmt_iterator si;
227
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
231 cases. */
232 for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
233 {
234 gimple phi = gsi_stmt (si);
235 tree lhs = gimple_phi_result (phi);
236
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)))
240 continue;
241
242 /* PHI produces a pointer result. See if any of the PHI's
243 arguments are NULL.
244
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);
250 i = next_i)
251 {
252 tree op = gimple_phi_arg_def (phi, i);
253
254 next_i = i + 1;
255
256 if (!integer_zerop (op))
257 continue;
258
259 edge e = gimple_phi_arg_edge (phi, i);
260 imm_use_iterator iter;
261 gimple use_stmt;
262
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)
266 {
267 /* We only care about uses in BB. Catching cases in
268 in other blocks would require more complex path
269 isolation code. */
270 if (gimple_bb (use_stmt) != bb)
271 continue;
272
273 if (infer_nonnull_range (use_stmt, lhs))
274 {
275 duplicate = isolate_path (bb, duplicate,
276 e, use_stmt, lhs);
277
278 /* When we remove an incoming edge, we need to
279 reprocess the Ith element. */
280 next_i = i;
281 cfg_altered = true;
282 }
283 }
284 }
285 }
286
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))
291 {
292 gimple stmt = gsi_stmt (si);
293
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))
298 {
299 insert_trap_and_remove_trailing_statements (&si,
300 null_pointer_node);
301
302 /* And finally, remove all outgoing edges from BB. */
303 edge e;
304 for (edge_iterator ei = ei_start (bb->succs);
305 (e = ei_safe_edge (ei)); )
306 remove_edge (e);
307
308 /* Ignore any more operands on this statement and
309 continue the statement iterator (which should
310 terminate its loop immediately. */
311 cfg_altered = true;
312 break;
313 }
314 }
315 }
316 free_original_copy_tables ();
317
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. */
321 if (cfg_altered)
322 {
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;
327 }
328 return 0;
329 }
330
331 static bool
332 gate_isolate_erroneous_paths (void)
333 {
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);
337 }
338
339 namespace {
340 const pass_data pass_data_isolate_erroneous_paths =
341 {
342 GIMPLE_PASS, /* type */
343 "isolate-paths", /* name */
344 OPTGROUP_NONE, /* optinfo_flags */
345 true, /* has_gate */
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 */
353 };
354
355 class pass_isolate_erroneous_paths : public gimple_opt_pass
356 {
357 public:
358 pass_isolate_erroneous_paths (gcc::context *ctxt)
359 : gimple_opt_pass (pass_data_isolate_erroneous_paths, ctxt)
360 {}
361
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 (); }
366
367 }; // class pass_uncprop
368 }
369
370 gimple_opt_pass *
371 make_pass_isolate_erroneous_paths (gcc::context *ctxt)
372 {
373 return new pass_isolate_erroneous_paths (ctxt);
374 }