OSDN Git Service

* g++.old-deja/g++.benjamin/16077.C: Adjust warnings.
[pf3gnuchains/gcc-fork.git] / gcc / gcse.c
index 3fdf47e..f0e588e 100644 (file)
@@ -479,6 +479,9 @@ struct ls_expr
   rtx reaching_reg;            /* Register to use when re-writing.  */
 };
 
+/* Array of implicit set patterns indexed by basic block index.  */
+static rtx *implicit_sets;
+
 /* Head of the list of load/store memory refs.  */
 static struct ls_expr * pre_ldst_mems = NULL;
 
@@ -590,7 +593,7 @@ static void compute_hash_table_work PARAMS ((struct hash_table *));
 static void dump_hash_table    PARAMS ((FILE *, const char *,
                                        struct hash_table *));
 static struct expr *lookup_expr        PARAMS ((rtx, struct hash_table *));
-static struct expr *lookup_set PARAMS ((unsigned int, rtx, struct hash_table *));
+static struct expr *lookup_set PARAMS ((unsigned int, struct hash_table *));
 static struct expr *next_set   PARAMS ((unsigned int, struct expr *));
 static void reset_opr_set_tables PARAMS ((void));
 static int oprs_not_set_p      PARAMS ((rtx, rtx));
@@ -614,6 +617,8 @@ static int load_killed_in_block_p    PARAMS ((basic_block, int, rtx, int));
 static void canon_list_insert        PARAMS ((rtx, rtx, void *));
 static int cprop_insn          PARAMS ((rtx, int));
 static int cprop               PARAMS ((int));
+static rtx fis_get_condition   PARAMS ((rtx));
+static void find_implicit_sets PARAMS ((void));
 static int one_cprop_pass      PARAMS ((int, int, int));
 static bool constprop_register PARAMS ((rtx, rtx, rtx, int));
 static struct expr *find_bypass_set PARAMS ((int, int));
@@ -701,6 +706,7 @@ static void free_insn_expr_list_list        PARAMS ((rtx *));
 static void clear_modify_mem_tables    PARAMS ((void));
 static void free_modify_mem_tables     PARAMS ((void));
 static rtx gcse_emit_move_after                PARAMS ((rtx, rtx, rtx));
+static void local_cprop_find_used_regs PARAMS ((rtx *, void *));
 static bool do_local_cprop             PARAMS ((rtx, rtx, int, rtx*));
 static bool adjust_libcall_notes       PARAMS ((rtx, rtx, rtx, rtx*));
 static void local_cprop_pass           PARAMS ((int));
@@ -1305,6 +1311,7 @@ want_to_gcse_p (x)
     case CONST_DOUBLE:
     case CONST_VECTOR:
     case CALL:
+    case CONSTANT_P_RTX:
       return 0;
 
     default:
@@ -2216,7 +2223,8 @@ hash_scan_set (pat, insn, table)
                    && REGNO (src) >= FIRST_PSEUDO_REGISTER
                    && can_copy_p [GET_MODE (dest)]
                    && REGNO (src) != regno)
-                  || CONSTANT_P (src))
+                  || (CONSTANT_P (src)
+                      && GET_CODE (src) != CONSTANT_P_RTX))
               /* A copy is not available if its src or dest is subsequently
                  modified.  Here we want to search from INSN+1 on, but
                  oprs_available_p searches from INSN on.  */
@@ -2468,7 +2476,6 @@ record_last_set_info (dest, setter, data)
 
    Currently src must be a pseudo-reg or a const_int.
 
-   F is the first insn.
    TABLE is the table computed.  */
 
 static void
@@ -2530,6 +2537,12 @@ compute_hash_table_work (table)
          note_stores (PATTERN (insn), record_last_set_info, insn);
        }
 
+      /* Insert implicit sets in the hash table.  */
+      if (table->set_p
+         && implicit_sets[current_bb->index] != NULL_RTX)
+       hash_scan_set (implicit_sets[current_bb->index],
+                      current_bb->head, table);
+
       /* The next pass builds the hash table.  */
 
       for (insn = current_bb->head, in_libcall_block = 0;
@@ -2628,14 +2641,12 @@ lookup_expr (pat, table)
   return expr;
 }
 
-/* Lookup REGNO in the set TABLE.  If PAT is non-NULL look for the entry that
-   matches it, otherwise return the first entry for REGNO.  The result is a
-   pointer to the table entry, or NULL if not found.  */
+/* Lookup REGNO in the set TABLE.  The result is a pointer to the
+   table entry, or NULL if not found.  */
 
 static struct expr *
-lookup_set (regno, pat, table)
+lookup_set (regno, table)
      unsigned int regno;
-     rtx pat;
      struct hash_table *table;
 {
   unsigned int hash = hash_set (regno, table->size);
@@ -2643,16 +2654,8 @@ lookup_set (regno, pat, table)
 
   expr = table->table[hash];
 
-  if (pat)
-    {
-      while (expr && ! expr_equiv_p (expr->expr, pat))
-       expr = expr->next_same_hash;
-    }
-  else
-    {
-      while (expr && REGNO (SET_DEST (expr->expr)) != regno)
-       expr = expr->next_same_hash;
-    }
+  while (expr && REGNO (SET_DEST (expr->expr)) != regno)
+    expr = expr->next_same_hash;
 
   return expr;
 }
@@ -3984,7 +3987,7 @@ find_avail_set (regno, insn)
   while (1)
     {
       rtx src;
-      struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
+      struct expr *set = lookup_set (regno, &set_hash_table);
 
       /* Find a set that is available at the start of the block
         which contains INSN.  */
@@ -4124,6 +4127,7 @@ constprop_register (insn, from, to, alter_jumps)
      conditional branch instructions first.  */
   if (alter_jumps
       && (sset = single_set (insn)) != NULL
+      && NEXT_INSN (insn)
       && any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
     {
       rtx dest = SET_DEST (sset);
@@ -4245,6 +4249,53 @@ cprop_insn (insn, alter_jumps)
   return changed;
 }
 
+/* Like find_used_regs, but avoid recording uses that appear in
+   input-output contexts such as zero_extract or pre_dec.  This
+   restricts the cases we consider to those for which local cprop
+   can legitimately make replacements.  */
+
+static void
+local_cprop_find_used_regs (xptr, data)
+     rtx *xptr;
+     void *data;
+{
+  rtx x = *xptr;
+
+  if (x == 0)
+    return;
+
+  switch (GET_CODE (x))
+    {
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case STRICT_LOW_PART:
+      return;
+
+    case PRE_DEC:
+    case PRE_INC:
+    case POST_DEC:
+    case POST_INC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      /* Can only legitimately appear this early in the context of
+        stack pushes for function arguments, but handle all of the
+        codes nonetheless.  */
+      return;
+
+    case SUBREG:
+      /* Setting a subreg of a register larger than word_mode leaves
+        the non-written words unchanged.  */
+      if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD)
+       return;
+      break;
+
+    default:
+      break;
+    }
+
+  find_used_regs (xptr, data);
+}
+  
 /* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
    their REG_EQUAL notes need updating.  */
 
@@ -4277,7 +4328,8 @@ do_local_cprop (x, insn, alter_jumps, libcall_sp)
          if (l->in_libcall)
            continue;
 
-         if (CONSTANT_P (this_rtx))
+         if (CONSTANT_P (this_rtx)
+             && GET_CODE (this_rtx) != CONSTANT_P_RTX)
            newcnst = this_rtx;
          if (REG_P (this_rtx) && REGNO (this_rtx) >= FIRST_PSEUDO_REGISTER
              /* Don't copy propagate if it has attached REG_EQUIV note.
@@ -4375,6 +4427,7 @@ local_cprop_pass (alter_jumps)
   rtx insn;
   struct reg_use *reg_used;
   rtx libcall_stack[MAX_NESTED_LIBCALLS + 1], *libcall_sp;
+  bool changed = false;
 
   cselib_init ();
   libcall_sp = &libcall_stack[MAX_NESTED_LIBCALLS];
@@ -4398,21 +4451,32 @@ local_cprop_pass (alter_jumps)
          do
            {
              reg_use_count = 0;
-             note_uses (&PATTERN (insn), find_used_regs, NULL);
+             note_uses (&PATTERN (insn), local_cprop_find_used_regs, NULL);
              if (note)
-               find_used_regs (&XEXP (note, 0), NULL);
+               local_cprop_find_used_regs (&XEXP (note, 0), NULL);
 
              for (reg_used = &reg_use_table[0]; reg_use_count > 0;
                   reg_used++, reg_use_count--)
                if (do_local_cprop (reg_used->reg_rtx, insn, alter_jumps,
                    libcall_sp))
-                 break;
+                 {
+                   changed = true;
+                   break;
+                 }
            }
          while (reg_use_count);
        }
       cselib_process_insn (insn);
     }
   cselib_finish ();
+  /* Global analysis may get into infinite loops for unreachable blocks.  */
+  if (changed && alter_jumps)
+    {
+      delete_unreachable_blocks ();
+      free_reg_set_mem ();
+      alloc_reg_set_mem (max_reg_num ());
+      compute_sets (get_insns ());
+    }
 }
 
 /* Forward propagate copies.  This includes copies and constants.  Return
@@ -4462,6 +4526,117 @@ cprop (alter_jumps)
   return changed;
 }
 
+/* Similar to get_condition, only the resulting condition must be
+   valid at JUMP, instead of at EARLIEST.
+
+   This differs from noce_get_condition in ifcvt.c in that we prefer not to
+   settle for the condition variable in the jump instruction being integral.
+   We prefer to be able to record the value of a user variable, rather than
+   the value of a temporary used in a condition.  This could be solved by
+   recording the value of *every* register scaned by canonicalize_condition,
+   but this would require some code reorganization.  */
+
+static rtx
+fis_get_condition (jump)
+     rtx jump;
+{
+  rtx cond, set, tmp, insn, earliest;
+  bool reverse;
+
+  if (! any_condjump_p (jump))
+    return NULL_RTX;
+
+  set = pc_set (jump);
+  cond = XEXP (SET_SRC (set), 0);
+
+  /* If this branches to JUMP_LABEL when the condition is false,
+     reverse the condition.  */
+  reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+            && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
+
+  /* Use canonicalize_condition to do the dirty work of manipulating
+     MODE_CC values and COMPARE rtx codes.  */
+  tmp = canonicalize_condition (jump, cond, reverse, &earliest, NULL_RTX);
+  if (!tmp)
+    return NULL_RTX;
+
+  /* Verify that the given condition is valid at JUMP by virtue of not
+     having been modified since EARLIEST.  */
+  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && modified_in_p (tmp, insn))
+      break;
+  if (insn == jump)
+    return tmp;
+
+  /* The condition was modified.  See if we can get a partial result
+     that doesn't follow all the reversals.  Perhaps combine can fold
+     them together later.  */
+  tmp = XEXP (tmp, 0);
+  if (!REG_P (tmp) || GET_MODE_CLASS (GET_MODE (tmp)) != MODE_INT)
+    return NULL_RTX;
+  tmp = canonicalize_condition (jump, cond, reverse, &earliest, tmp);
+  if (!tmp)
+    return NULL_RTX;
+
+  /* For sanity's sake, re-validate the new result.  */
+  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && modified_in_p (tmp, insn))
+      return NULL_RTX;
+
+  return tmp;
+}
+
+/* Find the implicit sets of a function.  An "implicit set" is a constraint
+   on the value of a variable, implied by a conditional jump.  For example,
+   following "if (x == 2)", the then branch may be optimized as though the
+   conditional performed an "explicit set", in this example, "x = 2".  This
+   function records the set patterns that are implicit at the start of each
+   basic block.  */
+
+static void
+find_implicit_sets ()
+{
+  basic_block bb, dest;
+  unsigned int count;
+  rtx cond, new;
+
+  count = 0;
+  FOR_EACH_BB (bb)
+    /* Check for more than one sucessor.  */
+    if (bb->succ && bb->succ->succ_next)
+      {
+       cond = fis_get_condition (bb->end);
+
+       if (cond
+           && (GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+           && GET_CODE (XEXP (cond, 0)) == REG
+           && REGNO (XEXP (cond, 0)) >= FIRST_PSEUDO_REGISTER
+           && CONSTANT_P (XEXP (cond, 1)))
+         {
+           dest = GET_CODE (cond) == EQ ? BRANCH_EDGE (bb)->dest
+                                        : FALLTHRU_EDGE (bb)->dest;
+
+           if (dest && ! dest->pred->pred_next
+               && dest != EXIT_BLOCK_PTR)
+             {
+               new = gen_rtx_SET (VOIDmode, XEXP (cond, 0),
+                                            XEXP (cond, 1));
+               implicit_sets[dest->index] = new;
+               if (gcse_file)
+                 {
+                   fprintf(gcse_file, "Implicit set of reg %d in ",
+                           REGNO (XEXP (cond, 0)));
+                   fprintf(gcse_file, "basic block %d\n", dest->index);
+                 }
+               count++;
+             }
+         }
+      }
+
+  if (gcse_file)
+    fprintf (gcse_file, "Found %d implicit sets\n", count);
+}
+
 /* Perform one copy/constant propagation pass.
    PASS is the pass count.  If CPROP_JUMPS is true, perform constant
    propagation into conditional jumps.  If BYPASS_JUMPS is true,
@@ -4480,8 +4655,17 @@ one_cprop_pass (pass, cprop_jumps, bypass_jumps)
 
   local_cprop_pass (cprop_jumps);
 
+  /* Determine implicit sets.  */
+  implicit_sets = (rtx *) xcalloc (last_basic_block, sizeof (rtx));
+  find_implicit_sets ();
+
   alloc_hash_table (max_cuid, &set_hash_table, 1);
   compute_hash_table (&set_hash_table);
+
+  /* Free implicit_sets before peak usage.  */
+  free (implicit_sets);
+  implicit_sets = NULL;
+
   if (gcse_file)
     dump_hash_table (gcse_file, "SET", &set_hash_table);
   if (set_hash_table.n_elems > 0)
@@ -4503,12 +4687,22 @@ one_cprop_pass (pass, cprop_jumps, bypass_jumps)
       fprintf (gcse_file, "%d const props, %d copy props\n\n",
               const_prop_count, copy_prop_count);
     }
+  /* Global analysis may get into infinite loops for unreachable blocks.  */
+  if (changed && cprop_jumps)
+    delete_unreachable_blocks ();
 
   return changed;
 }
 \f
 /* Bypass conditional jumps.  */
 
+/* The value of last_basic_block at the beginning of the jump_bypass
+   pass.  The use of redirect_edge_and_branch_force may introduce new
+   basic blocks, but the data flow analysis is only valid for basic
+   block indices less than bypass_last_basic_block.  */
+
+static int bypass_last_basic_block;
+
 /* Find a set of REGNO to a constant that is available at the end of basic
    block BB.  Returns NULL if no such set is found.  Based heavily upon
    find_avail_set.  */
@@ -4523,7 +4717,7 @@ find_bypass_set (regno, bb)
   for (;;)
     {
       rtx src;
-      struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
+      struct expr *set = lookup_set (regno, &set_hash_table);
 
       while (set)
        {
@@ -4565,6 +4759,7 @@ bypass_block (bb, setcc, jump)
   rtx insn, note;
   edge e, enext;
   int i, change;
+  int may_be_loop_header;
 
   insn = (setcc != NULL) ? setcc : jump;
 
@@ -4575,10 +4770,32 @@ bypass_block (bb, setcc, jump)
   if (note)
     find_used_regs (&XEXP (note, 0), NULL);
 
+  may_be_loop_header = false;
+  for (e = bb->pred; e; e = e->pred_next)
+    if (e->flags & EDGE_DFS_BACK)
+      {
+       may_be_loop_header = true;
+       break;
+      }
+
   change = 0;
   for (e = bb->pred; e; e = enext)
     {
       enext = e->pred_next;
+      if (e->flags & EDGE_COMPLEX)
+       continue;
+
+      /* We can't redirect edges from new basic blocks.  */
+      if (e->src->index >= bypass_last_basic_block)
+       continue;
+
+      /* The irreducible loops created by redirecting of edges entering the
+        loop from outside would decrease effectivity of some of the following
+        optimalizations, so prevent this.  */
+      if (may_be_loop_header
+         && !(e->flags & EDGE_DFS_BACK))
+       continue;
+
       for (i = 0; i < reg_use_count; i++)
        {
          struct reg_use *reg_used = &reg_use_table[i];
@@ -4608,16 +4825,17 @@ bypass_block (bb, setcc, jump)
          if (new == pc_rtx)
            dest = FALLTHRU_EDGE (bb)->dest;
          else if (GET_CODE (new) == LABEL_REF)
-           dest = BRANCH_EDGE (bb)->dest;
+           dest = BLOCK_FOR_INSN (XEXP (new, 0));
          else
            dest = NULL;
 
-         /* Once basic block indices are stable, we should be able
-            to use redirect_edge_and_branch_force instead.  */
          old_dest = e->dest;
-         if (dest != NULL && dest != old_dest
-             && redirect_edge_and_branch (e, dest))
-           {
+         if (dest != NULL
+             && dest != old_dest
+             && dest != EXIT_BLOCK_PTR)
+            {
+             redirect_edge_and_branch_force (e, dest);
+
              /* Copy the register setter to the redirected edge.
                 Don't copy CC0 setters, as CC0 is dead after jump.  */
              if (setcc)
@@ -4646,7 +4864,9 @@ bypass_block (bb, setcc, jump)
 /* Find basic blocks with more than one predecessor that only contain a
    single conditional jump.  If the result of the comparison is known at
    compile-time from any incoming edge, redirect that edge to the
-   appropriate target.  Returns nonzero if a change was made.  */
+   appropriate target.  Returns nonzero if a change was made.
+
+   This function is now mis-named, because we also handle indirect jumps.  */
 
 static int
 bypass_conditional_jumps ()
@@ -4661,6 +4881,9 @@ bypass_conditional_jumps ()
   if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
     return 0;
 
+  bypass_last_basic_block = last_basic_block;
+  mark_dfs_back_edges ();
+
   changed = 0;
   FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb,
                  EXIT_BLOCK_PTR, next_bb)
@@ -4687,7 +4910,8 @@ bypass_conditional_jumps ()
              }
            else if (GET_CODE (insn) == JUMP_INSN)
              {
-               if (any_condjump_p (insn) && onlyjump_p (insn))
+               if ((any_condjump_p (insn) || computed_jump_p (insn))
+                   && onlyjump_p (insn))
                  changed |= bypass_block (bb, setcc, insn);
                break;
              }