From 1098d3a5510e41a94ce96feaa087f608112ea01b Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Tue, 8 Sep 2009 11:25:47 +0200
Subject: [PATCH] re PR rtl-optimization/41239 (Scheduler reorders division by
 zero before a call that might not return)

	PR rtl-optimization/41239
	* sched-int.h (struct deps): Add last_function_call_may_noreturn field.
	* sched-rgn.c (deps_join): Join also last_function_call_may_noreturn
	lists.
	* sched-deps.c (sched_analyze_insn): Prevent moving trapping insns
	across calls, as the calls might not always return normally.
	(call_may_noreturn_p): New function.
	(deps_analyze_insn): Update last_function_call_may_noreturn list.
	(init_deps): Initialize it.
	(remove_from_deps): Also remove calls from
	last_function_call_may_noreturn list.

	* gcc.c-torture/execute/pr41239.c: New test.

From-SVN: r151500
---
 gcc/ChangeLog                                 | 14 +++
 gcc/sched-deps.c                              | 91 ++++++++++++++++++-
 gcc/sched-int.h                               |  6 ++
 gcc/sched-rgn.c                               |  5 +
 gcc/testsuite/ChangeLog                       |  5 +
 gcc/testsuite/gcc.c-torture/execute/pr41239.c | 67 ++++++++++++++
 6 files changed, 186 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr41239.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 66c0c145fa6e..bb965df4b390 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,17 @@
+2009-09-08  Jakub Jelinek  <jakub@redhat.com>
+
+	PR rtl-optimization/41239
+	* sched-int.h (struct deps): Add last_function_call_may_noreturn field.
+	* sched-rgn.c (deps_join): Join also last_function_call_may_noreturn
+	lists.
+	* sched-deps.c (sched_analyze_insn): Prevent moving trapping insns
+	across calls, as the calls might not always return normally.
+	(call_may_noreturn_p): New function.
+	(deps_analyze_insn): Update last_function_call_may_noreturn list.
+	(init_deps): Initialize it.
+	(remove_from_deps): Also remove calls from
+	last_function_call_may_noreturn list.
+
 2009-09-07  Bernd Schmidt  <bernd.schmidt@analog.com>
 
 	* config/bfin/bfin.md (UNSPEC_VOLATILE_STALL): New constant.
diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c
index e9dac316e9a9..cef383a1d0eb 100644
--- a/gcc/sched-deps.c
+++ b/gcc/sched-deps.c
@@ -2598,6 +2598,12 @@ sched_analyze_insn (struct deps *deps, rtx x, rtx insn)
   can_start_lhs_rhs_p = (NONJUMP_INSN_P (insn)
 			 && code == SET);
 
+  if (may_trap_p (x))
+    /* Avoid moving trapping instructions accross function calls that might
+       not always return.  */
+    add_dependence_list (insn, deps->last_function_call_may_noreturn,
+			 1, REG_DEP_ANTI);
+
   if (code == COND_EXEC)
     {
       sched_analyze_2 (deps, COND_EXEC_TEST (x), insn);
@@ -3114,6 +3120,73 @@ sched_analyze_insn (struct deps *deps, rtx x, rtx insn)
     }
 }
 
+/* Return TRUE if INSN might not always return normally (e.g. call exit,
+   longjmp, loop forever, ...).  */
+static bool
+call_may_noreturn_p (rtx insn)
+{
+  rtx call;
+
+  /* const or pure calls that aren't looping will always return.  */
+  if (RTL_CONST_OR_PURE_CALL_P (insn)
+      && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))
+    return false;
+
+  call = PATTERN (insn);
+  if (GET_CODE (call) == PARALLEL)
+    call = XVECEXP (call, 0, 0);
+  if (GET_CODE (call) == SET)
+    call = SET_SRC (call);
+  if (GET_CODE (call) == CALL
+      && MEM_P (XEXP (call, 0))
+      && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF)
+    {
+      rtx symbol = XEXP (XEXP (call, 0), 0);
+      if (SYMBOL_REF_DECL (symbol)
+	  && TREE_CODE (SYMBOL_REF_DECL (symbol)) == FUNCTION_DECL)
+	{
+	  if (DECL_BUILT_IN_CLASS (SYMBOL_REF_DECL (symbol))
+	      == BUILT_IN_NORMAL)
+	    switch (DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol)))
+	      {
+	      case BUILT_IN_BCMP:
+	      case BUILT_IN_BCOPY:
+	      case BUILT_IN_BZERO:
+	      case BUILT_IN_INDEX:
+	      case BUILT_IN_MEMCHR:
+	      case BUILT_IN_MEMCMP:
+	      case BUILT_IN_MEMCPY:
+	      case BUILT_IN_MEMMOVE:
+	      case BUILT_IN_MEMPCPY:
+	      case BUILT_IN_MEMSET:
+	      case BUILT_IN_RINDEX:
+	      case BUILT_IN_STPCPY:
+	      case BUILT_IN_STPNCPY:
+	      case BUILT_IN_STRCAT:
+	      case BUILT_IN_STRCHR:
+	      case BUILT_IN_STRCMP:
+	      case BUILT_IN_STRCPY:
+	      case BUILT_IN_STRCSPN:
+	      case BUILT_IN_STRLEN:
+	      case BUILT_IN_STRNCAT:
+	      case BUILT_IN_STRNCMP:
+	      case BUILT_IN_STRNCPY:
+	      case BUILT_IN_STRPBRK:
+	      case BUILT_IN_STRRCHR:
+	      case BUILT_IN_STRSPN:
+	      case BUILT_IN_STRSTR:
+		/* Assume certain string/memory builtins always return.  */
+		return false;
+	      default:
+		break;
+	      }
+	}
+    }
+
+  /* For all other calls assume that they might not always return.  */
+  return true;
+}
+
 /* Analyze INSN with DEPS as a context.  */
 void
 deps_analyze_insn (struct deps *deps, rtx insn)
@@ -3212,7 +3285,16 @@ deps_analyze_insn (struct deps *deps, rtx insn)
           /* Remember the last function call for limiting lifetimes.  */
           free_INSN_LIST_list (&deps->last_function_call);
           deps->last_function_call = alloc_INSN_LIST (insn, NULL_RTX);
-          
+
+	  if (call_may_noreturn_p (insn))
+	    {
+	      /* Remember the last function call that might not always return
+		 normally for limiting moves of trapping insns.  */
+	      free_INSN_LIST_list (&deps->last_function_call_may_noreturn);
+	      deps->last_function_call_may_noreturn
+		= alloc_INSN_LIST (insn, NULL_RTX);
+	    }
+
           /* Before reload, begin a post-call group, so as to keep the
              lifetimes of hard registers correct.  */
           if (! reload_completed)
@@ -3366,6 +3448,7 @@ init_deps (struct deps *deps)
   deps->pending_flush_length = 0;
   deps->last_pending_memory_flush = 0;
   deps->last_function_call = 0;
+  deps->last_function_call_may_noreturn = 0;
   deps->sched_before_next_call = 0;
   deps->in_post_call_group_p = not_post_call;
   deps->last_debug_insn = 0;
@@ -3446,7 +3529,11 @@ remove_from_deps (struct deps *deps, rtx insn)
     }
 
   if (CALL_P (insn))
-    remove_from_dependence_list (insn, &deps->last_function_call);
+    {
+      remove_from_dependence_list (insn, &deps->last_function_call);
+      remove_from_dependence_list (insn, 
+				   &deps->last_function_call_may_noreturn);
+    }
   remove_from_dependence_list (insn, &deps->sched_before_next_call);
 }
 
diff --git a/gcc/sched-int.h b/gcc/sched-int.h
index de780e5e395c..4d60ece28c9d 100644
--- a/gcc/sched-int.h
+++ b/gcc/sched-int.h
@@ -502,6 +502,12 @@ struct deps
      Used to prevent register lifetimes from expanding unnecessarily.  */
   rtx last_function_call;
 
+  /* A list of the last function calls that may not return normally
+     we have seen.  We use a list to represent last function calls from
+     multiple predecessor blocks.  Used to prevent moving trapping insns
+     across such calls.  */
+  rtx last_function_call_may_noreturn;
+
   /* A list of insns which use a pseudo register that does not already
      cross a call.  We create dependencies between each of those insn
      and the next call insn, to ensure that they won't cross a call after
diff --git a/gcc/sched-rgn.c b/gcc/sched-rgn.c
index ff559adcda53..de2dd0acd1d3 100644
--- a/gcc/sched-rgn.c
+++ b/gcc/sched-rgn.c
@@ -2645,6 +2645,11 @@ deps_join (struct deps *succ_deps, struct deps *pred_deps)
     = concat_INSN_LIST (pred_deps->last_function_call,
                         succ_deps->last_function_call);
 
+  /* last_function_call_may_noreturn is inherited by successor.  */
+  succ_deps->last_function_call_may_noreturn
+    = concat_INSN_LIST (pred_deps->last_function_call_may_noreturn,
+                        succ_deps->last_function_call_may_noreturn);
+
   /* sched_before_next_call is inherited by successor.  */
   succ_deps->sched_before_next_call
     = concat_INSN_LIST (pred_deps->sched_before_next_call,
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 1c00a6bdb7a2..026f8a95d587 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2009-09-07  Jakub Jelinek  <jakub@redhat.com>
+
+	PR rtl-optimization/41239
+	* gcc.c-torture/execute/pr41239.c: New test.
+
 2009-09-07  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
 
 	PR libgfortran/41192
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr41239.c b/gcc/testsuite/gcc.c-torture/execute/pr41239.c
new file mode 100644
index 000000000000..9966b867c3b4
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr41239.c
@@ -0,0 +1,67 @@
+/* PR rtl-optimization/41239 */
+
+struct S
+{
+  short nargs;
+  unsigned long arg[2];
+};
+
+extern void abort (void);
+extern void exit (int);
+extern char fn1 (int, const char *, int, const char *, const char *);
+extern void fn2 (int, ...);
+extern int fn3 (int);
+extern int fn4 (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+unsigned long
+test (struct S *x)
+{
+  signed int arg1 = x->arg[0];
+  long int arg2 = x->arg[1];
+
+  if (arg2 == 0)
+    (fn1 (20, "foo", 924, __func__, ((void *) 0))
+     ? (fn2 (fn3 (0x2040082), fn4 ("division by zero")))
+     : (void) 0);
+
+  return (long int) arg1 / arg2;
+}
+
+int
+main (void)
+{
+  struct S s = { 2, { 5, 0 } };
+  test (&s);
+  abort ();
+}
+
+__attribute__((noinline)) char
+fn1 (int x, const char *y, int z, const char *w, const char *v)
+{
+  asm volatile ("" : : "r" (w), "r" (v) : "memory");
+  asm volatile ("" : "+r" (x) : "r" (y), "r" (z) : "memory");
+  return x;
+}
+
+__attribute__((noinline)) int
+fn3 (int x)
+{
+  asm volatile ("" : "+r" (x) : : "memory");
+  return x;
+}
+
+__attribute__((noinline)) int
+fn4 (const char *x, ...)
+{
+  asm volatile ("" : "+r" (x) : : "memory");
+  return *x;
+}
+
+__attribute__((noinline)) void
+fn2 (int x, ...)
+{
+  asm volatile ("" : "+r" (x) : : "memory");
+  if (x)
+    /* Could be a longjmp or throw too.  */
+    exit (0);
+}
-- 
GitLab