PR 63347 is a case where we can end up scheduling an insn into the middle of a scheduling group.
Basically given two consecutive insns A & B, if B has SCHED_GROUP_P set, then it must always be immediately after A.
The problem we ran into in this PR was that there was a cost to issue B, so it got delayed several cycles. While B was delayed an earlier, unrelated insn Z which had been delayed earlier became ready and issued. The result being A Z B which ultimately led to incorrect code generation.
The fix is actually quite simple. In prune_ready_list, we queue insns that have had their dependencies satisfied, but have a cost (say for example, they're waiting for a functional unit to become available). We want to queue based on that cost, with one exception. If the insn to be queued is part of a scheduling group, then we want to queue it with cost 1.
That ensures that on each cycle, any SCHED_GROUP_P insn that has its dependencies satisfied is in the ready list. Once we do that, everything is in place to assure that nothing issues until after the SCHED_GROUP_P insn issues.
Bootstrapped and regression tested on x86_64-unknown-linux-gnu. Also verified the testcase works correctly on m68k-unknown-linux. Installed on the trunk.
commit 07e421b3fbc2d29c9636200906c3edf3cbd33499 Author: Jeff Law <l...@redhat.com> Date: Wed Feb 11 16:19:05 2015 -0700 PR target/63347 * haifa-sched.c (prune_ready_list): If we have a SCHED_GROUP_P insn that needs to be queued, just queue it for a single cycle. PR target/63347 * gcc.target/m68k/pr63347.c: New test. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d895209..c9ac045 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2015-02-11 Jeff Law <l...@redhat.com> + + PR target/63347 + * haifa-sched.c (prune_ready_list): If we have a SCHED_GROUP_P insn + that needs to be queued, just queue it for a single cycle. + 2015-02-11 Jan Hubicka <hubi...@ucw.cz> * ipa.c (symbol_table::remove_unreachable_nodes): Avoid releasing diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index 75d2421..64c8c9c1 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -6291,7 +6291,15 @@ prune_ready_list (state_t temp_state, bool first_cycle_insn_p, if (SCHED_GROUP_P (insn) && cost > min_cost_group) min_cost_group = cost; ready_remove (&ready, i); - queue_insn (insn, cost, reason); + /* Normally we'd want to queue INSN for COST cycles. However, + if SCHED_GROUP_P is set, then we must ensure that nothing + else comes between INSN and its predecessor. If there is + some other insn ready to fire on the next cycle, then that + invariant would be broken. + + So when SCHED_GROUP_P is set, just queue this insn for a + single cycle. */ + queue_insn (insn, SCHED_GROUP_P (insn) ? 1 : cost, reason); if (i + 1 < n) break; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c62cc23..ee5be51 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-02-11 Jeff Law <l...@redhat.com> + + PR target/63347 + * gcc.target/m68k/pr63347.c: New test. + 2015-02-11 Marek Polacek <pola...@redhat.com> * g++.dg/ubsan/shift-1.C: New test. diff --git a/gcc/testsuite/gcc.target/m68k/pr63347.c b/gcc/testsuite/gcc.target/m68k/pr63347.c new file mode 100644 index 0000000..1d23e9a --- /dev/null +++ b/gcc/testsuite/gcc.target/m68k/pr63347.c @@ -0,0 +1,46 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mcpu=5208" } */ + +#include <stdlib.h> + +void __attribute__ ((noinline)) +oof() +{ + asm volatile ("" ::: "memory"); +} +int print_info(unsigned int *ip_addr) +{ + int invalid = 0; + + if (ip_addr) { + unsigned int haddr = *ip_addr; + oof("stuff"); + if (0x0 == haddr) { + invalid = 1; + } + oof("stuff2"); + } else { + invalid = 1; + } + + return invalid; +} + +int main(int argc, char *argv[]) +{ + unsigned int myaddr; + int ret; + + myaddr = 0x0; + ret = print_info(&myaddr); + if (!ret) + abort (); + + myaddr = 0x01020304; + ret = print_info(&myaddr); + if (ret) + abort (); + exit (0); +} + +