annotate gcc/gimple-ssa-isolate-paths.c @ 131:84e7813d76e9

gcc-8.2
author mir3636
date Thu, 25 Oct 2018 07:37:49 +0900
parents 04ced10e8804
children 1830386684a0
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
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
4 Copyright (C) 2013-2018 Free Software Foundation, Inc.
111
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;
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
141 profile_count count = e->count ();
111
kono
parents:
diff changeset
142
kono
parents:
diff changeset
143 for (si = gsi_start_bb (bb); gsi_stmt (si) != stmt; gsi_next (&si))
kono
parents:
diff changeset
144 if (stmt_can_terminate_bb_p (gsi_stmt (si)))
kono
parents:
diff changeset
145 {
kono
parents:
diff changeset
146 impossible = false;
kono
parents:
diff changeset
147 break;
kono
parents:
diff changeset
148 }
kono
parents:
diff changeset
149 force_edge_cold (e, impossible);
kono
parents:
diff changeset
150
kono
parents:
diff changeset
151 /* First duplicate BB if we have not done so already and remove all
kono
parents:
diff changeset
152 the duplicate's outgoing edges as duplicate is going to unconditionally
kono
parents:
diff changeset
153 trap. Removing the outgoing edges is both an optimization and ensures
kono
parents:
diff changeset
154 we don't need to do any PHI node updates. */
kono
parents:
diff changeset
155 if (!duplicate)
kono
parents:
diff changeset
156 {
kono
parents:
diff changeset
157 duplicate = duplicate_block (bb, NULL, NULL);
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
158 duplicate->count = profile_count::zero ();
111
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 }
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
163 bb->count -= count;
111
kono
parents:
diff changeset
164
kono
parents:
diff changeset
165 /* Complete the isolation step by redirecting E to reach DUPLICATE. */
kono
parents:
diff changeset
166 e2 = redirect_edge_and_branch (e, duplicate);
kono
parents:
diff changeset
167 if (e2)
kono
parents:
diff changeset
168 {
kono
parents:
diff changeset
169 flush_pending_stmts (e2);
kono
parents:
diff changeset
170
kono
parents:
diff changeset
171 /* Update profile only when redirection is really processed. */
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
172 bb->count += e->count ();
111
kono
parents:
diff changeset
173 }
kono
parents:
diff changeset
174
kono
parents:
diff changeset
175 /* There may be more than one statement in DUPLICATE which exhibits
kono
parents:
diff changeset
176 undefined behavior. Ultimately we want the first such statement in
kono
parents:
diff changeset
177 DUPLCIATE so that we're able to delete as much code as possible.
kono
parents:
diff changeset
178
kono
parents:
diff changeset
179 So each time we discover undefined behavior in DUPLICATE, search for
kono
parents:
diff changeset
180 the statement which triggers undefined behavior. If found, then
kono
parents:
diff changeset
181 transform the statement into a trap and delete everything after the
kono
parents:
diff changeset
182 statement. If not found, then this particular instance was subsumed by
kono
parents:
diff changeset
183 an earlier instance of undefined behavior and there's nothing to do.
kono
parents:
diff changeset
184
kono
parents:
diff changeset
185 This is made more complicated by the fact that we have STMT, which is in
kono
parents:
diff changeset
186 BB rather than in DUPLICATE. So we set up two iterators, one for each
kono
parents:
diff changeset
187 block and walk forward looking for STMT in BB, advancing each iterator at
kono
parents:
diff changeset
188 each step.
kono
parents:
diff changeset
189
kono
parents:
diff changeset
190 When we find STMT the second iterator should point to STMT's equivalent in
kono
parents:
diff changeset
191 duplicate. If DUPLICATE ends before STMT is found in BB, then there's
kono
parents:
diff changeset
192 nothing to do.
kono
parents:
diff changeset
193
kono
parents:
diff changeset
194 Ignore labels and debug statements. */
kono
parents:
diff changeset
195 si = gsi_start_nondebug_after_labels_bb (bb);
kono
parents:
diff changeset
196 si2 = gsi_start_nondebug_after_labels_bb (duplicate);
kono
parents:
diff changeset
197 while (!gsi_end_p (si) && !gsi_end_p (si2) && gsi_stmt (si) != stmt)
kono
parents:
diff changeset
198 {
kono
parents:
diff changeset
199 gsi_next_nondebug (&si);
kono
parents:
diff changeset
200 gsi_next_nondebug (&si2);
kono
parents:
diff changeset
201 }
kono
parents:
diff changeset
202
kono
parents:
diff changeset
203 /* This would be an indicator that we never found STMT in BB, which should
kono
parents:
diff changeset
204 never happen. */
kono
parents:
diff changeset
205 gcc_assert (!gsi_end_p (si));
kono
parents:
diff changeset
206
kono
parents:
diff changeset
207 /* If we did not run to the end of DUPLICATE, then SI points to STMT and
kono
parents:
diff changeset
208 SI2 points to the duplicate of STMT in DUPLICATE. Insert a trap
kono
parents:
diff changeset
209 before SI2 and remove SI2 and all trailing statements. */
kono
parents:
diff changeset
210 if (!gsi_end_p (si2))
kono
parents:
diff changeset
211 {
kono
parents:
diff changeset
212 if (ret_zero)
kono
parents:
diff changeset
213 {
kono
parents:
diff changeset
214 greturn *ret = as_a <greturn *> (gsi_stmt (si2));
kono
parents:
diff changeset
215 tree zero = build_zero_cst (TREE_TYPE (gimple_return_retval (ret)));
kono
parents:
diff changeset
216 gimple_return_set_retval (ret, zero);
kono
parents:
diff changeset
217 update_stmt (ret);
kono
parents:
diff changeset
218 }
kono
parents:
diff changeset
219 else
kono
parents:
diff changeset
220 insert_trap (&si2, op);
kono
parents:
diff changeset
221 }
kono
parents:
diff changeset
222
kono
parents:
diff changeset
223 return duplicate;
kono
parents:
diff changeset
224 }
kono
parents:
diff changeset
225
kono
parents:
diff changeset
226 /* Return TRUE if STMT is a div/mod operation using DIVISOR as the divisor.
kono
parents:
diff changeset
227 FALSE otherwise. */
kono
parents:
diff changeset
228
kono
parents:
diff changeset
229 static bool
kono
parents:
diff changeset
230 is_divmod_with_given_divisor (gimple *stmt, tree divisor)
kono
parents:
diff changeset
231 {
kono
parents:
diff changeset
232 /* Only assignments matter. */
kono
parents:
diff changeset
233 if (!is_gimple_assign (stmt))
kono
parents:
diff changeset
234 return false;
kono
parents:
diff changeset
235
kono
parents:
diff changeset
236 /* Check for every DIV/MOD expression. */
kono
parents:
diff changeset
237 enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
kono
parents:
diff changeset
238 if (rhs_code == TRUNC_DIV_EXPR
kono
parents:
diff changeset
239 || rhs_code == FLOOR_DIV_EXPR
kono
parents:
diff changeset
240 || rhs_code == CEIL_DIV_EXPR
kono
parents:
diff changeset
241 || rhs_code == EXACT_DIV_EXPR
kono
parents:
diff changeset
242 || rhs_code == ROUND_DIV_EXPR
kono
parents:
diff changeset
243 || rhs_code == TRUNC_MOD_EXPR
kono
parents:
diff changeset
244 || rhs_code == FLOOR_MOD_EXPR
kono
parents:
diff changeset
245 || rhs_code == CEIL_MOD_EXPR
kono
parents:
diff changeset
246 || rhs_code == ROUND_MOD_EXPR)
kono
parents:
diff changeset
247 {
kono
parents:
diff changeset
248 /* Pointer equality is fine when DIVISOR is an SSA_NAME, but
kono
parents:
diff changeset
249 not sufficient for constants which may have different types. */
kono
parents:
diff changeset
250 if (operand_equal_p (gimple_assign_rhs2 (stmt), divisor, 0))
kono
parents:
diff changeset
251 return true;
kono
parents:
diff changeset
252 }
kono
parents:
diff changeset
253 return false;
kono
parents:
diff changeset
254 }
kono
parents:
diff changeset
255
kono
parents:
diff changeset
256 /* NAME is an SSA_NAME that we have already determined has the value 0 or NULL.
kono
parents:
diff changeset
257
kono
parents:
diff changeset
258 Return TRUE if USE_STMT uses NAME in a way where a 0 or NULL value results
kono
parents:
diff changeset
259 in undefined behavior, FALSE otherwise
kono
parents:
diff changeset
260
kono
parents:
diff changeset
261 LOC is used for issuing diagnostics. This case represents potential
kono
parents:
diff changeset
262 undefined behavior exposed by path splitting and that's reflected in
kono
parents:
diff changeset
263 the diagnostic. */
kono
parents:
diff changeset
264
kono
parents:
diff changeset
265 bool
kono
parents:
diff changeset
266 stmt_uses_name_in_undefined_way (gimple *use_stmt, tree name, location_t loc)
kono
parents:
diff changeset
267 {
kono
parents:
diff changeset
268 /* If we are working with a non pointer type, then see
kono
parents:
diff changeset
269 if this use is a DIV/MOD operation using NAME as the
kono
parents:
diff changeset
270 divisor. */
kono
parents:
diff changeset
271 if (!POINTER_TYPE_P (TREE_TYPE (name)))
kono
parents:
diff changeset
272 {
kono
parents:
diff changeset
273 if (!flag_non_call_exceptions)
kono
parents:
diff changeset
274 return is_divmod_with_given_divisor (use_stmt, name);
kono
parents:
diff changeset
275 return false;
kono
parents:
diff changeset
276 }
kono
parents:
diff changeset
277
kono
parents:
diff changeset
278 /* NAME is a pointer, so see if it's used in a context where it must
kono
parents:
diff changeset
279 be non-NULL. */
kono
parents:
diff changeset
280 bool by_dereference
kono
parents:
diff changeset
281 = infer_nonnull_range_by_dereference (use_stmt, name);
kono
parents:
diff changeset
282
kono
parents:
diff changeset
283 if (by_dereference
kono
parents:
diff changeset
284 || infer_nonnull_range_by_attribute (use_stmt, name))
kono
parents:
diff changeset
285 {
kono
parents:
diff changeset
286
kono
parents:
diff changeset
287 if (by_dereference)
kono
parents:
diff changeset
288 {
kono
parents:
diff changeset
289 warning_at (loc, OPT_Wnull_dereference,
kono
parents:
diff changeset
290 "potential null pointer dereference");
kono
parents:
diff changeset
291 if (!flag_isolate_erroneous_paths_dereference)
kono
parents:
diff changeset
292 return false;
kono
parents:
diff changeset
293 }
kono
parents:
diff changeset
294 else
kono
parents:
diff changeset
295 {
kono
parents:
diff changeset
296 if (!flag_isolate_erroneous_paths_attribute)
kono
parents:
diff changeset
297 return false;
kono
parents:
diff changeset
298 }
kono
parents:
diff changeset
299 return true;
kono
parents:
diff changeset
300 }
kono
parents:
diff changeset
301 return false;
kono
parents:
diff changeset
302 }
kono
parents:
diff changeset
303
kono
parents:
diff changeset
304 /* Return TRUE if USE_STMT uses 0 or NULL in a context which results in
kono
parents:
diff changeset
305 undefined behavior, FALSE otherwise.
kono
parents:
diff changeset
306
kono
parents:
diff changeset
307 These cases are explicit in the IL. */
kono
parents:
diff changeset
308
kono
parents:
diff changeset
309 bool
kono
parents:
diff changeset
310 stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
kono
parents:
diff changeset
311 {
kono
parents:
diff changeset
312 if (!flag_non_call_exceptions
kono
parents:
diff changeset
313 && is_divmod_with_given_divisor (stmt, integer_zero_node))
kono
parents:
diff changeset
314 return true;
kono
parents:
diff changeset
315
kono
parents:
diff changeset
316 /* By passing null_pointer_node, we can use the
kono
parents:
diff changeset
317 infer_nonnull_range functions to detect explicit NULL
kono
parents:
diff changeset
318 pointer dereferences and other uses where a non-NULL
kono
parents:
diff changeset
319 value is required. */
kono
parents:
diff changeset
320
kono
parents:
diff changeset
321 bool by_dereference
kono
parents:
diff changeset
322 = infer_nonnull_range_by_dereference (stmt, null_pointer_node);
kono
parents:
diff changeset
323 if (by_dereference
kono
parents:
diff changeset
324 || infer_nonnull_range_by_attribute (stmt, null_pointer_node))
kono
parents:
diff changeset
325 {
kono
parents:
diff changeset
326 if (by_dereference)
kono
parents:
diff changeset
327 {
kono
parents:
diff changeset
328 location_t loc = gimple_location (stmt);
kono
parents:
diff changeset
329 warning_at (loc, OPT_Wnull_dereference,
kono
parents:
diff changeset
330 "null pointer dereference");
kono
parents:
diff changeset
331 if (!flag_isolate_erroneous_paths_dereference)
kono
parents:
diff changeset
332 return false;
kono
parents:
diff changeset
333 }
kono
parents:
diff changeset
334 else
kono
parents:
diff changeset
335 {
kono
parents:
diff changeset
336 if (!flag_isolate_erroneous_paths_attribute)
kono
parents:
diff changeset
337 return false;
kono
parents:
diff changeset
338 }
kono
parents:
diff changeset
339 return true;
kono
parents:
diff changeset
340 }
kono
parents:
diff changeset
341 return false;
kono
parents:
diff changeset
342 }
kono
parents:
diff changeset
343
kono
parents:
diff changeset
344 /* Look for PHI nodes which feed statements in the same block where
kono
parents:
diff changeset
345 the value of the PHI node implies the statement is erroneous.
kono
parents:
diff changeset
346
kono
parents:
diff changeset
347 For example, a NULL PHI arg value which then feeds a pointer
kono
parents:
diff changeset
348 dereference.
kono
parents:
diff changeset
349
kono
parents:
diff changeset
350 When found isolate and optimize the path associated with the PHI
kono
parents:
diff changeset
351 argument feeding the erroneous statement. */
kono
parents:
diff changeset
352 static void
kono
parents:
diff changeset
353 find_implicit_erroneous_behavior (void)
kono
parents:
diff changeset
354 {
kono
parents:
diff changeset
355 basic_block bb;
kono
parents:
diff changeset
356
kono
parents:
diff changeset
357 FOR_EACH_BB_FN (bb, cfun)
kono
parents:
diff changeset
358 {
kono
parents:
diff changeset
359 gphi_iterator si;
kono
parents:
diff changeset
360
kono
parents:
diff changeset
361 /* Out of an abundance of caution, do not isolate paths to a
kono
parents:
diff changeset
362 block where the block has any abnormal outgoing edges.
kono
parents:
diff changeset
363
kono
parents:
diff changeset
364 We might be able to relax this in the future. We have to detect
kono
parents:
diff changeset
365 when we have to split the block with the NULL dereference and
kono
parents:
diff changeset
366 the trap we insert. We have to preserve abnormal edges out
kono
parents:
diff changeset
367 of the isolated block which in turn means updating PHIs at
kono
parents:
diff changeset
368 the targets of those abnormal outgoing edges. */
kono
parents:
diff changeset
369 if (has_abnormal_or_eh_outgoing_edge_p (bb))
kono
parents:
diff changeset
370 continue;
kono
parents:
diff changeset
371
kono
parents:
diff changeset
372
kono
parents:
diff changeset
373 /* If BB has an edge to itself, then duplication of BB below
kono
parents:
diff changeset
374 could result in reallocation of BB's PHI nodes. If that happens
kono
parents:
diff changeset
375 then the loop below over the PHIs would use the old PHI and
kono
parents:
diff changeset
376 thus invalid information. We don't have a good way to know
kono
parents:
diff changeset
377 if a PHI has been reallocated, so just avoid isolation in
kono
parents:
diff changeset
378 this case. */
kono
parents:
diff changeset
379 if (find_edge (bb, bb))
kono
parents:
diff changeset
380 continue;
kono
parents:
diff changeset
381
kono
parents:
diff changeset
382 /* First look for a PHI which sets a pointer to NULL and which
kono
parents:
diff changeset
383 is then dereferenced within BB. This is somewhat overly
kono
parents:
diff changeset
384 conservative, but probably catches most of the interesting
kono
parents:
diff changeset
385 cases. */
kono
parents:
diff changeset
386 for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
kono
parents:
diff changeset
387 {
kono
parents:
diff changeset
388 gphi *phi = si.phi ();
kono
parents:
diff changeset
389 tree lhs = gimple_phi_result (phi);
kono
parents:
diff changeset
390
kono
parents:
diff changeset
391 /* PHI produces a pointer result. See if any of the PHI's
kono
parents:
diff changeset
392 arguments are NULL.
kono
parents:
diff changeset
393
kono
parents:
diff changeset
394 When we remove an edge, we want to reprocess the current
kono
parents:
diff changeset
395 index, hence the ugly way we update I for each iteration. */
kono
parents:
diff changeset
396 basic_block duplicate = NULL;
kono
parents:
diff changeset
397 for (unsigned i = 0, next_i = 0;
kono
parents:
diff changeset
398 i < gimple_phi_num_args (phi);
kono
parents:
diff changeset
399 i = next_i)
kono
parents:
diff changeset
400 {
kono
parents:
diff changeset
401 tree op = gimple_phi_arg_def (phi, i);
kono
parents:
diff changeset
402 edge e = gimple_phi_arg_edge (phi, i);
kono
parents:
diff changeset
403 imm_use_iterator iter;
kono
parents:
diff changeset
404 gimple *use_stmt;
kono
parents:
diff changeset
405
kono
parents:
diff changeset
406 next_i = i + 1;
kono
parents:
diff changeset
407
kono
parents:
diff changeset
408 if (TREE_CODE (op) == ADDR_EXPR)
kono
parents:
diff changeset
409 {
kono
parents:
diff changeset
410 tree valbase = get_base_address (TREE_OPERAND (op, 0));
kono
parents:
diff changeset
411 if ((VAR_P (valbase) && !is_global_var (valbase))
kono
parents:
diff changeset
412 || TREE_CODE (valbase) == PARM_DECL)
kono
parents:
diff changeset
413 {
kono
parents:
diff changeset
414 FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
kono
parents:
diff changeset
415 {
kono
parents:
diff changeset
416 greturn *return_stmt
kono
parents:
diff changeset
417 = dyn_cast <greturn *> (use_stmt);
kono
parents:
diff changeset
418 if (!return_stmt)
kono
parents:
diff changeset
419 continue;
kono
parents:
diff changeset
420
kono
parents:
diff changeset
421 if (gimple_return_retval (return_stmt) != lhs)
kono
parents:
diff changeset
422 continue;
kono
parents:
diff changeset
423
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
424 {
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
425 auto_diagnostic_group d;
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
426 if (warning_at (gimple_location (use_stmt),
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
427 OPT_Wreturn_local_addr,
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
428 "function may return address "
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
429 "of local variable"))
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
430 inform (DECL_SOURCE_LOCATION(valbase),
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
431 "declared here");
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
432 }
111
kono
parents:
diff changeset
433
kono
parents:
diff changeset
434 if (gimple_bb (use_stmt) == bb)
kono
parents:
diff changeset
435 {
kono
parents:
diff changeset
436 duplicate = isolate_path (bb, duplicate, e,
kono
parents:
diff changeset
437 use_stmt, lhs, true);
kono
parents:
diff changeset
438
kono
parents:
diff changeset
439 /* When we remove an incoming edge, we need to
kono
parents:
diff changeset
440 reprocess the Ith element. */
kono
parents:
diff changeset
441 next_i = i;
kono
parents:
diff changeset
442 cfg_altered = true;
kono
parents:
diff changeset
443 }
kono
parents:
diff changeset
444 }
kono
parents:
diff changeset
445 }
kono
parents:
diff changeset
446 }
kono
parents:
diff changeset
447
kono
parents:
diff changeset
448 if (!integer_zerop (op))
kono
parents:
diff changeset
449 continue;
kono
parents:
diff changeset
450
kono
parents:
diff changeset
451 location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
kono
parents:
diff changeset
452
kono
parents:
diff changeset
453 /* We've got a NULL PHI argument. Now see if the
kono
parents:
diff changeset
454 PHI's result is dereferenced within BB. */
kono
parents:
diff changeset
455 FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
kono
parents:
diff changeset
456 {
kono
parents:
diff changeset
457 /* We only care about uses in BB. Catching cases in
kono
parents:
diff changeset
458 in other blocks would require more complex path
kono
parents:
diff changeset
459 isolation code. */
kono
parents:
diff changeset
460 if (gimple_bb (use_stmt) != bb)
kono
parents:
diff changeset
461 continue;
kono
parents:
diff changeset
462
kono
parents:
diff changeset
463 location_t loc = gimple_location (use_stmt)
kono
parents:
diff changeset
464 ? gimple_location (use_stmt)
kono
parents:
diff changeset
465 : phi_arg_loc;
kono
parents:
diff changeset
466
kono
parents:
diff changeset
467 if (stmt_uses_name_in_undefined_way (use_stmt, lhs, loc))
kono
parents:
diff changeset
468 {
kono
parents:
diff changeset
469 duplicate = isolate_path (bb, duplicate, e,
kono
parents:
diff changeset
470 use_stmt, lhs, false);
kono
parents:
diff changeset
471
kono
parents:
diff changeset
472 /* When we remove an incoming edge, we need to
kono
parents:
diff changeset
473 reprocess the Ith element. */
kono
parents:
diff changeset
474 next_i = i;
kono
parents:
diff changeset
475 cfg_altered = true;
kono
parents:
diff changeset
476 }
kono
parents:
diff changeset
477 }
kono
parents:
diff changeset
478 }
kono
parents:
diff changeset
479 }
kono
parents:
diff changeset
480 }
kono
parents:
diff changeset
481 }
kono
parents:
diff changeset
482
kono
parents:
diff changeset
483 /* Look for statements which exhibit erroneous behavior. For example
kono
parents:
diff changeset
484 a NULL pointer dereference.
kono
parents:
diff changeset
485
kono
parents:
diff changeset
486 When found, optimize the block containing the erroneous behavior. */
kono
parents:
diff changeset
487 static void
kono
parents:
diff changeset
488 find_explicit_erroneous_behavior (void)
kono
parents:
diff changeset
489 {
kono
parents:
diff changeset
490 basic_block bb;
kono
parents:
diff changeset
491
kono
parents:
diff changeset
492 FOR_EACH_BB_FN (bb, cfun)
kono
parents:
diff changeset
493 {
kono
parents:
diff changeset
494 gimple_stmt_iterator si;
kono
parents:
diff changeset
495
kono
parents:
diff changeset
496 /* Out of an abundance of caution, do not isolate paths to a
kono
parents:
diff changeset
497 block where the block has any abnormal outgoing edges.
kono
parents:
diff changeset
498
kono
parents:
diff changeset
499 We might be able to relax this in the future. We have to detect
kono
parents:
diff changeset
500 when we have to split the block with the NULL dereference and
kono
parents:
diff changeset
501 the trap we insert. We have to preserve abnormal edges out
kono
parents:
diff changeset
502 of the isolated block which in turn means updating PHIs at
kono
parents:
diff changeset
503 the targets of those abnormal outgoing edges. */
kono
parents:
diff changeset
504 if (has_abnormal_or_eh_outgoing_edge_p (bb))
kono
parents:
diff changeset
505 continue;
kono
parents:
diff changeset
506
kono
parents:
diff changeset
507 /* Now look at the statements in the block and see if any of
kono
parents:
diff changeset
508 them explicitly dereference a NULL pointer. This happens
kono
parents:
diff changeset
509 because of jump threading and constant propagation. */
kono
parents:
diff changeset
510 for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
kono
parents:
diff changeset
511 {
kono
parents:
diff changeset
512 gimple *stmt = gsi_stmt (si);
kono
parents:
diff changeset
513
kono
parents:
diff changeset
514 if (stmt_uses_0_or_null_in_undefined_way (stmt))
kono
parents:
diff changeset
515 {
kono
parents:
diff changeset
516 insert_trap (&si, null_pointer_node);
kono
parents:
diff changeset
517 bb = gimple_bb (gsi_stmt (si));
kono
parents:
diff changeset
518
kono
parents:
diff changeset
519 /* Ignore any more operands on this statement and
kono
parents:
diff changeset
520 continue the statement iterator (which should
kono
parents:
diff changeset
521 terminate its loop immediately. */
kono
parents:
diff changeset
522 cfg_altered = true;
kono
parents:
diff changeset
523 break;
kono
parents:
diff changeset
524 }
kono
parents:
diff changeset
525
kono
parents:
diff changeset
526 /* Detect returning the address of a local variable. This only
kono
parents:
diff changeset
527 becomes undefined behavior if the result is used, so we do not
kono
parents:
diff changeset
528 insert a trap and only return NULL instead. */
kono
parents:
diff changeset
529 if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
kono
parents:
diff changeset
530 {
kono
parents:
diff changeset
531 tree val = gimple_return_retval (return_stmt);
kono
parents:
diff changeset
532 if (val && TREE_CODE (val) == ADDR_EXPR)
kono
parents:
diff changeset
533 {
kono
parents:
diff changeset
534 tree valbase = get_base_address (TREE_OPERAND (val, 0));
kono
parents:
diff changeset
535 if ((VAR_P (valbase) && !is_global_var (valbase))
kono
parents:
diff changeset
536 || TREE_CODE (valbase) == PARM_DECL)
kono
parents:
diff changeset
537 {
kono
parents:
diff changeset
538 /* We only need it for this particular case. */
kono
parents:
diff changeset
539 calculate_dominance_info (CDI_POST_DOMINATORS);
kono
parents:
diff changeset
540 const char* msg;
kono
parents:
diff changeset
541 bool always_executed = dominated_by_p
kono
parents:
diff changeset
542 (CDI_POST_DOMINATORS,
kono
parents:
diff changeset
543 single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
kono
parents:
diff changeset
544 if (always_executed)
kono
parents:
diff changeset
545 msg = N_("function returns address of local variable");
kono
parents:
diff changeset
546 else
kono
parents:
diff changeset
547 msg = N_("function may return address of "
kono
parents:
diff changeset
548 "local variable");
131
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
549 {
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
550 auto_diagnostic_group d;
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
551 if (warning_at (gimple_location (stmt),
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
552 OPT_Wreturn_local_addr, msg))
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
553 inform (DECL_SOURCE_LOCATION(valbase),
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
554 "declared here");
84e7813d76e9 gcc-8.2
mir3636
parents: 111
diff changeset
555 }
111
kono
parents:
diff changeset
556 tree zero = build_zero_cst (TREE_TYPE (val));
kono
parents:
diff changeset
557 gimple_return_set_retval (return_stmt, zero);
kono
parents:
diff changeset
558 update_stmt (stmt);
kono
parents:
diff changeset
559 }
kono
parents:
diff changeset
560 }
kono
parents:
diff changeset
561 }
kono
parents:
diff changeset
562 }
kono
parents:
diff changeset
563 }
kono
parents:
diff changeset
564 }
kono
parents:
diff changeset
565
kono
parents:
diff changeset
566 /* Search the function for statements which, if executed, would cause
kono
parents:
diff changeset
567 the program to fault such as a dereference of a NULL pointer.
kono
parents:
diff changeset
568
kono
parents:
diff changeset
569 Such a program can't be valid if such a statement was to execute
kono
parents:
diff changeset
570 according to ISO standards.
kono
parents:
diff changeset
571
kono
parents:
diff changeset
572 We detect explicit NULL pointer dereferences as well as those implied
kono
parents:
diff changeset
573 by a PHI argument having a NULL value which unconditionally flows into
kono
parents:
diff changeset
574 a dereference in the same block as the PHI.
kono
parents:
diff changeset
575
kono
parents:
diff changeset
576 In the former case we replace the offending statement with an
kono
parents:
diff changeset
577 unconditional trap and eliminate the outgoing edges from the statement's
kono
parents:
diff changeset
578 basic block. This may expose secondary optimization opportunities.
kono
parents:
diff changeset
579
kono
parents:
diff changeset
580 In the latter case, we isolate the path(s) with the NULL PHI
kono
parents:
diff changeset
581 feeding the dereference. We can then replace the offending statement
kono
parents:
diff changeset
582 and eliminate the outgoing edges in the duplicate. Again, this may
kono
parents:
diff changeset
583 expose secondary optimization opportunities.
kono
parents:
diff changeset
584
kono
parents:
diff changeset
585 A warning for both cases may be advisable as well.
kono
parents:
diff changeset
586
kono
parents:
diff changeset
587 Other statically detectable violations of the ISO standard could be
kono
parents:
diff changeset
588 handled in a similar way, such as out-of-bounds array indexing. */
kono
parents:
diff changeset
589
kono
parents:
diff changeset
590 static unsigned int
kono
parents:
diff changeset
591 gimple_ssa_isolate_erroneous_paths (void)
kono
parents:
diff changeset
592 {
kono
parents:
diff changeset
593 initialize_original_copy_tables ();
kono
parents:
diff changeset
594
kono
parents:
diff changeset
595 /* Search all the blocks for edges which, if traversed, will
kono
parents:
diff changeset
596 result in undefined behavior. */
kono
parents:
diff changeset
597 cfg_altered = false;
kono
parents:
diff changeset
598
kono
parents:
diff changeset
599 /* First handle cases where traversal of a particular edge
kono
parents:
diff changeset
600 triggers undefined behavior. These cases require creating
kono
parents:
diff changeset
601 duplicate blocks and thus new SSA_NAMEs.
kono
parents:
diff changeset
602
kono
parents:
diff changeset
603 We want that process complete prior to the phase where we start
kono
parents:
diff changeset
604 removing edges from the CFG. Edge removal may ultimately result in
kono
parents:
diff changeset
605 removal of PHI nodes and thus releasing SSA_NAMEs back to the
kono
parents:
diff changeset
606 name manager.
kono
parents:
diff changeset
607
kono
parents:
diff changeset
608 If the two processes run in parallel we could release an SSA_NAME
kono
parents:
diff changeset
609 back to the manager but we could still have dangling references
kono
parents:
diff changeset
610 to the released SSA_NAME in unreachable blocks.
kono
parents:
diff changeset
611 that any released names not have dangling references in the IL. */
kono
parents:
diff changeset
612 find_implicit_erroneous_behavior ();
kono
parents:
diff changeset
613 find_explicit_erroneous_behavior ();
kono
parents:
diff changeset
614
kono
parents:
diff changeset
615 free_original_copy_tables ();
kono
parents:
diff changeset
616
kono
parents:
diff changeset
617 /* We scramble the CFG and loop structures a bit, clean up
kono
parents:
diff changeset
618 appropriately. We really should incrementally update the
kono
parents:
diff changeset
619 loop structures, in theory it shouldn't be that hard. */
kono
parents:
diff changeset
620 free_dominance_info (CDI_POST_DOMINATORS);
kono
parents:
diff changeset
621 if (cfg_altered)
kono
parents:
diff changeset
622 {
kono
parents:
diff changeset
623 free_dominance_info (CDI_DOMINATORS);
kono
parents:
diff changeset
624 loops_state_set (LOOPS_NEED_FIXUP);
kono
parents:
diff changeset
625 return TODO_cleanup_cfg | TODO_update_ssa;
kono
parents:
diff changeset
626 }
kono
parents:
diff changeset
627 return 0;
kono
parents:
diff changeset
628 }
kono
parents:
diff changeset
629
kono
parents:
diff changeset
630 namespace {
kono
parents:
diff changeset
631 const pass_data pass_data_isolate_erroneous_paths =
kono
parents:
diff changeset
632 {
kono
parents:
diff changeset
633 GIMPLE_PASS, /* type */
kono
parents:
diff changeset
634 "isolate-paths", /* name */
kono
parents:
diff changeset
635 OPTGROUP_NONE, /* optinfo_flags */
kono
parents:
diff changeset
636 TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */
kono
parents:
diff changeset
637 ( PROP_cfg | PROP_ssa ), /* properties_required */
kono
parents:
diff changeset
638 0, /* properties_provided */
kono
parents:
diff changeset
639 0, /* properties_destroyed */
kono
parents:
diff changeset
640 0, /* todo_flags_start */
kono
parents:
diff changeset
641 0, /* todo_flags_finish */
kono
parents:
diff changeset
642 };
kono
parents:
diff changeset
643
kono
parents:
diff changeset
644 class pass_isolate_erroneous_paths : public gimple_opt_pass
kono
parents:
diff changeset
645 {
kono
parents:
diff changeset
646 public:
kono
parents:
diff changeset
647 pass_isolate_erroneous_paths (gcc::context *ctxt)
kono
parents:
diff changeset
648 : gimple_opt_pass (pass_data_isolate_erroneous_paths, ctxt)
kono
parents:
diff changeset
649 {}
kono
parents:
diff changeset
650
kono
parents:
diff changeset
651 /* opt_pass methods: */
kono
parents:
diff changeset
652 opt_pass * clone () { return new pass_isolate_erroneous_paths (m_ctxt); }
kono
parents:
diff changeset
653 virtual bool gate (function *)
kono
parents:
diff changeset
654 {
kono
parents:
diff changeset
655 /* If we do not have a suitable builtin function for the trap statement,
kono
parents:
diff changeset
656 then do not perform the optimization. */
kono
parents:
diff changeset
657 return (flag_isolate_erroneous_paths_dereference != 0
kono
parents:
diff changeset
658 || flag_isolate_erroneous_paths_attribute != 0
kono
parents:
diff changeset
659 || warn_null_dereference);
kono
parents:
diff changeset
660 }
kono
parents:
diff changeset
661
kono
parents:
diff changeset
662 virtual unsigned int execute (function *)
kono
parents:
diff changeset
663 {
kono
parents:
diff changeset
664 return gimple_ssa_isolate_erroneous_paths ();
kono
parents:
diff changeset
665 }
kono
parents:
diff changeset
666
kono
parents:
diff changeset
667 }; // class pass_isolate_erroneous_paths
kono
parents:
diff changeset
668 }
kono
parents:
diff changeset
669
kono
parents:
diff changeset
670 gimple_opt_pass *
kono
parents:
diff changeset
671 make_pass_isolate_erroneous_paths (gcc::context *ctxt)
kono
parents:
diff changeset
672 {
kono
parents:
diff changeset
673 return new pass_isolate_erroneous_paths (ctxt);
kono
parents:
diff changeset
674 }