From: David Daney Date: Thu, 11 Jun 2009 23:55:45 +0000 (+0000) Subject: re PR c/39252 (Request new feature __builtin_unreachable ()) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=468059bcbb8efdb31fd90369dc2ed122126413fe;p=gcc.git re PR c/39252 (Request new feature __builtin_unreachable ()) 2009-06-11 David Daney PR c/39252 * doc/extend.texi ( __builtin_unreachable): Document new builtin. * builtins.c (expand_builtin_unreachable): New function. (expand_builtin): Handle BUILT_IN_UNREACHABLE case. * builtins.def (BUILT_IN_UNREACHABLE): Add new builtin. * cfgcleanup.c (try_optimize_cfg): Delete empty blocks with no successors. * cfgrtl.c (rtl_verify_flow_info): Handle empty blocks when searching for missing barriers. 2009-06-11 David Daney PR c/39252 * gcc.dg/builtin-unreachable-1.c: New test. * gcc.dg/builtin-unreachable-2.c: Same. From-SVN: r148403 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b90d23de1a2..7a19b5be40d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2009-06-11 David Daney + + PR c/39252 + * doc/extend.texi ( __builtin_unreachable): Document new builtin. + * builtins.c (expand_builtin_unreachable): New function. + (expand_builtin): Handle BUILT_IN_UNREACHABLE case. + * builtins.def (BUILT_IN_UNREACHABLE): Add new builtin. + * cfgcleanup.c (try_optimize_cfg): Delete empty blocks with no + successors. + * cfgrtl.c (rtl_verify_flow_info): Handle empty blocks when + searching for missing barriers. + 2009-06-11 Francois-Xavier Coudert * config/darwin.h (LINK_COMMAND_SPEC): Adjust spec to link libcov diff --git a/gcc/builtins.c b/gcc/builtins.c index a555e4fa14b..98919780e28 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -5298,6 +5298,17 @@ expand_builtin_trap (void) emit_barrier (); } +/* Expand a call to __builtin_unreachable. We do nothing except emit + a barrier saying that control flow will not pass here. + + It is the responsibility of the program being compiled to ensure + that control flow does never reach __builtin_unreachable. */ +static void +expand_builtin_unreachable (void) +{ + emit_barrier (); +} + /* Expand EXP, a call to fabs, fabsf or fabsl. Return NULL_RTX if a normal call should be emitted rather than expanding the function inline. If convenient, the result should be placed @@ -6795,6 +6806,10 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_trap (); return const0_rtx; + case BUILT_IN_UNREACHABLE: + expand_builtin_unreachable (); + return const0_rtx; + case BUILT_IN_PRINTF: target = expand_builtin_printf (exp, target, mode, false); if (target) diff --git a/gcc/builtins.def b/gcc/builtins.def index 3f4e251c3fd..8d1693605a6 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -698,6 +698,7 @@ DEF_GCC_BUILTIN (BUILT_IN_SETJMP, "setjmp", BT_FN_INT_PTR, ATTR_NULL) DEF_EXT_LIB_BUILTIN (BUILT_IN_STRFMON, "strfmon", BT_FN_SSIZE_STRING_SIZE_CONST_STRING_VAR, ATTR_FORMAT_STRFMON_NOTHROW_3_4) DEF_LIB_BUILTIN (BUILT_IN_STRFTIME, "strftime", BT_FN_SIZE_STRING_SIZE_CONST_STRING_CONST_PTR, ATTR_FORMAT_STRFTIME_NOTHROW_3_0) DEF_GCC_BUILTIN (BUILT_IN_TRAP, "trap", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LIST) +DEF_GCC_BUILTIN (BUILT_IN_UNREACHABLE, "unreachable", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LIST) DEF_GCC_BUILTIN (BUILT_IN_UNWIND_INIT, "unwind_init", BT_FN_VOID, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_UPDATE_SETJMP_BUF, "update_setjmp_buf", BT_FN_VOID_PTR_INT, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_VA_COPY, "va_copy", BT_FN_VOID_VALIST_REF_VALIST_ARG, ATTR_NOTHROW_LIST) diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index 8da967a4ee0..a8b38d72472 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -1873,8 +1873,12 @@ try_optimize_cfg (int mode) edge s; bool changed_here = false; - /* Delete trivially dead basic blocks. */ - if (EDGE_COUNT (b->preds) == 0) + /* Delete trivially dead basic blocks. This is either + blocks with no predecessors, or empty blocks with no + successors. Empty blocks may result from expanding + __builtin_unreachable (). */ + if (EDGE_COUNT (b->preds) == 0 + || (EDGE_COUNT (b->succs) == 0 && BB_HEAD (b) == BB_END (b))) { c = b->prev_bb; if (dump_file) diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c index 040d4184e61..3129ce6bc66 100644 --- a/gcc/cfgrtl.c +++ b/gcc/cfgrtl.c @@ -2046,15 +2046,17 @@ rtl_verify_flow_info (void) rtx insn; /* Ensure existence of barrier in BB with no fallthru edges. */ - for (insn = BB_END (bb); !insn || !BARRIER_P (insn); - insn = NEXT_INSN (insn)) - if (!insn - || NOTE_INSN_BASIC_BLOCK_P (insn)) + for (insn = NEXT_INSN (BB_END (bb)); ; insn = NEXT_INSN (insn)) + { + if (!insn || NOTE_INSN_BASIC_BLOCK_P (insn)) { error ("missing barrier after block %i", bb->index); err = 1; break; } + if (BARRIER_P (insn)) + break; + } } else if (e->src != ENTRY_BLOCK_PTR && e->dest != EXIT_BLOCK_PTR) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 222d7e0e132..6817af5d6aa 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -6815,6 +6815,61 @@ intentionally executing an illegal instruction) or by calling you should not rely on any particular implementation. @end deftypefn +@deftypefn {Built-in Function} void __builtin_unreachable (void) +If control flow reaches the point of the @code{__builtin_unreachable}, +the program is undefined. It is useful in situations where the +compiler cannot deduce the unreachability of the code. + +One such case is immediately following an @code{asm} statement that +will either never terminate, or one that transfers control elsewhere +and never returns. In this example, without the +@code{__builtin_unreachable}, GCC would issue a warning that control +reaches the end of a non-void function. It would also generate code +to return after the @code{asm}. + +@smallexample +int f (int c, int v) +@{ + if (c) + @{ + return v; + @} + else + @{ + asm("jmp error_handler"); + __builtin_unreachable (); + @} +@} +@end smallexample + +Because the @code{asm} statement unconditionally transfers control out +of the function, control will never reach the end of the function +body. The @code{__builtin_unreachable} is in fact unreachable and +communicates this fact to the compiler. + +Another use for @code{__builtin_unreachable} is following a call a +function that never returns but that is not declared +@code{__attribute__((noreturn))}, as in this example: + +@smallexample +void function_that_never_returns (void); + +int g (int c) +@{ + if (c) + @{ + return 1; + @} + else + @{ + function_that_never_returns (); + __builtin_unreachable (); + @} +@} +@end smallexample + +@end deftypefn + @deftypefn {Built-in Function} void __builtin___clear_cache (char *@var{begin}, char *@var{end}) This function is used to flush the processor's instruction cache for the region of memory between @var{begin} inclusive and @var{end} diff --git a/gcc/system.h b/gcc/system.h index 51d9c995c3c..3bb61ce8490 100644 --- a/gcc/system.h +++ b/gcc/system.h @@ -576,14 +576,20 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN; #if ENABLE_ASSERT_CHECKING #define gcc_assert(EXPR) \ ((void)(!(EXPR) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0)) +#elif (__GNUC__ == 4) && (__GNUC_MINOR__) && 0 +#define gcc_assert(EXPR) do { if (EXPR) __builtin_unreachable (); } while (0) #else /* Include EXPR, so that unused variable warnings do not occur. */ #define gcc_assert(EXPR) ((void)(0 && (EXPR))) #endif +#if !ENABLE_ASSERT_CHECKING && (__GNUC__ == 4) && (__GNUC_MINOR__) && 0 +#define gcc_unreachable() __builtin_unreachable () +#else /* Use gcc_unreachable() to mark unreachable locations (like an unreachable default case of a switch. Do not use gcc_assert(0). */ #define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__)) +#endif /* Provide a fake boolean type. We make no attempt to use the C99 _Bool, as it may not be available in the bootstrap compiler, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d49243f41d5..8d1a113d6ba 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2009-06-11 David Daney + + PR c/39252 + * gcc.dg/builtin-unreachable-1.c: New test. + * gcc.dg/builtin-unreachable-2.c: Same. + 2009-06-11 Paul Thomas PR fortran/40402 diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-1.c b/gcc/testsuite/gcc.dg/builtin-unreachable-1.c new file mode 100644 index 00000000000..165da3f944c --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-unreachable-1.c @@ -0,0 +1,17 @@ +/* Check that __builtin_unreachable() prevents the 'control reaches + end of non-void function' diagnostic. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wreturn-type" } */ +int +f(int a, int b) +{ + if (a) + { + return b; + } + else + { + asm ("bug"); + __builtin_unreachable(); + } +} diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-2.c b/gcc/testsuite/gcc.dg/builtin-unreachable-2.c new file mode 100644 index 00000000000..13bdb9f7395 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-unreachable-2.c @@ -0,0 +1,20 @@ +/* Check that __builtin_unreachable() is a no-return function thus + causing the dead call to foo() to be removed. The comparison is + dead too, and should be removed. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized -fdump-rtl-cse1" } */ +void foo (void); + +int +f (int i) +{ + if (i > 1) + __builtin_unreachable(); + if (i > 1) + foo (); + return 1; +} +/* { dg-final { scan-tree-dump-not "foo" "optimized" } } */ +/* { dg-final { scan-rtl-dump-not "\\(if_then_else" "cse1" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ +/* { dg-final { cleanup-rtl-dump "cse1" } } */