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