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:
parent
c584aca60c
commit
91bb9f80e5
@ -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
|
||||
|
@ -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;
|
||||
|
26
gcc/ipa-cp.c
26
gcc/ipa-cp.c
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
179
gcc/ipa-prop.c
179
gcc/ipa-prop.c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
37
gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c
Normal file
37
gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c
Normal 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" } } */
|
42
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c
Normal file
42
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c
Normal 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" } } */
|
46
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c
Normal file
46
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c
Normal 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" } } */
|
||||
|
58
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c
Normal file
58
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c
Normal 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" } } */
|
64
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c
Normal file
64
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c
Normal 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" } } */
|
Loading…
Reference in New Issue
Block a user