From 280713174b2bbda97ebd88cefa90d52df73813f5 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 10 Jun 2022 16:35:21 -0400 Subject: [PATCH] ubsan: default to trap on unreachable at -O0 and -Og [PR104642] To: gcc-patches@gcc.gnu.org When not optimizing, we can't do anything useful with unreachability in terms of code performance, so we might as well improve debugging by turning __builtin_unreachable into a trap. In the PR richi suggested introducing an -funreachable-traps flag for this, but this functionality is already implemented as -fsanitize=unreachable -fsanitize-trap=unreachable, we just need to set those flags by default. I think it also makes sense to do this when we're explicitly optimizing for the debugging experience. I then needed to make options-save handle -fsanitize; since it has custom parsing, that meant handling it explicitly in the awk scripts. Jakub observed that this would slow down -O0 by default from running the sanopt pass, so this revision avoids the need for sanopt by rewriting calls introduced by the compiler to __builtin_trap immediately, and calls written by the user at fold time. Many of the calls introduced by the compiler are also rewritten immediately to ubsan calls, which fixes ubsan-8b.C; previously the call to f() was optimized away before sanopt. But this early rewriting isn't practical for uses of __builtin_unreachable in devirtualization and such, so sanopt rewriting is still done for non-trapping sanitize. Do we still want -funreachable-traps as an alias for -fsanitize=unreachable,return -fsanitize-trap=unreachable,return? PR c++/104642 gcc/ChangeLog: * doc/invoke.texi (-fsanitize=unreachable): On by default at -O0. * opts.cc (finish_options): At -O0, trap on unreachable code. * optc-save-gen.awk, opth-gen.awk: Include flag_sanitize. * tree.cc (build_common_builtin_nodes): Add __builtin_trap. * sanopt.cc: Don't run for just SANITIZE_RETURN or SANITIZE_UNREACHABLE when trapping. * ubsan.cc (builtin_decl_unreachable): New. (unreachable_1): Factor out. (build_builtin_unreachable): Use it. (gimple_build_builtin_unreachable): Use it. (ubsan_instrument_unreachable): Use it. * builtins.cc (expand_builtin_unreachable): Add assert. (fold_builtin_0): Call build_builtin_unreachable. * tree.h (builtin_decl_unreachable) (gimple_build_builtin_unreachable) (build_builtin_unreachable): Declare. * cgraphunit.cc (walk_polymorphic_call_targets): Use them. * gimple-fold.cc (gimple_fold_call) (gimple_get_virt_method_for_vtable) * ipa-fnsummary.cc (redirect_to_unreachable) * ipa-prop.cc (ipa_make_edge_direct_to_target) (ipa_impossible_devirt_target) * ipa.cc (walk_polymorphic_call_targets) * tree-cfg.cc (pass_warn_function_return::execute) (execute_fixup_cfg) * tree-ssa-loop-ivcanon.cc (remove_exits_and_undefined_stmts) (unloop_loops) * tree-ssa-sccvn.cc (eliminate_dom_walker::eliminate_stmt): Likewise. gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_builtin_function_call): Handle unreachable/trap earlier. * cp-gimplify.cc (cp_maybe_instrument_return): Use build_builtin_unreachable. gcc/testsuite/ChangeLog: * g++.dg/ubsan/return-8a.C: New test. * g++.dg/ubsan/return-8b.C: New test. * g++.dg/ubsan/return-8d.C: New test. * g++.dg/ubsan/return-8e.C: New test. --- gcc/doc/invoke.texi | 4 ++ gcc/tree.h | 5 ++ gcc/builtins.cc | 7 ++ gcc/cgraphunit.cc | 3 +- gcc/cp/constexpr.cc | 29 +++++---- gcc/cp/cp-gimplify.cc | 5 +- gcc/gimple-fold.cc | 7 +- gcc/ipa-fnsummary.cc | 4 +- gcc/ipa-prop.cc | 4 +- gcc/ipa.cc | 3 +- gcc/opts.cc | 11 ++++ gcc/sanopt.cc | 10 ++- gcc/testsuite/g++.dg/ubsan/return-8a.C | 17 +++++ gcc/testsuite/g++.dg/ubsan/return-8b.C | 17 +++++ gcc/testsuite/g++.dg/ubsan/return-8d.C | 17 +++++ gcc/testsuite/g++.dg/ubsan/return-8e.C | 18 ++++++ gcc/tree-cfg.cc | 7 +- gcc/tree-ssa-loop-ivcanon.cc | 7 +- gcc/tree-ssa-sccvn.cc | 2 +- gcc/tree.cc | 5 ++ gcc/ubsan.cc | 89 +++++++++++++++++++++----- gcc/optc-save-gen.awk | 8 ++- gcc/opth-gen.awk | 3 +- 23 files changed, 225 insertions(+), 57 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8a.C create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8b.C create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8d.C create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8e.C diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 50f57877477..62b2f45ed64 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -15926,6 +15926,10 @@ With this option, the compiler turns the @code{__builtin_unreachable} call into a diagnostics message call instead. When reaching the @code{__builtin_unreachable} call, the behavior is undefined. +If @option{-fsanitize} has not been specified, this option is enabled +by default at @option{-O0} and @option{-Og}, along with +@option{-fsanitize=unreachable,return}. + @item -fsanitize=vla-bound @opindex fsanitize=vla-bound This option instructs the compiler to check that the size of a variable diff --git a/gcc/tree.h b/gcc/tree.h index 507ea252b95..b0aa6d961ee 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5858,6 +5858,11 @@ builtin_decl_implicit (enum built_in_function fncode) return builtin_info[uns_fncode].decl; } +/* For BUILTIN_UNREACHABLE, use one of these instead of one of the above. */ +extern tree builtin_decl_unreachable (); +extern gcall *gimple_build_builtin_unreachable (location_t); +extern tree build_builtin_unreachable (location_t); + /* Set explicit builtin function nodes and whether it is an implicit function. */ diff --git a/gcc/builtins.cc b/gcc/builtins.cc index 971b18c3745..8d1002992f5 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -5184,6 +5184,7 @@ expand_builtin_trap (void) static void expand_builtin_unreachable (void) { + gcc_checking_assert (!sanitize_flags_p (SANITIZE_UNREACHABLE)); emit_barrier (); } @@ -9261,6 +9262,12 @@ fold_builtin_0 (location_t loc, tree fndecl) case BUILT_IN_CLASSIFY_TYPE: return fold_builtin_classify_type (NULL_TREE); + case BUILT_IN_UNREACHABLE: + /* Rewrite any explicit calls to __builtin_unreachable. */ + if (sanitize_flags_p (SANITIZE_UNREACHABLE)) + return build_builtin_unreachable (loc); + break; + default: break; } diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc index e77bf97bea3..836e759cdf1 100644 --- a/gcc/cgraphunit.cc +++ b/gcc/cgraphunit.cc @@ -1033,8 +1033,7 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, if (targets.length () == 1) target = targets[0]; else - target = cgraph_node::create - (builtin_decl_implicit (BUILT_IN_UNREACHABLE)); + target = cgraph_node::create (builtin_decl_unreachable ()); if (symtab->dump_file) { diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index fd7f8c0fb88..0dc94d9445d 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1438,6 +1438,20 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, /* These builtins shall be ignored during constant expression evaluation. */ return void_node; + case BUILT_IN_UNREACHABLE: + case BUILT_IN_TRAP: + if (!*non_constant_p && !ctx->quiet) + { + /* Do not allow__builtin_unreachable in constexpr function. + The __builtin_unreachable call with BUILTINS_LOCATION + comes from cp_maybe_instrument_return. */ + if (EXPR_LOCATION (t) == BUILTINS_LOCATION) + error ("% call flows off the end of the function"); + else + error ("%q+E is not a constant expression", t); + } + *non_constant_p = true; + return t; default: break; } @@ -1531,18 +1545,9 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, { if (!*non_constant_p && !ctx->quiet) { - /* Do not allow__builtin_unreachable in constexpr function. - The __builtin_unreachable call with BUILTINS_LOCATION - comes from cp_maybe_instrument_return. */ - if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE) - && EXPR_LOCATION (t) == BUILTINS_LOCATION) - error ("% call flows off the end of the function"); - else - { - new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t), - CALL_EXPR_FN (t), nargs, args); - error ("%q+E is not a constant expression", new_call); - } + new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t), + CALL_EXPR_FN (t), nargs, args); + error ("%q+E is not a constant expression", new_call); } *non_constant_p = true; return t; diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 6f84d157c98..7b0465729a3 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1864,10 +1864,7 @@ cp_maybe_instrument_return (tree fndecl) if (sanitize_flags_p (SANITIZE_RETURN, fndecl)) t = ubsan_instrument_return (loc); else - { - tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE); - t = build_call_expr_loc (BUILTINS_LOCATION, fndecl, 0); - } + t = build_builtin_unreachable (BUILTINS_LOCATION); append_to_statement_list (t, p); } diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index f61bc87da63..a1704784bc9 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -5510,9 +5510,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) } else { - tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE); - gimple *new_stmt = gimple_build_call (fndecl, 0); - gimple_set_location (new_stmt, gimple_location (stmt)); + location_t loc = gimple_location (stmt); + gimple *new_stmt = gimple_build_builtin_unreachable (loc); /* If the call had a SSA name as lhs morph that into an uninitialized value. */ if (lhs && TREE_CODE (lhs) == SSA_NAME) @@ -8396,7 +8395,7 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, if (!fn || (TREE_CODE (fn) != ADDR_EXPR && TREE_CODE (fn) != FDESC_EXPR) || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) - fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE); + fn = builtin_decl_unreachable (); else { fn = TREE_OPERAND (fn, 0); diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc index b12e7a1124d..c9564451f26 100644 --- a/gcc/ipa-fnsummary.cc +++ b/gcc/ipa-fnsummary.cc @@ -250,8 +250,8 @@ static struct cgraph_edge * redirect_to_unreachable (struct cgraph_edge *e) { struct cgraph_node *callee = !e->inline_failed ? e->callee : NULL; - struct cgraph_node *target = cgraph_node::get_create - (builtin_decl_implicit (BUILT_IN_UNREACHABLE)); + struct cgraph_node *target + = cgraph_node::get_create (builtin_decl_unreachable ()); if (e->speculative) e = cgraph_edge::resolve_speculation (e, target->decl); diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index c037668e7d8..e1fc481423b 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -3410,7 +3410,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, ie->caller->dump_name ()); } - target = builtin_decl_implicit (BUILT_IN_UNREACHABLE); + target = builtin_decl_unreachable (); callee = cgraph_node::get_create (target); unreachable = true; } @@ -3821,7 +3821,7 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target) "No devirtualization target in %s\n", ie->caller->dump_name ()); } - tree new_target = builtin_decl_implicit (BUILT_IN_UNREACHABLE); + tree new_target = builtin_decl_unreachable (); cgraph_node::get_create (new_target); return new_target; } diff --git a/gcc/ipa.cc b/gcc/ipa.cc index f53f15f5f0a..4d5729f8370 100644 --- a/gcc/ipa.cc +++ b/gcc/ipa.cc @@ -232,8 +232,7 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, if (targets.length () == 1) target = targets[0]; else - target = cgraph_node::get_create - (builtin_decl_implicit (BUILT_IN_UNREACHABLE)); + target = cgraph_node::get_create (builtin_decl_unreachable ()); if (dump_enabled_p ()) { diff --git a/gcc/opts.cc b/gcc/opts.cc index 959d48d173f..d92699a1bc9 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1122,6 +1122,17 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, opts->x_flag_no_inline = 1; } + /* At -O0 or -Og, turn __builtin_unreachable into a trap. */ + if (!opts_set->x_flag_sanitize) + { + if (!opts->x_optimize || opts->x_optimize_debug) + opts->x_flag_sanitize = SANITIZE_UNREACHABLE|SANITIZE_RETURN; + + /* Change this without regard to optimization level so we don't need to + deal with it in optc-save-gen.awk. */ + opts->x_flag_sanitize_trap = SANITIZE_UNREACHABLE|SANITIZE_RETURN; + } + /* Pipelining of outer loops is only possible when general pipelining capabilities are requested. */ if (!opts->x_flag_sel_sched_pipelining) diff --git a/gcc/sanopt.cc b/gcc/sanopt.cc index c3187631153..2b05553baeb 100644 --- a/gcc/sanopt.cc +++ b/gcc/sanopt.cc @@ -942,7 +942,15 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *) { return flag_sanitize; } + virtual bool gate (function *) + { + /* SANITIZE_RETURN is handled in the front-end. When trapping, + SANITIZE_UNREACHABLE is handled by builtin_decl_unreachable. */ + unsigned int mask = SANITIZE_RETURN; + if (flag_sanitize_trap & SANITIZE_UNREACHABLE) + mask |= SANITIZE_UNREACHABLE; + return flag_sanitize & ~mask; + } virtual unsigned int execute (function *); }; // class pass_sanopt diff --git a/gcc/testsuite/g++.dg/ubsan/return-8a.C b/gcc/testsuite/g++.dg/ubsan/return-8a.C new file mode 100644 index 00000000000..5ecb6f4189e --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8a.C @@ -0,0 +1,17 @@ +// PR c++/104642 + +// At -O0 and -Og we default to +// -fsanitize=unreachable,return -fsanitize-trap=unreachable,return +// so the below should abort at runtime. + +// { dg-do run } +// { dg-shouldfail { *-*-* } } +// { dg-additional-options "-O0" } + +bool b; + +int f() { + if (b) return 42; +} // { dg-warning "-Wreturn-type" } + +int main() { f(); } diff --git a/gcc/testsuite/g++.dg/ubsan/return-8b.C b/gcc/testsuite/g++.dg/ubsan/return-8b.C new file mode 100644 index 00000000000..bdaea60f809 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8b.C @@ -0,0 +1,17 @@ +// PR c++/104642 + +// With -fsanitize=unreachable we shouldn't optimize away the call to f. + +// { dg-do run } +// { dg-shouldfail { *-*-* } } +// { dg-additional-options "-O -fsanitize=unreachable" } + +bool b; + +int f() { + if (b) return 42; + __builtin_unreachable (); + return 24; +} + +int main() { f(); } diff --git a/gcc/testsuite/g++.dg/ubsan/return-8d.C b/gcc/testsuite/g++.dg/ubsan/return-8d.C new file mode 100644 index 00000000000..74d86ce337c --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8d.C @@ -0,0 +1,17 @@ +// PR c++/104642 + +// At -O0 and -Og we default to +// -fsanitize=unreachable,return -fsanitize-trap=unreachable,return +// so the below should abort at runtime. + +// { dg-do run } +// { dg-shouldfail { *-*-* } } +// { dg-additional-options "-Og" } + +bool b; + +int f() { + if (b) return 42; +} // { dg-warning "-Wreturn-type" } + +int main() { f(); } diff --git a/gcc/testsuite/g++.dg/ubsan/return-8e.C b/gcc/testsuite/g++.dg/ubsan/return-8e.C new file mode 100644 index 00000000000..d32e2c1d518 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8e.C @@ -0,0 +1,18 @@ +// PR c++/104642 + +// At -O0 and -Og we default to +// -fsanitize=unreachable,return -fsanitize-trap=unreachable,return +// so the below should abort at runtime. + +// { dg-do run } +// { dg-shouldfail { *-*-* } } +// { dg-additional-options "-O2" } + +bool b; + +__attribute__ ((optimize ("Og"))) +int f() { + if (b) return 42; +} // { dg-warning "-Wreturn-type" } + +int main() { f(); } diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc index c67c278dad0..734fdddbf97 100644 --- a/gcc/tree-cfg.cc +++ b/gcc/tree-cfg.cc @@ -9503,9 +9503,8 @@ pass_warn_function_return::execute (function *fun) with __builtin_unreachable () call. */ if (optimize && gimple_code (last) == GIMPLE_RETURN) { - tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE); - gimple *new_stmt = gimple_build_call (fndecl, 0); - gimple_set_location (new_stmt, gimple_location (last)); + location_t loc = gimple_location (last); + gimple *new_stmt = gimple_build_builtin_unreachable (loc); gimple_stmt_iterator gsi = gsi_for_stmt (last); gsi_replace (&gsi, new_stmt, true); remove_edge (e); @@ -9834,7 +9833,7 @@ execute_fixup_cfg (void) { if (stmt && is_gimple_call (stmt)) gimple_call_set_ctrl_altering (stmt, false); - tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE); + tree fndecl = builtin_decl_unreachable (); stmt = gimple_build_call (fndecl, 0); gimple_stmt_iterator gsi = gsi_last_bb (bb); gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc index 2ee00a3f843..6a38b77ab7a 100644 --- a/gcc/tree-ssa-loop-ivcanon.cc +++ b/gcc/tree-ssa-loop-ivcanon.cc @@ -505,9 +505,8 @@ remove_exits_and_undefined_stmts (class loop *loop, unsigned int npeeled) && wi::ltu_p (elt->bound, npeeled)) { gimple_stmt_iterator gsi = gsi_for_stmt (elt->stmt); - gcall *stmt = gimple_build_call - (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); - gimple_set_location (stmt, gimple_location (elt->stmt)); + location_t loc = gimple_location (elt->stmt); + gcall *stmt = gimple_build_builtin_unreachable (loc); gsi_insert_before (&gsi, stmt, GSI_NEW_STMT); split_block (gimple_bb (stmt), stmt); changed = true; @@ -641,7 +640,7 @@ unloop_loops (bitmap loop_closed_ssa_invalidated, /* Create new basic block for the latch edge destination and wire it in. */ - stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0); + stmt = gimple_build_builtin_unreachable (locus); latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags); latch_edge->probability = profile_probability::never (); latch_edge->flags |= flags; diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc index ed68557f0b2..776dccbbf5a 100644 --- a/gcc/tree-ssa-sccvn.cc +++ b/gcc/tree-ssa-sccvn.cc @@ -6807,7 +6807,7 @@ eliminate_dom_walker::eliminate_stmt (basic_block b, gimple_stmt_iterator *gsi) if (targets.length () == 1) fn = targets[0]->decl; else - fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE); + fn = builtin_decl_unreachable (); if (dump_enabled_p ()) { dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt, diff --git a/gcc/tree.cc b/gcc/tree.cc index 2bfb67489c6..51831f80512 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -9649,6 +9649,7 @@ build_common_builtin_nodes (void) } if (!builtin_decl_explicit_p (BUILT_IN_UNREACHABLE) + || !builtin_decl_explicit_p (BUILT_IN_TRAP) || !builtin_decl_explicit_p (BUILT_IN_ABORT)) { ftype = build_function_type (void_type_node, void_list_node); @@ -9662,6 +9663,10 @@ build_common_builtin_nodes (void) local_define_builtin ("__builtin_abort", ftype, BUILT_IN_ABORT, "abort", ECF_LEAF | ECF_NORETURN | ECF_CONST | ECF_COLD); + if (!builtin_decl_explicit_p (BUILT_IN_TRAP)) + local_define_builtin ("__builtin_trap", ftype, BUILT_IN_TRAP, + "__builtin_trap", + ECF_NORETURN | ECF_NOTHROW | ECF_LEAF | ECF_COLD); } if (!builtin_decl_explicit_p (BUILT_IN_MEMCPY) diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc index 3aa25b534bb..4a34b8fee58 100644 --- a/gcc/ubsan.cc +++ b/gcc/ubsan.cc @@ -638,27 +638,84 @@ ubsan_create_data (const char *name, int loccnt, const location_t *ploc, ...) return var; } -/* Instrument the __builtin_unreachable call. We just call the libubsan - routine instead. */ +/* The built-in decl to use to mark code points believed to be unreachable. + Typically __builtin_unreachable, but __builtin_trap if + -fsanitize=unreachable -fsanitize-trap=unreachable. If only + -fsanitize=unreachable, we rely on sanopt to replace any calls with the + appropriate ubsan function. When building a call directly, use + {gimple_},build_builtin_unreachable instead. */ + +tree +builtin_decl_unreachable () +{ + enum built_in_function fncode = BUILT_IN_UNREACHABLE; + + if (sanitize_flags_p (SANITIZE_UNREACHABLE)) + { + if (flag_sanitize_trap & SANITIZE_UNREACHABLE) + fncode = BUILT_IN_TRAP; + /* Otherwise we want __builtin_unreachable () later folded into + __ubsan_handle_builtin_unreachable with extra args. */ + } + return builtin_decl_explicit (fncode); +} + +/* Shared between *build_builtin_unreachable. */ + +static void +unreachable_1 (tree &fn, tree &data, location_t loc) +{ + if (!sanitize_flags_p (SANITIZE_UNREACHABLE)) + { + fn = builtin_decl_explicit (BUILT_IN_UNREACHABLE); + data = NULL_TREE; + } + else if (flag_sanitize_trap & SANITIZE_UNREACHABLE) + { + fn = builtin_decl_explicit (BUILT_IN_TRAP); + data = NULL_TREE; + } + else + { + fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE); + data = ubsan_create_data ("__ubsan_unreachable_data", 1, &loc, + NULL_TREE, NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + } +} + +/* Build a call to __builtin_unreachable as rewritten by + -fsanitize=unreachable. */ + +tree +build_builtin_unreachable (location_t loc) +{ + tree fn, data; + unreachable_1 (fn, data, loc); + return build_call_expr_loc (loc, fn, data != NULL_TREE, data); +} + +/* Build a gcall to __builtin_unreachable as rewritten by + -fsanitize=unreachable. */ + +gcall * +gimple_build_builtin_unreachable (location_t loc) +{ + tree fn, data; + unreachable_1 (fn, data, loc); + gcall *g = gimple_build_call (fn, data != NULL_TREE, data); + gimple_set_location (g, loc); + return g; +} + +/* Rewrite a gcall to __builtin_unreachable for -fsanitize=unreachable. Called + by the sanopt pass. */ bool ubsan_instrument_unreachable (gimple_stmt_iterator *gsi) { - gimple *g; location_t loc = gimple_location (gsi_stmt (*gsi)); - - if (flag_sanitize_trap & SANITIZE_UNREACHABLE) - g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); - else - { - tree data = ubsan_create_data ("__ubsan_unreachable_data", 1, &loc, - NULL_TREE, NULL_TREE); - data = build_fold_addr_expr_loc (loc, data); - tree fn - = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE); - g = gimple_build_call (fn, 1, data); - } - gimple_set_location (g, loc); + gimple *g = gimple_build_builtin_unreachable (loc); gsi_replace (gsi, g, false); return false; } diff --git a/gcc/optc-save-gen.awk b/gcc/optc-save-gen.awk index 233d1fbb637..38c02bcc2cf 100644 --- a/gcc/optc-save-gen.awk +++ b/gcc/optc-save-gen.awk @@ -84,7 +84,7 @@ print "{"; n_opt_char = 4; n_opt_short = 0; -n_opt_int = 0; +n_opt_int = 1; n_opt_enum = 0; n_opt_string = 0; n_opt_other = 0; @@ -96,6 +96,7 @@ var_opt_range["optimize"] = "0, 255"; var_opt_range["optimize_size"] = "0, 2"; var_opt_range["optimize_debug"] = "0, 1"; var_opt_range["optimize_fast"] = "0, 1"; +var_opt_int[0] = "flag_sanitize"; # Sort by size to mimic how the structure is laid out to be friendlier to the # cache. @@ -1264,7 +1265,7 @@ for (i = 0; i < n_target_str; i++) { } print "}"; -n_opt_val = 4; +n_opt_val = 5; var_opt_val[0] = "x_optimize" var_opt_val_type[0] = "char " var_opt_hash[0] = 1; @@ -1277,6 +1278,9 @@ var_opt_hash[2] = 1; var_opt_val[3] = "x_optimize_fast" var_opt_val_type[3] = "char " var_opt_hash[3] = 1; +var_opt_val[4] = "x_flag_sanitize" +var_opt_val_type[4] = "unsigned int " +var_opt_hash[4] = 1; for (i = 0; i < n_opts; i++) { if (flag_set_p("(Optimization|PerFunction)", flags[i])) { name = var_name(flags[i]) diff --git a/gcc/opth-gen.awk b/gcc/opth-gen.awk index 8bba8ec4549..b3bedaa6da2 100644 --- a/gcc/opth-gen.awk +++ b/gcc/opth-gen.awk @@ -134,7 +134,7 @@ print "{"; n_opt_char = 4; n_opt_short = 0; -n_opt_int = 0; +n_opt_int = 1; n_opt_enum = 0; n_opt_other = 0; n_opt_explicit = 4; @@ -142,6 +142,7 @@ var_opt_char[0] = "unsigned char x_optimize"; var_opt_char[1] = "unsigned char x_optimize_size"; var_opt_char[2] = "unsigned char x_optimize_debug"; var_opt_char[3] = "unsigned char x_optimize_fast"; +var_opt_int[0] = "unsigned int x_flag_sanitize"; for (i = 0; i < n_opts; i++) { if (flag_set_p("(Optimization|PerFunction)", flags[i])) { -- 2.27.0