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