]>
Commit | Line | Data |
---|---|---|
06cefae9 | 1 | /* Optimize and expand sanitizer functions. |
5624e564 | 2 | Copyright (C) 2014-2015 Free Software Foundation, Inc. |
06cefae9 MP |
3 | Contributed by Marek Polacek <polacek@redhat.com> |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 3, or (at your option) any later | |
10 | version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "system.h" | |
23 | #include "coretypes.h" | |
40e23961 | 24 | #include "alias.h" |
c7131fb2 | 25 | #include "backend.h" |
06cefae9 | 26 | #include "tree.h" |
c7131fb2 | 27 | #include "gimple.h" |
06cefae9 | 28 | #include "hard-reg-set.h" |
c7131fb2 AM |
29 | #include "options.h" |
30 | #include "fold-const.h" | |
06cefae9 | 31 | #include "internal-fn.h" |
06cefae9 MP |
32 | #include "gimplify.h" |
33 | #include "gimple-iterator.h" | |
06cefae9 MP |
34 | #include "tree-pass.h" |
35 | #include "asan.h" | |
36 | #include "gimple-pretty-print.h" | |
37 | #include "tm_p.h" | |
38 | #include "langhooks.h" | |
39 | #include "ubsan.h" | |
40 | #include "params.h" | |
ab9a4330 | 41 | #include "tree-ssa-operands.h" |
d6a818c5 | 42 | #include "tree-hash-traits.h" |
06cefae9 MP |
43 | |
44 | ||
45 | /* This is used to carry information about basic blocks. It is | |
46 | attached to the AUX field of the standard CFG block. */ | |
47 | ||
48 | struct sanopt_info | |
49 | { | |
ab9a4330 JJ |
50 | /* True if this BB might call (directly or indirectly) free/munmap |
51 | or similar operation. */ | |
52 | bool has_freeing_call_p; | |
53 | ||
54 | /* True if HAS_FREEING_CALL_P flag has been computed. */ | |
55 | bool has_freeing_call_computed_p; | |
56 | ||
57 | /* True if there is a block with HAS_FREEING_CALL_P flag set | |
58 | on any path between an immediate dominator of BB, denoted | |
59 | imm(BB), and BB. */ | |
60 | bool imm_dom_path_with_freeing_call_p; | |
61 | ||
62 | /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed. */ | |
63 | bool imm_dom_path_with_freeing_call_computed_p; | |
64 | ||
65 | /* Number of possibly freeing calls encountered in this bb | |
66 | (so far). */ | |
67 | uint64_t freeing_call_events; | |
68 | ||
69 | /* True if BB is currently being visited during computation | |
70 | of IMM_DOM_PATH_WITH_FREEING_CALL_P flag. */ | |
71 | bool being_visited_p; | |
72 | ||
73 | /* True if this BB has been visited in the dominator walk. */ | |
06cefae9 MP |
74 | bool visited_p; |
75 | }; | |
76 | ||
e28f2090 YG |
77 | /* If T has a single definition of form T = T2, return T2. */ |
78 | ||
79 | static tree | |
80 | maybe_get_single_definition (tree t) | |
81 | { | |
82 | if (TREE_CODE (t) == SSA_NAME) | |
83 | { | |
355fe088 | 84 | gimple *g = SSA_NAME_DEF_STMT (t); |
e28f2090 YG |
85 | if (gimple_assign_single_p (g)) |
86 | return gimple_assign_rhs1 (g); | |
87 | } | |
88 | return NULL_TREE; | |
89 | } | |
90 | ||
35228ac7 JJ |
91 | /* Tree triplet for vptr_check_map. */ |
92 | struct sanopt_tree_triplet | |
93 | { | |
94 | tree t1, t2, t3; | |
95 | }; | |
96 | ||
97 | /* Traits class for tree triplet hash maps below. */ | |
98 | ||
9654754b | 99 | struct sanopt_tree_triplet_hash : typed_noop_remove <sanopt_tree_triplet> |
35228ac7 | 100 | { |
9654754b RS |
101 | typedef sanopt_tree_triplet value_type; |
102 | typedef sanopt_tree_triplet compare_type; | |
103 | ||
35228ac7 JJ |
104 | static inline hashval_t |
105 | hash (const sanopt_tree_triplet &ref) | |
106 | { | |
107 | inchash::hash hstate (0); | |
108 | inchash::add_expr (ref.t1, hstate); | |
109 | inchash::add_expr (ref.t2, hstate); | |
110 | inchash::add_expr (ref.t3, hstate); | |
111 | return hstate.end (); | |
112 | } | |
113 | ||
114 | static inline bool | |
9654754b | 115 | equal (const sanopt_tree_triplet &ref1, const sanopt_tree_triplet &ref2) |
35228ac7 JJ |
116 | { |
117 | return operand_equal_p (ref1.t1, ref2.t1, 0) | |
118 | && operand_equal_p (ref1.t2, ref2.t2, 0) | |
119 | && operand_equal_p (ref1.t3, ref2.t3, 0); | |
120 | } | |
121 | ||
35228ac7 | 122 | static inline void |
9654754b | 123 | mark_deleted (sanopt_tree_triplet &ref) |
35228ac7 | 124 | { |
9654754b | 125 | ref.t1 = reinterpret_cast<tree> (1); |
35228ac7 JJ |
126 | } |
127 | ||
35228ac7 | 128 | static inline void |
9654754b | 129 | mark_empty (sanopt_tree_triplet &ref) |
35228ac7 | 130 | { |
9654754b | 131 | ref.t1 = NULL; |
35228ac7 JJ |
132 | } |
133 | ||
35228ac7 | 134 | static inline bool |
9654754b | 135 | is_deleted (const sanopt_tree_triplet &ref) |
35228ac7 | 136 | { |
9654754b | 137 | return ref.t1 == (void *) 1; |
35228ac7 JJ |
138 | } |
139 | ||
35228ac7 | 140 | static inline bool |
9654754b | 141 | is_empty (const sanopt_tree_triplet &ref) |
35228ac7 | 142 | { |
9654754b | 143 | return ref.t1 == NULL; |
35228ac7 JJ |
144 | } |
145 | }; | |
146 | ||
06cefae9 MP |
147 | /* This is used to carry various hash maps and variables used |
148 | in sanopt_optimize_walker. */ | |
149 | ||
150 | struct sanopt_ctx | |
151 | { | |
152 | /* This map maps a pointer (the first argument of UBSAN_NULL) to | |
153 | a vector of UBSAN_NULL call statements that check this pointer. */ | |
355fe088 | 154 | hash_map<tree, auto_vec<gimple *> > null_check_map; |
06cefae9 | 155 | |
ab9a4330 JJ |
156 | /* This map maps a pointer (the second argument of ASAN_CHECK) to |
157 | a vector of ASAN_CHECK call statements that check the access. */ | |
355fe088 | 158 | hash_map<tree_operand_hash, auto_vec<gimple *> > asan_check_map; |
35228ac7 JJ |
159 | |
160 | /* This map maps a tree triplet (the first, second and fourth argument | |
161 | of UBSAN_VPTR) to a vector of UBSAN_VPTR call statements that check | |
162 | that virtual table pointer. */ | |
355fe088 | 163 | hash_map<sanopt_tree_triplet_hash, auto_vec<gimple *> > vptr_check_map; |
ab9a4330 | 164 | |
06cefae9 MP |
165 | /* Number of IFN_ASAN_CHECK statements. */ |
166 | int asan_num_accesses; | |
167 | }; | |
168 | ||
169 | ||
ab9a4330 JJ |
170 | /* Return true if there might be any call to free/munmap operation |
171 | on any path in between DOM (which should be imm(BB)) and BB. */ | |
172 | ||
173 | static bool | |
174 | imm_dom_path_with_freeing_call (basic_block bb, basic_block dom) | |
175 | { | |
176 | sanopt_info *info = (sanopt_info *) bb->aux; | |
177 | edge e; | |
178 | edge_iterator ei; | |
179 | ||
180 | if (info->imm_dom_path_with_freeing_call_computed_p) | |
181 | return info->imm_dom_path_with_freeing_call_p; | |
182 | ||
183 | info->being_visited_p = true; | |
184 | ||
185 | FOR_EACH_EDGE (e, ei, bb->preds) | |
186 | { | |
187 | sanopt_info *pred_info = (sanopt_info *) e->src->aux; | |
188 | ||
189 | if (e->src == dom) | |
190 | continue; | |
191 | ||
192 | if ((pred_info->imm_dom_path_with_freeing_call_computed_p | |
193 | && pred_info->imm_dom_path_with_freeing_call_p) | |
194 | || (pred_info->has_freeing_call_computed_p | |
195 | && pred_info->has_freeing_call_p)) | |
196 | { | |
197 | info->imm_dom_path_with_freeing_call_computed_p = true; | |
198 | info->imm_dom_path_with_freeing_call_p = true; | |
199 | info->being_visited_p = false; | |
200 | return true; | |
201 | } | |
202 | } | |
203 | ||
204 | FOR_EACH_EDGE (e, ei, bb->preds) | |
205 | { | |
206 | sanopt_info *pred_info = (sanopt_info *) e->src->aux; | |
207 | ||
208 | if (e->src == dom) | |
209 | continue; | |
210 | ||
211 | if (pred_info->has_freeing_call_computed_p) | |
212 | continue; | |
213 | ||
214 | gimple_stmt_iterator gsi; | |
215 | for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi)) | |
216 | { | |
355fe088 | 217 | gimple *stmt = gsi_stmt (gsi); |
ab9a4330 JJ |
218 | |
219 | if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt)) | |
220 | { | |
221 | pred_info->has_freeing_call_p = true; | |
222 | break; | |
223 | } | |
224 | } | |
225 | ||
226 | pred_info->has_freeing_call_computed_p = true; | |
227 | if (pred_info->has_freeing_call_p) | |
228 | { | |
229 | info->imm_dom_path_with_freeing_call_computed_p = true; | |
230 | info->imm_dom_path_with_freeing_call_p = true; | |
231 | info->being_visited_p = false; | |
232 | return true; | |
233 | } | |
234 | } | |
235 | ||
236 | FOR_EACH_EDGE (e, ei, bb->preds) | |
237 | { | |
238 | if (e->src == dom) | |
239 | continue; | |
240 | ||
241 | basic_block src; | |
242 | for (src = e->src; src != dom; ) | |
243 | { | |
244 | sanopt_info *pred_info = (sanopt_info *) src->aux; | |
245 | if (pred_info->being_visited_p) | |
246 | break; | |
247 | basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src); | |
248 | if (imm_dom_path_with_freeing_call (src, imm)) | |
249 | { | |
250 | info->imm_dom_path_with_freeing_call_computed_p = true; | |
251 | info->imm_dom_path_with_freeing_call_p = true; | |
252 | info->being_visited_p = false; | |
253 | return true; | |
254 | } | |
255 | src = imm; | |
256 | } | |
257 | } | |
258 | ||
259 | info->imm_dom_path_with_freeing_call_computed_p = true; | |
260 | info->imm_dom_path_with_freeing_call_p = false; | |
261 | info->being_visited_p = false; | |
262 | return false; | |
263 | } | |
264 | ||
e28f2090 YG |
265 | /* Get the first dominating check from the list of stored checks. |
266 | Non-dominating checks are silently dropped. */ | |
267 | ||
355fe088 TS |
268 | static gimple * |
269 | maybe_get_dominating_check (auto_vec<gimple *> &v) | |
e28f2090 YG |
270 | { |
271 | for (; !v.is_empty (); v.pop ()) | |
272 | { | |
355fe088 | 273 | gimple *g = v.last (); |
e28f2090 YG |
274 | sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux; |
275 | if (!si->visited_p) | |
276 | /* At this point we shouldn't have any statements | |
277 | that aren't dominating the current BB. */ | |
278 | return g; | |
279 | } | |
280 | return NULL; | |
281 | } | |
282 | ||
ab9a4330 JJ |
283 | /* Optimize away redundant UBSAN_NULL calls. */ |
284 | ||
285 | static bool | |
355fe088 | 286 | maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple *stmt) |
ab9a4330 JJ |
287 | { |
288 | gcc_assert (gimple_call_num_args (stmt) == 3); | |
289 | tree ptr = gimple_call_arg (stmt, 0); | |
290 | tree cur_align = gimple_call_arg (stmt, 2); | |
291 | gcc_assert (TREE_CODE (cur_align) == INTEGER_CST); | |
292 | bool remove = false; | |
293 | ||
355fe088 TS |
294 | auto_vec<gimple *> &v = ctx->null_check_map.get_or_insert (ptr); |
295 | gimple *g = maybe_get_dominating_check (v); | |
e28f2090 | 296 | if (!g) |
ab9a4330 JJ |
297 | { |
298 | /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's | |
299 | nothing to optimize yet. */ | |
300 | v.safe_push (stmt); | |
301 | return false; | |
302 | } | |
303 | ||
304 | /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we | |
305 | can drop this one. But only if this check doesn't specify stricter | |
306 | alignment. */ | |
ab9a4330 | 307 | |
e28f2090 YG |
308 | tree align = gimple_call_arg (g, 2); |
309 | int kind = tree_to_shwi (gimple_call_arg (g, 1)); | |
310 | /* If this is a NULL pointer check where we had segv anyway, we can | |
311 | remove it. */ | |
312 | if (integer_zerop (align) | |
313 | && (kind == UBSAN_LOAD_OF | |
314 | || kind == UBSAN_STORE_OF | |
315 | || kind == UBSAN_MEMBER_ACCESS)) | |
316 | remove = true; | |
317 | /* Otherwise remove the check in non-recovering mode, or if the | |
318 | stmts have same location. */ | |
319 | else if (integer_zerop (align)) | |
320 | remove = (flag_sanitize_recover & SANITIZE_NULL) == 0 | |
321 | || flag_sanitize_undefined_trap_on_error | |
322 | || gimple_location (g) == gimple_location (stmt); | |
323 | else if (tree_int_cst_le (cur_align, align)) | |
324 | remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0 | |
325 | || flag_sanitize_undefined_trap_on_error | |
326 | || gimple_location (g) == gimple_location (stmt); | |
327 | ||
328 | if (!remove && gimple_bb (g) == gimple_bb (stmt) | |
329 | && tree_int_cst_compare (cur_align, align) == 0) | |
330 | v.pop (); | |
ab9a4330 JJ |
331 | |
332 | if (!remove) | |
333 | v.safe_push (stmt); | |
334 | return remove; | |
335 | } | |
336 | ||
35228ac7 JJ |
337 | /* Optimize away redundant UBSAN_VPTR calls. The second argument |
338 | is the value loaded from the virtual table, so rely on FRE to find out | |
339 | when we can actually optimize. */ | |
340 | ||
341 | static bool | |
355fe088 | 342 | maybe_optimize_ubsan_vptr_ifn (struct sanopt_ctx *ctx, gimple *stmt) |
35228ac7 JJ |
343 | { |
344 | gcc_assert (gimple_call_num_args (stmt) == 5); | |
345 | sanopt_tree_triplet triplet; | |
346 | triplet.t1 = gimple_call_arg (stmt, 0); | |
347 | triplet.t2 = gimple_call_arg (stmt, 1); | |
348 | triplet.t3 = gimple_call_arg (stmt, 3); | |
349 | ||
355fe088 TS |
350 | auto_vec<gimple *> &v = ctx->vptr_check_map.get_or_insert (triplet); |
351 | gimple *g = maybe_get_dominating_check (v); | |
35228ac7 JJ |
352 | if (!g) |
353 | { | |
354 | /* For this PTR we don't have any UBSAN_VPTR stmts recorded, so there's | |
355 | nothing to optimize yet. */ | |
356 | v.safe_push (stmt); | |
357 | return false; | |
358 | } | |
359 | ||
360 | return true; | |
361 | } | |
362 | ||
e28f2090 YG |
363 | /* Returns TRUE if ASan check of length LEN in block BB can be removed |
364 | if preceded by checks in V. */ | |
ab9a4330 JJ |
365 | |
366 | static bool | |
355fe088 | 367 | can_remove_asan_check (auto_vec<gimple *> &v, tree len, basic_block bb) |
ab9a4330 | 368 | { |
ab9a4330 | 369 | unsigned int i; |
355fe088 TS |
370 | gimple *g; |
371 | gimple *to_pop = NULL; | |
ab9a4330 JJ |
372 | bool remove = false; |
373 | basic_block last_bb = bb; | |
374 | bool cleanup = false; | |
375 | ||
376 | FOR_EACH_VEC_ELT_REVERSE (v, i, g) | |
377 | { | |
378 | basic_block gbb = gimple_bb (g); | |
379 | sanopt_info *si = (sanopt_info *) gbb->aux; | |
380 | if (gimple_uid (g) < si->freeing_call_events) | |
381 | { | |
382 | /* If there is a potentially freeing call after g in gbb, we should | |
383 | remove it from the vector, can't use in optimization. */ | |
384 | cleanup = true; | |
385 | continue; | |
386 | } | |
387 | ||
ab9a4330 | 388 | tree glen = gimple_call_arg (g, 2); |
e28f2090 YG |
389 | gcc_assert (TREE_CODE (glen) == INTEGER_CST); |
390 | ||
ab9a4330 JJ |
391 | /* If we've checked only smaller length than we want to check now, |
392 | we can't remove the current stmt. If g is in the same basic block, | |
393 | we want to remove it though, as the current stmt is better. */ | |
394 | if (tree_int_cst_lt (glen, len)) | |
395 | { | |
396 | if (gbb == bb) | |
397 | { | |
398 | to_pop = g; | |
399 | cleanup = true; | |
400 | } | |
401 | continue; | |
402 | } | |
403 | ||
404 | while (last_bb != gbb) | |
405 | { | |
406 | /* Paths from last_bb to bb have been checked before. | |
407 | gbb is necessarily a dominator of last_bb, but not necessarily | |
408 | immediate dominator. */ | |
409 | if (((sanopt_info *) last_bb->aux)->freeing_call_events) | |
410 | break; | |
411 | ||
412 | basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb); | |
413 | gcc_assert (imm); | |
414 | if (imm_dom_path_with_freeing_call (last_bb, imm)) | |
415 | break; | |
416 | ||
417 | last_bb = imm; | |
418 | } | |
419 | if (last_bb == gbb) | |
420 | remove = true; | |
421 | break; | |
422 | } | |
423 | ||
424 | if (cleanup) | |
425 | { | |
426 | unsigned int j = 0, l = v.length (); | |
427 | for (i = 0; i < l; i++) | |
428 | if (v[i] != to_pop | |
429 | && (gimple_uid (v[i]) | |
430 | == ((sanopt_info *) | |
431 | gimple_bb (v[i])->aux)->freeing_call_events)) | |
432 | { | |
433 | if (i != j) | |
434 | v[j] = v[i]; | |
435 | j++; | |
436 | } | |
437 | v.truncate (j); | |
438 | } | |
439 | ||
e28f2090 YG |
440 | return remove; |
441 | } | |
442 | ||
443 | /* Optimize away redundant ASAN_CHECK calls. */ | |
444 | ||
445 | static bool | |
355fe088 | 446 | maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple *stmt) |
e28f2090 YG |
447 | { |
448 | gcc_assert (gimple_call_num_args (stmt) == 4); | |
449 | tree ptr = gimple_call_arg (stmt, 1); | |
450 | tree len = gimple_call_arg (stmt, 2); | |
451 | basic_block bb = gimple_bb (stmt); | |
452 | sanopt_info *info = (sanopt_info *) bb->aux; | |
453 | ||
454 | if (TREE_CODE (len) != INTEGER_CST) | |
455 | return false; | |
456 | if (integer_zerop (len)) | |
457 | return false; | |
458 | ||
459 | gimple_set_uid (stmt, info->freeing_call_events); | |
460 | ||
355fe088 | 461 | auto_vec<gimple *> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr); |
e28f2090 YG |
462 | |
463 | tree base_addr = maybe_get_single_definition (ptr); | |
355fe088 | 464 | auto_vec<gimple *> *base_checks = NULL; |
e28f2090 YG |
465 | if (base_addr) |
466 | { | |
467 | base_checks = &ctx->asan_check_map.get_or_insert (base_addr); | |
468 | /* Original pointer might have been invalidated. */ | |
469 | ptr_checks = ctx->asan_check_map.get (ptr); | |
470 | } | |
471 | ||
355fe088 TS |
472 | gimple *g = maybe_get_dominating_check (*ptr_checks); |
473 | gimple *g2 = NULL; | |
e28f2090 | 474 | |
06dd2ace | 475 | if (base_checks) |
e28f2090 | 476 | /* Try with base address as well. */ |
06dd2ace | 477 | g2 = maybe_get_dominating_check (*base_checks); |
e28f2090 | 478 | |
06dd2ace | 479 | if (g == NULL && g2 == NULL) |
e28f2090 YG |
480 | { |
481 | /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's | |
482 | nothing to optimize yet. */ | |
483 | ptr_checks->safe_push (stmt); | |
484 | if (base_checks) | |
485 | base_checks->safe_push (stmt); | |
486 | return false; | |
487 | } | |
488 | ||
489 | bool remove = false; | |
490 | ||
491 | if (ptr_checks) | |
492 | remove = can_remove_asan_check (*ptr_checks, len, bb); | |
493 | ||
494 | if (!remove && base_checks) | |
495 | /* Try with base address as well. */ | |
496 | remove = can_remove_asan_check (*base_checks, len, bb); | |
497 | ||
ab9a4330 | 498 | if (!remove) |
e28f2090 YG |
499 | { |
500 | ptr_checks->safe_push (stmt); | |
501 | if (base_checks) | |
502 | base_checks->safe_push (stmt); | |
503 | } | |
504 | ||
ab9a4330 JJ |
505 | return remove; |
506 | } | |
507 | ||
508 | /* Try to optimize away redundant UBSAN_NULL and ASAN_CHECK calls. | |
509 | ||
06cefae9 | 510 | We walk blocks in the CFG via a depth first search of the dominator |
ab9a4330 JJ |
511 | tree; we push unique UBSAN_NULL or ASAN_CHECK statements into a vector |
512 | in the NULL_CHECK_MAP or ASAN_CHECK_MAP hash maps as we enter the | |
513 | blocks. When leaving a block, we mark the block as visited; then | |
514 | when checking the statements in the vector, we ignore statements that | |
515 | are coming from already visited blocks, because these cannot dominate | |
516 | anything anymore. CTX is a sanopt context. */ | |
06cefae9 MP |
517 | |
518 | static void | |
519 | sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx) | |
520 | { | |
521 | basic_block son; | |
522 | gimple_stmt_iterator gsi; | |
ab9a4330 | 523 | sanopt_info *info = (sanopt_info *) bb->aux; |
e28f2090 | 524 | bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0; |
06cefae9 MP |
525 | |
526 | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);) | |
527 | { | |
355fe088 | 528 | gimple *stmt = gsi_stmt (gsi); |
06cefae9 MP |
529 | bool remove = false; |
530 | ||
ab9a4330 JJ |
531 | if (!is_gimple_call (stmt)) |
532 | { | |
533 | /* Handle asm volatile or asm with "memory" clobber | |
534 | the same as potentionally freeing call. */ | |
538dd0b7 DM |
535 | gasm *asm_stmt = dyn_cast <gasm *> (stmt); |
536 | if (asm_stmt | |
ab9a4330 | 537 | && asan_check_optimize |
538dd0b7 DM |
538 | && (gimple_asm_clobbers_memory_p (asm_stmt) |
539 | || gimple_asm_volatile_p (asm_stmt))) | |
ab9a4330 JJ |
540 | info->freeing_call_events++; |
541 | gsi_next (&gsi); | |
542 | continue; | |
543 | } | |
544 | ||
545 | if (asan_check_optimize && !nonfreeing_call_p (stmt)) | |
546 | info->freeing_call_events++; | |
547 | ||
548 | if (gimple_call_internal_p (stmt)) | |
06cefae9 MP |
549 | switch (gimple_call_internal_fn (stmt)) |
550 | { | |
551 | case IFN_UBSAN_NULL: | |
ab9a4330 JJ |
552 | remove = maybe_optimize_ubsan_null_ifn (ctx, stmt); |
553 | break; | |
35228ac7 JJ |
554 | case IFN_UBSAN_VPTR: |
555 | remove = maybe_optimize_ubsan_vptr_ifn (ctx, stmt); | |
556 | break; | |
06cefae9 | 557 | case IFN_ASAN_CHECK: |
ab9a4330 JJ |
558 | if (asan_check_optimize) |
559 | remove = maybe_optimize_asan_check_ifn (ctx, stmt); | |
560 | if (!remove) | |
561 | ctx->asan_num_accesses++; | |
06cefae9 MP |
562 | break; |
563 | default: | |
564 | break; | |
565 | } | |
566 | ||
ab9a4330 JJ |
567 | if (remove) |
568 | { | |
569 | /* Drop this check. */ | |
570 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
571 | { | |
572 | fprintf (dump_file, "Optimizing out\n "); | |
573 | print_gimple_stmt (dump_file, stmt, 0, dump_flags); | |
574 | fprintf (dump_file, "\n"); | |
575 | } | |
576 | unlink_stmt_vdef (stmt); | |
577 | gsi_remove (&gsi, true); | |
578 | } | |
579 | else | |
06cefae9 MP |
580 | gsi_next (&gsi); |
581 | } | |
582 | ||
ab9a4330 JJ |
583 | if (asan_check_optimize) |
584 | { | |
585 | info->has_freeing_call_p = info->freeing_call_events != 0; | |
586 | info->has_freeing_call_computed_p = true; | |
587 | } | |
588 | ||
06cefae9 MP |
589 | for (son = first_dom_son (CDI_DOMINATORS, bb); |
590 | son; | |
591 | son = next_dom_son (CDI_DOMINATORS, son)) | |
592 | sanopt_optimize_walker (son, ctx); | |
593 | ||
594 | /* We're leaving this BB, so mark it to that effect. */ | |
06cefae9 MP |
595 | info->visited_p = true; |
596 | } | |
597 | ||
598 | /* Try to remove redundant sanitizer checks in function FUN. */ | |
599 | ||
600 | static int | |
601 | sanopt_optimize (function *fun) | |
602 | { | |
603 | struct sanopt_ctx ctx; | |
604 | ctx.asan_num_accesses = 0; | |
605 | ||
606 | /* Set up block info for each basic block. */ | |
607 | alloc_aux_for_blocks (sizeof (sanopt_info)); | |
608 | ||
609 | /* We're going to do a dominator walk, so ensure that we have | |
610 | dominance information. */ | |
611 | calculate_dominance_info (CDI_DOMINATORS); | |
612 | ||
613 | /* Recursively walk the dominator tree optimizing away | |
614 | redundant checks. */ | |
615 | sanopt_optimize_walker (ENTRY_BLOCK_PTR_FOR_FN (fun), &ctx); | |
616 | ||
617 | free_aux_for_blocks (); | |
618 | ||
619 | return ctx.asan_num_accesses; | |
620 | } | |
621 | ||
622 | /* Perform optimization of sanitize functions. */ | |
623 | ||
17795822 TS |
624 | namespace { |
625 | ||
626 | const pass_data pass_data_sanopt = | |
06cefae9 MP |
627 | { |
628 | GIMPLE_PASS, /* type */ | |
629 | "sanopt", /* name */ | |
630 | OPTGROUP_NONE, /* optinfo_flags */ | |
631 | TV_NONE, /* tv_id */ | |
632 | ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */ | |
633 | 0, /* properties_provided */ | |
634 | 0, /* properties_destroyed */ | |
635 | 0, /* todo_flags_start */ | |
636 | TODO_update_ssa, /* todo_flags_finish */ | |
637 | }; | |
638 | ||
17795822 | 639 | class pass_sanopt : public gimple_opt_pass |
06cefae9 MP |
640 | { |
641 | public: | |
642 | pass_sanopt (gcc::context *ctxt) | |
643 | : gimple_opt_pass (pass_data_sanopt, ctxt) | |
644 | {} | |
645 | ||
646 | /* opt_pass methods: */ | |
647 | virtual bool gate (function *) { return flag_sanitize; } | |
648 | virtual unsigned int execute (function *); | |
649 | ||
650 | }; // class pass_sanopt | |
651 | ||
652 | unsigned int | |
653 | pass_sanopt::execute (function *fun) | |
654 | { | |
655 | basic_block bb; | |
656 | int asan_num_accesses = 0; | |
657 | ||
658 | /* Try to remove redundant checks. */ | |
659 | if (optimize | |
ab9a4330 | 660 | && (flag_sanitize |
35228ac7 JJ |
661 | & (SANITIZE_NULL | SANITIZE_ALIGNMENT |
662 | | SANITIZE_ADDRESS | SANITIZE_VPTR))) | |
06cefae9 MP |
663 | asan_num_accesses = sanopt_optimize (fun); |
664 | else if (flag_sanitize & SANITIZE_ADDRESS) | |
665 | { | |
666 | gimple_stmt_iterator gsi; | |
667 | FOR_EACH_BB_FN (bb, fun) | |
668 | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) | |
669 | { | |
355fe088 | 670 | gimple *stmt = gsi_stmt (gsi); |
06cefae9 MP |
671 | if (is_gimple_call (stmt) && gimple_call_internal_p (stmt) |
672 | && gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK) | |
673 | ++asan_num_accesses; | |
674 | } | |
675 | } | |
676 | ||
677 | bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX | |
678 | && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD; | |
679 | ||
680 | FOR_EACH_BB_FN (bb, fun) | |
681 | { | |
682 | gimple_stmt_iterator gsi; | |
683 | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ) | |
684 | { | |
355fe088 | 685 | gimple *stmt = gsi_stmt (gsi); |
06cefae9 MP |
686 | bool no_next = false; |
687 | ||
688 | if (!is_gimple_call (stmt)) | |
689 | { | |
690 | gsi_next (&gsi); | |
691 | continue; | |
692 | } | |
693 | ||
694 | if (gimple_call_internal_p (stmt)) | |
695 | { | |
696 | enum internal_fn ifn = gimple_call_internal_fn (stmt); | |
697 | switch (ifn) | |
698 | { | |
699 | case IFN_UBSAN_NULL: | |
700 | no_next = ubsan_expand_null_ifn (&gsi); | |
701 | break; | |
702 | case IFN_UBSAN_BOUNDS: | |
703 | no_next = ubsan_expand_bounds_ifn (&gsi); | |
704 | break; | |
705 | case IFN_UBSAN_OBJECT_SIZE: | |
706 | no_next = ubsan_expand_objsize_ifn (&gsi); | |
707 | break; | |
35228ac7 JJ |
708 | case IFN_UBSAN_VPTR: |
709 | no_next = ubsan_expand_vptr_ifn (&gsi); | |
710 | break; | |
06cefae9 MP |
711 | case IFN_ASAN_CHECK: |
712 | no_next = asan_expand_check_ifn (&gsi, use_calls); | |
713 | break; | |
714 | default: | |
715 | break; | |
716 | } | |
717 | } | |
4088b790 MP |
718 | else if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) |
719 | { | |
720 | tree callee = gimple_call_fndecl (stmt); | |
721 | switch (DECL_FUNCTION_CODE (callee)) | |
722 | { | |
723 | case BUILT_IN_UNREACHABLE: | |
724 | if (flag_sanitize & SANITIZE_UNREACHABLE | |
725 | && !lookup_attribute ("no_sanitize_undefined", | |
726 | DECL_ATTRIBUTES (fun->decl))) | |
727 | no_next = ubsan_instrument_unreachable (&gsi); | |
728 | break; | |
729 | default: | |
730 | break; | |
731 | } | |
732 | } | |
06cefae9 MP |
733 | |
734 | if (dump_file && (dump_flags & TDF_DETAILS)) | |
735 | { | |
736 | fprintf (dump_file, "Expanded\n "); | |
737 | print_gimple_stmt (dump_file, stmt, 0, dump_flags); | |
738 | fprintf (dump_file, "\n"); | |
739 | } | |
740 | ||
741 | if (!no_next) | |
742 | gsi_next (&gsi); | |
743 | } | |
744 | } | |
745 | return 0; | |
746 | } | |
747 | ||
17795822 TS |
748 | } // anon namespace |
749 | ||
06cefae9 MP |
750 | gimple_opt_pass * |
751 | make_pass_sanopt (gcc::context *ctxt) | |
752 | { | |
753 | return new pass_sanopt (ctxt); | |
754 | } |