regmove.c (discover_flags_reg): New function.
authorRichard Henderson <rth@cygnus.com>
Fri, 19 Feb 1999 23:02:58 +0000 (15:02 -0800)
committerRichard Henderson <rth@gcc.gnu.org>
Fri, 19 Feb 1999 23:02:58 +0000 (15:02 -0800)
        * regmove.c (discover_flags_reg): New function.
        (flags_set_1, mark_flags_life_zones): New functions.
        (regmove_optimize): Call them.
        (fixup_match_1): Use insn modes rather than sets_cc0_p.

From-SVN: r25332

gcc/ChangeLog
gcc/regmove.c

index a1946ef7246b7a711904853469f915eb591c3e55..7cee31cd5de9fcb0023329bd28d02d3c7e73dc1b 100644 (file)
@@ -1,3 +1,10 @@
+Fri Feb 19 23:02:02 1999  Richard Henderson  <rth@cygnus.com>
+
+       * regmove.c (discover_flags_reg): New function.
+       (flags_set_1, mark_flags_life_zones): New functions.
+       (regmove_optimize): Call them.
+       (fixup_match_1): Use insn modes rather than sets_cc0_p.
+
 Fri Feb 19 22:47:01 1999  J"orn Rennecke <amylaar@cygnus.co.uk>
 
        * rtlanal.c (insn_first_p): Fix return value for insn == reference.
index 70071ac71623befbeff6631eefaac84c525376dd..04fcb5da5a4878abc48b75dff5894cfca84d7eea 100644 (file)
@@ -53,6 +53,10 @@ struct match {
   int early_clobber[MAX_RECOG_OPERANDS];
 };
 
+static rtx discover_flags_reg PROTO((void));
+static void mark_flags_life_zones PROTO((rtx));
+static void flags_set_1 PROTO((rtx, rtx));
+
 static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
 static int find_matches PROTO((rtx, struct match *));
 static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
@@ -150,7 +154,172 @@ try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
     }
   return 0;
 }
+\f
+/* Determine if the pattern generated by add_optab has a clobber,
+   such as might be issued for a flags hard register.  To make the
+   code elsewhere simpler, we handle cc0 in this same framework.
+
+   Return the register if one was discovered.  Return NULL_RTX if
+   if no flags were found.  Return pc_rtx if we got confused.  */
+
+static rtx
+discover_flags_reg ()
+{
+  rtx tmp;
+  tmp = gen_rtx_REG (SImode, 10000);
+  tmp = gen_add3_insn (tmp, tmp, GEN_INT (2));
+
+  /* If we get something that isn't a simple set, or a 
+     [(set ..) (clobber ..)], this whole function will go wrong.  */
+  if (GET_CODE (tmp) == SET)
+    return NULL_RTX;
+  else if (GET_CODE (tmp) == PARALLEL)
+    {
+      int found;
+
+      if (XVECLEN (tmp, 0) != 2)
+       return pc_rtx;
+      tmp = XVECEXP (tmp, 0, 1);
+      if (GET_CODE (tmp) != CLOBBER)
+       return pc_rtx;
+      tmp = XEXP (tmp, 0);
+
+      /* Don't do anything foolish if the md wanted to clobber a
+        scratch or something.  We only care about hard regs.
+        Moreover we don't like the notion of subregs of hard regs.  */
+      if (GET_CODE (tmp) == SUBREG
+         && GET_CODE (SUBREG_REG (tmp)) == REG
+         && REGNO (SUBREG_REG (tmp)) < FIRST_PSEUDO_REGISTER)
+       return pc_rtx;
+      found = (GET_CODE (tmp) == REG && REGNO (tmp) < FIRST_PSEUDO_REGISTER);
+
+#ifdef HAVE_cc0
+      /* If we're cc0, and we found a potential flags reg, bail.  */
+      return (found ? pc_rtx : cc0_rtx);
+#else
+      return (found ? tmp : NULL_RTX);
+#endif
+    }
+
+  return pc_rtx;
+}
+
+/* It is a tedious task identifying when the flags register is live and
+   when it is safe to optimize.  Since we process the instruction stream
+   multiple times, locate and record these live zones by marking the
+   mode of the instructions -- 
+
+   QImode is used on the instruction at which the flags becomes live.
+
+   HImode is used within the range (exclusive) that the flags are
+   live.  Thus the user of the flags is not marked.
+
+   All other instructions are cleared to VOIDmode.  */
+
+/* Used to communicate with flags_set_1.  */
+static rtx flags_set_1_rtx;
+static int flags_set_1_set;
+
+static void
+mark_flags_life_zones (flags)
+     rtx flags;
+{
+  int flags_regno;
+  int flags_nregs;
+  int block;
+
+  /* Simple cases first: if no flags, clear all modes.  If confusing,
+     mark the entire function as being in a flags shadow.  */
+  if (flags == NULL_RTX || flags == pc_rtx)
+    {
+      enum machine_mode mode = (flags ? HImode : VOIDmode);
+      rtx insn;
+      for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+       PUT_MODE (insn, mode);
+      return;
+    }
+
+#ifdef HAVE_cc0
+  flags_regno = -1;
+  flags_nregs = 1;
+#else
+  flags_regno = REGNO (flags);
+  flags_nregs = HARD_REGNO_NREGS (flags_regno, GET_MODE (flags));
+#endif
+  flags_set_1_rtx = flags;
+
+  /* Process each basic block.  */
+  for (block = n_basic_blocks - 1; block >= 0; block--)
+    {
+      rtx insn, end;
+      int live;
+
+      insn = BLOCK_HEAD (block);
+      end = BLOCK_END (block);
+
+      /* Look out for the (unlikely) case of flags being live across
+        basic block boundaries.  */
+      live = 0;
+#ifndef HAVE_cc0
+      {
+       int i;
+       for (i = 0; i < flags_nregs; ++i)
+          live |= REGNO_REG_SET_P (basic_block_live_at_start[block],
+                                  flags_regno + i);
+      }
+#endif
+
+      while (1)
+       {
+         /* Process liveness in reverse order of importance --
+            alive, death, birth.  This lets more important info
+            overwrite the mode of lesser info.  */
+
+         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+           {
+#ifdef HAVE_cc0
+             /* In the cc0 case, death is not marked in reg notes,
+                but is instead the mere use of cc0 when it is alive.  */
+             if (live && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+               live = 0;
+#else
+             /* In the hard reg case, we watch death notes.  */
+             if (live && find_regno_note (insn, REG_DEAD, flags_regno))
+               live = 0;
+#endif
+             PUT_MODE (insn, (live ? HImode : VOIDmode));
+
+             /* In either case, birth is denoted simply by it's presence
+                as the destination of a set.  */
+             flags_set_1_set = 0;
+             note_stores (PATTERN (insn), flags_set_1);
+             if (flags_set_1_set)
+               {
+                 live = 1;
+                 PUT_MODE (insn, QImode);
+               }
+           }
+         else
+           PUT_MODE (insn, (live ? HImode : VOIDmode));
+
+         if (insn == end)
+           break;
+         insn = NEXT_INSN (insn);
+       }
+    }
+}
 
+/* A subroutine of mark_flags_life_zones, called through note_stores.  */
+
+static void
+flags_set_1 (x, pat)
+     rtx x, pat;
+{
+  if (GET_CODE (pat) == SET
+      && reg_overlap_mentioned_p (x, flags_set_1_rtx))
+    flags_set_1_set = 1;
+}
+\f
 static int *regno_src_regno;
 
 /* Indicate how good a choice REG (which appears as a source) is to replace
@@ -908,6 +1077,10 @@ regmove_optimize (f, nregs, regmove_dump_file)
   int i;
   rtx copy_src, copy_dst;
 
+  /* Find out where a potential flags register is live, and so that we
+     can supress some optimizations in those zones.  */
+  mark_flags_life_zones (discover_flags_reg ());
+
   regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs);
   for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1;
 
@@ -1617,13 +1790,9 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
                            && GET_CODE (SET_DEST (single_set (p))) == REG
                            && (REGNO (SET_DEST (single_set (p)))
                                < FIRST_PSEUDO_REGISTER))
-#ifdef HAVE_cc0
-                     /* We may not emit an insn directly
-                        after P if the latter sets CC0.  */
-                     && ! sets_cc0_p (PATTERN (p))
-#endif
-                     )
-
+                     /* We may only emit an insn directly after P if we
+                        are not in the shadow of a live flags register.  */
+                     && GET_MODE (p) == VOIDmode)
                    {
                      search_end = q;
                      q = insn;