Hi,

The attached test case that uses __builtin_unreachable in the default case
in a switch statement triggers a situation where a wrong instruction is placed
in the delay slot by the eager delay slot filler.

The issue should be reproducible with ToT compiler with -mips32r2 -G0 
-mno-abicalls -O2.

It appears that a possibly related issue is already reported to Bugzilla (bug 
51513)
where the branch is not optimized away, leaving the compare and branch 
instructions.

It would also appear that this should be fixed at the tree level, however, 
handling
a special case when a branch is generated, pointing to 'nowhere' block, is 
probably 
more suitable.

Without the fix, the generated assembly is following:

        ...
        addiu   $2,$3,-3                 
        sltu    $4,$2,33                 
        .set    noreorder                
        .set    nomacro                  
        beq     $4,$0,$L22               
        lw      $4,%lo(mips_cm_is64)($4)
        .set    macro    
        .set    reorder  
                 
        sll     $4,$2,2  
        ...

The load instruction should not be placed in the delay slot as it is a part of
the taken branch with an undefined behaviour. The 'sll' was expected to be in 
the slot.

This is a series of unfortunate events that causes this to happen:
1. After the expansion, the 'nowhere' block is placed after the switch 
statement and
   behaves as a fallthrough case with a diamond shape.
2. The block reordering pass randomly rearranges the nowhere block and it 
happens to be
   placed before the load instruction.
3. The eager delay slot filler firstly redirects jumps by skipping consecutive 
labels
   (ignoring barriers), pointing directly to the load. The analysis is doomed 
to failure at this
   stage as the own thread is analysed and the shift instruction is rejected as 
it uses
   conflicting resources (most likely because $4 is referenced in the load) but 
the opposite
   thread succeeds partly because the set and needed registers sets do not 
intersect when
   the resources are being computed by mark_target_live_regs ().

IMO, the fix is to recognize the empty basic block that has a code_label 
followed by
a barrier (ignoring notes and debug_insns), forbid going beyond the barrier if 
the empty block
is found in skip_consecutive_labels () and first_active_target_insn ().

The patch was cross tested on mips-img-linux-gnu and sparc-linux-gnu.


Regards,
Robert

gcc/
        * reorg.c (label_with_barrier_p): New function.
        (skip_consecutive_labels): Use it.  Don't skip the label if an empty
        block is found.
        (first_active_target_insn): Likewise.  Don't ignore the empty
        block when searching for the next active instruction.

gcc/testsuite
        * gcc.target/mips/builtin-unreachable-bug-1.c: New test.
---
 gcc/reorg.c                                        | 28 +++++++
 .../gcc.target/mips/builtin-unreachable-bug-1.c    | 90 ++++++++++++++++++++++
 2 files changed, 118 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/mips/builtin-unreachable-bug-1.c

diff --git a/gcc/reorg.c b/gcc/reorg.c
index cdaa60c..269f666 100644
--- a/gcc/reorg.c
+++ b/gcc/reorg.c
@@ -145,6 +145,30 @@ along with GCC; see the file COPYING3.  If not see
    These functions are now only used here in reorg.c, and have therefore
    been moved here to avoid inadvertent misuse elsewhere in the compiler.  */
 
+/* Return true iff a LABEL is followed by a BARRIER.  Ignore notes and debug
+   instructions.  */
+
+static bool
+label_with_barrier_p (rtx_insn *label)
+{
+  bool empty_bb = true;
+
+  if (GET_CODE (label) != CODE_LABEL)
+    empty_bb = false;
+  else
+    label = NEXT_INSN (label);
+
+  while (!BARRIER_P (label) && empty_bb)
+  {
+    if (!(DEBUG_INSN_P (label)
+         || NOTE_P (label)))
+      empty_bb = false;
+    label = NEXT_INSN (label);
+  }
+
+  return empty_bb;
+}
+
 /* Return the last label to mark the same position as LABEL.  Return LABEL
    itself if it is null or any return rtx.  */
 
@@ -159,6 +183,8 @@ skip_consecutive_labels (rtx label_or_return)
   rtx_insn *label = as_a <rtx_insn *> (label_or_return);
 
   for (insn = label; insn != 0 && !INSN_P (insn); insn = NEXT_INSN (insn))
+    if (LABEL_P (insn) && label_with_barrier_p (insn))
+      break;
     if (LABEL_P (insn))
       label = insn;
 
@@ -267,6 +293,8 @@ first_active_target_insn (rtx insn)
 {
   if (ANY_RETURN_P (insn))
     return insn;
+  if (LABEL_P (insn) && label_with_barrier_p (as_a <rtx_insn *> (insn)))
+    return NULL_RTX;
   return next_active_insn (as_a <rtx_insn *> (insn));
 }
 

diff --git a/gcc/testsuite/gcc.target/mips/builtin-unreachable-bug-1.c 
b/gcc/testsuite/gcc.target/mips/builtin-unreachable-bug-1.c
new file mode 100644
index 0000000..65eb9a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/builtin-unreachable-bug-1.c
@@ -0,0 +1,90 @@
+/* { dg-options "-mcompact-branches=never -G0 -mno-abicalls" } */
+/* { dg-final { scan-assembler-not 
"beq\t\\\$4,\\\$0,\\\$L\[0-9\]+\n\tlw\t\\\$4,%lo\\(mips_cm_is64\\)\\(\\\$4\\)" 
} } */
+
+enum
+{
+  CPU_R4700,
+  CPU_R5000,
+  CPU_R5500,
+  CPU_NEVADA,
+  CPU_RM7000,
+  CPU_SR71000,
+  CPU_4KC,
+  CPU_4KEC,
+  CPU_4KSC,
+  CPU_24K,
+  CPU_34K,
+  CPU_1004K,
+  CPU_74K,
+  CPU_ALCHEMY,
+  CPU_PR4450,
+  CPU_BMIPS32,
+  CPU_BMIPS3300,
+  CPU_BMIPS5000,
+  CPU_JZRISC,
+  CPU_M14KC,
+  CPU_M14KEC,
+  CPU_INTERAPTIV,
+  CPU_P5600,
+  CPU_PROAPTIV,
+  CPU_1074K,
+  CPU_M5150,
+  CPU_I6400,
+  CPU_R3000,
+  CPU_5KC,
+  CPU_5KE,
+  CPU_20KC,
+  CPU_25KF,
+  CPU_SB1,
+  CPU_SB1A,
+  CPU_XLP,
+  CPU_QEMU_GENERIC
+};
+
+struct cpuinfo_mips
+{
+  long options;
+  int isa_level;
+} cpu_data[1];
+
+struct thread_info
+{
+  int cpu;
+};
+
+int a, b, c, d, e, mips_cm_is64;
+
+static int __attribute__ ((__cold__))
+mips_sc_probe_cm3 ()
+{
+  struct thread_info *info;
+  struct cpuinfo_mips *c = &cpu_data[info->cpu];
+  if (mips_cm_is64)
+    c->options = 0;
+  return 1;
+}
+
+int
+mips_sc_probe ()
+{
+  struct cpuinfo_mips ci = cpu_data[c];
+  if (mips_cm_is64)
+    __asm__("" ::: "memory");
+  if (d)
+    return mips_sc_probe_cm3 ();
+  if (ci.isa_level)
+    switch (a)
+      {
+      case CPU_QEMU_GENERIC:
+      case CPU_I6400:
+      case CPU_R3000:
+      case CPU_NEVADA:
+      case CPU_BMIPS3300:
+       break;
+      default:
+       __builtin_unreachable ();
+      }
+  switch (a)
+    case CPU_R3000:
+      e = b;
+}
-- 
2.4.5

Reply via email to