re PR fortran/48636 (Enable more inlining with -O2 and higher)
authorMartin Jambor <mjambor@suse.cz>
Sat, 11 Aug 2012 10:50:24 +0000 (12:50 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Sat, 11 Aug 2012 10:50:24 +0000 (12:50 +0200)
2012-08-11  Martin Jambor  <mjambor@suse.cz>

PR fortran/48636
* ipa-inline.h (condition): New fields offset, agg_contents and by_ref.
* ipa-inline-analysis.c (agg_position_info): New type.
(add_condition): New parameter aggpos, also store agg_contents, by_ref
and offset.
(dump_condition): Also dump aggregate conditions.
(evaluate_conditions_for_known_args): Also handle aggregate
conditions.  New parameter known_aggs.
(evaluate_properties_for_edge): Gather known aggregate contents.
(inline_node_duplication_hook): Pass NULL known_aggs to
evaluate_conditions_for_known_args.
(unmodified_parm): Split into unmodified_parm and unmodified_parm_1.
(unmodified_parm_or_parm_agg_item): New function.
(set_cond_stmt_execution_predicate): Handle values passed in
aggregates.
(set_switch_stmt_execution_predicate): Likewise.
(will_be_nonconstant_predicate): Likewise.
(estimate_edge_devirt_benefit): Pass new parameter known_aggs to
ipa_get_indirect_edge_target.
(estimate_calls_size_and_time): New parameter known_aggs, pass it
recrsively to itself and to estimate_edge_devirt_benefit.
(estimate_node_size_and_time): New vector known_aggs, pass it o
functions which need it.
(remap_predicate): New parameter offset_map, use it to remap aggregate
conditions.
(remap_edge_summaries): New parameter offset_map, pass it recursively
to itself and to remap_predicate.
(inline_merge_summary): Also create and populate vector offset_map.
(do_estimate_edge_time): New vector of known aggregate contents,
passed to functions which need it.
(inline_read_section): Stream new fields of condition.
(inline_write_summary): Likewise.
* ipa-cp.c (ipa_get_indirect_edge_target): Also examine the aggregate
contents.  Let all local callers pass NULL for known_aggs.

* testsuite/gfortran.dg/pr48636.f90: New test.

From-SVN: r190313

gcc/ChangeLog
gcc/ipa-cp.c
gcc/ipa-inline-analysis.c
gcc/ipa-inline.h
gcc/ipa-prop.h
gcc/testsuite/ChangeLog
gcc/testsuite/gfortran.dg/pr48636.f90 [new file with mode: 0644]

index 6eb422b0ca890caf9d5feaebf46335934ca77f97..8a4bcb56487b4152adaf8fadc0a48199f903d9f8 100644 (file)
@@ -1,3 +1,40 @@
+2012-08-11  Martin Jambor  <mjambor@suse.cz>
+
+       PR fortran/48636
+       * ipa-inline.h (condition): New fields offset, agg_contents and by_ref.
+       * ipa-inline-analysis.c (agg_position_info): New type.
+       (add_condition): New parameter aggpos, also store agg_contents, by_ref
+       and offset.
+       (dump_condition): Also dump aggregate conditions.
+       (evaluate_conditions_for_known_args): Also handle aggregate
+       conditions.  New parameter known_aggs.
+       (evaluate_properties_for_edge): Gather known aggregate contents.
+       (inline_node_duplication_hook): Pass NULL known_aggs to
+       evaluate_conditions_for_known_args.
+       (unmodified_parm): Split into unmodified_parm and unmodified_parm_1.
+       (unmodified_parm_or_parm_agg_item): New function.
+       (set_cond_stmt_execution_predicate): Handle values passed in
+       aggregates.
+       (set_switch_stmt_execution_predicate): Likewise.
+       (will_be_nonconstant_predicate): Likewise.
+       (estimate_edge_devirt_benefit): Pass new parameter known_aggs to
+       ipa_get_indirect_edge_target.
+       (estimate_calls_size_and_time): New parameter known_aggs, pass it
+       recrsively to itself and to estimate_edge_devirt_benefit.
+       (estimate_node_size_and_time): New vector known_aggs, pass it o
+       functions which need it.
+       (remap_predicate): New parameter offset_map, use it to remap aggregate
+       conditions.
+       (remap_edge_summaries): New parameter offset_map, pass it recursively
+       to itself and to remap_predicate.
+       (inline_merge_summary): Also create and populate vector offset_map.
+       (do_estimate_edge_time): New vector of known aggregate contents,
+       passed to functions which need it.
+       (inline_read_section): Stream new fields of condition.
+       (inline_write_summary): Likewise.
+       * ipa-cp.c (ipa_get_indirect_edge_target): Also examine the aggregate
+       contents.  Let all local callers pass NULL for known_aggs.
+
 2012-08-11  Jan Hubicka  <jh@suse.cz>
 
        * lto-cgraph.c (output_cgraph): Rename to ...
index b9184702aed6d64d8f6e56ff9d649c96893bc90a..1f2ea92fb50bf14abfe9ac2d2cf2fd49efc3bed6 100644 (file)
@@ -1084,7 +1084,8 @@ propagate_constants_accross_call (struct cgraph_edge *cs)
 tree
 ipa_get_indirect_edge_target (struct cgraph_edge *ie,
                              VEC (tree, heap) *known_vals,
-                             VEC (tree, heap) *known_binfos)
+                             VEC (tree, heap) *known_binfos,
+                             VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   int param_index = ie->indirect_info->param_index;
   HOST_WIDE_INT token, anc_offset;
@@ -1096,8 +1097,26 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie,
 
   if (!ie->indirect_info->polymorphic)
     {
-      tree t = (VEC_length (tree, known_vals) > (unsigned int) param_index
-               ? VEC_index (tree, known_vals, param_index) : NULL);
+      tree t;
+
+      if (ie->indirect_info->agg_contents)
+       {
+         if (VEC_length (ipa_agg_jump_function_p, known_aggs)
+             > (unsigned int) param_index)
+           {
+             struct ipa_agg_jump_function *agg;
+             agg = VEC_index (ipa_agg_jump_function_p, known_aggs,
+                              param_index);
+             t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
+                                             ie->indirect_info->by_ref);
+           }
+         else
+           t = NULL;
+       }
+      else
+       t = (VEC_length (tree, known_vals) > (unsigned int) param_index
+            ? VEC_index (tree, known_vals, param_index) : NULL);
+
       if (t &&
          TREE_CODE (t) == ADDR_EXPR
          && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
@@ -1106,6 +1125,7 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie,
        return NULL_TREE;
     }
 
+  gcc_assert (!ie->indirect_info->agg_contents);
   token = ie->indirect_info->otr_token;
   anc_offset = ie->indirect_info->offset;
   otr_type = ie->indirect_info->otr_type;
@@ -1156,7 +1176,8 @@ devirtualization_time_bonus (struct cgraph_node *node,
       struct inline_summary *isummary;
       tree target;
 
-      target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos);
+      target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos,
+                                            NULL);
       if (!target)
        continue;
 
@@ -1673,7 +1694,7 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node,
       tree target;
 
       next_ie = ie->next_callee;
-      target = ipa_get_indirect_edge_target (ie, known_vals, NULL);
+      target = ipa_get_indirect_edge_target (ie, known_vals, NULL, NULL);
       if (target)
        ipa_make_edge_direct_to_target (ie, target);
     }
index 474f2a9af195b41410c0ff9ce67756a21d378ac3..1a3afc456997f52d983b2f20b14e4de822c7fab8 100644 (file)
@@ -203,22 +203,54 @@ not_inlined_predicate (void)
   return single_cond_predicate (predicate_not_inlined_condition);
 }
 
+/* Simple description of whether a memory load or a condition refers to a load
+   from an aggregate and if so, how and where from in the aggregate.
+   Individual fields have the same meaning like fields with the same name in
+   struct condition.  */
 
-/* Add condition to condition list CONDS.  */
+struct agg_position_info
+{
+  HOST_WIDE_INT offset;
+  bool agg_contents;
+  bool by_ref;
+};
+
+/* Add condition to condition list CONDS.  AGGPOS describes whether the used
+   oprand is loaded from an aggregate and where in the aggregate it is.  It can
+   be NULL, which means this not a load from an aggregate.  */
 
 static struct predicate
 add_condition (struct inline_summary *summary, int operand_num,
+              struct agg_position_info *aggpos,
               enum tree_code code, tree val)
 {
   int i;
   struct condition *c;
   struct condition new_cond;
+  HOST_WIDE_INT offset;
+  bool agg_contents, by_ref;
 
+  if (aggpos)
+    {
+      offset = aggpos->offset;
+      agg_contents = aggpos->agg_contents;
+      by_ref = aggpos->by_ref;
+    }
+  else
+    {
+      offset = 0;
+      agg_contents = false;
+      by_ref = false;
+    }
+
+  gcc_checking_assert (operand_num >= 0);
   for (i = 0; VEC_iterate (condition, summary->conds, i, c); i++)
     {
       if (c->operand_num == operand_num
          && c->code == code
-         && c->val == val)
+         && c->val == val
+         && c->agg_contents == agg_contents
+         && (!agg_contents || (c->offset == offset && c->by_ref == by_ref)))
         return single_cond_predicate (i + predicate_first_dynamic_condition);
     }
   /* Too many conditions.  Give up and return constant true.  */
@@ -228,6 +260,9 @@ add_condition (struct inline_summary *summary, int operand_num,
   new_cond.operand_num = operand_num;
   new_cond.code = code;
   new_cond.val = val;
+  new_cond.agg_contents = agg_contents;
+  new_cond.by_ref = by_ref;
+  new_cond.offset = offset;
   VEC_safe_push (condition, gc, summary->conds, &new_cond);
   return single_cond_predicate (i + predicate_first_dynamic_condition);
 }
@@ -519,6 +554,9 @@ dump_condition (FILE *f, conditions conditions, int cond)
       c = VEC_index (condition, conditions,
                     cond - predicate_first_dynamic_condition);
       fprintf (f, "op%i", c->operand_num);
+      if (c->agg_contents)
+       fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]",
+                c->by_ref ? "ref " : "", c->offset);
       if (c->code == IS_NOT_CONSTANT)
        {
          fprintf (f, " not constant");
@@ -659,15 +697,17 @@ edge_set_predicate (struct cgraph_edge *e, struct predicate *predicate)
 
 
 /* KNOWN_VALS is partial mapping of parameters of NODE to constant values.
-   Return clause of possible truths. When INLINE_P is true, assume that
-   we are inlining. 
+   KNOWN_AGGS is a vector of aggreggate jump functions for each parameter.
+   Return clause of possible truths. When INLINE_P is true, assume that we are
+   inlining.
 
    ERROR_MARK means compile time invariant.  */
 
 static clause_t
 evaluate_conditions_for_known_args (struct cgraph_node *node,
-                                   bool inline_p,
-                                   VEC (tree, heap) *known_vals)
+                               bool inline_p,
+                               VEC (tree, heap) *known_vals,
+                               VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   clause_t clause = inline_p ? 0 : 1 << predicate_not_inlined_condition;
   struct inline_summary *info = inline_summary (node);
@@ -679,16 +719,45 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
       tree val;
       tree res;
 
-      /* We allow call stmt to have fewer arguments than the callee
-        function (especially for K&R style programs).  So bound
-        check here.  */
-      if (c->operand_num < (int)VEC_length (tree, known_vals))
-        val = VEC_index (tree, known_vals, c->operand_num);
-      else
-       val = NULL;
+      /* We allow call stmt to have fewer arguments than the callee function
+        (especially for K&R style programs).  So bound check here (we assume
+        known_aggs vector, if non-NULL, has the same length as
+        known_vals).  */
+      gcc_checking_assert (!known_aggs
+                          || (VEC_length (tree, known_vals)
+                              == VEC_length (ipa_agg_jump_function_p,
+                                             known_aggs)));
+      if (c->operand_num >= (int) VEC_length (tree, known_vals))
+       {
+         clause |= 1 << (i + predicate_first_dynamic_condition);
+         continue;
+       }
 
-      if (val == error_mark_node && c->code != CHANGED)
-       val = NULL;
+      if (c->agg_contents)
+       {
+         struct ipa_agg_jump_function *agg;
+
+         if (c->code == CHANGED
+             && !c->by_ref
+             && (VEC_index (tree, known_vals, c->operand_num)
+                 == error_mark_node))
+           continue;
+
+         if (known_aggs)
+           {
+             agg = VEC_index (ipa_agg_jump_function_p, known_aggs,
+                              c->operand_num);
+             val = ipa_find_agg_cst_for_param (agg, c->offset, c->by_ref);
+           }
+         else
+           val = NULL_TREE;
+       }
+      else
+       {
+         val = VEC_index (tree, known_vals, c->operand_num);
+         if (val == error_mark_node && c->code != CHANGED)
+           val = NULL_TREE;
+       }
 
       if (!val)
        {
@@ -711,13 +780,15 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
 
 static void
 evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
-                             clause_t *clause_ptr,
-                             VEC (tree, heap) **known_vals_ptr,
-                             VEC (tree, heap) **known_binfos_ptr)
+                          clause_t *clause_ptr,
+                          VEC (tree, heap) **known_vals_ptr,
+                          VEC (tree, heap) **known_binfos_ptr,
+                          VEC (ipa_agg_jump_function_p, heap) **known_aggs_ptr)
 {
   struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL);
   struct inline_summary *info = inline_summary (callee);
   VEC (tree, heap) *known_vals = NULL;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs = NULL;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition;
@@ -742,13 +813,16 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
 
       if (count && (info->conds || known_vals_ptr))
        VEC_safe_grow_cleared (tree, heap, known_vals, count);
+      if (count && (info->conds || known_aggs_ptr))
+       VEC_safe_grow_cleared (ipa_agg_jump_function_p, heap, known_aggs,
+                              count);
       if (count && known_binfos_ptr)
        VEC_safe_grow_cleared (tree, heap, *known_binfos_ptr, count);
 
       for (i = 0; i < count; i++)
        {
-         tree cst = ipa_value_from_jfunc (parms_info,
-                                          ipa_get_ith_jump_func (args, i));
+         struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i);
+         tree cst = ipa_value_from_jfunc (parms_info, jf);
          if (cst)
            {
              if (known_vals && TREE_CODE (cst) != TREE_BINFO)
@@ -761,17 +835,26 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
                                  es->param,
                                  i)->change_prob)
            VEC_replace (tree, known_vals, i, error_mark_node);
+         /* TODO: When IPA-CP starts propagating and merging aggregate jump
+            functions, use its knowledge of the caller too, just like the
+            scalar case above.  */
+         VEC_replace (ipa_agg_jump_function_p, known_aggs, i, &jf->agg);
        }
     }
 
   if (clause_ptr)
     *clause_ptr = evaluate_conditions_for_known_args (callee, inline_p,
-                                                     known_vals);
+                                                     known_vals, known_aggs);
 
   if (known_vals_ptr)
     *known_vals_ptr = known_vals;
   else
     VEC_free (tree, heap, known_vals);
+
+  if (known_aggs_ptr)
+    *known_aggs_ptr = known_aggs;
+  else
+    VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
 }
 
 
@@ -917,8 +1000,8 @@ inline_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
                }
            }
        }
-      possible_truths = evaluate_conditions_for_known_args (dst,
-                                                           false, known_vals);
+      possible_truths = evaluate_conditions_for_known_args (dst, false,
+                                                           known_vals, NULL);
       VEC_free (tree, heap, known_vals);
 
       account_size_time (info, 0, 0, &true_pred);
@@ -1262,11 +1345,11 @@ mark_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED,
   return true;
 }
 
-/* If OP reffers to value of function parameter, return 
-   the corresponding parameter.  */
+/* If OP refers to value of function parameter, return the corresponding
+   parameter.  */
 
 static tree
-unmodified_parm (gimple stmt, tree op)
+unmodified_parm_1 (gimple stmt, tree op)
 {
   /* SSA_NAME referring to parm default def?  */
   if (TREE_CODE (op) == SSA_NAME
@@ -1285,13 +1368,67 @@ unmodified_parm (gimple stmt, tree op)
       if (!modified)
        return op;
     }
-  /* Assignment from a parameter?  */
+  return NULL_TREE;
+}
+
+/* If OP refers to value of function parameter, return the corresponding
+   parameter.  Also traverse chains of SSA register assignments.  */
+
+static tree
+unmodified_parm (gimple stmt, tree op)
+{
+  tree res = unmodified_parm_1 (stmt, op);
+  if (res)
+    return res;
+
   if (TREE_CODE (op) == SSA_NAME
       && !SSA_NAME_IS_DEFAULT_DEF (op)
       && gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
     return unmodified_parm (SSA_NAME_DEF_STMT (op),
                            gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op)));
-  return NULL;
+  return NULL_TREE;
+}
+
+/* If OP refers to a value of a function parameter or value loaded from an
+   aggregate passed to a parameter (either by value or reference), return TRUE
+   and store the number of the parameter to *INDEX_P and information whether
+   and how it has been loaded from an aggregate into *AGGPOS.  INFO describes
+   the function parameters, STMT is the statement in which OP is used or
+   loaded.  */
+
+static bool
+unmodified_parm_or_parm_agg_item (struct ipa_node_params *info,
+                                 gimple stmt, tree op, int *index_p,
+                                 struct agg_position_info *aggpos)
+{
+  tree res = unmodified_parm_1 (stmt, op);
+
+  gcc_checking_assert (aggpos);
+  if (res)
+    {
+      *index_p = ipa_get_param_decl_index (info, res);
+      if (*index_p < 0)
+       return false;
+      aggpos->agg_contents = false;
+      aggpos->by_ref = false;
+      return true;
+    }
+
+  if (TREE_CODE (op) == SSA_NAME)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (op)
+         || !gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
+       return false;
+      stmt = SSA_NAME_DEF_STMT (op);
+      op = gimple_assign_rhs1 (stmt);
+      if (!REFERENCE_CLASS_P (op))
+       return unmodified_parm_or_parm_agg_item (info, stmt, op, index_p,
+                                                aggpos);
+    }
+
+  aggpos->agg_contents = true;
+  return ipa_load_from_parm_agg (info, stmt, op, index_p, &aggpos->offset,
+                                &aggpos->by_ref);
 }
 
 /* See if statement might disappear after inlining.
@@ -1422,13 +1559,12 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info,
   gimple last;
   tree op;
   int index;
+  struct agg_position_info aggpos;
   enum tree_code code, inverted_code;
   edge e;
   edge_iterator ei;
   gimple set_stmt;
   tree op2;
-  tree parm;
-  tree base;
 
   last = last_stmt (bb);
   if (!last
@@ -1440,12 +1576,8 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info,
   /* TODO: handle conditionals like
      var = op0 < 4;
      if (var != 0).  */
-  parm = unmodified_parm (last, op);
-  if (parm)
+  if (unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos))
     {
-      index = ipa_get_param_decl_index (info, parm);
-      if (index == -1)
-       return;
       code = gimple_cond_code (last);
       inverted_code
         = invert_tree_comparison (code,
@@ -1453,8 +1585,7 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info,
 
       FOR_EACH_EDGE (e, ei, bb->succs)
        {
-         struct predicate p = add_condition (summary,
-                                             index,
+         struct predicate p = add_condition (summary, index, &aggpos,
                                              e->flags & EDGE_TRUE_VALUE
                                              ? code : inverted_code,
                                              gimple_cond_rhs (last));
@@ -1475,28 +1606,21 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info,
      for this and also the constant code is not known to be
      optimized away when inliner doen't see operand is constant.
      Other optimizers might think otherwise.  */
+  if (gimple_cond_code (last) != NE_EXPR
+      || !integer_zerop (gimple_cond_rhs (last)))
+    return;
   set_stmt = SSA_NAME_DEF_STMT (op);
   if (!gimple_call_builtin_p (set_stmt, BUILT_IN_CONSTANT_P)
       || gimple_call_num_args (set_stmt) != 1)
     return;
   op2 = gimple_call_arg (set_stmt, 0);
-  base = get_base_address (op2);
-  parm = unmodified_parm (set_stmt, base ? base : op2);
-  if (!parm)
-    return;
-  index = ipa_get_param_decl_index (info, parm);
-  if (index == -1)
-    return;
-  if (gimple_cond_code (last) != NE_EXPR
-      || !integer_zerop (gimple_cond_rhs (last)))
+  if (!unmodified_parm_or_parm_agg_item (info, set_stmt, op2, &index, &aggpos))
     return;
   FOR_EACH_EDGE (e, ei, bb->succs)
     if (e->flags & EDGE_FALSE_VALUE)
       {
-       struct predicate p = add_condition (summary,
-                                           index,
-                                           IS_NOT_CONSTANT,
-                                           NULL);
+       struct predicate p = add_condition (summary, index, &aggpos,
+                                           IS_NOT_CONSTANT, NULL_TREE);
        e->aux = pool_alloc (edge_predicate_pool);
        *(struct predicate *)e->aux = p;
       }
@@ -1514,22 +1638,18 @@ set_switch_stmt_execution_predicate (struct ipa_node_params *info,
   gimple last;
   tree op;
   int index;
+  struct agg_position_info aggpos;
   edge e;
   edge_iterator ei;
   size_t n;
   size_t case_idx;
-  tree parm;
 
   last = last_stmt (bb);
   if (!last
       || gimple_code (last) != GIMPLE_SWITCH)
     return;
   op = gimple_switch_index (last);
-  parm = unmodified_parm (last, op);
-  if (!parm)
-    return;
-  index = ipa_get_param_decl_index (info, parm);
-  if (index == -1)
+  if (!unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos))
     return;
 
   FOR_EACH_EDGE (e, ei, bb->succs)
@@ -1554,18 +1674,12 @@ set_switch_stmt_execution_predicate (struct ipa_node_params *info,
       if (!min && !max)
        p = true_predicate ();
       else if (!max)
-       p = add_condition (summary, index,
-                          EQ_EXPR,
-                          min);
+       p = add_condition (summary, index, &aggpos, EQ_EXPR, min);
       else
        {
          struct predicate p1, p2;
-         p1 = add_condition (summary, index,
-                             GE_EXPR,
-                             min);
-         p2 = add_condition (summary, index,
-                             LE_EXPR,
-                             max);
+         p1 = add_condition (summary, index, &aggpos, GE_EXPR, min);
+         p2 = add_condition (summary, index, &aggpos, LE_EXPR, max);
          p = and_predicates (summary->conds, &p1, &p2);
        }
       *(struct predicate *)e->aux
@@ -1659,13 +1773,14 @@ will_be_nonconstant_predicate (struct ipa_node_params *info,
                               struct inline_summary *summary,
                               gimple stmt,
                               VEC (predicate_t, heap) *nonconstant_names)
-                             
 {
   struct predicate p = true_predicate ();
   ssa_op_iter iter;
   tree use;
   struct predicate op_non_const;
   bool is_load;
+  int base_index;
+  struct agg_position_info aggpos;
 
   /* What statments might be optimized away
      when their arguments are constant
@@ -1681,23 +1796,18 @@ will_be_nonconstant_predicate (struct ipa_node_params *info,
     return p;
 
   is_load = gimple_vuse (stmt) != NULL;
-
   /* Loads can be optimized when the value is known.  */
   if (is_load)
     {
-      tree op = gimple_assign_rhs1 (stmt);
-      tree base = get_base_address (op);
-      tree parm;
-
+      tree op;
       gcc_assert (gimple_assign_single_p (stmt));
-      if (!base)
-       return p;
-      parm = unmodified_parm (stmt, base);
-      if (!parm )
-       return p;
-      if (ipa_get_param_decl_index (info, parm) < 0)
+      op = gimple_assign_rhs1 (stmt);
+      if (!unmodified_parm_or_parm_agg_item (info, stmt, op, &base_index,
+                                            &aggpos))
        return p;
     }
+  else
+    base_index = -1;
 
   /* See if we understand all operands before we start
      adding conditionals.  */
@@ -1716,23 +1826,24 @@ will_be_nonconstant_predicate (struct ipa_node_params *info,
        continue;
       return p;
     }
-  op_non_const = false_predicate ();
+
   if (is_load)
-    {
-      tree parm = unmodified_parm
-                   (stmt, get_base_address (gimple_assign_rhs1 (stmt)));
-      p = add_condition (summary,
-                        ipa_get_param_decl_index (info, parm),
-                        CHANGED, NULL);
-      op_non_const = or_predicates (summary->conds, &p, &op_non_const);
-    }
+    op_non_const = add_condition (summary, base_index, &aggpos, CHANGED, NULL);
+  else
+    op_non_const = false_predicate ();
   FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
       tree parm = unmodified_parm (stmt, use);
-      if (parm && ipa_get_param_decl_index (info, parm) >= 0)
-       p = add_condition (summary,
-                          ipa_get_param_decl_index (info, parm),
-                          CHANGED, NULL);
+      int index;
+
+      if (parm
+         && (index = ipa_get_param_decl_index (info, parm)) >= 0)
+       {
+         if (index != base_index)
+           p = add_condition (summary, index, NULL, CHANGED, NULL_TREE);
+         else
+           continue;
+       }
       else
        p = *VEC_index (predicate_t, nonconstant_names,
                        SSA_NAME_VERSION (use));
@@ -2194,7 +2305,8 @@ static void
 estimate_edge_devirt_benefit (struct cgraph_edge *ie,
                              int *size, int *time, int prob,
                              VEC (tree, heap) *known_vals,
-                             VEC (tree, heap) *known_binfos)
+                             VEC (tree, heap) *known_binfos,
+                             VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   tree target;
   int time_diff, size_diff;
@@ -2202,7 +2314,8 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
   if (!known_vals && !known_binfos)
     return;
 
-  target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos);
+  target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos,
+                                        known_aggs);
   if (!target)
     return;
 
@@ -2259,7 +2372,8 @@ static void
 estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
                              clause_t possible_truths,
                              VEC (tree, heap) *known_vals,
-                             VEC (tree, heap) *known_binfos)
+                             VEC (tree, heap) *known_binfos,
+                             VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   struct cgraph_edge *e;
   for (e = node->callees; e; e = e->next_callee)
@@ -2276,7 +2390,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
          else
            estimate_calls_size_and_time (e->callee, size, time,
                                          possible_truths,
-                                         known_vals, known_binfos);
+                                         known_vals, known_binfos, known_aggs);
        }
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
@@ -2286,7 +2400,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
        {
          estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE);
          estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE,
-                                       known_vals, known_binfos);
+                                       known_vals, known_binfos, known_aggs);
        }
     }
 }
@@ -2301,6 +2415,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
                             clause_t possible_truths,
                             VEC (tree, heap) *known_vals,
                             VEC (tree, heap) *known_binfos,
+                            VEC (ipa_agg_jump_function_p, heap) *known_aggs,
                             int *ret_size, int *ret_time,
                             VEC (inline_param_summary_t, heap)
                               *inline_param_summary)
@@ -2352,7 +2467,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
     time = MAX_TIME * INLINE_TIME_SCALE;
 
   estimate_calls_size_and_time (node, &size, &time, possible_truths,
-                               known_vals, known_binfos);
+                               known_vals, known_binfos, known_aggs);
   time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
   size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE;
 
@@ -2381,27 +2496,31 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
 {
   clause_t clause;
 
-  clause = evaluate_conditions_for_known_args (node, false, known_vals);
-  estimate_node_size_and_time (node, clause, known_vals, known_binfos,
+  clause = evaluate_conditions_for_known_args (node, false, known_vals, NULL);
+  estimate_node_size_and_time (node, clause, known_vals, known_binfos, NULL,
                               ret_size, ret_time,
                               NULL);
 }
 
-
 /* Translate all conditions from callee representation into caller
    representation and symbolically evaluate predicate P into new predicate.
 
-   INFO is inline_summary of function we are adding predicate into,
-   CALLEE_INFO is summary of function predicate P is from. OPERAND_MAP is
-   array giving callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is
-   clausule of all callee conditions that may be true in caller context.
-   TOPLEV_PREDICATE is predicate under which callee is executed.  */
+   INFO is inline_summary of function we are adding predicate into, CALLEE_INFO
+   is summary of function predicate P is from. OPERAND_MAP is array giving
+   callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is clausule of all
+   callee conditions that may be true in caller context.  TOPLEV_PREDICATE is
+   predicate under which callee is executed.  OFFSET_MAP is an array of of
+   offsets that need to be added to conditions, negative offset means that
+   conditions relying on values passed by reference have to be discarded
+   because they might not be preserved (and should be considered offset zero
+   for other purposes).  */
 
 static struct predicate
 remap_predicate (struct inline_summary *info,
                 struct inline_summary *callee_info,
                 struct predicate *p,
                 VEC (int, heap) *operand_map,
+                VEC (int, heap) *offset_map,
                 clause_t possible_truths,
                 struct predicate *toplev_predicate)
 {
@@ -2436,13 +2555,34 @@ remap_predicate (struct inline_summary *info,
                    Otherwise give up.  */
                 if (!operand_map
                     || (int)VEC_length (int, operand_map) <= c->operand_num
-                    || VEC_index (int, operand_map, c->operand_num) == -1)
+                    || VEC_index (int, operand_map, c->operand_num) == -1
+                    || (!c->agg_contents
+                        && VEC_index (int, offset_map, c->operand_num) != 0)
+                    || (c->agg_contents && c->by_ref
+                        && VEC_index (int, offset_map, c->operand_num) < 0))
                   cond_predicate = true_predicate ();
                 else
-                  cond_predicate = add_condition (info,
-                                                  VEC_index (int, operand_map,
-                                                             c->operand_num),
-                                                  c->code, c->val);
+                  {
+                    struct agg_position_info ap;
+                    HOST_WIDE_INT offset_delta = VEC_index (int, offset_map,
+                                                            c->operand_num);
+                    if (offset_delta < 0)
+                      {
+                        gcc_checking_assert (!c->agg_contents || !c->by_ref);
+                        offset_delta = 0;
+                      }
+                    gcc_assert (!c->agg_contents
+                                || c->by_ref
+                                || offset_delta == 0);
+                    ap.offset = c->offset + offset_delta;
+                    ap.agg_contents = c->agg_contents;
+                    ap.by_ref = c->by_ref;
+                    cond_predicate = add_condition (info,
+                                                    VEC_index (int,
+                                                               operand_map,
+                                                               c->operand_num),
+                                                    &ap, c->code, c->val);
+                  }
              }
            /* Fixed conditions remains same, construct single
               condition predicate.  */
@@ -2549,6 +2689,7 @@ remap_edge_summaries  (struct cgraph_edge *inlined_edge,
                       struct inline_summary *info,
                       struct inline_summary *callee_info,
                       VEC (int, heap) *operand_map,
+                      VEC (int, heap) *offset_map,
                       clause_t possible_truths,
                       struct predicate *toplev_predicate)
 {
@@ -2565,7 +2706,8 @@ remap_edge_summaries  (struct cgraph_edge *inlined_edge,
          if (es->predicate)
            {
              p = remap_predicate (info, callee_info,
-                                  es->predicate, operand_map, possible_truths,
+                                  es->predicate, operand_map, offset_map,
+                                  possible_truths,
                                   toplev_predicate);
              edge_set_predicate (e, &p);
              /* TODO: We should remove the edge for code that will be
@@ -2582,7 +2724,8 @@ remap_edge_summaries  (struct cgraph_edge *inlined_edge,
        }
       else
        remap_edge_summaries (inlined_edge, e->callee, info, callee_info,
-                             operand_map, possible_truths, toplev_predicate);
+                             operand_map, offset_map, possible_truths,
+                             toplev_predicate);
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
     {
@@ -2593,8 +2736,8 @@ remap_edge_summaries  (struct cgraph_edge *inlined_edge,
       if (es->predicate)
        {
          p = remap_predicate (info, callee_info,
-                              es->predicate, operand_map, possible_truths,
-                              toplev_predicate);
+                              es->predicate, operand_map, offset_map,
+                              possible_truths, toplev_predicate);
          edge_set_predicate (e, &p);
          /* TODO: We should remove the edge for code that will be optimized
             out, but we need to keep verifiers and tree-inline happy.
@@ -2623,6 +2766,7 @@ inline_merge_summary (struct cgraph_edge *edge)
   clause_t clause = 0;         /* not_inline is known to be false.  */
   size_time_entry *e;
   VEC (int, heap) *operand_map = NULL;
+  VEC (int, heap) *offset_map = NULL;
   int i;
   struct predicate toplev_predicate;
   struct predicate true_p = true_predicate ();
@@ -2639,17 +2783,36 @@ inline_merge_summary (struct cgraph_edge *edge)
       int count = ipa_get_cs_argument_count (args);
       int i;
 
-      evaluate_properties_for_edge (edge, true, &clause, NULL, NULL);
+      evaluate_properties_for_edge (edge, true, &clause, NULL, NULL, NULL);
       if (count)
-       VEC_safe_grow_cleared (int, heap, operand_map, count);
+       {
+         VEC_safe_grow_cleared (int, heap, operand_map, count);
+         VEC_safe_grow_cleared (int, heap, offset_map, count);
+       }
       for (i = 0; i < count; i++)
        {
          struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, i);
          int map = -1;
+
          /* TODO: handle non-NOPs when merging.  */
-         if (jfunc->type == IPA_JF_PASS_THROUGH
-             && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
-           map = ipa_get_jf_pass_through_formal_id (jfunc);
+         if (jfunc->type == IPA_JF_PASS_THROUGH)
+           {
+             if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
+               map = ipa_get_jf_pass_through_formal_id (jfunc);
+             if (!ipa_get_jf_pass_through_agg_preserved (jfunc))
+               VEC_replace (int, offset_map, i, -1);
+           }
+         else if (jfunc->type == IPA_JF_ANCESTOR)
+           {
+             HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc);
+             if (offset >= 0 && offset < INT_MAX)
+               {
+                 map = ipa_get_jf_ancestor_formal_id (jfunc);
+                 if (!ipa_get_jf_ancestor_agg_preserved (jfunc))
+                   offset = -1;
+                 VEC_replace (int, offset_map, i, offset);
+               }
+           }
          VEC_replace (int, operand_map, i, map);
          gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to)));
        }
@@ -2657,7 +2820,8 @@ inline_merge_summary (struct cgraph_edge *edge)
   for (i = 0; VEC_iterate (size_time_entry, callee_info->entry, i, e); i++)
     {
       struct predicate p = remap_predicate (info, callee_info,
-                                           &e->predicate, operand_map, clause,
+                                           &e->predicate, operand_map,
+                                           offset_map, clause,
                                            &toplev_predicate);
       if (!false_predicate_p (&p))
        {
@@ -2679,7 +2843,7 @@ inline_merge_summary (struct cgraph_edge *edge)
        }
     }
   remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map,
-                       clause, &toplev_predicate);
+                       offset_map, clause, &toplev_predicate);
 
   inline_update_callee_summaries (edge->callee,
                                  inline_edge_summary (edge)->loop_depth);
@@ -2689,6 +2853,7 @@ inline_merge_summary (struct cgraph_edge *edge)
   /* Similarly remove param summaries.  */
   VEC_free (inline_param_summary_t, heap, es->param);
   VEC_free (int, heap, operand_map);
+  VEC_free (int, heap, offset_map);
 }
 
 /* For performance reasons inline_merge_summary is not updating overall size
@@ -2707,7 +2872,7 @@ inline_update_overall_summary (struct cgraph_node *node)
     info->size += e->size, info->time += e->time;
   estimate_calls_size_and_time (node, &info->size, &info->time,
                                ~(clause_t)(1 << predicate_false_condition),
-                               NULL, NULL);
+                               NULL, NULL, NULL);
   info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
   info->size = (info->size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE;
 }
@@ -2729,17 +2894,20 @@ do_estimate_edge_time (struct cgraph_edge *edge)
   clause_t clause;
   VEC (tree, heap) *known_vals;
   VEC (tree, heap) *known_binfos;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs;
   struct inline_edge_summary *es = inline_edge_summary (edge);
 
   callee = cgraph_function_or_thunk_node (edge->callee, NULL);
 
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-                               &clause, &known_vals, &known_binfos);
+                               &clause, &known_vals, &known_binfos,
+                               &known_aggs);
   estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
-                              &size, &time, es->param);
+                              known_aggs, &size, &time, es->param);
   VEC_free (tree, heap, known_vals);
   VEC_free (tree, heap, known_binfos);
+  VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
 
   ret = (((gcov_type)time
           - es->call_stmt_time) * edge->frequency
@@ -2776,6 +2944,7 @@ do_estimate_edge_growth (struct cgraph_edge *edge)
   clause_t clause;
   VEC (tree, heap) *known_vals;
   VEC (tree, heap) *known_binfos;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
 
@@ -2794,11 +2963,13 @@ do_estimate_edge_growth (struct cgraph_edge *edge)
   /* Early inliner runs without caching, go ahead and do the dirty work.  */
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-                               &clause, &known_vals, &known_binfos);
+                               &clause, &known_vals, &known_binfos,
+                               &known_aggs);
   estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
-                              &size, NULL, NULL);
+                              known_aggs, &size, NULL, NULL);
   VEC_free (tree, heap, known_vals);
   VEC_free (tree, heap, known_binfos);
+  VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
   gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size);
   return size - inline_edge_summary (edge)->call_stmt_size;
 }
@@ -3078,6 +3249,11 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data,
          c.operand_num = streamer_read_uhwi (&ib);
          c.code = (enum tree_code) streamer_read_uhwi (&ib);
          c.val = stream_read_tree (&ib, data_in);
+         bp = streamer_read_bitpack (&ib);
+         c.agg_contents = bp_unpack_value (&bp, 1);
+         c.by_ref = bp_unpack_value (&bp, 1);
+         if (c.agg_contents)
+           c.offset = streamer_read_uhwi (&ib);
          VEC_safe_push (condition, gc, info->conds, &c);
        }
       count2 = streamer_read_uhwi (&ib);
@@ -3223,6 +3399,12 @@ inline_write_summary (cgraph_node_set set,
              streamer_write_uhwi (ob, c->operand_num);
              streamer_write_uhwi (ob, c->code);
              stream_write_tree (ob, c->val, true);
+             bp = bitpack_create (ob->main_stream);
+             bp_pack_value (&bp, c->agg_contents, 1);
+             bp_pack_value (&bp, c->by_ref, 1);
+             streamer_write_bitpack (&bp);
+             if (c->agg_contents)
+               streamer_write_uhwi (ob, c->offset);
            }
          streamer_write_uhwi (ob, VEC_length (size_time_entry, info->entry));
          for (i = 0;
index fbd0b9982b7cbf022a0bafa94fb2b7012728e1dc..db3f8d4e56bd06f8448fda9e59812ec6a4d56492 100644 (file)
@@ -28,9 +28,18 @@ along with GCC; see the file COPYING3.  If not see
 
 typedef struct GTY(()) condition
   {
+    /* If agg_contents is set, this is the offset from which the used data was
+       loaded.  */
+    HOST_WIDE_INT offset;
     tree val;
     int operand_num;
-    enum tree_code code;
+    ENUM_BITFIELD(tree_code) code : 16;
+    /* Set if the used data were loaded from an aggregate parameter or from
+       data received by reference.  */
+    unsigned agg_contents : 1;
+    /* If agg_contents is set, this differentiates between loads from data
+       passed by reference and by value.  */
+    unsigned by_ref : 1;
   } condition;
 
 DEF_VEC_O (condition);
index 489e5d859b803ff121500bbb9a03cddf3214dcd2..2d2a54be0cff1174f866b52db37948a3dc524cba 100644 (file)
@@ -494,8 +494,9 @@ bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
 
 /* Indirect edge and binfo processing.  */
 tree ipa_get_indirect_edge_target (struct cgraph_edge *ie,
-                                  VEC (tree, heap) *known_csts,
-                                  VEC (tree, heap) *known_binfs);
+                                  VEC (tree, heap) *,
+                                  VEC (tree, heap) *,
+                                  VEC (ipa_agg_jump_function_p, heap) *);
 struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
 
 /* Functions related to both.  */
index 50c97413c15da6b269b8b2e55ce6bff02b27f401..5c5b9b52567e050be93ded098f506de5212df49a 100644 (file)
@@ -1,3 +1,8 @@
+2012-08-11  Martin Jambor  <mjambor@suse.cz>
+
+       PR fortran/48636
+       * gfortran.dg/pr48636.f90: New test.
+
 2012-08-10  Jakub Jelinek  <jakub@redhat.com>
 
        * gcc.dg/torture/vector-shuffle1.c (f): Pass vectors indirectly
diff --git a/gcc/testsuite/gfortran.dg/pr48636.f90 b/gcc/testsuite/gfortran.dg/pr48636.f90
new file mode 100644 (file)
index 0000000..44515ae
--- /dev/null
@@ -0,0 +1,37 @@
+! { dg-do compile }
+! { dg-options "-O3 -fdump-ipa-inline" }
+
+module foo
+  implicit none
+contains
+  subroutine bar(a,x)
+    real, dimension(:,:), intent(in) :: a
+    real, intent(out) :: x
+    integer :: i,j
+
+    x = 0
+    do j=1,ubound(a,2)
+       do i=1,ubound(a,1)
+          x = x + a(i,j)**2
+       end do
+    end do
+  end subroutine bar
+end module foo
+
+program main
+  use foo
+  implicit none
+  real, dimension(2,3) :: a
+  real :: x
+  integer :: i
+
+  data a /1.0, 2.0, 3.0, -1.0, -2.0, -3.0/
+
+  do i=1,2000000
+     call bar(a,x)
+  end do
+  print *,x
+end program main
+
+! { dg-final { scan-ipa-dump "bar\[^\\n\]*inline copy in MAIN" "inline" } }
+! { dg-final { cleanup-ipa-dump "inline" } }