Indirect inlining of targets from references of global constants

2016-05-18  Martin Jambor  <mjambor@suse.cz>

	PR ipa/69708
	* cgraph.h (cgraph_indirect_call_info): New field
	guaranteed_unmodified.
	* ipa-cp.c (ipa_get_indirect_edge_target_1): Also pass parameter value
	to ipa_find_agg_cst_for_param, check guaranteed_unmodified when
	appropriate.
	* ipa-inline-analysis.c (evaluate_conditions_for_known_args): Also
	pass the parameter value to ipa_find_agg_cst_for_param.
	* ipa-prop.c (ipa_load_from_parm_agg): New parameter
	guaranteed_unmodified, store AA results there instead of bailing out
	if present.
	(ipa_note_param_call): Also initialize guaranteed_unmodified flag.
	(ipa_analyze_indirect_call_uses): Also set guaranteed_unmodified flag.
	(find_constructor_constant_at_offset): New function.
	(ipa_find_agg_cst_from_init): Likewise.
	(ipa_find_agg_cst_for_param): Also seearch for aggregate values in
	static initializers of contants, report back through a new paameter
	from_global_constant if that was the case.
	(try_make_edge_direct_simple_call): Also pass parameter value to
	ipa_find_agg_cst_for_param, check guaranteed_unmodified when
	appropriate.
	(ipa_write_indirect_edge_info): Stream new flag guaranteed_unmodified.
	(ipa_read_indirect_edge_info): Likewise.
	* ipa-prop.h (ipa_find_agg_cst_for_param): Update declaration.
	(ipa_load_from_parm_agg): Likewise.

testsuite/
	* gcc.dg/ipa/iinline-cstagg-1.c: New test.
	* gcc.dg/ipa/ipcp-cstagg-1.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-2.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-3.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-4.c: Likewise.

From-SVN: r236416
This commit is contained in:
Martin Jambor 2016-05-18 18:38:56 +02:00 committed by Martin Jambor
parent c584aca60c
commit 91bb9f80e5
12 changed files with 476 additions and 38 deletions

View File

@ -1,3 +1,31 @@
2016-05-18 Martin Jambor <mjambor@suse.cz>
PR ipa/69708
* cgraph.h (cgraph_indirect_call_info): New field
guaranteed_unmodified.
* ipa-cp.c (ipa_get_indirect_edge_target_1): Also pass parameter value
to ipa_find_agg_cst_for_param, check guaranteed_unmodified when
appropriate.
* ipa-inline-analysis.c (evaluate_conditions_for_known_args): Also
pass the parameter value to ipa_find_agg_cst_for_param.
* ipa-prop.c (ipa_load_from_parm_agg): New parameter
guaranteed_unmodified, store AA results there instead of bailing out
if present.
(ipa_note_param_call): Also initialize guaranteed_unmodified flag.
(ipa_analyze_indirect_call_uses): Also set guaranteed_unmodified flag.
(find_constructor_constant_at_offset): New function.
(ipa_find_agg_cst_from_init): Likewise.
(ipa_find_agg_cst_for_param): Also seearch for aggregate values in
static initializers of contants, report back through a new paameter
from_global_constant if that was the case.
(try_make_edge_direct_simple_call): Also pass parameter value to
ipa_find_agg_cst_for_param, check guaranteed_unmodified when
appropriate.
(ipa_write_indirect_edge_info): Stream new flag guaranteed_unmodified.
(ipa_read_indirect_edge_info): Likewise.
* ipa-prop.h (ipa_find_agg_cst_for_param): Update declaration.
(ipa_load_from_parm_agg): Likewise.
2016-05-18 Jiong Wang <jiong.wang@arm.com>
PR rtl-optimization/71150

View File

@ -1579,9 +1579,14 @@ struct GTY(()) cgraph_indirect_call_info
unsigned agg_contents : 1;
/* Set when this is a call through a member pointer. */
unsigned member_ptr : 1;
/* When the previous bit is set, this one determines whether the destination
is loaded from a parameter passed by reference. */
/* When the agg_contents bit is set, this one determines whether the
destination is loaded from a parameter passed by reference. */
unsigned by_ref : 1;
/* When the agg_contents bit is set, this one determines whether we can
deduce from the function body that the loaded value from the reference is
never modified between the invocation of the function and the load
point. */
unsigned guaranteed_unmodified : 1;
/* For polymorphic calls this specify whether the virtual table pointer
may have changed in between function entry and the call. */
unsigned vptr_changed : 1;

View File

@ -1999,9 +1999,9 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
if (ie->indirect_info->agg_contents)
{
if (agg_reps)
t = NULL;
if (agg_reps && ie->indirect_info->guaranteed_unmodified)
{
t = NULL;
while (agg_reps)
{
if (agg_reps->index == param_index
@ -2014,15 +2014,22 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
agg_reps = agg_reps->next;
}
}
else if (known_aggs.length () > (unsigned int) param_index)
if (!t)
{
struct ipa_agg_jump_function *agg;
agg = known_aggs[param_index];
t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
ie->indirect_info->by_ref);
if (known_aggs.length () > (unsigned int) param_index)
agg = known_aggs[param_index];
else
agg = NULL;
bool from_global_constant;
t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
ie->indirect_info->offset,
ie->indirect_info->by_ref,
&from_global_constant);
if (!from_global_constant
&& !ie->indirect_info->guaranteed_unmodified)
t = NULL_TREE;
}
else
t = NULL;
}
else
t = known_csts[param_index];
@ -2066,7 +2073,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
{
struct ipa_agg_jump_function *agg;
agg = known_aggs[param_index];
t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
ie->indirect_info->offset,
true);
}

View File

@ -853,7 +853,8 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
if (known_aggs.exists ())
{
agg = known_aggs[c->operand_num];
val = ipa_find_agg_cst_for_param (agg, c->offset, c->by_ref);
val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num],
c->offset, c->by_ref);
}
else
val = NULL_TREE;

View File

@ -930,10 +930,15 @@ parm_ref_data_pass_through_p (struct ipa_func_body_info *fbi, int index,
return !modified;
}
/* Return true if we can prove that OP is a memory reference loading unmodified
data from an aggregate passed as a parameter and if the aggregate is passed
by reference, that the alias type of the load corresponds to the type of the
formal parameter (so that we can rely on this type for TBAA in callers).
/* Return true if we can prove that OP is a memory reference loading
data from an aggregate passed as a parameter.
The function works in two modes. If GUARANTEED_UNMODIFIED is NULL, it return
false if it cannot prove that the value has not been modified before the
load in STMT. If GUARANTEED_UNMODIFIED is not NULL, it will return true even
if it cannot prove the value has not been modified, in that case it will
store false to *GUARANTEED_UNMODIFIED, otherwise it will store true there.
INFO and PARMS_AINFO describe parameters of the current function (but the
latter can be NULL), STMT is the load statement. If function returns true,
*INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset
@ -945,7 +950,7 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
vec<ipa_param_descriptor> descriptors,
gimple *stmt, tree op, int *index_p,
HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
bool *by_ref_p)
bool *by_ref_p, bool *guaranteed_unmodified)
{
int index;
HOST_WIDE_INT size, max_size;
@ -966,6 +971,8 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
*by_ref_p = false;
if (size_p)
*size_p = size;
if (guaranteed_unmodified)
*guaranteed_unmodified = true;
return true;
}
return false;
@ -1002,13 +1009,18 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
index = load_from_unmodified_param (fbi, descriptors, def);
}
if (index >= 0
&& parm_ref_data_preserved_p (fbi, index, stmt, op))
if (index >= 0)
{
bool data_preserved = parm_ref_data_preserved_p (fbi, index, stmt, op);
if (!data_preserved && !guaranteed_unmodified)
return false;
*index_p = index;
*by_ref_p = true;
if (size_p)
*size_p = size;
if (guaranteed_unmodified)
*guaranteed_unmodified = data_preserved;
return true;
}
return false;
@ -1824,6 +1836,7 @@ ipa_note_param_call (struct cgraph_node *node, int param_index,
cs->indirect_info->param_index = param_index;
cs->indirect_info->agg_contents = 0;
cs->indirect_info->member_ptr = 0;
cs->indirect_info->guaranteed_unmodified = 0;
return cs;
}
@ -1905,15 +1918,17 @@ ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call,
int index;
gimple *def = SSA_NAME_DEF_STMT (target);
bool guaranteed_unmodified;
if (gimple_assign_single_p (def)
&& ipa_load_from_parm_agg (fbi, info->descriptors, def,
gimple_assign_rhs1 (def), &index, &offset,
NULL, &by_ref))
NULL, &by_ref, &guaranteed_unmodified))
{
struct cgraph_edge *cs = ipa_note_param_call (fbi->node, index, call);
cs->indirect_info->offset = offset;
cs->indirect_info->agg_contents = 1;
cs->indirect_info->by_ref = by_ref;
cs->indirect_info->guaranteed_unmodified = guaranteed_unmodified;
return;
}
@ -2014,6 +2029,7 @@ ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call,
cs->indirect_info->offset = offset;
cs->indirect_info->agg_contents = 1;
cs->indirect_info->member_ptr = 1;
cs->indirect_info->guaranteed_unmodified = 1;
}
return;
@ -2701,18 +2717,125 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
return ie;
}
/* Retrieve value from aggregate jump function AGG for the given OFFSET or
return NULL if there is not any. BY_REF specifies whether the value has to
be passed by reference or by value. */
/* Attempt to locate an interprocedural constant at a given REQ_OFFSET in
CONSTRUCTOR and return it. Return NULL if the search fails for some
reason. */
static tree
find_constructor_constant_at_offset (tree constructor, HOST_WIDE_INT req_offset)
{
tree type = TREE_TYPE (constructor);
if (TREE_CODE (type) != ARRAY_TYPE
&& TREE_CODE (type) != RECORD_TYPE)
return NULL;
unsigned ix;
tree index, val;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
{
HOST_WIDE_INT elt_offset;
if (TREE_CODE (type) == ARRAY_TYPE)
{
offset_int off;
tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
if (index)
{
off = wi::to_offset (index);
if (TYPE_DOMAIN (type) && TYPE_MIN_VALUE (TYPE_DOMAIN (type)))
{
tree low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
off = wi::sext (off - wi::to_offset (low_bound),
TYPE_PRECISION (TREE_TYPE (index)));
}
off *= wi::to_offset (unit_size);
}
else
off = wi::to_offset (unit_size) * ix;
off = wi::lshift (off, LOG2_BITS_PER_UNIT);
if (!wi::fits_shwi_p (off) || wi::neg_p (off))
continue;
elt_offset = off.to_shwi ();
}
else if (TREE_CODE (type) == RECORD_TYPE)
{
gcc_checking_assert (index && TREE_CODE (index) == FIELD_DECL);
if (DECL_BIT_FIELD (index))
continue;
elt_offset = int_bit_position (index);
}
else
gcc_unreachable ();
if (elt_offset > req_offset)
return NULL;
if (TREE_CODE (val) == CONSTRUCTOR)
return find_constructor_constant_at_offset (val,
req_offset - elt_offset);
if (elt_offset == req_offset
&& is_gimple_reg_type (TREE_TYPE (val))
&& is_gimple_ip_invariant (val))
return val;
}
return NULL;
}
/* Check whether SCALAR could be used to look up an aggregate interprocedural
invariant from a static constructor and if so, return it. Otherwise return
NULL. */
static tree
ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref)
{
if (by_ref)
{
if (TREE_CODE (scalar) != ADDR_EXPR)
return NULL;
scalar = TREE_OPERAND (scalar, 0);
}
if (TREE_CODE (scalar) != VAR_DECL
|| !is_global_var (scalar)
|| !TREE_READONLY (scalar)
|| !DECL_INITIAL (scalar)
|| TREE_CODE (DECL_INITIAL (scalar)) != CONSTRUCTOR)
return NULL;
return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset);
}
/* Retrieve value from aggregate jump function AGG or static initializer of
SCALAR (which can be NULL) for the given OFFSET or return NULL if there is
none. BY_REF specifies whether the value has to be passed by reference or
by value. If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it points
to is set to true if the value comes from an initializer of a constant. */
tree
ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
HOST_WIDE_INT offset, bool by_ref)
ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
HOST_WIDE_INT offset, bool by_ref,
bool *from_global_constant)
{
struct ipa_agg_jf_item *item;
int i;
if (by_ref != agg->by_ref)
if (scalar)
{
tree res = ipa_find_agg_cst_from_init (scalar, offset, by_ref);
if (res)
{
if (from_global_constant)
*from_global_constant = true;
return res;
}
}
if (!agg
|| by_ref != agg->by_ref)
return NULL;
FOR_EACH_VEC_SAFE_ELT (agg->items, i, item)
@ -2721,6 +2844,8 @@ ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
/* Currently we do not have clobber values, return NULL for them once
we do. */
gcc_checking_assert (is_gimple_ip_invariant (item->value));
if (from_global_constant)
*from_global_constant = false;
return item->value;
}
return NULL;
@ -2819,13 +2944,21 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
struct cgraph_edge *cs;
tree target;
bool agg_contents = ie->indirect_info->agg_contents;
if (ie->indirect_info->agg_contents)
target = ipa_find_agg_cst_for_param (&jfunc->agg,
ie->indirect_info->offset,
ie->indirect_info->by_ref);
tree scalar = ipa_value_from_jfunc (new_root_info, jfunc);
if (agg_contents)
{
bool from_global_constant;
target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
ie->indirect_info->offset,
ie->indirect_info->by_ref,
&from_global_constant);
if (target
&& !from_global_constant
&& !ie->indirect_info->guaranteed_unmodified)
return NULL;
}
else
target = ipa_value_from_jfunc (new_root_info, jfunc);
target = scalar;
if (!target)
return NULL;
cs = ipa_make_edge_direct_to_target (ie, target);
@ -2893,7 +3026,9 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
{
tree vtable;
unsigned HOST_WIDE_INT offset;
tree t = ipa_find_agg_cst_for_param (&jfunc->agg,
tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc)
: NULL;
tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
ie->indirect_info->offset,
true);
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
@ -4560,6 +4695,7 @@ ipa_write_indirect_edge_info (struct output_block *ob,
bp_pack_value (&bp, ii->agg_contents, 1);
bp_pack_value (&bp, ii->member_ptr, 1);
bp_pack_value (&bp, ii->by_ref, 1);
bp_pack_value (&bp, ii->guaranteed_unmodified, 1);
bp_pack_value (&bp, ii->vptr_changed, 1);
streamer_write_bitpack (&bp);
if (ii->agg_contents || ii->polymorphic)
@ -4592,6 +4728,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib,
ii->agg_contents = bp_unpack_value (&bp, 1);
ii->member_ptr = bp_unpack_value (&bp, 1);
ii->by_ref = bp_unpack_value (&bp, 1);
ii->guaranteed_unmodified = bp_unpack_value (&bp, 1);
ii->vptr_changed = bp_unpack_value (&bp, 1);
if (ii->agg_contents || ii->polymorphic)
ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);

View File

@ -636,11 +636,14 @@ tree ipa_impossible_devirt_target (struct cgraph_edge *, tree);
void ipa_analyze_node (struct cgraph_node *);
/* Aggregate jump function related functions. */
tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
bool);
bool ipa_load_from_parm_agg (struct ipa_func_body_info *,
vec<ipa_param_descriptor>, gimple *, tree, int *,
HOST_WIDE_INT *, HOST_WIDE_INT *, bool *);
tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
HOST_WIDE_INT offset, bool by_ref,
bool *from_global_constant = NULL);
bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
vec<ipa_param_descriptor> descriptors,
gimple *stmt, tree op, int *index_p,
HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
bool *by_ref, bool *guaranteed_unmodified = NULL);
/* Debugging interface. */
void ipa_print_node_params (FILE *, struct cgraph_node *node);

View File

@ -1,3 +1,12 @@
2016-05-18 Martin Jambor <mjambor@suse.cz>
PR ipa/69708
* gcc.dg/ipa/iinline-cstagg-1.c: New test.
* gcc.dg/ipa/ipcp-cstagg-1.c: Likewise.
* gcc.dg/ipa/ipcp-cstagg-2.c: Likewise.
* gcc.dg/ipa/ipcp-cstagg-3.c: Likewise.
* gcc.dg/ipa/ipcp-cstagg-4.c: Likewise.
2016-05-18 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/69793

View File

@ -0,0 +1,37 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-sra -fno-ipa-cp" } */
typedef struct S
{
int add_offset;
int (*call)(int);
} S;
static int
bar (const S *f, int x)
{
x = f->call(x);
return x;
}
static int
thisisthetarget (int x)
{
return x * x;
}
static const S s = {16, thisisthetarget};
int
outerfunction (int x)
{
return bar (&s, x);
}
int
obfuscate (int x)
{
return bar ((S *) 0, x);
}
/* { dg-final { scan-ipa-dump "thisisthetarget\[^\\n\]*inline copy in outerfunction" "inline" } } */

View File

@ -0,0 +1,42 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details" } */
typedef struct S
{
int add_offset;
int (*call)(int);
} S;
extern const S *gs;
static int __attribute__((noinline))
bar (const S *f, int x)
{
x = f->call(x);
x = f->call(x);
x = f->call(x);
gs = f;
return x;
}
static int
sq (int x)
{
return x * x;
}
static const S s = {16, sq};
int
g (int x)
{
return bar (&s, x);
}
int
obfuscate (int x)
{
return bar ((S *) 0, x);
}
/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */

View File

@ -0,0 +1,46 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details" } */
typedef struct S
{
int (*call)(int);
} S;
static int __attribute__((noinline))
bar (const S *f, int x)
{
x = f->call(x);
return x;
}
extern void impossible_aa (void);
static int __attribute__((noinline))
baz (const S *f, int x)
{
impossible_aa ();
return bar (f, x);
}
static int
sq (int x)
{
return x * x;
}
static const S s = {sq};
int
g (int x)
{
return baz (&s, x);
}
int
obfuscate (int x)
{
return baz ((S *) 0, x);
}
/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */

View File

@ -0,0 +1,58 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details" } */
#define N 4
typedef int (* const A[N])(int);
extern const A *ga;
static int __attribute__((noinline))
bar (const A *f, int x)
{
x = (*f)[2](x);
x = (*f)[2](x);
x = (*f)[2](x);
ga = f;
return x;
}
static int
zero (int x)
{
return 0;
}
static int
addone (int x)
{
return x + 1;
}
static int
sq (int x)
{
return x * x;
}
static int
cube (int x)
{
return x * x * x;
}
static const A a = {zero, addone, sq, cube};
int
g (int x)
{
return bar (&a, x);
}
int
obfuscate (int x)
{
return bar ((A *) 0, x);
}
/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */

View File

@ -0,0 +1,64 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details" } */
#define N 4
typedef int (* const A[N])(int);
typedef struct S
{
int add_offset;
A a;
} S;
extern const S *gs;
static int __attribute__((noinline))
bar (const S *f, int x)
{
gs = f;
x = f->a[2](x);
x = f->a[2](x);
x = f->a[2](x);
return x;
}
static int
zero (int x)
{
return 0;
}
static int
addone (int x)
{
return x + 1;
}
static int
sq (int x)
{
return x * x;
}
static int
cube (int x)
{
return x * x * x;
}
static const S s = {64, {zero, addone, sq, cube}};
int
g (int x)
{
return bar (&s, x);
}
int
obfuscate (int x)
{
return bar ((S *) 0, x);
}
/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */