===================================================================
@@ -172,6 +172,10 @@ struct ipa_bb_info
/* Aggregate contents of tracked references at the beginning of each BB. */
vec<ipa_known_agg_contents_list *> begin_agg_cnt;
+ /* True if this BB is the designated owner of the corresponding pointer in
+ begin_agg_cnt. */
+ vec<bool> own_begin_agg_cnt;
+
/* Changes in aggregate contents of tracked references. */
vec<ipa_known_agg_contents_list *> agg_deltas;
@@ -1993,26 +1997,77 @@ determine_locally_known_aggregate_parts
}
}
-/* Apply basic block DELTAS to INITial aggregate contents description. */
+/* Apply basic block DELTAS to INITial aggregate contents description. FBI
+ describes the current function. Set *OWN to true if the result is
+ exclusively held and can be modified in place. */
static struct ipa_known_agg_contents_list *
-apply_agg_contents_deltas (struct ipa_known_agg_contents_list *init,
- struct ipa_known_agg_contents_list *deltas)
+apply_agg_contents_deltas (struct func_body_info *fbi,
+ struct ipa_known_agg_contents_list *init,
+ struct ipa_known_agg_contents_list *deltas,
+ bool *own)
{
- /* TODO: This over-conservative but should work for Fortran descriptors.
- Will be replaced in a subsequent patches with real merging. */
-
- gcc_assert (init != AGG_CONTENTS_TOP);
- if (deltas)
- return deltas;
- else
- {
+ gcc_checking_assert (init != AGG_CONTENTS_TOP);
#ifdef ENABLE_CHECKING
- for (struct ipa_known_agg_contents_list *p = init; p; p = p->next)
- gcc_assert (p->only_unescaped);
+ for (struct ipa_known_agg_contents_list *p = init; p; p = p->next)
+ gcc_assert (p->only_unescaped);
#endif
+
+ if (!init)
+ {
+ *own = false;
+ return deltas;
+ }
+ if (!deltas)
+ {
+ *own = false;
return init;
}
+
+ *own = true;
+ struct ipa_known_agg_contents_list *list = NULL, **r = &list;
+ while (init || deltas)
+ {
+ struct ipa_known_agg_contents_list *p = NULL;
+ if (deltas && (!init || deltas->offset < init->offset))
+ {
+ if (deltas->constant)
+ p = deltas;
+ while (init && init->offset < deltas->offset + deltas->size)
+ init = init->next;
+ deltas = deltas->next;
+ }
+ else if (init && (!deltas || init->offset < deltas->offset))
+ {
+ if (init->constant
+ && (!deltas
+ || init->offset + init->size >= deltas->offset))
+ p = init;
+ init = init->next;
+ }
+ else
+ {
+ gcc_checking_assert (init->offset == deltas->offset);
+ if (deltas->constant)
+ p = deltas;
+ while (init && init->offset < deltas->offset + deltas->size)
+ init = init->next;
+ deltas = deltas->next;
+ }
+
+ if (p)
+ {
+ *r = (struct ipa_known_agg_contents_list *)
+ pool_alloc (fbi->agg_contents_pool);
+ (*r)->offset = p->offset;
+ (*r)->size = p->size;
+ (*r)->constant = p->constant;
+ (*r)->only_unescaped = p->only_unescaped;
+ (*r)->next = NULL;
+ r = &(*r)->next;
+ }
+ }
+ return list;
}
static tree
@@ -2264,9 +2319,10 @@ ipa_compute_jump_functions_for_edge (str
struct ipa_bb_info *bi;
bi = ipa_get_bb_info (fbi, gimple_bb (cs->call_stmt));
struct ipa_known_agg_contents_list *begin, *final, *p;
+ bool dummy;
begin = bi->begin_agg_cnt[ref_index];
- final = apply_agg_contents_deltas (begin, (*dvec)[n]);
-
+ final = apply_agg_contents_deltas (fbi, begin, (*dvec)[n],
+ &dummy);
int const_count = 0;
for (p = final; p; p = p->next)
if (p->constant)
@@ -2892,6 +2948,8 @@ ipa_analyze_bb_statements (struct func_b
sizeof (int) * ipa_get_tracked_refs_count (fbi->info));
bi->begin_agg_cnt.safe_grow (ipa_get_tracked_refs_count (fbi->info));
+ bi->own_begin_agg_cnt.safe_grow_cleared
+ (ipa_get_tracked_refs_count (fbi->info));
for (int i = 0; i < ipa_get_tracked_refs_count (fbi->info); ++i)
bi->begin_agg_cnt[i] = AGG_CONTENTS_TOP;
}
@@ -2982,6 +3040,7 @@ free_ipa_bb_info (struct ipa_bb_info *bi
bi->cg_edges.release ();
bi->param_aa_statuses.release ();
bi->begin_agg_cnt.release ();
+ bi->own_begin_agg_cnt.release ();
bi->agg_deltas.release ();
}
@@ -3313,31 +3372,123 @@ gather_picked_escapes (struct func_body_
}
}
-/* Merge aggregate contents FINAL with those in *TARGET. Return true if those
- in *TARGET have changed. */
+/* Remova all items from the list in *TARGET that are not also in INCOMING.
+ Return true if any was actually removed. */
static bool
-merge_agg_contents (struct ipa_known_agg_contents_list *final,
+prune_agg_contents (struct ipa_known_agg_contents_list *incoming,
struct ipa_known_agg_contents_list **target)
{
- /* TODO: This over-conservative but should work for Fortran descriptors.
- Will be replaced in a subsequent patches by real merging. */
+ bool res = false;
+ while (*target && incoming)
+ {
+ if ((*target)->offset > incoming->offset)
+ {
+ while (*target
+ && (*target)->offset > incoming->offset
+ && (*target)->offset < incoming->offset + incoming->size)
+ {
+ *target = (*target)->next;
+ res = true;
+ }
+
+ incoming = incoming->next;
+ }
+ else if ((*target)->offset < incoming->offset)
+ {
+ while (incoming
+ && (*target)->offset < incoming->offset
+ && (*target)->offset + (*target)->size > incoming->size)
+ incoming = incoming->next;
+
+ *target = (*target)->next;
+ res = true;
+ }
+ else if ((*target)->size != incoming->size
+ || !(*target)->constant
+ || !incoming->constant
+ || !operand_equal_p ((*target)->constant, incoming->constant, 0))
+ {
+ *target = (*target)->next;
+ incoming = incoming->next;
+ res = true;
+ }
+ else
+ {
+ gcc_checking_assert ((*target)->only_unescaped);
+ target = &(*target)->next;
+ incoming = incoming->next;
+ }
+ }
+
+ res |= *target != NULL;
+ *target = NULL;
+ return res;
+}
+
+/* Merge aggregate contents INCOMING with those in *TARGET. Return true if
+ those in *TARGET have changed. FBI describes the current function. INC_OWN
+ determines whether the INCOMING list is exclusively owned and can be
+ modified in place or not. TARGET_OWN points to a flag that describes
+ *TARGET in the same way and which may be modified. */
+
+static bool
+merge_agg_contents (struct func_body_info *fbi,
+ struct ipa_known_agg_contents_list *incoming, bool inc_own,
+ struct ipa_known_agg_contents_list **target,
+ bool *target_own)
+{
if (*target == AGG_CONTENTS_TOP)
{
- *target = final;
+ *target = incoming;
+ *target_own = inc_own;
return true;
}
- else if (*target != final)
+ if (*target == NULL
+ || *target == incoming)
+ return false;
+
+ if (*target_own)
+ return prune_agg_contents (incoming, target);
+
+ /* TODO: This may save some memory but is it worthwile? */
+ for (struct ipa_known_agg_contents_list *p = (*target)->next; p; p = p->next)
+ if (p == incoming)
+ {
+ *target = p;
+ return true;
+ }
+
+ struct ipa_known_agg_contents_list *p = *target, *list = NULL, **r = &list;
+ while (p && incoming)
{
- if (*target)
+ if (p->offset > incoming->offset)
+ incoming = incoming->next;
+ else if (p->offset < incoming->offset)
+ p = p->next;
+ else
{
- *target = NULL;
- return true;
+ if (p->size == incoming->size
+ && p->constant
+ && incoming->constant
+ && operand_equal_p (p->constant, incoming->constant, 0))
+ {
+ *r = (struct ipa_known_agg_contents_list *)
+ pool_alloc (fbi->agg_contents_pool);
+ (*r)->offset = p->offset;
+ (*r)->size = p->size;
+ (*r)->constant = p->constant;
+ (*r)->only_unescaped = true;
+ (*r)->next = NULL;
+ r = &(*r)->next;
+ }
+ p = p->next;
+ incoming = incoming->next;
}
- else
- return false;
}
- return false;
+ *target_own = true;
+ *target = list;
+ return true;
}
/* Apply all computed aggregate deltas for the given BB and merge results into
@@ -3356,7 +3507,10 @@ propagate_agg_cnts_through_bb (struct fu
else
deltas = bi->agg_deltas[i];
- final = apply_agg_contents_deltas (bi->begin_agg_cnt[i], deltas);
+ bool fin_own;
+ final = apply_agg_contents_deltas (fbi, bi->begin_agg_cnt[i], deltas,
+ &fin_own);
+ fin_own = fin_own && single_succ_p (bb);
edge e;
edge_iterator ei;
@@ -3371,7 +3525,9 @@ propagate_agg_cnts_through_bb (struct fu
gcc_checking_assert (succ_info->begin_agg_cnt.length ()
>= (unsigned) i);
- if (merge_agg_contents (final, &succ_info->begin_agg_cnt[i])
+ if (merge_agg_contents (fbi, final, fin_own,
+ &succ_info->begin_agg_cnt[i],
+ &succ_info->own_begin_agg_cnt[i])
&& !succ_info->queued)
{
succ_info->queued = true;
===================================================================
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details" } */
+/* { dg-add-options bind_pic_locally } */
+
+struct S
+{
+ int i,j;
+};
+
+volatile int g;
+int *p;
+
+static int __attribute__ ((noinline, noclone))
+something_unpredictable (void)
+{
+ *p = 6;
+ return 1;
+}
+
+
+static void __attribute__ ((noinline))
+bar (struct S *ps)
+{
+ something_unpredictable ();
+ g = ps->j;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct S s;
+
+ s.i = 2;
+ s.j = 8;
+
+ if (something_unpredictable ())
+ s.i = 6;
+
+ bar (&s);
+
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Creating a specialized node of bar.*for all known contexts" "cp" } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */