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