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 ();
}
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;
}
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)
{
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.
/* 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;
}
{
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;
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;
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);
}
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
}
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)
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);
#include "stringpool.h"
#include "attribs.h"
#include "asan.h"
+#include "ubsan.h"
#include "langhooks.h"
#include "attr-fnspec.h"
#include "ipa-modref-tree.h"
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.
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. */
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);
ie->caller->dump_name ());
}
- target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+ target = builtin_decl_unreachable ();
callee = cgraph_node::get_create (target);
unreachable = true;
}
"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;
}
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 ())
{
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)
{}
/* 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
--- /dev/null
+// 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(); }
--- /dev/null
+// 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(); }
--- /dev/null
+// 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(); }
--- /dev/null
+// 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(); }
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);
{
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);
&& 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;
/* 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;
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,
#include "gimple-range.h"
#include "gomp-constants.h"
#include "dfp.h"
+#include "asan.h"
+#include "ubsan.h"
/* Tree code classes. */
}
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);
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)
}
}
+/* 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
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. */
return var;
}
-/* Instrument the __builtin_unreachable call. We just call the libubsan
- routine instead. */
+/* Shared between *build_builtin_unreachable. */
-bool
-ubsan_instrument_unreachable (gimple_stmt_iterator *gsi)
+tree
+sanitize_unreachable_fn (tree *data, location_t loc)
{
- 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);
+ 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
{
- 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);
+ fn = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
+ *data = NULL_TREE;
}
- gimple_set_location (g, loc);
+ return fn;
+}
+
+/* Rewrite a gcall to __builtin_unreachable for -fsanitize=unreachable. Called
+ by the sanopt pass. */
+
+bool
+ubsan_instrument_unreachable (gimple_stmt_iterator *gsi)
+{
+ location_t loc = gimple_location (gsi_stmt (*gsi));
+ gimple *g = gimple_build_builtin_unreachable (loc);
gsi_replace (gsi, g, false);
return false;
}
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 */