annotate gcc/gimple-ssa-isolate-paths.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
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
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
4 Copyright (C) 2013-2020 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
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
131 Return BB' (which may be equal to DUPLICATE). */
111
kono
parents:
diff changeset
132
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
133 ATTRIBUTE_RETURNS_NONNULL basic_block
111
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 {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
273 if (!cfun->can_throw_non_call_exceptions)
111
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 {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
312 if (!cfun->can_throw_non_call_exceptions
111
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
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
344 /* Describes the property of a return statement that may return
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
345 the address of one or more local variables. The type must
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
346 be safely assignable and copyable so that it can be stored in
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
347 a hash_map. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
348 class args_loc_t
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
349 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
350 public:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
351
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
352 args_loc_t (): nargs (), locvec (), ptr (&ptr)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
353 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
354 locvec.create (4);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
355 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
356
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
357 args_loc_t (const args_loc_t &rhs)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
358 : nargs (rhs.nargs), locvec (rhs.locvec.copy ()), ptr (&ptr) { }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
359
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
360 args_loc_t& operator= (const args_loc_t &rhs)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
361 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
362 nargs = rhs.nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
363 locvec.release ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
364 locvec = rhs.locvec.copy ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
365 return *this;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
366 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
367
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
368 ~args_loc_t ()
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
369 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
370 locvec.release ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
371 gcc_assert (ptr == &ptr);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
372 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
373
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
374 /* For a PHI in a return statement its number of arguments. When greater
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
375 than LOCVEC.LENGTH () implies that an address of one of the locals in
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
376 LOCVEC may but need not be returned by the statement. Otherwise,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
377 unless both are zero, it implies it definitely is returned. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
378 unsigned nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
379 /* The locations of local variables/alloca calls returned by the return
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
380 statement. Avoid using auto_vec here since it's not safe to copy due
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
381 to pr90904. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
382 vec <location_t> locvec;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
383 void *ptr;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
384 };
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
385
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
386 /* A mapping from a return statement to the locations of local variables
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
387 whose addresses it may return. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
388 typedef hash_map <gimple *, args_loc_t> locmap_t;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
389
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
390 /* Given the LOCMAP mapping, issue diagnostics about returning addresses
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
391 of local variables. When MAYBE is set, all diagnostics will be of
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
392 the "may return" kind. Otherwise each will be determined based on
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
393 the equality of the corresponding NARGS and LOCVEC.LENGTH () values. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
394
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
395 static void
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
396 diag_returned_locals (bool maybe, const locmap_t &locmap)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
397 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
398 for (locmap_t::iterator it = locmap.begin (); it != locmap.end (); ++it)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
399 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
400 gimple *stmt = (*it).first;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
401 const args_loc_t &argsloc = (*it).second;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
402 location_t stmtloc = gimple_location (stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
403
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
404 auto_diagnostic_group d;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
405 unsigned nargs = argsloc.locvec.length ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
406 if (warning_at (stmtloc, OPT_Wreturn_local_addr,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
407 (maybe || argsloc.nargs > nargs
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
408 ? G_("function may return address of local variable")
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
409 : G_("function returns address of local variable"))))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
410 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
411 for (unsigned i = 0; i != nargs; ++i)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
412 inform (argsloc.locvec[i], "declared here");
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
413 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
414 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
415 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
416
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
417 /* Return true if EXPR is an expression of pointer type that refers
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
418 to the address of one or more variables with automatic storage
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
419 duration. If so, add an entry to *PLOCMAP and insert into
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
420 PLOCMAP->LOCVEC the locations of the corresponding local variables
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
421 whose address is returned by the RETURN_STMT (which may be set to
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
422 (gimple*)-1 as a placeholder for such a statement). VISITED is
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
423 a bitmap of PHI nodes already visited by recursive calls. When
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
424 null, PHI expressions are not considered. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
425
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
426 static bool
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
427 is_addr_local (gimple *return_stmt, tree exp, locmap_t *plocmap,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
428 hash_set<gphi *> *visited)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
429 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
430 if (TREE_CODE (exp) == ADDR_EXPR)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
431 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
432 tree baseaddr = get_base_address (TREE_OPERAND (exp, 0));
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
433 if (TREE_CODE (baseaddr) == MEM_REF)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
434 return is_addr_local (return_stmt, TREE_OPERAND (baseaddr, 0),
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
435 plocmap, visited);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
436
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
437 if ((!VAR_P (baseaddr)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
438 || is_global_var (baseaddr))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
439 && TREE_CODE (baseaddr) != PARM_DECL)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
440 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
441
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
442 args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
443 argsloc.locvec.safe_push (DECL_SOURCE_LOCATION (baseaddr));
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
444 return true;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
445 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
446
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
447 if (!POINTER_TYPE_P (TREE_TYPE (exp)))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
448 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
449
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
450 if (TREE_CODE (exp) == SSA_NAME)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
451 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
452 gimple *def_stmt = SSA_NAME_DEF_STMT (exp);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
453 enum gimple_code code = gimple_code (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
454
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
455 if (is_gimple_assign (def_stmt))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
456 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
457 tree type = TREE_TYPE (gimple_assign_lhs (def_stmt));
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
458 if (POINTER_TYPE_P (type))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
459 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
460 tree_code code = gimple_assign_rhs_code (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
461 tree ptr1 = NULL_TREE, ptr2 = NULL_TREE;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
462
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
463 /* Set to the number of arguments examined that should
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
464 be added to ARGSLOC->NARGS to identify expressions
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
465 only some but not all of whose operands refer to local
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
466 addresses. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
467 unsigned nargs = 0;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
468 if (code == COND_EXPR)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
469 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
470 ptr1 = gimple_assign_rhs2 (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
471 ptr2 = gimple_assign_rhs3 (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
472 nargs = 2;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
473 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
474 else if (code == MAX_EXPR || code == MIN_EXPR)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
475 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
476 ptr1 = gimple_assign_rhs1 (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
477 ptr2 = gimple_assign_rhs2 (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
478 nargs = 2;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
479 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
480 else if (code == ADDR_EXPR
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
481 || code == NOP_EXPR
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
482 || code == POINTER_PLUS_EXPR)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
483 /* Leave NARGS at zero and let the recursive call set it. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
484 ptr1 = gimple_assign_rhs1 (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
485
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
486 /* Avoid short-circuiting the logical OR result in case
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
487 both operands refer to local variables, in which case
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
488 both should be considered and identified in the warning. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
489 bool res1 = false, res2 = false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
490 if (ptr1)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
491 res1 = is_addr_local (return_stmt, ptr1, plocmap, visited);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
492 if (ptr2)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
493 res2 = is_addr_local (return_stmt, ptr2, plocmap, visited);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
494
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
495 if (nargs)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
496 if (args_loc_t *argsloc = plocmap->get (return_stmt))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
497 argsloc->nargs += nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
498
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
499 return res1 || res2;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
500 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
501 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
502 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
503
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
504 if (code == GIMPLE_CALL
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
505 && gimple_call_builtin_p (def_stmt, BUILT_IN_NORMAL))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
506 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
507 /* Handle alloca and friends that return pointers to automatic
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
508 storage. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
509 tree fn = gimple_call_fndecl (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
510 int code = DECL_FUNCTION_CODE (fn);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
511 if (code == BUILT_IN_ALLOCA
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
512 || code == BUILT_IN_ALLOCA_WITH_ALIGN
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
513 || code == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
514 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
515 args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
516 argsloc.locvec.safe_push (gimple_location (def_stmt));
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
517 return true;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
518 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
519
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
520 if (gimple_call_num_args (def_stmt) < 1)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
521 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
522
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
523 /* Recursively examine the first argument of calls to built-ins
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
524 that return it. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
525 switch (code)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
526 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
527 case BUILT_IN_MEMCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
528 case BUILT_IN_MEMCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
529 case BUILT_IN_MEMPCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
530 case BUILT_IN_MEMPCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
531 case BUILT_IN_MEMMOVE:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
532 case BUILT_IN_MEMMOVE_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
533 case BUILT_IN_STPCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
534 case BUILT_IN_STPCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
535 case BUILT_IN_STPNCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
536 case BUILT_IN_STPNCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
537 case BUILT_IN_STRCAT:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
538 case BUILT_IN_STRCAT_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
539 case BUILT_IN_STRCHR:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
540 case BUILT_IN_STRCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
541 case BUILT_IN_STRCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
542 case BUILT_IN_STRNCAT:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
543 case BUILT_IN_STRNCAT_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
544 case BUILT_IN_STRNCPY:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
545 case BUILT_IN_STRNCPY_CHK:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
546 case BUILT_IN_STRRCHR:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
547 case BUILT_IN_STRSTR:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
548 return is_addr_local (return_stmt,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
549 gimple_call_arg (def_stmt, 0),
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
550 plocmap, visited);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
551 default:
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
552 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
553 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
554 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
555
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
556 if (code == GIMPLE_PHI && visited)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
557 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
558 gphi *phi_stmt = as_a <gphi *> (def_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
559 if (visited->add (phi_stmt))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
560 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
561
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
562 unsigned count = 0;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
563 unsigned nargs = gimple_phi_num_args (phi_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
564 args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
565 /* Bump up the number of operands examined by the number of
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
566 operands of this PHI. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
567 argsloc.nargs += nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
568 for (unsigned i = 0; i < gimple_phi_num_args (phi_stmt); ++i)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
569 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
570 tree arg = gimple_phi_arg_def (phi_stmt, i);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
571 if (is_addr_local (return_stmt, arg, plocmap, visited))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
572 ++count;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
573 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
574 return count != 0;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
575 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
576 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
577
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
578 return false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
579 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
580
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
581 /* Detect returning the address of a local variable in a PHI result LHS
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
582 and argument ARG and PHI edge E in basic block BB. Add an entry for
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
583 each use to LOCMAP, setting its NARGS member to the NARGS argument
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
584 (the number of PHI operands) plus the number of arguments in binary
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
585 expressions refereced by ARG. Call isolate_path for each returned
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
586 address and set *ISOLATED to true if called.
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
587 Return either DUPLICATE or the most recent result of isolate_path. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
588
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
589 static basic_block
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
590 handle_return_addr_local_phi_arg (basic_block bb, basic_block duplicate,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
591 tree lhs, tree arg, edge e, locmap_t &locmap,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
592 unsigned nargs, bool *isolated)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
593 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
594 /* Use (gimple*)-1 as a temporary placeholder and replace it with
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
595 the return statement below once it is known. Using a null doesn't
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
596 work because it's used by the hash_map to mean "no-entry." Pass
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
597 null instead of a visited_phis bitmap to avoid descending into
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
598 PHIs since they are being processed by the caller. Those that
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
599 remain will be checked again later. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
600 if (!is_addr_local ((gimple*)-1, arg, &locmap, NULL))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
601 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
602 /* Remove the placeholder regardless of success or failure. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
603 locmap.remove ((gimple*)-1);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
604 return duplicate;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
605 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
606
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
607 const args_loc_t* const placeargsloc = locmap.get ((gimple*)-1);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
608 const unsigned nlocs = placeargsloc->locvec.length ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
609 gcc_assert (nlocs);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
610
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
611 /* Add to the number of PHI arguments determined by the caller
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
612 the number of operands of the expressions referenced by ARG.
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
613 This lets the caller determine whether it's dealing with
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
614 a "may return" or "definitely returns." */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
615 nargs += placeargsloc->nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
616
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
617 /* Set to true if any expressions referenced by ARG involve
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
618 multiple addresses only some of which are those of locals. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
619 bool maybe = placeargsloc->nargs > placeargsloc->locvec.length ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
620
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
621 gimple *use_stmt;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
622 imm_use_iterator iter;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
623
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
624 /* Look for uses of the PHI result LHS in return statements. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
625 FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
626 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
627 greturn *return_stmt = dyn_cast <greturn *> (use_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
628 if (!return_stmt)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
629 continue;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
630
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
631 if (gimple_return_retval (return_stmt) != lhs)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
632 continue;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
633
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
634 /* Add an entry for the return statement and the locations
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
635 oof the PHI arguments obtained above to the map. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
636 args_loc_t &argsloc = locmap.get_or_insert (use_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
637 argsloc.nargs = nargs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
638 unsigned nelts = argsloc.locvec.length () + nlocs;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
639 argsloc.locvec.reserve (nelts);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
640 argsloc.locvec.splice (placeargsloc->locvec);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
641
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
642 if (!maybe
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
643 && (flag_isolate_erroneous_paths_dereference
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
644 || flag_isolate_erroneous_paths_attribute)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
645 && gimple_bb (use_stmt) == bb)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
646 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
647 duplicate = isolate_path (bb, duplicate, e,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
648 use_stmt, lhs, true);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
649
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
650 /* Let caller know the path has been isolated. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
651 *isolated = true;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
652 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
653 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
654
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
655 locmap.remove ((gimple*)-1);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
656
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
657 return duplicate;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
658 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
659
111
kono
parents:
diff changeset
660 /* Look for PHI nodes which feed statements in the same block where
kono
parents:
diff changeset
661 the value of the PHI node implies the statement is erroneous.
kono
parents:
diff changeset
662
kono
parents:
diff changeset
663 For example, a NULL PHI arg value which then feeds a pointer
kono
parents:
diff changeset
664 dereference.
kono
parents:
diff changeset
665
kono
parents:
diff changeset
666 When found isolate and optimize the path associated with the PHI
kono
parents:
diff changeset
667 argument feeding the erroneous statement. */
kono
parents:
diff changeset
668 static void
kono
parents:
diff changeset
669 find_implicit_erroneous_behavior (void)
kono
parents:
diff changeset
670 {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
671 locmap_t locmap;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
672
111
kono
parents:
diff changeset
673 basic_block bb;
kono
parents:
diff changeset
674
kono
parents:
diff changeset
675 FOR_EACH_BB_FN (bb, cfun)
kono
parents:
diff changeset
676 {
kono
parents:
diff changeset
677 gphi_iterator si;
kono
parents:
diff changeset
678
kono
parents:
diff changeset
679 /* Out of an abundance of caution, do not isolate paths to a
kono
parents:
diff changeset
680 block where the block has any abnormal outgoing edges.
kono
parents:
diff changeset
681
kono
parents:
diff changeset
682 We might be able to relax this in the future. We have to detect
kono
parents:
diff changeset
683 when we have to split the block with the NULL dereference and
kono
parents:
diff changeset
684 the trap we insert. We have to preserve abnormal edges out
kono
parents:
diff changeset
685 of the isolated block which in turn means updating PHIs at
kono
parents:
diff changeset
686 the targets of those abnormal outgoing edges. */
kono
parents:
diff changeset
687 if (has_abnormal_or_eh_outgoing_edge_p (bb))
kono
parents:
diff changeset
688 continue;
kono
parents:
diff changeset
689
kono
parents:
diff changeset
690
kono
parents:
diff changeset
691 /* If BB has an edge to itself, then duplication of BB below
kono
parents:
diff changeset
692 could result in reallocation of BB's PHI nodes. If that happens
kono
parents:
diff changeset
693 then the loop below over the PHIs would use the old PHI and
kono
parents:
diff changeset
694 thus invalid information. We don't have a good way to know
kono
parents:
diff changeset
695 if a PHI has been reallocated, so just avoid isolation in
kono
parents:
diff changeset
696 this case. */
kono
parents:
diff changeset
697 if (find_edge (bb, bb))
kono
parents:
diff changeset
698 continue;
kono
parents:
diff changeset
699
kono
parents:
diff changeset
700 /* First look for a PHI which sets a pointer to NULL and which
kono
parents:
diff changeset
701 is then dereferenced within BB. This is somewhat overly
kono
parents:
diff changeset
702 conservative, but probably catches most of the interesting
kono
parents:
diff changeset
703 cases. */
kono
parents:
diff changeset
704 for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
kono
parents:
diff changeset
705 {
kono
parents:
diff changeset
706 gphi *phi = si.phi ();
kono
parents:
diff changeset
707 tree lhs = gimple_phi_result (phi);
kono
parents:
diff changeset
708
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
709 /* Initial number of PHI arguments. The result may change
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
710 from one iteration of the loop below to the next in
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
711 response to changes to the CFG but only the initial
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
712 value is stored below for use by diagnostics. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
713 unsigned nargs = gimple_phi_num_args (phi);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
714
111
kono
parents:
diff changeset
715 /* PHI produces a pointer result. See if any of the PHI's
kono
parents:
diff changeset
716 arguments are NULL.
kono
parents:
diff changeset
717
kono
parents:
diff changeset
718 When we remove an edge, we want to reprocess the current
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
719 index since the argument at that index will have been
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
720 removed, hence the ugly way we update I for each iteration. */
111
kono
parents:
diff changeset
721 basic_block duplicate = NULL;
kono
parents:
diff changeset
722 for (unsigned i = 0, next_i = 0;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
723 i < gimple_phi_num_args (phi); i = next_i)
111
kono
parents:
diff changeset
724 {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
725 tree arg = gimple_phi_arg_def (phi, i);
111
kono
parents:
diff changeset
726 edge e = gimple_phi_arg_edge (phi, i);
kono
parents:
diff changeset
727
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
728 /* Advance the argument index unless a path involving
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
729 the current argument has been isolated. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
730 next_i = i + 1;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
731 bool isolated = false;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
732 duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
733 arg, e, locmap,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
734 nargs, &isolated);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
735 if (isolated)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
736 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
737 cfg_altered = true;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
738 next_i = i;
111
kono
parents:
diff changeset
739 }
kono
parents:
diff changeset
740
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
741 if (!integer_zerop (arg))
111
kono
parents:
diff changeset
742 continue;
kono
parents:
diff changeset
743
kono
parents:
diff changeset
744 location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
kono
parents:
diff changeset
745
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
746 imm_use_iterator iter;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
747 gimple *use_stmt;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
748
111
kono
parents:
diff changeset
749 /* We've got a NULL PHI argument. Now see if the
kono
parents:
diff changeset
750 PHI's result is dereferenced within BB. */
kono
parents:
diff changeset
751 FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
kono
parents:
diff changeset
752 {
kono
parents:
diff changeset
753 /* We only care about uses in BB. Catching cases in
kono
parents:
diff changeset
754 in other blocks would require more complex path
kono
parents:
diff changeset
755 isolation code. */
kono
parents:
diff changeset
756 if (gimple_bb (use_stmt) != bb)
kono
parents:
diff changeset
757 continue;
kono
parents:
diff changeset
758
kono
parents:
diff changeset
759 location_t loc = gimple_location (use_stmt)
kono
parents:
diff changeset
760 ? gimple_location (use_stmt)
kono
parents:
diff changeset
761 : phi_arg_loc;
kono
parents:
diff changeset
762
kono
parents:
diff changeset
763 if (stmt_uses_name_in_undefined_way (use_stmt, lhs, loc))
kono
parents:
diff changeset
764 {
kono
parents:
diff changeset
765 duplicate = isolate_path (bb, duplicate, e,
kono
parents:
diff changeset
766 use_stmt, lhs, false);
kono
parents:
diff changeset
767
kono
parents:
diff changeset
768 /* When we remove an incoming edge, we need to
kono
parents:
diff changeset
769 reprocess the Ith element. */
kono
parents:
diff changeset
770 next_i = i;
kono
parents:
diff changeset
771 cfg_altered = true;
kono
parents:
diff changeset
772 }
kono
parents:
diff changeset
773 }
kono
parents:
diff changeset
774 }
kono
parents:
diff changeset
775 }
kono
parents:
diff changeset
776 }
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
777
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
778 diag_returned_locals (false, locmap);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
779 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
780
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
781 /* Detect and diagnose returning the address of a local variable
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
782 in RETURN_STMT in basic block BB. This only becomes undefined
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
783 behavior if the result is used, so we do not insert a trap and
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
784 only return NULL instead. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
785
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
786 static void
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
787 warn_return_addr_local (basic_block bb, greturn *return_stmt)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
788 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
789 tree val = gimple_return_retval (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
790 if (!val)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
791 return;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
792
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
793 locmap_t locmap;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
794 hash_set<gphi *> visited_phis;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
795 if (!is_addr_local (return_stmt, val, &locmap, &visited_phis))
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
796 return;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
797
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
798 /* We only need it for this particular case. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
799 calculate_dominance_info (CDI_POST_DOMINATORS);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
800
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
801 const args_loc_t *argsloc = locmap.get (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
802 gcc_assert (argsloc);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
803
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
804 bool maybe = argsloc->nargs > argsloc->locvec.length ();
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
805 if (!maybe)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
806 maybe = !dominated_by_p (CDI_POST_DOMINATORS,
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
807 single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
808
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
809 diag_returned_locals (maybe, locmap);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
810
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
811 /* Bail if the statement isn't certain to return the address
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
812 of a local (e.g., if it involves a conditional expression
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
813 that wasn't trasnformed into a PHI or if it involves
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
814 a MAX_EXPR or MIN_EXPR only one of whose operands is a local
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
815 (even though such an expression isn't valid in C or has
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
816 defined semantics in C++). */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
817 if (maybe)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
818 return;
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
819
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
820 /* Do not modify code if the user only asked for warnings. */
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
821 if (flag_isolate_erroneous_paths_dereference
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
822 || flag_isolate_erroneous_paths_attribute)
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
823 {
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
824 tree zero = build_zero_cst (TREE_TYPE (val));
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
825 gimple_return_set_retval (return_stmt, zero);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
826 update_stmt (return_stmt);
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
827 }
111
kono
parents:
diff changeset
828 }
kono
parents:
diff changeset
829
kono
parents:
diff changeset
830 /* Look for statements which exhibit erroneous behavior. For example
kono
parents:
diff changeset
831 a NULL pointer dereference.
kono
parents:
diff changeset
832
kono
parents:
diff changeset
833 When found, optimize the block containing the erroneous behavior. */
kono
parents:
diff changeset
834 static void
kono
parents:
diff changeset
835 find_explicit_erroneous_behavior (void)
kono
parents:
diff changeset
836 {
kono
parents:
diff changeset
837 basic_block bb;
kono
parents:
diff changeset
838
kono
parents:
diff changeset
839 FOR_EACH_BB_FN (bb, cfun)
kono
parents:
diff changeset
840 {
kono
parents:
diff changeset
841 gimple_stmt_iterator si;
kono
parents:
diff changeset
842
kono
parents:
diff changeset
843 /* Out of an abundance of caution, do not isolate paths to a
kono
parents:
diff changeset
844 block where the block has any abnormal outgoing edges.
kono
parents:
diff changeset
845
kono
parents:
diff changeset
846 We might be able to relax this in the future. We have to detect
kono
parents:
diff changeset
847 when we have to split the block with the NULL dereference and
kono
parents:
diff changeset
848 the trap we insert. We have to preserve abnormal edges out
kono
parents:
diff changeset
849 of the isolated block which in turn means updating PHIs at
kono
parents:
diff changeset
850 the targets of those abnormal outgoing edges. */
kono
parents:
diff changeset
851 if (has_abnormal_or_eh_outgoing_edge_p (bb))
kono
parents:
diff changeset
852 continue;
kono
parents:
diff changeset
853
kono
parents:
diff changeset
854 /* Now look at the statements in the block and see if any of
kono
parents:
diff changeset
855 them explicitly dereference a NULL pointer. This happens
kono
parents:
diff changeset
856 because of jump threading and constant propagation. */
kono
parents:
diff changeset
857 for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
kono
parents:
diff changeset
858 {
kono
parents:
diff changeset
859 gimple *stmt = gsi_stmt (si);
kono
parents:
diff changeset
860
kono
parents:
diff changeset
861 if (stmt_uses_0_or_null_in_undefined_way (stmt))
kono
parents:
diff changeset
862 {
kono
parents:
diff changeset
863 insert_trap (&si, null_pointer_node);
kono
parents:
diff changeset
864 bb = gimple_bb (gsi_stmt (si));
kono
parents:
diff changeset
865
kono
parents:
diff changeset
866 /* Ignore any more operands on this statement and
kono
parents:
diff changeset
867 continue the statement iterator (which should
kono
parents:
diff changeset
868 terminate its loop immediately. */
kono
parents:
diff changeset
869 cfg_altered = true;
kono
parents:
diff changeset
870 break;
kono
parents:
diff changeset
871 }
kono
parents:
diff changeset
872
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
873 /* Look for a return statement that returns the address
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
874 of a local variable or the result of alloca. */
111
kono
parents:
diff changeset
875 if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
876 warn_return_addr_local (bb, return_stmt);
111
kono
parents:
diff changeset
877 }
kono
parents:
diff changeset
878 }
kono
parents:
diff changeset
879 }
kono
parents:
diff changeset
880
kono
parents:
diff changeset
881 /* Search the function for statements which, if executed, would cause
kono
parents:
diff changeset
882 the program to fault such as a dereference of a NULL pointer.
kono
parents:
diff changeset
883
kono
parents:
diff changeset
884 Such a program can't be valid if such a statement was to execute
kono
parents:
diff changeset
885 according to ISO standards.
kono
parents:
diff changeset
886
kono
parents:
diff changeset
887 We detect explicit NULL pointer dereferences as well as those implied
kono
parents:
diff changeset
888 by a PHI argument having a NULL value which unconditionally flows into
kono
parents:
diff changeset
889 a dereference in the same block as the PHI.
kono
parents:
diff changeset
890
kono
parents:
diff changeset
891 In the former case we replace the offending statement with an
kono
parents:
diff changeset
892 unconditional trap and eliminate the outgoing edges from the statement's
kono
parents:
diff changeset
893 basic block. This may expose secondary optimization opportunities.
kono
parents:
diff changeset
894
kono
parents:
diff changeset
895 In the latter case, we isolate the path(s) with the NULL PHI
kono
parents:
diff changeset
896 feeding the dereference. We can then replace the offending statement
kono
parents:
diff changeset
897 and eliminate the outgoing edges in the duplicate. Again, this may
kono
parents:
diff changeset
898 expose secondary optimization opportunities.
kono
parents:
diff changeset
899
kono
parents:
diff changeset
900 A warning for both cases may be advisable as well.
kono
parents:
diff changeset
901
kono
parents:
diff changeset
902 Other statically detectable violations of the ISO standard could be
kono
parents:
diff changeset
903 handled in a similar way, such as out-of-bounds array indexing. */
kono
parents:
diff changeset
904
kono
parents:
diff changeset
905 static unsigned int
kono
parents:
diff changeset
906 gimple_ssa_isolate_erroneous_paths (void)
kono
parents:
diff changeset
907 {
kono
parents:
diff changeset
908 initialize_original_copy_tables ();
kono
parents:
diff changeset
909
kono
parents:
diff changeset
910 /* Search all the blocks for edges which, if traversed, will
kono
parents:
diff changeset
911 result in undefined behavior. */
kono
parents:
diff changeset
912 cfg_altered = false;
kono
parents:
diff changeset
913
kono
parents:
diff changeset
914 /* First handle cases where traversal of a particular edge
kono
parents:
diff changeset
915 triggers undefined behavior. These cases require creating
kono
parents:
diff changeset
916 duplicate blocks and thus new SSA_NAMEs.
kono
parents:
diff changeset
917
kono
parents:
diff changeset
918 We want that process complete prior to the phase where we start
kono
parents:
diff changeset
919 removing edges from the CFG. Edge removal may ultimately result in
kono
parents:
diff changeset
920 removal of PHI nodes and thus releasing SSA_NAMEs back to the
kono
parents:
diff changeset
921 name manager.
kono
parents:
diff changeset
922
kono
parents:
diff changeset
923 If the two processes run in parallel we could release an SSA_NAME
kono
parents:
diff changeset
924 back to the manager but we could still have dangling references
kono
parents:
diff changeset
925 to the released SSA_NAME in unreachable blocks.
kono
parents:
diff changeset
926 that any released names not have dangling references in the IL. */
kono
parents:
diff changeset
927 find_implicit_erroneous_behavior ();
kono
parents:
diff changeset
928 find_explicit_erroneous_behavior ();
kono
parents:
diff changeset
929
kono
parents:
diff changeset
930 free_original_copy_tables ();
kono
parents:
diff changeset
931
kono
parents:
diff changeset
932 /* We scramble the CFG and loop structures a bit, clean up
kono
parents:
diff changeset
933 appropriately. We really should incrementally update the
kono
parents:
diff changeset
934 loop structures, in theory it shouldn't be that hard. */
kono
parents:
diff changeset
935 free_dominance_info (CDI_POST_DOMINATORS);
kono
parents:
diff changeset
936 if (cfg_altered)
kono
parents:
diff changeset
937 {
kono
parents:
diff changeset
938 free_dominance_info (CDI_DOMINATORS);
kono
parents:
diff changeset
939 loops_state_set (LOOPS_NEED_FIXUP);
kono
parents:
diff changeset
940 return TODO_cleanup_cfg | TODO_update_ssa;
kono
parents:
diff changeset
941 }
kono
parents:
diff changeset
942 return 0;
kono
parents:
diff changeset
943 }
kono
parents:
diff changeset
944
kono
parents:
diff changeset
945 namespace {
kono
parents:
diff changeset
946 const pass_data pass_data_isolate_erroneous_paths =
kono
parents:
diff changeset
947 {
kono
parents:
diff changeset
948 GIMPLE_PASS, /* type */
kono
parents:
diff changeset
949 "isolate-paths", /* name */
kono
parents:
diff changeset
950 OPTGROUP_NONE, /* optinfo_flags */
kono
parents:
diff changeset
951 TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */
kono
parents:
diff changeset
952 ( PROP_cfg | PROP_ssa ), /* properties_required */
kono
parents:
diff changeset
953 0, /* properties_provided */
kono
parents:
diff changeset
954 0, /* properties_destroyed */
kono
parents:
diff changeset
955 0, /* todo_flags_start */
kono
parents:
diff changeset
956 0, /* todo_flags_finish */
kono
parents:
diff changeset
957 };
kono
parents:
diff changeset
958
kono
parents:
diff changeset
959 class pass_isolate_erroneous_paths : public gimple_opt_pass
kono
parents:
diff changeset
960 {
kono
parents:
diff changeset
961 public:
kono
parents:
diff changeset
962 pass_isolate_erroneous_paths (gcc::context *ctxt)
kono
parents:
diff changeset
963 : gimple_opt_pass (pass_data_isolate_erroneous_paths, ctxt)
kono
parents:
diff changeset
964 {}
kono
parents:
diff changeset
965
kono
parents:
diff changeset
966 /* opt_pass methods: */
kono
parents:
diff changeset
967 opt_pass * clone () { return new pass_isolate_erroneous_paths (m_ctxt); }
kono
parents:
diff changeset
968 virtual bool gate (function *)
kono
parents:
diff changeset
969 {
kono
parents:
diff changeset
970 /* If we do not have a suitable builtin function for the trap statement,
kono
parents:
diff changeset
971 then do not perform the optimization. */
kono
parents:
diff changeset
972 return (flag_isolate_erroneous_paths_dereference != 0
kono
parents:
diff changeset
973 || flag_isolate_erroneous_paths_attribute != 0
kono
parents:
diff changeset
974 || warn_null_dereference);
kono
parents:
diff changeset
975 }
kono
parents:
diff changeset
976
kono
parents:
diff changeset
977 virtual unsigned int execute (function *)
kono
parents:
diff changeset
978 {
kono
parents:
diff changeset
979 return gimple_ssa_isolate_erroneous_paths ();
kono
parents:
diff changeset
980 }
kono
parents:
diff changeset
981
kono
parents:
diff changeset
982 }; // class pass_isolate_erroneous_paths
kono
parents:
diff changeset
983 }
kono
parents:
diff changeset
984
kono
parents:
diff changeset
985 gimple_opt_pass *
kono
parents:
diff changeset
986 make_pass_isolate_erroneous_paths (gcc::context *ctxt)
kono
parents:
diff changeset
987 {
kono
parents:
diff changeset
988 return new pass_isolate_erroneous_paths (ctxt);
kono
parents:
diff changeset
989 }