Generalized value pass-through for self-recusive function (PR ipa/93203)
Besides simple pass-through (aggregate) jump function, arithmetic (aggregate) jump function could also bring same (aggregate) value as parameter passed-in for self-feeding recursive call. For example, f1 (int i) /* normal jump function */ { f1 (i & 1); } Suppose i is 0, recursive propagation via (i & 1) also gets 0, which can be seen as a simple pass-through of i. f2 (int *p) /* aggregate jump function */ { int t = *p & 1; f2 (&t); } Likewise, if *p is 0, (*p & 1) is also 0, and &t is an aggregate simple pass-through of p. 2020-02-10 Feng Xue <fxue@os.amperecomputing.com> PR ipa/93203 * ipa-cp.c (ipcp_lattice::add_value): Add source with same call edge but different source value. (adjust_callers_for_value_intersection): New function. (gather_edges_for_value): Adjust order of callers to let a non-self-recursive caller be the first element. (self_recursive_pass_through_p): Add a new parameter "simple", and check generalized self-recursive pass-through jump function. (self_recursive_agg_pass_through_p): Likewise. (find_more_scalar_values_for_callers_subset): Compute value from pass-through jump function for self-recursive. (intersect_with_plats): Cleanup previous implementation code for value itersection with self-recursive call edge. (intersect_with_agg_replacements): Likewise. (intersect_aggregates_with_edge): Deduce value from pass-through jump function for self-recursive call edge. Cleanup previous implementation code for value intersection with self-recursive call edge. (decide_whether_version_node): Remove dead callers and adjust order to let a non-self-recursive caller be the first element. PR ipa/93203 * g++.dg/ipa/pr93203.C: New test.
This commit is contained in:
parent
04c3a1f2c6
commit
a0f6a8cb41
@ -1,3 +1,25 @@
|
||||
2020-02-10 Feng Xue <fxue@os.amperecomputing.com>
|
||||
|
||||
PR ipa/93203
|
||||
* ipa-cp.c (ipcp_lattice::add_value): Add source with same call edge
|
||||
but different source value.
|
||||
(adjust_callers_for_value_intersection): New function.
|
||||
(gather_edges_for_value): Adjust order of callers to let a
|
||||
non-self-recursive caller be the first element.
|
||||
(self_recursive_pass_through_p): Add a new parameter "simple", and
|
||||
check generalized self-recursive pass-through jump function.
|
||||
(self_recursive_agg_pass_through_p): Likewise.
|
||||
(find_more_scalar_values_for_callers_subset): Compute value from
|
||||
pass-through jump function for self-recursive.
|
||||
(intersect_with_plats): Cleanup previous implementation code for value
|
||||
itersection with self-recursive call edge.
|
||||
(intersect_with_agg_replacements): Likewise.
|
||||
(intersect_aggregates_with_edge): Deduce value from pass-through jump
|
||||
function for self-recursive call edge. Cleanup previous implementation
|
||||
code for value intersection with self-recursive call edge.
|
||||
(decide_whether_version_node): Remove dead callers and adjust order
|
||||
to let a non-self-recursive caller be the first element.
|
||||
|
||||
2020-02-09 Uroš Bizjak <ubizjak@gmail.com>
|
||||
|
||||
* recog.c: Move pass_split_before_sched2 code in front of
|
||||
|
194
gcc/ipa-cp.c
194
gcc/ipa-cp.c
@ -1850,7 +1850,7 @@ ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
|
||||
{
|
||||
ipcp_value_source<valtype> *s;
|
||||
for (s = val->sources; s; s = s->next)
|
||||
if (s->cs == cs)
|
||||
if (s->cs == cs && s->val == src_val)
|
||||
break;
|
||||
if (s)
|
||||
return false;
|
||||
@ -4204,6 +4204,33 @@ get_info_about_necessary_edges (ipcp_value<valtype> *val, cgraph_node *dest,
|
||||
return hot;
|
||||
}
|
||||
|
||||
/* Given a NODE, and a set of its CALLERS, try to adjust order of the callers
|
||||
to let a non-self-recursive caller be the first element. Thus, we can
|
||||
simplify intersecting operations on values that arrive from all of these
|
||||
callers, especially when there exists self-recursive call. Return true if
|
||||
this kind of adjustment is possible. */
|
||||
|
||||
static bool
|
||||
adjust_callers_for_value_intersection (vec<cgraph_edge *> callers,
|
||||
cgraph_node *node)
|
||||
{
|
||||
for (unsigned i = 0; i < callers.length (); i++)
|
||||
{
|
||||
cgraph_edge *cs = callers[i];
|
||||
|
||||
if (cs->caller != node)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
callers[i] = callers[0];
|
||||
callers[0] = cs;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return a vector of incoming edges that do bring value VAL to node DEST. It
|
||||
is assumed their number is known and equal to CALLER_COUNT. */
|
||||
|
||||
@ -4227,6 +4254,9 @@ gather_edges_for_value (ipcp_value<valtype> *val, cgraph_node *dest,
|
||||
}
|
||||
}
|
||||
|
||||
if (caller_count > 1)
|
||||
adjust_callers_for_value_intersection (ret, dest);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -4238,7 +4268,6 @@ get_replacement_map (class ipa_node_params *info, tree value, int parm_num)
|
||||
{
|
||||
struct ipa_replace_map *replace_map;
|
||||
|
||||
|
||||
replace_map = ggc_alloc<ipa_replace_map> ();
|
||||
if (dump_file)
|
||||
{
|
||||
@ -4589,36 +4618,40 @@ create_specialized_node (struct cgraph_node *node,
|
||||
}
|
||||
|
||||
/* Return true, if JFUNC, which describes a i-th parameter of call CS, is a
|
||||
simple no-operation pass-through function to itself. */
|
||||
pass-through function to itself. When SIMPLE is true, further check if
|
||||
JFUNC is a simple no-operation pass-through. */
|
||||
|
||||
static bool
|
||||
self_recursive_pass_through_p (cgraph_edge *cs, ipa_jump_func *jfunc, int i)
|
||||
self_recursive_pass_through_p (cgraph_edge *cs, ipa_jump_func *jfunc, int i,
|
||||
bool simple = true)
|
||||
{
|
||||
enum availability availability;
|
||||
if (cs->caller == cs->callee->function_symbol (&availability)
|
||||
&& availability > AVAIL_INTERPOSABLE
|
||||
&& jfunc->type == IPA_JF_PASS_THROUGH
|
||||
&& ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR
|
||||
&& (!simple || ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
|
||||
&& ipa_get_jf_pass_through_formal_id (jfunc) == i)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true, if JFUNC, which describes a part of an aggregate represented
|
||||
or pointed to by the i-th parameter of call CS, is a simple no-operation
|
||||
pass-through function to itself. */
|
||||
or pointed to by the i-th parameter of call CS, is a pass-through function
|
||||
to itself. When SIMPLE is true, further check if JFUNC is a simple
|
||||
no-operation pass-through. */
|
||||
|
||||
static bool
|
||||
self_recursive_agg_pass_through_p (cgraph_edge *cs, ipa_agg_jf_item *jfunc,
|
||||
int i)
|
||||
int i, bool simple = true)
|
||||
{
|
||||
enum availability availability;
|
||||
if (cs->caller == cs->callee->function_symbol (&availability)
|
||||
&& availability > AVAIL_INTERPOSABLE
|
||||
&& jfunc->jftype == IPA_JF_LOAD_AGG
|
||||
&& jfunc->offset == jfunc->value.load_agg.offset
|
||||
&& jfunc->value.pass_through.operation == NOP_EXPR
|
||||
&& jfunc->value.pass_through.formal_id == i)
|
||||
&& (!simple || jfunc->value.pass_through.operation == NOP_EXPR)
|
||||
&& jfunc->value.pass_through.formal_id == i
|
||||
&& useless_type_conversion_p (jfunc->value.load_agg.type, jfunc->type))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -4650,9 +4683,6 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
|
||||
struct ipa_jump_func *jump_func;
|
||||
tree t;
|
||||
|
||||
if (IPA_NODE_REF (cs->caller) && IPA_NODE_REF (cs->caller)->node_dead)
|
||||
continue;
|
||||
|
||||
if (!IPA_EDGE_REF (cs)
|
||||
|| i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
|
||||
|| (i == 0
|
||||
@ -4662,10 +4692,30 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
|
||||
break;
|
||||
}
|
||||
jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
|
||||
if (self_recursive_pass_through_p (cs, jump_func, i))
|
||||
continue;
|
||||
|
||||
t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func, type);
|
||||
/* Besides simple pass-through jump function, arithmetic jump
|
||||
function could also introduce argument-direct-pass-through for
|
||||
self-feeding recursive call. For example,
|
||||
|
||||
fn (int i)
|
||||
{
|
||||
fn (i & 1);
|
||||
}
|
||||
|
||||
Given that i is 0, recursive propagation via (i & 1) also gets
|
||||
0. */
|
||||
if (self_recursive_pass_through_p (cs, jump_func, i, false))
|
||||
{
|
||||
gcc_assert (newval);
|
||||
t = ipa_get_jf_arith_result (
|
||||
ipa_get_jf_pass_through_operation (jump_func),
|
||||
newval,
|
||||
ipa_get_jf_pass_through_operand (jump_func),
|
||||
type);
|
||||
}
|
||||
else
|
||||
t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func,
|
||||
type);
|
||||
if (!t
|
||||
|| (newval
|
||||
&& !values_equal_for_ipcp_p (t, newval))
|
||||
@ -4814,19 +4864,12 @@ intersect_with_plats (class ipcp_param_lattices *plats,
|
||||
break;
|
||||
if (aglat->offset - offset == item->offset)
|
||||
{
|
||||
gcc_checking_assert (item->value);
|
||||
if (aglat->is_single_const ())
|
||||
{
|
||||
tree value = aglat->values->value;
|
||||
|
||||
if (values_equal_for_ipcp_p (item->value, value))
|
||||
found = true;
|
||||
else if (item->value == error_mark_node)
|
||||
{
|
||||
/* Replace unknown place holder value with real one. */
|
||||
item->value = value;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -4895,12 +4938,6 @@ intersect_with_agg_replacements (struct cgraph_node *node, int index,
|
||||
{
|
||||
if (values_equal_for_ipcp_p (item->value, av->value))
|
||||
found = true;
|
||||
else if (item->value == error_mark_node)
|
||||
{
|
||||
/* Replace place holder value with real one. */
|
||||
item->value = av->value;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -5005,31 +5042,16 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
|
||||
for (unsigned i = 0; i < jfunc->agg.items->length (); i++)
|
||||
{
|
||||
struct ipa_agg_jf_item *agg_item = &(*jfunc->agg.items)[i];
|
||||
struct ipa_agg_value agg_value;
|
||||
|
||||
if (self_recursive_agg_pass_through_p (cs, agg_item, index))
|
||||
tree value = ipa_agg_value_from_node (caller_info, cs->caller,
|
||||
agg_item);
|
||||
if (value)
|
||||
{
|
||||
/* For a self-recursive call, if aggregate jump function is a
|
||||
simple pass-through, the exact value that it stands for is
|
||||
not known at this point, which must comes from other call
|
||||
sites. But we still need to add a place holder in value
|
||||
sets to indicate it, here we use error_mark_node to
|
||||
represent the special unknown value, which will be replaced
|
||||
with real one during later intersecting operations. */
|
||||
agg_value.value = error_mark_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree value = ipa_agg_value_from_node (caller_info, cs->caller,
|
||||
agg_item);
|
||||
if (!value)
|
||||
continue;
|
||||
struct ipa_agg_value agg_value;
|
||||
|
||||
agg_value.value = value;
|
||||
agg_value.offset = agg_item->offset;
|
||||
inter.safe_push (agg_value);
|
||||
}
|
||||
|
||||
agg_value.offset = agg_item->offset;
|
||||
inter.safe_push (agg_value);
|
||||
}
|
||||
else
|
||||
FOR_EACH_VEC_ELT (inter, k, item)
|
||||
@ -5050,25 +5072,32 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
|
||||
{
|
||||
tree value;
|
||||
|
||||
if (self_recursive_agg_pass_through_p (cs, ti, index))
|
||||
{
|
||||
/* A simple aggregate pass-through in self-recursive
|
||||
call should lead to same value. */
|
||||
found = true;
|
||||
}
|
||||
else if ((value = ipa_agg_value_from_node (caller_info,
|
||||
cs->caller, ti)))
|
||||
{
|
||||
if (values_equal_for_ipcp_p (item->value, value))
|
||||
found = true;
|
||||
else if (item->value == error_mark_node)
|
||||
{
|
||||
/* Replace unknown place holder value with real
|
||||
one. */
|
||||
item->value = value;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
/* Besides simple pass-through aggregate jump function,
|
||||
arithmetic aggregate jump function could also bring
|
||||
same aggregate value as parameter passed-in for
|
||||
self-feeding recursive call. For example,
|
||||
|
||||
fn (int *i)
|
||||
{
|
||||
int j = *i & 1;
|
||||
fn (&j);
|
||||
}
|
||||
|
||||
Given that *i is 0, recursive propagation via (*i & 1)
|
||||
also gets 0. */
|
||||
if (self_recursive_agg_pass_through_p (cs, ti, index,
|
||||
false))
|
||||
value = ipa_get_jf_arith_result (
|
||||
ti->value.pass_through.operation,
|
||||
item->value,
|
||||
ti->value.pass_through.operand,
|
||||
ti->type);
|
||||
else
|
||||
value = ipa_agg_value_from_node (caller_info,
|
||||
cs->caller, ti);
|
||||
|
||||
if (value && values_equal_for_ipcp_p (item->value, value))
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
l++;
|
||||
@ -5144,9 +5173,6 @@ find_aggregate_values_for_callers_subset (struct cgraph_node *node,
|
||||
if (!item->value)
|
||||
continue;
|
||||
|
||||
/* All values must be real values, not unknown place holders. */
|
||||
gcc_assert (item->value != error_mark_node);
|
||||
|
||||
v = ggc_alloc<ipa_agg_replacement_value> ();
|
||||
v->index = i;
|
||||
v->offset = item->offset;
|
||||
@ -5542,13 +5568,33 @@ decide_whether_version_node (struct cgraph_node *node)
|
||||
if (info->do_clone_for_all_contexts)
|
||||
{
|
||||
struct cgraph_node *clone;
|
||||
vec<cgraph_edge *> callers;
|
||||
vec<cgraph_edge *> callers = node->collect_callers ();
|
||||
|
||||
for (int i = callers.length () - 1; i >= 0; i--)
|
||||
{
|
||||
cgraph_edge *cs = callers[i];
|
||||
class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
|
||||
|
||||
if (caller_info && caller_info->node_dead)
|
||||
callers.unordered_remove (i);
|
||||
}
|
||||
|
||||
if (!adjust_callers_for_value_intersection (callers, node))
|
||||
{
|
||||
/* If node is not called by anyone, or all its caller edges are
|
||||
self-recursive, the node is not really be in use, no need to
|
||||
do cloning. */
|
||||
callers.release ();
|
||||
known_csts.release ();
|
||||
known_contexts.release ();
|
||||
info->do_clone_for_all_contexts = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " - Creating a specialized node of %s "
|
||||
"for all known contexts.\n", node->dump_name ());
|
||||
|
||||
callers = node->collect_callers ();
|
||||
find_more_scalar_values_for_callers_subset (node, known_csts, callers);
|
||||
find_more_contexts_for_caller_subset (node, &known_contexts, callers);
|
||||
ipa_agg_replacement_value *aggvals
|
||||
|
@ -1,3 +1,8 @@
|
||||
2020-02-10 Feng Xue <fxue@os.amperecomputing.com>
|
||||
|
||||
PR ipa/93203
|
||||
* g++.dg/ipa/pr93203.C: New test.
|
||||
|
||||
2020-02-09 Uroš Bizjak <ubizjak@gmail.com>
|
||||
|
||||
* gcc.target/i386/pr91333.c (dg-do): Fix target selector.
|
||||
|
95
gcc/testsuite/g++.dg/ipa/pr93203.C
Normal file
95
gcc/testsuite/g++.dg/ipa/pr93203.C
Normal file
@ -0,0 +1,95 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -w -std=gnu++11" } */
|
||||
|
||||
class a {
|
||||
public:
|
||||
a(char *);
|
||||
};
|
||||
class ad {
|
||||
public:
|
||||
ad(a *);
|
||||
};
|
||||
class b {};
|
||||
using ah = class ak {
|
||||
using al = char;
|
||||
|
||||
public:
|
||||
ak(b) : ak(0) {}
|
||||
ak an() { return ap & 1; }
|
||||
al ap;
|
||||
ak(al af) : ap(af) {}
|
||||
};
|
||||
struct at {
|
||||
ah au;
|
||||
int av;
|
||||
char aw;
|
||||
char ax;
|
||||
};
|
||||
class az {
|
||||
public:
|
||||
struct ba {
|
||||
void bb(ak am) {
|
||||
ak bc = am.an();
|
||||
bb(bc);
|
||||
}
|
||||
};
|
||||
void bd(ak am) { be.bb(am); }
|
||||
ba be;
|
||||
};
|
||||
class bg {
|
||||
public:
|
||||
int bh;
|
||||
at bi;
|
||||
};
|
||||
int bj;
|
||||
int *bk;
|
||||
int c;
|
||||
class bl {
|
||||
bool bm();
|
||||
bg bn;
|
||||
az bo;
|
||||
int e;
|
||||
int bq;
|
||||
};
|
||||
class bs {
|
||||
public:
|
||||
bs(int *, ah *, char *, char *, int);
|
||||
};
|
||||
template < typename bt > class bu : bs {
|
||||
using bv = typename bt::bv;
|
||||
|
||||
public:
|
||||
template < typename... bx >
|
||||
bu(a *by, int *bz, at body, bx...)
|
||||
: bs(bz, &body.au, &body.aw, &body.ax, body.av), ca(bx()...), cb(by),
|
||||
cc(by), cd(by), ce(by) {}
|
||||
void cf() {
|
||||
auto cg = ch();
|
||||
auto ci = *cj();
|
||||
ca.ck(this, cg, &ci);
|
||||
}
|
||||
bt ca;
|
||||
ad cb;
|
||||
ad cc;
|
||||
ad cd;
|
||||
ad ce;
|
||||
bv *cj();
|
||||
bv ch();
|
||||
};
|
||||
class cl {
|
||||
public:
|
||||
using bv = struct {};
|
||||
cl(az *, int, int, int, int, a *, int, int **);
|
||||
void ck(bs *, bv, bv *) {
|
||||
b d;
|
||||
ak ci(d);
|
||||
bo.bd(ci);
|
||||
}
|
||||
az bo;
|
||||
};
|
||||
bool bl::bm() {
|
||||
a by("");
|
||||
bu< cl > cn(&by, &bj, bn.bi, &bo, c, bn.bh, e, 0, &by, bq, &bk);
|
||||
cn.cf();
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ main (int argc, char *argv[])
|
||||
}
|
||||
|
||||
|
||||
/* { dg-final { scan-ipa-dump "Creating a specialized node of f.*for all known contexts" "cp" } } */
|
||||
/* { dg-final { scan-ipa-dump "Creating a specialized node of f" "cp" } } */
|
||||
/* { dg-final { scan-ipa-dump "replacing param .0 a with const 7" "cp" } } */
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user