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