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