On 6/16/22 09:14, Jakub Jelinek wrote:
On Wed, Jun 15, 2022 at 04:38:49PM -0400, Jason Merrill wrote:
Furthermore, handling it the UBSan way means we slow down the compiler
(enable a bunch of extra passes, like sanopt, ubsan), which is undesirable
e.g. for -O0 compilation speed.

The ubsan pass is not enabled for unreachable|return.  sanopt does a single

You're right.

pass over the function to rewrite __builtin_unreachable, but that doesn't
seem like much overhead.

But I think we are trying to avoid hard any kind of unnecessary whole IL
extra walks, especially for -O0.

OK.

So, I think -funreachable-traps should be a separate flag and not an alias,
enabled by default for -O0 and -Og, which would be handled elsewhere
(I'd say e.g. in fold_builtin_0 and perhaps gimple_fold_builtin too to
avoid allocating trees unnecessary)

I tried this approach, but it misses some __builtin_unreachable calls added
by e.g. execute_fixup_cfg; it seems they never get folded by any subsequent
pass.

We could also expand BUILT_IN_UNREACHABLE as BUILT_IN_TRAP during expansion
to catch whatever isn't caught by folding.

That was an early thing I tried, but that's too late to prevent it from being used for optimization. More recently I've put an assert in expand_builtin_unreachable to catch ones that slip past.

and would be done if
flag_unreachable_traps && !sanitize_flag_p (SANITIZE_UNREACHABLE),
just replacing that __builtin_unreachable call with __builtin_trap.
For the function ends in fact under those conditions we could emit
__builtin_trap right away instead of emitting __builtin_unreachable
and waiting on folding it later to __builtin_trap.

Sure, but I generally prefer to change fewer places.

I'd say this would be very small change and the fastest + most reliable.
Simply replace all builtin_decl_implicit (BUILT_IN_UNREACHABLE) calls
with builtin_decl_unreachable () (12 of them) and define
tree
builtin_decl_unreachable ()
{
   enum built_in_function fncode = BUILT_IN_UNREACHABLE;

   if (sanitize_flag_p (SANITIZE_UNREACHABLE))
     {
       if (flag_sanitize_undefined_trap_on_error)
        fncode = BUILT_IN_TRAP;
       /* Otherwise we want __builtin_unreachable () later folded into
         __ubsan_handle_builtin_unreachable with extra args.  */
     }
   else if (flag_unreachable_traps)
     fncode = BUILT_IN_TRAP;
   return builtin_decl_implicit (fncode);
}
and that's it (well, also in build_common_builtin_nodes
declare __builtin_trap for FEs that don't do that - like it is done
for __builtin_unreachable).

OK, here's another version of the patch using that approach.

From 280713174b2bbda97ebd88cefa90d52df73813f5 Mon Sep 17 00:00:00 2001
From: Jason Merrill <ja...@redhat.com>
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<void *> *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 ("%<constexpr%> 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 ("%<constexpr%> 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<void *> *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

Reply via email to