On 6/21/22 07:17, Jakub Jelinek wrote:
On Mon, Jun 20, 2022 at 04:30:51PM -0400, Jason Merrill wrote:
I'd still prefer to see a separate -funreachable-traps.
The thing is that -fsanitize{,-recover,-trap}= are global options, not per
function (and only tweaked by no_sanitize attribute), while something
that needs to depend on the per-function -O0/-Og setting is necessarily per
function.  The *.awk changes I understand make -fsanitize= kind of per
function but -fsanitize-{recover,trap}= remain global, that is going to be a
nightmare especially with LTO which saves/restores the per function flags
and for the global ones merges them across TUs.
By separating sanitizers (which would remain global with no_sanitize
overrides) from -funreachable-traps which would be Optimization option
(with default set if unset in default_options_optimization or so)
saved/restored upon function changes that issue is gone.

Done.

--- 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);

I think we generally try to declare functions in the header with same
basename as the source file in which they are defined.
So, the question is if builtin_decl_unreachable and build_builtin_unreachable
shouldn't be defined in tree.cc and declared in tree.h and
gimple_build_builtin_unreachable in gimple.cc and declared in gimple.h,
using a helper defined in ubsan.cc and declared in ubsan.h (your current
unreachable_1).

Done.

+
  /* Set explicit builtin function nodes and whether it is an implicit
     function.  */
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
--- a/gcc/ipa-fnsummary.cc
+++ b/gcc/ipa-fnsummary.cc
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
--- a/gcc/ipa.cc
+++ b/gcc/ipa.cc

The above changes LGTM.
          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)

See above.

--- 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;

There are other sanitizers purely handled in the FEs, guess as a follow-up
we should look at which of them don't really need any sanopt handling.

+    if (flag_sanitize_trap & SANITIZE_UNREACHABLE)
+      mask |= SANITIZE_UNREACHABLE;
+    return flag_sanitize & ~mask;
+  }
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
--- a/gcc/tree-ssa-loop-ivcanon.cc
+++ b/gcc/tree-ssa-loop-ivcanon.cc
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
--- a/gcc/tree.cc
+++ b/gcc/tree.cc

LGTM.

--- 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.  */
+    }

I'd add the flag_unreachable_traps stuff here as else

+/* Shared between *build_builtin_unreachable.  */
+
+static void
+unreachable_1 (tree &fn, tree &data, location_t loc)

Besides the potential moving, I think the coding guidelines don't recommend
using references that way.  But even if it is used, wouldn't it be better
to return fn instead of void and just set data (either using reference
or taking address of data)?

Done.
From 5eef7f71ee8acd4929e1c4b121c4f8c58251a591 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.  I think it also makes sense to do this
when we're explicitly optimizing for the debugging experience.

In the PR richi suggested introducing an -funreachable-traps flag for this.
This functionality is already implemented as -fsanitize=unreachable
-fsanitize-trap=unreachable, and we want to share the implementation, but it
does seem useful to have a separate flag that isn't affected by the various
sanitization controls.

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 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 when not trapping, 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.

In this patch -fsanitize=unreachable overrides -funreachable-traps; we could
also choose to go the other way.

	PR c++/104642

gcc/ChangeLog:

	* common.opt: Add -funreachable-traps.
	* doc/invoke.texi (-funreachable-traps): Document it.
	* opts.cc (finish_options): Enable at -O0 or -Og.
	* tree.cc (build_common_builtin_nodes): Add __builtin_trap.
	(builtin_decl_unreachable, build_builtin_unreachable): New.
	* tree.h: Declare them.
	* ubsan.cc (sanitize_unreachable_fn): Factor out.
	(ubsan_instrument_unreachable): Use
	gimple_build_builtin_unreachable.
	* ubsan.h (sanitize_unreachable_fn): Declare.
	* gimple.cc (gimple_build_builtin_unreachable): New.
	* gimple.h: Declare it.
	* builtins.cc (expand_builtin_unreachable): Add assert.
	(fold_builtin_0): Call build_builtin_unreachable.
	* sanopt.cc: Don't run for just SANITIZE_RETURN
	or SANITIZE_UNREACHABLE when trapping.
	* cgraphunit.cc (walk_polymorphic_call_targets): Use new
	unreachable functions.
	* 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                    | 14 ++++++++
 gcc/common.opt                         |  4 +++
 gcc/gimple.h                           |  1 +
 gcc/tree.h                             |  5 +++
 gcc/ubsan.h                            |  1 +
 gcc/builtins.cc                        |  9 +++++
 gcc/cgraphunit.cc                      |  3 +-
 gcc/cp/constexpr.cc                    | 29 +++++++++-------
 gcc/cp/cp-gimplify.cc                  |  7 ++--
 gcc/gimple-fold.cc                     |  7 ++--
 gcc/gimple.cc                          | 13 +++++++
 gcc/ipa-fnsummary.cc                   |  4 +--
 gcc/ipa-prop.cc                        |  4 +--
 gcc/ipa.cc                             |  3 +-
 gcc/opts.cc                            |  4 +++
 gcc/sanopt.cc                          | 10 +++++-
 gcc/testsuite/g++.dg/ubsan/return-8a.C | 16 +++++++++
 gcc/testsuite/g++.dg/ubsan/return-8b.C | 17 +++++++++
 gcc/testsuite/g++.dg/ubsan/return-8d.C | 16 +++++++++
 gcc/testsuite/g++.dg/ubsan/return-8e.C | 17 +++++++++
 gcc/tree-cfg.cc                        |  7 ++--
 gcc/tree-ssa-loop-ivcanon.cc           |  7 ++--
 gcc/tree-ssa-sccvn.cc                  |  2 +-
 gcc/tree.cc                            | 40 +++++++++++++++++++++
 gcc/ubsan.cc                           | 48 +++++++++++++++++---------
 25 files changed, 233 insertions(+), 55 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 81d13f4e78e..e077eaa773c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12939,6 +12939,20 @@ also at @option{-O0} if @option{-fsection-anchors} is explicitly requested.
 Additionally @option{-fno-toplevel-reorder} implies
 @option{-fno-section-anchors}.
 
+@item -funreachable-traps
+@opindex funreachable-traps
+With this option, the compiler turns calls to
+@code{__builtin_unreachable} into traps, instead of using them for
+optimization.  This also affects any such calls implicitly generated
+by the compiler.
+
+This option has the same effect as @option{-fsanitize=unreachable
+-fsanitize-trap=unreachable}, but does not affect the values of those
+options.  If @option{-fsanitize=unreachable} is enabled, that option
+takes priority over this one.
+
+This option is enabled by default at @option{-O0} and @option{-Og}.
+
 @item -fweb
 @opindex fweb
 Constructs webs as commonly used for register allocation purposes and assign
diff --git a/gcc/common.opt b/gcc/common.opt
index 32917aafcae..da5d23d1de8 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3017,6 +3017,10 @@ funit-at-a-time
 Common Var(flag_unit_at_a_time) Init(1)
 Compile whole compilation unit at a time.
 
+funreachable-traps
+Common Var(flag_unreachable_traps) Optimization
+Trap on __builtin_unreachable instead of using it for optimization.
+
 funroll-loops
 Common Var(flag_unroll_loops) Optimization EnabledBy(funroll-all-loops)
 Perform loop unrolling when iteration count is known.
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 870629cd562..1d15ff98ac2 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1638,6 +1638,7 @@ extern void maybe_remove_unused_call_args (struct function *, gimple *);
 extern bool gimple_inexpensive_call_p (gcall *);
 extern bool stmt_can_terminate_bb_p (gimple *);
 extern location_t gimple_or_expr_nonartificial_location (gimple *, tree);
+gcall *gimple_build_builtin_unreachable (location_t);
 
 /* Return the disposition for a warning (or all warnings by default)
    for a statement.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 507ea252b95..6f6ad5a3a5f 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 or
+   gimple_build_builtin_unreachable instead of one of the above.  */
+extern tree builtin_decl_unreachable ();
+extern tree build_builtin_unreachable (location_t);
+
 /* Set explicit builtin function nodes and whether it is an implicit
    function.  */
 
diff --git a/gcc/ubsan.h b/gcc/ubsan.h
index 17c5254c17e..71a40a399e4 100644
--- a/gcc/ubsan.h
+++ b/gcc/ubsan.h
@@ -65,5 +65,6 @@ extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree,
 					  tree, tree *);
 extern tree ubsan_instrument_float_cast (location_t, tree, tree);
 extern tree ubsan_get_source_location_type (void);
+extern tree sanitize_unreachable_fn (tree *data, location_t loc);
 
 #endif  /* GCC_UBSAN_H  */
diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index 971b18c3745..e6816d5c865 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -5184,6 +5184,9 @@ expand_builtin_trap (void)
 static void
 expand_builtin_unreachable (void)
 {
+  /* Use gimple_build_builtin_unreachable or builtin_decl_unreachable
+     to avoid this.  */
+  gcc_checking_assert (!sanitize_flags_p (SANITIZE_UNREACHABLE));
   emit_barrier ();
 }
 
@@ -9261,6 +9264,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..c05be833357 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1814,7 +1814,7 @@ cp_maybe_instrument_return (tree fndecl)
 	 information is provided, while the __builtin_unreachable () below
 	 if return sanitization is disabled will just result in hard to
 	 understand runtime error without location.  */
-      && (!optimize
+      && ((!optimize && !flag_unreachable_traps)
 	  || sanitize_flags_p (SANITIZE_UNREACHABLE, fndecl)))
     return;
 
@@ -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/gimple.cc b/gcc/gimple.cc
index b70ab4d2523..9b156399ba1 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "ubsan.h"
 #include "langhooks.h"
 #include "attr-fnspec.h"
 #include "ipa-modref-tree.h"
@@ -421,6 +422,18 @@ gimple_build_call_from_tree (tree t, tree fnptrtype)
   return call;
 }
 
+/* Build a gcall to __builtin_unreachable as rewritten by
+   -fsanitize=unreachable.  */
+
+gcall *
+gimple_build_builtin_unreachable (location_t loc)
+{
+  tree data = NULL_TREE;
+  tree fn = sanitize_unreachable_fn (&data, loc);
+  gcall *g = gimple_build_call (fn, data != NULL_TREE, data);
+  gimple_set_location (g, loc);
+  return g;
+}
 
 /* Build a GIMPLE_ASSIGN statement.
 
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..9982974c8b5 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -1122,6 +1122,10 @@ 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->x_optimize || opts->x_optimize_debug)
+    SET_OPTION_IF_UNSET (opts, opts_set, flag_unreachable_traps, true);
+
   /* 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..54f1c7b5326
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/return-8a.C
@@ -0,0 +1,16 @@
+// PR c++/104642
+
+// At -O0 and -Og we default to -funreachable-traps
+// 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..7eaded035ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/return-8d.C
@@ -0,0 +1,16 @@
+// PR c++/104642
+
+// At -O0 and -Og we default to -funreachable-traps
+// 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..fba402b35f2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/return-8e.C
@@ -0,0 +1,17 @@
+// PR c++/104642
+
+// At -O0 and -Og we default to -funreachable-traps
+// 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..84000dd8b69 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -71,6 +71,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-range.h"
 #include "gomp-constants.h"
 #include "dfp.h"
+#include "asan.h"
+#include "ubsan.h"
 
 /* Tree code classes.  */
 
@@ -9649,6 +9651,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 +9665,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)
@@ -10779,6 +10786,39 @@ build_alloca_call_expr (tree size, unsigned int align, HOST_WIDE_INT max_size)
     }
 }
 
+/* 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 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)
+      ? (flag_sanitize_trap & SANITIZE_UNREACHABLE)
+      : flag_unreachable_traps)
+    fncode = BUILT_IN_TRAP;
+  /* For non-trapping sanitize, we will rewrite __builtin_unreachable () later,
+     in the sanopt pass.  */
+
+  return builtin_decl_explicit (fncode);
+}
+
+/* Build a call to __builtin_unreachable, possibly rewritten by
+   -fsanitize=unreachable.  Use this rather than the above when practical.  */
+
+tree
+build_builtin_unreachable (location_t loc)
+{
+  tree data = NULL_TREE;
+  tree fn = sanitize_unreachable_fn (&data, loc);
+  return build_call_expr_loc (loc, fn, data != NULL_TREE, data);
+}
+
 /* Create a new constant string literal of type ELTYPE[SIZE] (or LEN
    if SIZE == -1) and return a tree node representing char* pointer to
    it as an ADDR_EXPR (ARRAY_REF (ELTYPE, ...)).  When STR is nonnull
diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc
index 3aa25b534bb..f15026872a1 100644
--- a/gcc/ubsan.cc
+++ b/gcc/ubsan.cc
@@ -638,27 +638,43 @@ 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.  */
+/* Shared between *build_builtin_unreachable.  */
+
+tree
+sanitize_unreachable_fn (tree *data, location_t loc)
+{
+  tree fn = NULL_TREE;
+  bool san = sanitize_flags_p (SANITIZE_UNREACHABLE);
+  if (san
+      ? (flag_sanitize_trap & SANITIZE_UNREACHABLE)
+      : flag_unreachable_traps)
+    {
+      fn = builtin_decl_explicit (BUILT_IN_TRAP);
+      *data = NULL_TREE;
+    }
+  else if (san)
+    {
+      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);
+    }
+  else
+    {
+      fn = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
+      *data = NULL_TREE;
+    }
+  return fn;
+}
+
+/* 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;
 }
-- 
2.27.0

Reply via email to