OSDN Git Service

PR 17051
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index 2e2e85a..e6b254c 100644 (file)
@@ -357,18 +357,32 @@ can_completely_scalarize_p (struct sra_elt *elt)
 static hashval_t
 sra_hash_tree (tree t)
 {
+  hashval_t h;
+
   switch (TREE_CODE (t))
     {
     case VAR_DECL:
     case PARM_DECL:
     case RESULT_DECL:
-    case FIELD_DECL:
-      return DECL_UID (t);
+      h = DECL_UID (t);
+      break;
+
     case INTEGER_CST:
-      return TREE_INT_CST_LOW (t) ^ TREE_INT_CST_HIGH (t);
+      h = TREE_INT_CST_LOW (t) ^ TREE_INT_CST_HIGH (t);
+      break;
+
+    case FIELD_DECL:
+      /* We can have types that are compatible, but have different member
+        lists, so we can't hash fields by ID.  Use offsets instead.  */
+      h = iterative_hash_expr (DECL_FIELD_OFFSET (t), 0);
+      h = iterative_hash_expr (DECL_FIELD_BIT_OFFSET (t), h);
+      break;
+
     default:
       abort ();
     }
+
+  return h;
 }
 
 /* Hash function for type SRA_PAIR.  */
@@ -399,20 +413,41 @@ sra_elt_eq (const void *x, const void *y)
 {
   const struct sra_elt *a = x;
   const struct sra_elt *b = y;
+  tree ae, be;
 
   if (a->parent != b->parent)
     return false;
 
-  /* All the field/decl stuff is unique.  */
-  if (a->element == b->element)
-    return true;
+  ae = a->element;
+  be = b->element;
 
-  /* The only thing left is integer equality.  */
-  if (TREE_CODE (a->element) == INTEGER_CST
-      && TREE_CODE (b->element) == INTEGER_CST)
-    return tree_int_cst_equal (a->element, b->element);
-  else
+  if (ae == be)
+    return true;
+  if (TREE_CODE (ae) != TREE_CODE (be))
     return false;
+
+  switch (TREE_CODE (ae))
+    {
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      /* These are all pointer unique.  */
+      return false;
+
+    case INTEGER_CST:
+      /* Integers are not pointer unique, so compare their values.  */
+      return tree_int_cst_equal (ae, be);
+
+    case FIELD_DECL:
+      /* Fields are unique within a record, but not between
+        compatible records.  */
+      if (DECL_FIELD_CONTEXT (ae) == DECL_FIELD_CONTEXT (be))
+       return false;
+      return fields_compatible_p (ae, be);
+
+    default:
+      abort ();
+    }
 }
 
 /* Create or return the SRA_ELT structure for CHILD in PARENT.  PARENT
@@ -696,6 +731,11 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
           type other than the one we've scalarized.  */
        goto use_all;
 
+      case WITH_SIZE_EXPR:
+       /* This is a transparent wrapper.  The entire inner expression really
+          is being used.  */
+       goto use_all;
+
       use_all:
         expr_p = &TREE_OPERAND (inner, 0);
        inner = expr = *expr_p;
@@ -786,7 +826,7 @@ sra_walk_modify_expr (tree expr, block_stmt_iterator *bsi,
         The lvalue requirement prevents us from trying to directly scalarize
         the result of a function call.  Which would result in trying to call
         the function multiple times, and other evil things.  */
-      else if (!lhs_elt->is_scalar && is_gimple_addr_expr_arg (rhs))
+      else if (!lhs_elt->is_scalar && is_gimple_addressable (rhs))
        fns->ldst (lhs_elt, rhs, bsi, true);
        
       /* Otherwise we're being used in some context that requires the
@@ -811,10 +851,14 @@ sra_walk_modify_expr (tree expr, block_stmt_iterator *bsi,
       else
        fns->use (rhs_elt, &TREE_OPERAND (expr, 1), bsi, false);
     }
-  else if (TREE_CODE (rhs) == CALL_EXPR)
-    sra_walk_call_expr (rhs, bsi, fns);
   else
-    sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
+    {
+      tree call = get_call_expr_in (rhs);
+      if (call)
+       sra_walk_call_expr (call, bsi, fns);
+      else
+       sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
+    }
 }
 
 /* Entry point to the walk functions.  Search the entire function,
@@ -1388,7 +1432,15 @@ generate_one_element_ref (struct sra_elt *elt, tree base)
   switch (TREE_CODE (TREE_TYPE (base)))
     {
     case RECORD_TYPE:
-      return build (COMPONENT_REF, elt->type, base, elt->element, NULL);
+      {
+       tree field = elt->element;
+
+       /* Watch out for compatible records with differing field lists.  */
+       if (DECL_FIELD_CONTEXT (field) != TYPE_MAIN_VARIANT (TREE_TYPE (base)))
+         field = find_compatible_field (TREE_TYPE (base), field);
+
+        return build (COMPONENT_REF, elt->type, base, field, NULL);
+      }
 
     case ARRAY_TYPE:
       return build (ARRAY_REF, elt->type, base, elt->element, NULL, NULL);
@@ -1509,6 +1561,55 @@ generate_element_zero (struct sra_elt *elt, tree *list_p)
     }
 }
 
+/* Find all variables within the gimplified statement that were not previously
+   visible to the function and add them to the referenced variables list.  */
+
+static tree
+find_new_referenced_vars_1 (tree *tp, int *walk_subtrees,
+                           void *data ATTRIBUTE_UNUSED)
+{
+  tree t = *tp;
+
+  if (TREE_CODE (t) == VAR_DECL && !var_ann (t))
+    add_referenced_tmp_var (t);
+
+  if (DECL_P (t) || TYPE_P (t))
+    *walk_subtrees = 0;
+
+  return NULL;
+}
+
+static inline void
+find_new_referenced_vars (tree *stmt_p)
+{
+  walk_tree (stmt_p, find_new_referenced_vars_1, NULL, NULL);
+}
+
+/* Generate an assignment VAR = INIT, where INIT may need gimplification.
+   Add the result to *LIST_P.  */
+
+static void
+generate_one_element_init (tree var, tree init, tree *list_p)
+{
+  tree stmt;
+
+  /* The replacement can be almost arbitrarily complex.  Gimplify.  */
+  stmt = build (MODIFY_EXPR, void_type_node, var, init);
+  gimplify_stmt (&stmt);
+
+  /* The replacement can expose previously unreferenced variables.  */
+  if (TREE_CODE (stmt) == STATEMENT_LIST)
+    {
+      tree_stmt_iterator i;
+      for (i = tsi_start (stmt); !tsi_end_p (i); tsi_next (&i))
+       find_new_referenced_vars (tsi_stmt_ptr (i));
+    }
+  else
+    find_new_referenced_vars (&stmt);
+
+  append_to_statement_list (stmt, list_p);
+}
+
 /* Generate a set of assignment statements in *LIST_P to set all instantiated
    elements under ELT with the contents of the initializer INIT.  In addition,
    mark all assigned elements VISITED; this allows easy coordination with
@@ -1532,8 +1633,7 @@ generate_element_init (struct sra_elt *elt, tree init, tree *list_p)
     {
       if (elt->replacement)
        {
-         t = build (MODIFY_EXPR, void_type_node, elt->replacement, init);
-         append_to_statement_list (t, list_p);
+         generate_one_element_init (elt->replacement, init, list_p);
          elt->visited = true;
        }
       return result;
@@ -1680,11 +1780,9 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
       generate_copy_inout (elt, is_output, generate_element_ref (elt), &list);
       if (list == NULL)
        return;
+      mark_all_v_defs (expr_first (list));
       if (is_output)
-       {
-         mark_all_v_defs (expr_first (list));
-         sra_insert_after (bsi, list);
-       }
+       sra_insert_after (bsi, list);
       else
        sra_insert_before (bsi, list);
     }
@@ -1770,7 +1868,11 @@ scalarize_init (struct sra_elt *lhs_elt, tree rhs, block_stmt_iterator *bsi)
 
   /* Generate initialization statements for all members extant in the RHS.  */
   if (rhs)
-    result = generate_element_init (lhs_elt, rhs, &list);
+    {
+      push_gimplify_context ();
+      result = generate_element_init (lhs_elt, rhs, &list);
+      pop_gimplify_context (NULL);
+    }
 
   /* CONSTRUCTOR is defined such that any member not mentioned is assigned
      a zero value.  Initialize the rest of the instantiated elements.  */
@@ -2014,7 +2116,7 @@ struct tree_opt_pass pass_sra =
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   TV_TREE_SRA,                         /* tv_id */
-  PROP_cfg | PROP_ssa,                 /* properties_required */
+  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */