annotate gcc/gimple-ssa-isolate-paths.c @ 118:fd00160c1b76

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