This is a regression present on the 6 branch for MIPS (and latent on the 7 
branch and mainline).  The reorg pass generates wrong code for the attached 
testcase with a combination of options under the form of a double lo_sum(sym) 
instruction for a single hi(sum).

The pass starts from a convoluted CFG with 4 instances of the same pair:

  set $16,hi(sym)          (1)
  set $16,lo_sum($16,sym)  (2)

and generates wrong code after 12 steps: first, the 4 (1) instructions are 
moved into delay slots, then one of them is stolen and moved into another 
slot, then a second one is deleted, then a third one is also stolen, and then 
the first stolen one is deleted.  Then 3 (2) instructions are moved into the 
same delay slots (again empty) as the (1) and, finally, one of them is stolen.

All this dance considerably changes the live ranges of the $16 register, in 
particular the deletion of 2 (1) instructions.  The strategy of reorg is not 
to update the liveness info, but to leave markers instead so that it can be 
recomputed locally.  But there is a hitch: it doesn't leave a marker when

  /* Ignore if this was in a delay slot and it came from the target of
     a branch.  */
  if (INSN_FROM_TARGET_P (insn))
    return;

This exception has been there since the dawn of time and I can guess what kind 
of reasoning led to it, but it's probably valid only for simple situations and 
not for the kind of big transformations described above.

Lifting it fixes the wrong code because it leaves the necessary markers when 
instructions that were stolen are then deleted.  Surprisingly(?) enough, it 
doesn't seem to have much effect outside this case (e.g. 0 changes for the 
entire compile.exp testsuite at -O2 on SPARC and virtually same cc1 binaries).

Tested on SPARC/Solaris, objections to applying it on mainline,7 & 6 branches?


2017-06-06  Eric Botcazou  <ebotca...@adacore.com>

        PR rtl-optimization/80474
        * reorg.c (update_block): Do not ignore instructions in a delay slot.

-- 
Eric Botcazou
int b;
static int a(char *h) { return 5; }
typedef enum { c } d;
typedef char *(*f)(unsigned char, unsigned char, d, unsigned, unsigned, char *,
                   unsigned);
typedef struct {
  char **g;
  f *i;
} j;
char *k(unsigned char, unsigned char, d, unsigned, unsigned, char *, unsigned);
char *m(unsigned char, unsigned char, d, unsigned, unsigned, char *, unsigned);
char *badFn(unsigned char,
                                                           unsigned char, d,
                                                           unsigned, unsigned,
                                                           char *, unsigned);
char *n(unsigned char, unsigned char, d, unsigned, unsigned, char *, unsigned);
char *o(unsigned char, unsigned char, d, unsigned, unsigned, char *, unsigned);
void InitEvent(j *h, unsigned p, int q, char *r, f s, char w) {
  int e = 0;
  for (; e < q; e++) {
    h->i[p] = s;
    h->g[p] = r;
  }
}
f t;
void u(j *h, unsigned p) {
  unsigned ac = p & (65536 | 2097152) ? 1 : 8;
  if (p & (65536 | 2097152 | 524288))
    if (ac == 1) {
      j v = *h, aa = v;
      aa.i[3] = n;
    }
  if (p & (8192 | 65536 | 2097152))
    if (p & 524288)
      InitEvent(h, p & 65536, 1, "", 0, 1);
  if (ac == 1)
    InitEvent(h, p, 1, "", 0, 1);
  if (p)
    if (ac == 1)
      t = 0;
  if (p & 8192)
    t = 0;
  if (p & (8192 | 65536 | 2097152))
    InitEvent(h, 0, 1, "", 0, 1);
  if (p)
    InitEvent(h, 0, 1, "", 0, 1);
  if (p & 2097152)
    t = 0;
  if (p & 8)
    t = 0;
  if (p & (8192 | 65536 | 2097152 | 524288))
    if (ac == 1)
      InitEvent(h, 0, 1, "", 0, 1);
  if (p & 8192)
    InitEvent(h, 0, 1, "OUTRESEG_DRAM_DATA_ECC_CORRECTED_1", m, 1);
  if (p & (8192 | 65536 | 2097152 | 524288)) {
    if (ac == 1)
      InitEvent(h, p & 524288 ? 0 : p & 8192 ?: p & 2097152 ?: 5, 1, "",
                      badFn, 1);
    else
      InitEvent(h, 0, 1, "",
                      badFn, 1);
  }
  if (p & 8192)
    InitEvent(h, 0, 1, "OUTRESEG_DRAM_DATA_ECC_UNCORRECTABLE_1",
                    badFn, 1);
  if (p & (8192 | 65536 | 2097152 | 524288))
    if (ac == 1)
      InitEvent(h, 0, 1, "OUTRESEG_COLOUR_MARKING_OVERWRITE", k, 1);
  if (p & (65536 | 2097152 | 524288))
    if (ac == 1)
      InitEvent(h, 0, 0, "", 0, 1);
  if (p & 65536)
    if (p & (65536 | 2097152))
      InitEvent(h, 5, 0, "", 0, 1);
  if (p & 524288)
    b = a("");
  InitEvent(h, p * (p & 2097152), 1,
                  "DMFE_QUAD_RBUF_RBUF_UNCORRECTED_ECC", o, 1);
}
char *l(char h, char p, d q, unsigned r, unsigned s, char *w, unsigned x) {
  return w;
}
char *ak(char h, char p, d q, unsigned r, unsigned s, char *w, unsigned x) {
  return w;
}
Index: reorg.c
===================================================================
--- reorg.c	(revision 248571)
+++ reorg.c	(working copy)
@@ -1697,9 +1697,8 @@ own_thread_p (rtx thread, rtx label, int
 }
 
 /* Called when INSN is being moved from a location near the target of a jump.
-   We leave a marker of the form (use (INSN)) immediately in front
-   of WHERE for mark_target_live_regs.  These markers will be deleted when
-   reorg finishes.
+   We leave a marker of the form (use (INSN)) immediately in front of WHERE
+   for mark_target_live_regs.  These markers will be deleted at the end.
 
    We used to try to update the live status of registers if WHERE is at
    the start of a basic block, but that can't work since we may remove a
@@ -1708,16 +1707,10 @@ own_thread_p (rtx thread, rtx label, int
 static void
 update_block (rtx_insn *insn, rtx where)
 {
-  /* Ignore if this was in a delay slot and it came from the target of
-     a branch.  */
-  if (INSN_FROM_TARGET_P (insn))
-    return;
-
   emit_insn_before (gen_rtx_USE (VOIDmode, insn), where);
 
   /* INSN might be making a value live in a block where it didn't use to
      be.  So recompute liveness information for this block.  */
-
   incr_ticks_for_insn (insn);
 }
 

Reply via email to