Commit 46771da5 by Feng Xue Committed by Feng Xue

re PR ipa/90401 (Missed propagation of by-ref constant argument to callee function)

PR ipa/90401

gcc/ChangeLog:

	* ipa-prop.c (add_to_agg_contents_list): New function.
	(clobber_by_agg_contents_list_p): Likewise.
	(extract_mem_content): Likewise.
	(get_place_in_agg_contents_list): Delete.
	(determine_known_aggregate_parts): Renamed from
	determine_locally_known_aggregate_parts.  New parameter
	aa_walk_budget_p.

gcc/testsuite/ChangeLog:

	* gcc.dg/ipa/ipcp-agg-10.c: New test.

From-SVN: r272282
parent bc09939d
2019-06-14 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/90401
* ipa-prop.c (add_to_agg_contents_list): New function.
(clobber_by_agg_contents_list_p): Likewise.
(extract_mem_content): Likewise.
(get_place_in_agg_contents_list): Delete.
(determine_known_aggregate_parts): Renamed from
determine_locally_known_aggregate_parts. New parameter
aa_walk_budget_p.
2019-06-13 Martin Sebor <msebor@redhat.com> 2019-06-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/90662 PR tree-optimization/90662
......
...@@ -1458,7 +1458,7 @@ get_ssa_def_if_simple_copy (tree rhs) ...@@ -1458,7 +1458,7 @@ get_ssa_def_if_simple_copy (tree rhs)
return rhs; return rhs;
} }
/* Simple linked list, describing known contents of an aggregate beforere /* Simple linked list, describing known contents of an aggregate before
call. */ call. */
struct ipa_known_agg_contents_list struct ipa_known_agg_contents_list
...@@ -1471,41 +1471,48 @@ struct ipa_known_agg_contents_list ...@@ -1471,41 +1471,48 @@ struct ipa_known_agg_contents_list
struct ipa_known_agg_contents_list *next; struct ipa_known_agg_contents_list *next;
}; };
/* Find the proper place in linked list of ipa_known_agg_contents_list /* Add a known content item into a linked list of ipa_known_agg_contents_list
structures where to put a new one with the given LHS_OFFSET and LHS_SIZE, structure, in which all elements are sorted ascendingly by offset. */
unless there is a partial overlap, in which case return NULL, or such
element is already there, in which case set *ALREADY_THERE to true. */
static struct ipa_known_agg_contents_list ** static inline void
get_place_in_agg_contents_list (struct ipa_known_agg_contents_list **list, add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
HOST_WIDE_INT lhs_offset, struct ipa_known_agg_contents_list *item)
HOST_WIDE_INT lhs_size,
bool *already_there)
{ {
struct ipa_known_agg_contents_list **p = list; struct ipa_known_agg_contents_list *list = *plist;
while (*p && (*p)->offset < lhs_offset)
for (; list; list = list->next)
{ {
if ((*p)->offset + (*p)->size > lhs_offset) if (list->offset >= item->offset)
return NULL; break;
p = &(*p)->next;
plist = &list->next;
} }
if (*p && (*p)->offset < lhs_offset + lhs_size) item->next = list;
*plist = item;
}
/* Check whether a given known content is clobbered by certain element in
a linked list of ipa_known_agg_contents_list. */
static inline bool
clobber_by_agg_contents_list_p (struct ipa_known_agg_contents_list *list,
struct ipa_known_agg_contents_list *item)
{
for (; list; list = list->next)
{ {
if ((*p)->offset == lhs_offset && (*p)->size == lhs_size) if (list->offset >= item->offset)
/* We already know this value is subsequently overwritten with return list->offset < item->offset + item->size;
something else. */
*already_there = true; if (list->offset + list->size > item->offset)
else return true;
/* Otherwise this is a partial overlap which we cannot
represent. */
return NULL;
} }
return p;
return false;
} }
/* Build aggregate jump function from LIST, assuming there are exactly /* Build aggregate jump function from LIST, assuming there are exactly
CONST_COUNT constant entries there and that th offset of the passed argument CONST_COUNT constant entries there and that offset of the passed argument
is ARG_OFFSET and store it into JFUNC. */ is ARG_OFFSET and store it into JFUNC. */
static void static void
...@@ -1528,26 +1535,79 @@ build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list, ...@@ -1528,26 +1535,79 @@ build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
} }
} }
/* If STMT is a memory store to the object whose address is BASE, extract
information (offset, size, and value) into CONTENT, and return true,
otherwise we conservatively assume the whole object is modified with
unknown content, and return false. CHECK_REF means that access to object
is expected to be in form of MEM_REF expression. */
static bool
extract_mem_content (gimple *stmt, tree base, bool check_ref,
struct ipa_known_agg_contents_list *content)
{
HOST_WIDE_INT lhs_offset, lhs_size;
tree lhs, rhs, lhs_base;
bool reverse;
if (!gimple_assign_single_p (stmt))
return false;
lhs = gimple_assign_lhs (stmt);
rhs = gimple_assign_rhs1 (stmt);
if (!is_gimple_reg_type (TREE_TYPE (rhs))
|| TREE_CODE (lhs) == BIT_FIELD_REF
|| contains_bitfld_component_ref_p (lhs))
return false;
lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset,
&lhs_size, &reverse);
if (!lhs_base)
return false;
if (check_ref)
{
if (TREE_CODE (lhs_base) != MEM_REF
|| TREE_OPERAND (lhs_base, 0) != base
|| !integer_zerop (TREE_OPERAND (lhs_base, 1)))
return false;
}
else if (lhs_base != base)
return false;
rhs = get_ssa_def_if_simple_copy (rhs);
content->size = lhs_size;
content->offset = lhs_offset;
content->constant = is_gimple_ip_invariant (rhs) ? rhs : NULL_TREE;
content->next = NULL;
return true;
}
/* Traverse statements from CALL backwards, scanning whether an aggregate given /* Traverse statements from CALL backwards, scanning whether an aggregate given
in ARG is filled in with constant values. ARG can either be an aggregate in ARG is filled in with constant values. ARG can either be an aggregate
expression or a pointer to an aggregate. ARG_TYPE is the type of the expression or a pointer to an aggregate. ARG_TYPE is the type of the
aggregate. JFUNC is the jump function into which the constants are aggregate. JFUNC is the jump function into which the constants are
subsequently stored. */ subsequently stored. AA_WALK_BUDGET_P points to limit on number of
statements we allow get_continuation_for_phi to examine. */
static void static void
determine_locally_known_aggregate_parts (gcall *call, tree arg, determine_known_aggregate_parts (gcall *call, tree arg,
tree arg_type, tree arg_type,
struct ipa_jump_func *jfunc) struct ipa_jump_func *jfunc,
unsigned *aa_walk_budget_p)
{ {
struct ipa_known_agg_contents_list *list = NULL; struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL;
bitmap visited = NULL;
int item_count = 0, const_count = 0; int item_count = 0, const_count = 0;
int ipa_max_agg_items = PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS);
HOST_WIDE_INT arg_offset, arg_size; HOST_WIDE_INT arg_offset, arg_size;
gimple_stmt_iterator gsi;
tree arg_base; tree arg_base;
bool check_ref, by_ref; bool check_ref, by_ref;
ao_ref r; ao_ref r;
if (PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS) == 0) if (ipa_max_agg_items == 0)
return; return;
/* The function operates in three stages. First, we prepare check_ref, r, /* The function operates in three stages. First, we prepare check_ref, r,
...@@ -1606,82 +1666,61 @@ determine_locally_known_aggregate_parts (gcall *call, tree arg, ...@@ -1606,82 +1666,61 @@ determine_locally_known_aggregate_parts (gcall *call, tree arg,
ao_ref_init (&r, arg); ao_ref_init (&r, arg);
} }
/* Second stage walks back the BB, looks at individual statements and as long /* Second stage traverses virtual SSA web backwards starting from the call
as it is confident of how the statements affect contents of the statement, only looks at individual dominating virtual operand (its
aggregates, it builds a sorted linked list of ipa_agg_jf_list structures definition dominates the call), as long as it is confident that content
describing it. */ of the aggregate is affected by definition of the virtual operand, it
gsi = gsi_for_stmt (call); builds a sorted linked list of ipa_agg_jf_list describing that. */
gsi_prev (&gsi);
for (; !gsi_end_p (gsi); gsi_prev (&gsi))
{
struct ipa_known_agg_contents_list *n, **p;
gimple *stmt = gsi_stmt (gsi);
HOST_WIDE_INT lhs_offset, lhs_size;
tree lhs, rhs, lhs_base;
bool reverse;
if (!stmt_may_clobber_ref_p_1 (stmt, &r))
continue;
if (!gimple_assign_single_p (stmt))
break;
lhs = gimple_assign_lhs (stmt); for (tree dom_vuse = gimple_vuse (call); dom_vuse;)
rhs = gimple_assign_rhs1 (stmt); {
if (!is_gimple_reg_type (TREE_TYPE (rhs)) gimple *stmt = SSA_NAME_DEF_STMT (dom_vuse);
|| TREE_CODE (lhs) == BIT_FIELD_REF
|| contains_bitfld_component_ref_p (lhs))
break;
lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset,
&lhs_size, &reverse);
if (!lhs_base)
break;
if (check_ref) if (gimple_code (stmt) == GIMPLE_PHI)
{ {
if (TREE_CODE (lhs_base) != MEM_REF dom_vuse = get_continuation_for_phi (stmt, &r, *aa_walk_budget_p,
|| TREE_OPERAND (lhs_base, 0) != arg_base &visited, false, NULL, NULL);
|| !integer_zerop (TREE_OPERAND (lhs_base, 1))) continue;
break;
} }
else if (lhs_base != arg_base)
if (stmt_may_clobber_ref_p_1 (stmt, &r))
{ {
if (DECL_P (lhs_base)) struct ipa_known_agg_contents_list *content
continue; = XALLOCA (struct ipa_known_agg_contents_list);
else
if (!extract_mem_content (stmt, arg_base, check_ref, content))
break; break;
}
bool already_there = false; /* Now we get a dominating virtual operand, and need to check
p = get_place_in_agg_contents_list (&list, lhs_offset, lhs_size, whether its value is clobbered any other dominating one. */
&already_there); if (content->constant
if (!p) && !clobber_by_agg_contents_list_p (all_list, content))
break; {
if (already_there) struct ipa_known_agg_contents_list *copy
continue; = XALLOCA (struct ipa_known_agg_contents_list);
rhs = get_ssa_def_if_simple_copy (rhs); /* Add to the list consisting of only dominating virtual
n = XALLOCA (struct ipa_known_agg_contents_list); operands, whose definitions can finally reach the call. */
n->size = lhs_size; add_to_agg_contents_list (&list, (*copy = *content, copy));
n->offset = lhs_offset;
if (is_gimple_ip_invariant (rhs)) if (++const_count == ipa_max_agg_items)
{ break;
n->constant = rhs; }
const_count++;
/* Add to the list consisting of all dominating virtual operands. */
add_to_agg_contents_list (&all_list, content);
if (++item_count == 2 * ipa_max_agg_items)
break;
} }
else dom_vuse = gimple_vuse (stmt);
n->constant = NULL_TREE; }
n->next = *p;
*p = n;
item_count++; if (visited)
if (const_count == PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS) BITMAP_FREE (visited);
|| item_count == 2 * PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS))
break;
}
/* Third stage just goes over the list and creates an appropriate vector of /* Third stage just goes over the list and creates an appropriate vector of
ipa_agg_jf_item structures out of it, of sourse only if there are ipa_agg_jf_item structures out of it, of course only if there are
any known constants to begin with. */ any known constants to begin with. */
if (const_count) if (const_count)
...@@ -1691,6 +1730,7 @@ determine_locally_known_aggregate_parts (gcall *call, tree arg, ...@@ -1691,6 +1730,7 @@ determine_locally_known_aggregate_parts (gcall *call, tree arg,
} }
} }
/* Return the Ith param type of callee associated with call graph /* Return the Ith param type of callee associated with call graph
edge E. */ edge E. */
...@@ -1797,7 +1837,7 @@ ipa_set_jfunc_vr (ipa_jump_func *jf, enum value_range_kind type, ...@@ -1797,7 +1837,7 @@ ipa_set_jfunc_vr (ipa_jump_func *jf, enum value_range_kind type,
jf->m_vr = ipa_get_value_range (type, min, max); jf->m_vr = ipa_get_value_range (type, min, max);
} }
/* Assign to JF a pointer to a value_range just liek TMP but either fetch a /* Assign to JF a pointer to a value_range just like TMP but either fetch a
copy from ipa_vr_hash_table or allocate a new on in GC memory. */ copy from ipa_vr_hash_table or allocate a new on in GC memory. */
static void static void
...@@ -1978,7 +2018,8 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi, ...@@ -1978,7 +2018,8 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
|| !ipa_get_jf_ancestor_agg_preserved (jfunc)) || !ipa_get_jf_ancestor_agg_preserved (jfunc))
&& (AGGREGATE_TYPE_P (TREE_TYPE (arg)) && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
|| POINTER_TYPE_P (param_type))) || POINTER_TYPE_P (param_type)))
determine_locally_known_aggregate_parts (call, arg, param_type, jfunc); determine_known_aggregate_parts (call, arg, param_type, jfunc,
&fbi->aa_walk_budget);
} }
if (!useful_context) if (!useful_context)
vec_free (args->polymorphic_call_contexts); vec_free (args->polymorphic_call_contexts);
......
2019-06-14 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/90401
* gcc.dg/ipa/ipcp-agg-10.c: New test.
2019-06-13 Martin Sebor <msebor@redhat.com> 2019-06-13 Martin Sebor <msebor@redhat.com>
PR tree-optimization/90662 PR tree-optimization/90662
......
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details -fno-inline" } */
int data1;
int callee1(int *v)
{
if (*v < 2)
return 0;
else
{
int t = data1;
data1 = *v;
*v = t;
return 1;
}
}
int __attribute__((pure)) callee2(int *v)
{
if (*v < 2)
return 0;
else
{
data1 = v[0] + v[2];
return 1;
}
}
int caller1(int c, int *r)
{
int a = 1;
if (c)
return callee1(&a);
else
{
*r = 2;
return callee1(r);
}
}
int data2[200];
int data3;
int __attribute__((const)) gen_cond(int);
int caller2(void)
{
int i, j;
int sum = 0;
int a[8];
a[0] = 3;
for (i = 0; i < 100; i++)
{
if (gen_cond (i))
continue;
a[2] = 4;
for (j = 0; j < 100; j++)
{
data2[i + j] = (i ^ j) + data3;
sum += callee2(a);
}
}
return sum;
}
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 1" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 2" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 3" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 64, cst: 4" 1 "cp" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment