From: Martin Jambor Date: Wed, 18 May 2016 16:38:56 +0000 (+0200) Subject: Indirect inlining of targets from references of global constants X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=91bb9f80e58963d65a5cd305b438a2d7009cdb09;p=gcc.git Indirect inlining of targets from references of global constants 2016-05-18 Martin Jambor 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8d09b501d6b..3a8e9a76373 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +2016-05-18 Martin Jambor + + 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 PR rtl-optimization/71150 diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 8ad9f456a22..ecafe63a536 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -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; diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 5900d4d9158..2183da07b68 100644 --- a/gcc/ipa-cp.c +++ b/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); } diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 4a547a2f188..5d6721813d8 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -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; diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index f02ec474dc9..785f2f43ab7 100644 --- a/gcc/ipa-prop.c +++ b/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 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); diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 4e11ca6d35c..e32d078edb3 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -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, 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 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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0434803c599..d7dc01607da 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2016-05-18 Martin Jambor + + 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 PR c++/69793 diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c new file mode 100644 index 00000000000..8656cb336fa --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c new file mode 100644 index 00000000000..4d64ea7d4ce --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c new file mode 100644 index 00000000000..f82014024d4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c @@ -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" } } */ + diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c new file mode 100644 index 00000000000..917f1389f3e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c new file mode 100644 index 00000000000..7458402c88e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c @@ -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" } } */