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
This commit is contained in:
parent
c673a2c7b4
commit
1098d3a551
6 changed files with 186 additions and 2 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
67
gcc/testsuite/gcc.c-torture/execute/pr41239.c
Normal file
67
gcc/testsuite/gcc.c-torture/execute/pr41239.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Add table
Reference in a new issue