This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add new built-in: __builtin_unreachable()


This patch introduces a new built-in: __builtin_unreachable().  The
motivation is to be able to signal to the compiler that control flow
never leaves an asm statement.

Back in:

http://gcc.gnu.org/ml/gcc-patches/2000-01/msg00190.html

Zack Weinberg suggested the approach of clobbering the pc in the asm's
clobber clause.  This was rejected.

The new __builtin_unreachable() is meant to be placed directly after
an asm that never returns.  I cannot think of any other valid uses for
it, and if control does erroneously reach a __builtin_unreachable(),
program behavior is completely undefined.  I think this is acceptable
behavior as the only reason for having the new built-in is to
communicate control flow information to the compiler in a case where
it cannot deduce it.

Consider the case of the Linux kernel's BUG macro/function.  For the
default config on x86_64, we currently have:

#define BUG()							\
do {								\
	asm volatile("1:\tud2\n"				\
		     ".pushsection __bug_table,\"a\"\n"		\
		     __BUG_C0					\
		     "\t.word %c1, 0\n"				\
		     "\t.org 2b+%c2\n"				\
		     ".popsection"				\
		     : : "i" (__FILE__), "i" (__LINE__),	\
		     "i" (sizeof(struct bug_entry)));		\
	for (;;) ;						\
} while (0)

If we replace that 'for (;;) ;' with a '__builtin_unreachable()', code
size savings is achieved:

$ size vmlinux.pre vmlinux.post
   text	   data	    bss	    dec	    hex	filename
7543782	1122345	 978944	9645071	 932c0f	vmlinux.pre
7532413	1126441	 978944	9637798	 930fa6	vmlinux.post

A savings of 7273 bytes.

Similar savings are observed on a mips64 Linux kernel.


Bootstrapped and regression tested on x86_64-pc-linux-gnu all default languages with no regressions observed.

Also tested by booting x86_64 and mips64 Linux kernels with the
s/for(;;);/__builtin_unreachable();/ change indicated above.

OK to commit?

gcc/
2009-06-03  David Daney  <ddaney@caviumnetworks.com>

	* 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.

gcc/testsuite/
2009-06-03  David Daney  <ddaney@caviumnetworks.com>

* gcc.dg/builtin-unreachable.c: New test.
Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 147888)
+++ gcc/doc/extend.texi	(working copy)
@@ -6813,6 +6813,38 @@ intentionally executing an illegal instr
 you should not rely on any particular implementation.
 @end deftypefn
 
+@deftypefn {Built-in Function} void __builtin_unreachable (void)
+This function is used to indicate to the compiler that control flow
+will never reach the point of the @code{__builtin_unreachable}.  If
+control flow does reach @code{__builtin_unreachable}, program behavior
+is undefined.  The only valid use of this builtin is immediately
+following an @code{asm} statement that either never exits or transfers
+control elsewhere never returning.  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.
+@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}
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 147888)
+++ gcc/builtins.c	(working copy)
@@ -5298,6 +5298,16 @@ expand_builtin_trap (void)
   emit_barrier ();
 }
 
+/* Expand a call to __builtin_unreachable.  We do nothing except emit
+   the 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 here.  */
+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 +6805,10 @@ expand_builtin (tree exp, rtx target, rt
       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)
Index: gcc/testsuite/gcc.dg/builtin-unreachable.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-unreachable.c	(revision 0)
+++ gcc/testsuite/gcc.dg/builtin-unreachable.c	(revision 0)
@@ -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();
+    }
+}
Index: gcc/builtins.def
===================================================================
--- gcc/builtins.def	(revision 147888)
+++ gcc/builtins.def	(working copy)
@@ -698,6 +698,7 @@ DEF_GCC_BUILTIN        (BUILT_IN_SETJMP,
 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)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]