OSDN Git Service

2009-04-10 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / var-tracking.c
index 46752fa..b9ff8c5 100644 (file)
@@ -1,5 +1,6 @@
 /* Variable tracking routines for the GNU compiler.
-   Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008
+   Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -466,22 +467,31 @@ static void
 insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
                                   HOST_WIDE_INT *post)
 {
+  rtx pattern;
+
   *pre = 0;
   *post = 0;
 
-  if (GET_CODE (PATTERN (insn)) == SET)
-    stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
-  else if (GET_CODE (PATTERN (insn)) == PARALLEL
-          || GET_CODE (PATTERN (insn)) == SEQUENCE)
+  pattern = PATTERN (insn);
+  if (RTX_FRAME_RELATED_P (insn))
+    {
+      rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+      if (expr)
+       pattern = XEXP (expr, 0);
+    }
+
+  if (GET_CODE (pattern) == SET)
+    stack_adjust_offset_pre_post (pattern, pre, post);
+  else if (GET_CODE (pattern) == PARALLEL
+          || GET_CODE (pattern) == SEQUENCE)
     {
       int i;
 
       /* There may be stack adjustments inside compound insns.  Search
         for them.  */
-      for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
-       if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
-         stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
-                                       pre, post);
+      for ( i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+       if (GET_CODE (XVECEXP (pattern, 0, i)) == SET)
+         stack_adjust_offset_pre_post (XVECEXP (pattern, 0, i), pre, post);
     }
 }
 
@@ -693,7 +703,7 @@ attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc)
 {
   attrs list;
 
-  list = pool_alloc (attrs_pool);
+  list = (attrs) pool_alloc (attrs_pool);
   list->loc = loc;
   list->decl = decl;
   list->offset = offset;
@@ -711,7 +721,7 @@ attrs_list_copy (attrs *dstp, attrs src)
   attrs_list_clear (dstp);
   for (; src; src = src->next)
     {
-      n = pool_alloc (attrs_pool);
+      n = (attrs) pool_alloc (attrs_pool);
       n->loc = src->loc;
       n->decl = src->decl;
       n->offset = src->offset;
@@ -750,7 +760,7 @@ unshare_variable (dataflow_set *set, variable var,
   variable new_var;
   int i;
 
-  new_var = pool_alloc (var_pool);
+  new_var = (variable) pool_alloc (var_pool);
   new_var->decl = var->decl;
   new_var->refcount = 1;
   var->refcount--;
@@ -767,7 +777,7 @@ unshare_variable (dataflow_set *set, variable var,
        {
          location_chain new_lc;
 
-         new_lc = pool_alloc (loc_chain_pool);
+         new_lc = (location_chain) pool_alloc (loc_chain_pool);
          new_lc->next = NULL;
          if (node->init > initialized)
            new_lc->init = node->init;
@@ -1095,8 +1105,10 @@ struct variable_union_info
 static int
 variable_union_info_cmp_pos (const void *n1, const void *n2)
 {
-  const struct variable_union_info *i1 = n1;
-  const struct variable_union_info *i2 = n2;
+  const struct variable_union_info *const i1 =
+    (const struct variable_union_info *) n1;
+  const struct variable_union_info *const i2 =
+    ( const struct variable_union_info *) n2;
 
   if (i1->pos != i2->pos)
     return i1->pos - i2->pos;
@@ -1275,7 +1287,7 @@ variable_union (void **slot, void *data)
                  location_chain new_node;
 
                  /* Copy the location from SRC.  */
-                 new_node = pool_alloc (loc_chain_pool);
+                 new_node = (location_chain) pool_alloc (loc_chain_pool);
                  new_node->loc = node->loc;
                  new_node->init = node->init;
                  if (!node->set_src || MEM_P (node->set_src))
@@ -1326,7 +1338,7 @@ variable_union (void **slot, void *data)
            {
              location_chain new_lc;
 
-             new_lc = pool_alloc (loc_chain_pool);
+             new_lc = (location_chain) pool_alloc (loc_chain_pool);
              new_lc->next = NULL;
              new_lc->init = node->init;
              if (!node->set_src || MEM_P (node->set_src))
@@ -1455,7 +1467,7 @@ dataflow_set_different_1 (void **slot, void *data)
   variable var1, var2;
 
   var1 = *(variable *) slot;
-  var2 = htab_find_with_hash (htab, var1->decl,
+  var2 = (variable) htab_find_with_hash (htab, var1->decl,
                              VARIABLE_HASH_VAL (var1->decl));
   if (!var2)
     {
@@ -1487,7 +1499,7 @@ dataflow_set_different_2 (void **slot, void *data)
   variable var1, var2;
 
   var1 = *(variable *) slot;
-  var2 = htab_find_with_hash (htab, var1->decl,
+  var2 = (variable) htab_find_with_hash (htab, var1->decl,
                              VARIABLE_HASH_VAL (var1->decl));
   if (!var2)
     {
@@ -1645,18 +1657,6 @@ track_expr_p (tree expr)
   return 1;
 }
 
-/* Return true if OFFSET is a valid offset for a register or memory
-   access we want to track.  This is used to reject out-of-bounds
-   accesses that can cause assertions to fail later.  Note that we
-   don't reject negative offsets because they can be generated for
-   paradoxical subregs on big-endian architectures.  */
-
-static inline bool
-offset_valid_for_tracked_p (HOST_WIDE_INT offset)
-{
-  return (-MAX_VAR_PARTS < offset) && (offset < MAX_VAR_PARTS);
-}
-
 /* Determine whether a given LOC refers to the same variable part as
    EXPR+OFFSET.  */
 
@@ -1691,28 +1691,65 @@ same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
   return (expr == expr2 && offset == offset2);
 }
 
-/* REG is a register we want to track.  If not all of REG contains useful
-   information, return the mode of the lowpart that does contain useful
-   information, otherwise return the mode of REG.
+/* LOC is a REG or MEM that we would like to track if possible.
+   If EXPR is null, we don't know what expression LOC refers to,
+   otherwise it refers to EXPR + OFFSET.  STORE_REG_P is true if
+   LOC is an lvalue register.
 
-   If REG was a paradoxical subreg, its REG_ATTRS will describe the
-   whole subreg, but only the old inner part is really relevant.  */
+   Return true if EXPR is nonnull and if LOC, or some lowpart of it,
+   is something we can track.  When returning true, store the mode of
+   the lowpart we can track in *MODE_OUT (if nonnull) and its offset
+   from EXPR in *OFFSET_OUT (if nonnull).  */
 
-static enum machine_mode
-mode_for_reg_attrs (rtx reg)
+static bool
+track_loc_p (rtx loc, tree expr, HOST_WIDE_INT offset, bool store_reg_p,
+            enum machine_mode *mode_out, HOST_WIDE_INT *offset_out)
 {
   enum machine_mode mode;
 
-  mode = GET_MODE (reg);
-  if (!HARD_REGISTER_NUM_P (ORIGINAL_REGNO (reg)))
+  if (expr == NULL || !track_expr_p (expr))
+    return false;
+
+  /* If REG was a paradoxical subreg, its REG_ATTRS will describe the
+     whole subreg, but only the old inner part is really relevant.  */
+  mode = GET_MODE (loc);
+  if (REG_P (loc) && !HARD_REGISTER_NUM_P (ORIGINAL_REGNO (loc)))
     {
       enum machine_mode pseudo_mode;
 
-      pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (reg));
+      pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (loc));
       if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (pseudo_mode))
-       mode = pseudo_mode;
+       {
+         offset += byte_lowpart_offset (pseudo_mode, mode);
+         mode = pseudo_mode;
+       }
     }
-  return mode;
+
+  /* If LOC is a paradoxical lowpart of EXPR, refer to EXPR itself.
+     Do the same if we are storing to a register and EXPR occupies
+     the whole of register LOC; in that case, the whole of EXPR is
+     being changed.  We exclude complex modes from the second case
+     because the real and imaginary parts are represented as separate
+     pseudo registers, even if the whole complex value fits into one
+     hard register.  */
+  if ((GET_MODE_SIZE (mode) > GET_MODE_SIZE (DECL_MODE (expr))
+       || (store_reg_p
+          && !COMPLEX_MODE_P (DECL_MODE (expr))
+          && hard_regno_nregs[REGNO (loc)][DECL_MODE (expr)] == 1))
+      && offset + byte_lowpart_offset (DECL_MODE (expr), mode) == 0)
+    {
+      mode = DECL_MODE (expr);
+      offset = 0;
+    }
+
+  if (offset < 0 || offset >= MAX_VAR_PARTS)
+    return false;
+
+  if (mode_out)
+    *mode_out = mode;
+  if (offset_out)
+    *offset_out = offset;
+  return true;
 }
 
 /* Return the MODE lowpart of LOC, or null if LOC is not something we
@@ -1722,7 +1759,7 @@ mode_for_reg_attrs (rtx reg)
 static rtx
 var_lowpart (enum machine_mode mode, rtx loc)
 {
-  unsigned int offset, regno;
+  unsigned int offset, reg_offset, regno;
 
   if (!REG_P (loc) && !MEM_P (loc))
     return NULL;
@@ -1730,13 +1767,14 @@ var_lowpart (enum machine_mode mode, rtx loc)
   if (GET_MODE (loc) == mode)
     return loc;
 
-  offset = subreg_lowpart_offset (mode, GET_MODE (loc));
+  offset = byte_lowpart_offset (mode, GET_MODE (loc));
 
   if (MEM_P (loc))
     return adjust_address_nv (loc, mode, offset);
 
+  reg_offset = subreg_lowpart_offset (mode, GET_MODE (loc));
   regno = REGNO (loc) + subreg_regno_offset (REGNO (loc), GET_MODE (loc),
-                                            offset, mode);
+                                            reg_offset, mode);
   return gen_rtx_REG_offset (loc, mode, regno, offset);
 }
 
@@ -1754,9 +1792,8 @@ count_uses (rtx *loc, void *insn)
       VTI (bb)->n_mos++;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, NULL, NULL))
     {
       VTI (bb)->n_mos++;
     }
@@ -1787,17 +1824,18 @@ count_stores (rtx loc, const_rtx expr ATTRIBUTE_UNUSED, void *insn)
 static int
 add_uses (rtx *loc, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (*loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      if (REG_EXPR (*loc)
-         && track_expr_p (REG_EXPR (*loc))
-         && offset_valid_for_tracked_p (REG_OFFSET (*loc)))
+      if (track_loc_p (*loc, REG_EXPR (*loc), REG_OFFSET (*loc),
+                      false, &mode, NULL))
        {
          mo->type = MO_USE;
-         mo->u.loc = var_lowpart (mode_for_reg_attrs (*loc), *loc);
+         mo->u.loc = var_lowpart (mode, *loc);
        }
       else
        {
@@ -1807,15 +1845,14 @@ add_uses (rtx *loc, void *insn)
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       mo->type = MO_USE;
-      mo->u.loc = *loc;
+      mo->u.loc = var_lowpart (mode, *loc);
       mo->insn = (rtx) insn;
     }
 
@@ -1837,22 +1874,22 @@ add_uses_1 (rtx *x, void *insn)
 static void
 add_stores (rtx loc, const_rtx expr, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       if (GET_CODE (expr) == CLOBBER
-         || !(REG_EXPR (loc)
-              && track_expr_p (REG_EXPR (loc))
-              && offset_valid_for_tracked_p (REG_OFFSET (loc))))
+         || !track_loc_p (loc, REG_EXPR (loc), REG_OFFSET (loc),
+                          true, &mode, NULL))
        {
          mo->type = MO_CLOBBER;
          mo->u.loc = loc;
        }
       else
        {
-         enum machine_mode mode = mode_for_reg_attrs (loc);
          rtx src = NULL;
 
          if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
@@ -1878,9 +1915,8 @@ add_stores (rtx loc, const_rtx expr, void *insn)
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (loc)
-          && MEM_EXPR (loc)
-          && track_expr_p (MEM_EXPR (loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (loc)))
+          && track_loc_p (loc, MEM_EXPR (loc), INT_MEM_OFFSET (loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
@@ -1888,14 +1924,15 @@ add_stores (rtx loc, const_rtx expr, void *insn)
       if (GET_CODE (expr) == CLOBBER)
        {
          mo->type = MO_CLOBBER;
-         mo->u.loc = loc;
+         mo->u.loc = var_lowpart (mode, loc);
        }
       else
        {
          rtx src = NULL;
 
          if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
-           src = var_lowpart (GET_MODE (loc), SET_SRC (expr));
+           src = var_lowpart (mode, SET_SRC (expr));
+         loc = var_lowpart (mode, loc);
 
          if (src == NULL)
            {
@@ -1904,6 +1941,8 @@ add_stores (rtx loc, const_rtx expr, void *insn)
            }
          else
            {
+             if (SET_SRC (expr) != src)
+               expr = gen_rtx_SET (VOIDmode, loc, src);
              if (same_variable_part_p (SET_SRC (expr),
                                        MEM_EXPR (loc),
                                        INT_MEM_OFFSET (loc)))
@@ -2150,7 +2189,7 @@ vt_find_locations (void)
 
       while (!fibheap_empty (worklist))
        {
-         bb = fibheap_extract_min (worklist);
+         bb = (basic_block) fibheap_extract_min (worklist);
          RESET_BIT (in_worklist, bb->index);
          if (!TEST_BIT (visited, bb->index))
            {
@@ -2325,7 +2364,7 @@ variable_was_changed (variable var, htab_t htab)
          variable empty_var;
          void **old;
 
-         empty_var = pool_alloc (var_pool);
+         empty_var = (variable) pool_alloc (var_pool);
          empty_var->decl = var->decl;
          empty_var->refcount = 1;
          empty_var->n_var_parts = 0;
@@ -2406,7 +2445,7 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
   if (!*slot)
     {
       /* Create new variable information.  */
-      var = pool_alloc (var_pool);
+      var = (variable) pool_alloc (var_pool);
       var->decl = decl;
       var->refcount = 1;
       var->n_var_parts = 1;
@@ -2498,7 +2537,7 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
     }
 
   /* Add the location to the beginning.  */
-  node = pool_alloc (loc_chain_pool);
+  node = (location_chain) pool_alloc (loc_chain_pool);
   node->loc = loc;
   node->init = initialized;
   node->set_src = set_src;
@@ -2568,6 +2607,8 @@ clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
                              pool_free (attrs_pool, anode);
                              *anextp = anext;
                            }
+                         else
+                           anextp = &anode->next;
                        }
                    }
 
@@ -2859,7 +2900,7 @@ emit_notes_for_differences_1 (void **slot, void *data)
   variable old_var, new_var;
 
   old_var = *(variable *) slot;
-  new_var = htab_find_with_hash (new_vars, old_var->decl,
+  new_var = (variable) htab_find_with_hash (new_vars, old_var->decl,
                                 VARIABLE_HASH_VAL (old_var->decl));
 
   if (!new_var)
@@ -2867,7 +2908,7 @@ emit_notes_for_differences_1 (void **slot, void *data)
       /* Variable has disappeared.  */
       variable empty_var;
 
-      empty_var = pool_alloc (var_pool);
+      empty_var = (variable) pool_alloc (var_pool);
       empty_var->decl = old_var->decl;
       empty_var->refcount = 1;
       empty_var->n_var_parts = 0;
@@ -2892,7 +2933,7 @@ emit_notes_for_differences_2 (void **slot, void *data)
   variable old_var, new_var;
 
   new_var = *(variable *) slot;
-  old_var = htab_find_with_hash (old_vars, new_var->decl,
+  old_var = (variable) htab_find_with_hash (old_vars, new_var->decl,
                                 VARIABLE_HASH_VAL (new_var->decl));
   if (!old_var)
     {
@@ -3115,6 +3156,7 @@ vt_add_function_parameters (void)
       rtx decl_rtl = DECL_RTL_IF_SET (parm);
       rtx incoming = DECL_INCOMING_RTL (parm);
       tree decl;
+      enum machine_mode mode;
       HOST_WIDE_INT offset;
       dataflow_set *out;
 
@@ -3131,18 +3173,35 @@ vt_add_function_parameters (void)
        continue;
 
       if (!vt_get_decl_and_offset (incoming, &decl, &offset))
-       if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
-         continue;
+       {
+         if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
+           continue;
+         offset += byte_lowpart_offset (GET_MODE (incoming),
+                                        GET_MODE (decl_rtl));
+       }
 
       if (!decl)
        continue;
 
-      gcc_assert (parm == decl);
+      if (parm != decl)
+       {
+         /* Assume that DECL_RTL was a pseudo that got spilled to
+            memory.  The spill slot sharing code will force the
+            memory to reference spill_slot_decl (%sfp), so we don't
+            match above.  That's ok, the pseudo must have referenced
+            the entire parameter, so just reset OFFSET.  */
+         gcc_assert (decl == get_spill_slot_decl (false));
+         offset = 0;
+       }
+
+      if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+       continue;
 
       out = &VTI (ENTRY_BLOCK_PTR)->out;
 
       if (REG_P (incoming))
        {
+         incoming = var_lowpart (mode, incoming);
          gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
          attrs_list_insert (&out->regs[REGNO (incoming)],
                             parm, offset, incoming);
@@ -3150,8 +3209,11 @@ vt_add_function_parameters (void)
                             NULL);
        }
       else if (MEM_P (incoming))
-       set_variable_part (out, incoming, parm, offset, VAR_INIT_STATUS_INITIALIZED, 
-                          NULL);
+       {
+         incoming = var_lowpart (mode, incoming);
+         set_variable_part (out, incoming, parm, offset,
+                            VAR_INIT_STATUS_INITIALIZED, NULL);
+       }
     }
 }
 
@@ -3365,8 +3427,10 @@ gate_handle_var_tracking (void)
 
 
 
-struct tree_opt_pass pass_variable_tracking =
+struct rtl_opt_pass pass_variable_tracking =
 {
+ {
+  RTL_PASS,
   "vartrack",                           /* name */
   gate_handle_var_tracking,             /* gate */
   variable_tracking_main,               /* execute */
@@ -3378,7 +3442,7 @@ struct tree_opt_pass pass_variable_tracking =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func | TODO_verify_rtl_sharing,/* todo_flags_finish */
-  'V'                                   /* letter */
+  TODO_dump_func | TODO_verify_rtl_sharing/* todo_flags_finish */
+ }
 };