expr.c (store_constructor): When initializing a field that is smaller than a word...
authorJ"orn Rennecke <amylaar@cygnus.co.uk>
Thu, 24 Sep 1998 09:59:41 +0000 (09:59 +0000)
committerJoern Rennecke <amylaar@gcc.gnu.org>
Thu, 24 Sep 1998 09:59:41 +0000 (10:59 +0100)
* expr.c (store_constructor): When initializing a field that is smaller
than a word, at the start of a word, try to widen it to a full word.
* cse.c (cse_insn): When we are about to change a register,
remove any invalid references to it.
(remove_invalid_subreg_refs): New function.
(mention_regs): Special treatment for SUBREGs.
(insert_regs): Don't strip SUBREG for call to mention_regs.
Check if reg_tick needs to be bumped up before that call.
(lookup_as_function): Try to match known word_mode constants when
looking for a norrower constant.
(canon_hash): Special treatment for SUBREGs.

From-SVN: r22567

gcc/ChangeLog
gcc/cse.c
gcc/expr.c

index 0c2c7fba96c5ce7b8c9b8ac1f7cc94d5a94caaba..e178f528dbc96347c663b243822030d6290e7116 100644 (file)
@@ -1,3 +1,19 @@
+Thu Sep 24 17:45:55 1998  J"orn Rennecke <amylaar@cygnus.co.uk>
+
+       * expr.c (store_constructor): When initializing a field that is smaller
+       than a word, at the start of a word, try to widen it to a full word.
+
+       * cse.c (cse_insn): When we are about to change a register,
+       remove any invalid references to it.
+
+       (remove_invalid_subreg_refs): New function.
+       (mention_regs): Special treatment for SUBREGs.
+       (insert_regs): Don't strip SUBREG for call to mention_regs.
+       Check if reg_tick needs to be bumped up before that call.
+       (lookup_as_function): Try to match known word_mode constants when
+       looking for a norrower constant.
+       (canon_hash): Special treatment for SUBREGs.
+
 Thu Sep 24 01:35:34 1998  David S. Miller  <davem@pierdol.cobaltmicro.com>
 
        * config/sparc/sol2-sld-64.h (TRANSFER_FROM_TRAMPOLINE): Define.
index 94e80c3ee6d6a4be396432194eac2e823561ccb5..ddb164d9629b7c3bd288a7c8b710398e31941bf6 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -626,6 +626,7 @@ static void merge_equiv_classes PROTO((struct table_elt *,
 static void invalidate         PROTO((rtx, enum machine_mode));
 static int cse_rtx_varies_p    PROTO((rtx));
 static void remove_invalid_refs        PROTO((int));
+static void remove_invalid_subreg_refs PROTO((int, int, enum machine_mode));
 static void rehash_using_reg   PROTO((rtx));
 static void invalidate_memory  PROTO((void));
 static void invalidate_for_call        PROTO((void));
@@ -980,6 +981,30 @@ mention_regs (x)
       return 0;
     }
 
+  /* If this is a SUBREG, we don't want to discard other SUBREGs of the same
+     pseudo if they don't use overlapping words.  We handle only pseudos
+     here for simplicity.  */
+  if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+      && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
+    {
+      int i = REGNO (SUBREG_REG (x));
+
+      if (reg_in_table[i] >= 0 && reg_in_table[i] != reg_tick[i])
+       {
+         /* If reg_tick has been incremented more than once since
+            reg_in_table was last set, that means that the entire
+            register has been set before, so discard anything memorized
+            for the entrire register, including all SUBREG expressions.  */
+         if (reg_in_table[i] != reg_tick[i] - 1)
+           remove_invalid_refs (i);
+         else
+           remove_invalid_subreg_refs (i, SUBREG_WORD (x), GET_MODE (x));
+       }
+
+      reg_in_table[i] = reg_tick[i];
+      return 0;
+    }
+
   /* If X is a comparison or a COMPARE and either operand is a register
      that does not have a quantity, give it one.  This is so that a later
      call to record_jump_equiv won't cause X to be assigned a different
@@ -1077,8 +1102,19 @@ insert_regs (x, classp, modified)
   else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
           && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x))))
     {
+      int regno = REGNO (SUBREG_REG (x));
+
       insert_regs (SUBREG_REG (x), NULL_PTR, 0);
-      mention_regs (SUBREG_REG (x));
+      /* Mention_regs checks if REG_TICK is exactly one larger than
+        REG_IN_TABLE to find out if there was only a single preceding
+        invalidation - for the SUBREG - or another one, which would be
+        for the full register.  Since we don't invalidate the SUBREG
+        here first, we might have to bump up REG_TICK so that mention_regs
+        will do the right thing.  */
+      if (reg_in_table[regno] >= 0
+         && reg_tick[regno] == reg_in_table[regno] + 1)
+       reg_tick++;
+      mention_regs (x);
       return 1;
     }
   else
@@ -1254,6 +1290,17 @@ lookup_as_function (x, code)
 {
   register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS,
                                         GET_MODE (x));
+  /* If we are looking for a CONST_INT, the mode doesn't really matter, as
+     long as we are narrowing.  So if we looked in vain for a mode narrower
+     than word_mode before, look for word_mode now.  */
+  if (p == 0 && code == CONST_INT
+      && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
+    {
+      x = copy_rtx (x);
+      PUT_MODE (x, word_mode);
+      p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, word_mode);
+    }
+
   if (p == 0)
     return 0;
 
@@ -1684,6 +1731,37 @@ remove_invalid_refs (regno)
          remove_from_table (p, i);
       }
 }
+
+/* Likewise for a subreg with subreg_reg WORD and mode MODE.  */
+static void
+remove_invalid_subreg_refs (regno, word, mode)
+     int regno;
+     int word;
+     enum machine_mode mode;
+{
+  register int i;
+  register struct table_elt *p, *next;
+  int end = word + (GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD;
+
+  for (i = 0; i < NBUCKETS; i++)
+    for (p = table[i]; p; p = next)
+      {
+       rtx exp;
+       next = p->next_same_hash;
+       
+       exp = p->exp;
+       if (GET_CODE (p->exp) != REG
+           && (GET_CODE (exp) != SUBREG
+               || GET_CODE (SUBREG_REG (exp)) != REG
+               || REGNO (SUBREG_REG (exp)) != regno
+               || (((SUBREG_WORD (exp)
+                     + (GET_MODE_SIZE (GET_MODE (exp)) - 1) / UNITS_PER_WORD)
+                    >= word)
+                && SUBREG_WORD (exp) <= end))
+           && refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR))
+         remove_from_table (p, i);
+      }
+}
 \f
 /* Recompute the hash codes of any valid entries in the hash table that
    reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG.
@@ -1930,6 +2008,20 @@ canon_hash (x, mode)
        return hash;
       }
 
+    /* We handle SUBREG of a REG specially because the underlying
+       reg changes its hash value with every value change; we don't
+       want to have to forget unrelated subregs when one subreg changes.  */
+    case SUBREG:
+      {
+       if (GET_CODE (SUBREG_REG (x)) == REG)
+         {
+           hash += (((unsigned) SUBREG << 7)
+                    + REGNO (SUBREG_REG (x)) + SUBREG_WORD (x));
+           return hash;
+         }
+       break;
+      }
+
     case CONST_INT:
       {
        unsigned HOST_WIDE_INT tem = INTVAL (x);
@@ -7409,8 +7501,44 @@ cse_insn (insn, libcall_insn)
      we are going to hash the SET_DEST values unconditionally.  */
 
   for (i = 0; i < n_sets; i++)
-    if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG)
-      mention_regs (SET_DEST (sets[i].rtl));
+    {
+      if (sets[i].rtl)
+       {
+         rtx x = SET_DEST (sets[i].rtl);
+
+         if (GET_CODE (x) != REG)
+           mention_regs (x);
+         else
+           {
+             /* We used to rely on all references to a register becoming
+                inaccessible when a register changes to a new quantity,
+                since that changes the hash code.  However, that is not
+                safe, since after NBUCKETS new quantities we get a
+                hash 'collision' of a register with its own invalid
+                entries.  And since SUBREGs have been changed not to
+                change their hash code with the hash code of the register,
+                it wouldn't work any longer at all.  So we have to check
+                for any invalid references lying around now.
+                This code is similar to the REG case in mention_regs,
+                but it knows that reg_tick has been incremented, and
+                it leaves reg_in_table as -1 .  */
+             register int regno = REGNO (x);
+             register int endregno
+               = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
+                          : HARD_REGNO_NREGS (regno, GET_MODE (x)));
+             int i;
+
+             for (i = regno; i < endregno; i++)
+               {
+                 if (reg_in_table[i] >= 0)
+                   {
+                     remove_invalid_refs (i);
+                     reg_in_table[i] = -1;
+                   }
+               }
+           }
+       }
+    }
 
   /* We may have just removed some of the src_elt's from the hash table.
      So replace each one with the current head of the same class.  */
index 028f9b21cfda28e73651d81caa8a95e58c745607..2c82924d2208e2be38883821119efd03e3b293bb 100644 (file)
@@ -3819,6 +3819,7 @@ store_constructor (exp, target, cleared)
      int cleared;
 {
   tree type = TREE_TYPE (exp);
+  rtx exp_size = expr_size (exp);
 
   /* We know our target cannot conflict, since safe_from_p has been called.  */
 #if 0
@@ -3880,6 +3881,7 @@ store_constructor (exp, target, cleared)
       for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
        {
          register tree field = TREE_PURPOSE (elt);
+         tree value = TREE_VALUE (elt);
          register enum machine_mode mode;
          int bitsize;
          int bitpos = 0;
@@ -3951,8 +3953,36 @@ store_constructor (exp, target, cleared)
              RTX_UNCHANGING_P (to_rtx) = 1;
            }
 
+#ifdef WORD_REGISTER_OPERATIONS
+         /* If this initializes a field that is smaller than a word, at the
+            start of a word, try to widen it to a full word.
+            This special case allows us to output C++ member function
+            initializations in a form that the optimizers can understand.  */
+         if (constant
+             && GET_CODE (target) == REG
+             && bitsize < BITS_PER_WORD
+             && bitpos % BITS_PER_WORD == 0
+             && GET_MODE_CLASS (mode) == MODE_INT
+             && TREE_CODE (value) == INTEGER_CST
+             && GET_CODE (exp_size) == CONST_INT
+             && bitpos + BITS_PER_WORD <= INTVAL (exp_size) * BITS_PER_UNIT)
+           {
+             tree type = TREE_TYPE (value);
+             if (TYPE_PRECISION (type) < BITS_PER_WORD)
+               {
+                 type = type_for_size (BITS_PER_WORD, TREE_UNSIGNED (type));
+                 value = convert (type, value);
+               }
+             if (BYTES_BIG_ENDIAN)
+               value
+                 = fold (build (LSHIFT_EXPR, type, value,
+                                build_int_2 (BITS_PER_WORD - bitsize, 0)));
+             bitsize = BITS_PER_WORD;
+             mode = word_mode;
+           }
+#endif
          store_constructor_field (to_rtx, bitsize, bitpos,
-                                  mode, TREE_VALUE (elt), type, cleared);
+                                  mode, value, type, cleared);
        }
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)