From 85942f45e92cf0d42196018892633944a234ab57 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Mon, 3 Feb 2014 21:41:30 +0100 Subject: [PATCH] re PR ipa/59831 (ice in cgraph_speculative_call_info with -O3) PR ipa/59831 * g++.dg/ipa/devirt-24.C: New testcase. * ipa-cp.c (ipa_get_indirect_edge_target_1): Give up on -fno-devirtualize; Try to devirtualize by the knowledge of virtual table pointer given by aggregate propagation. * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. ipa_print_node_jump_functions): Dump also offset that is relevant for polymorphic calls. (determine_known_aggregate_parts): Add arg_type parameter; use it instead of determining the type from pointer type. (ipa_compute_jump_functions_for_edge): Update call of determine_known_aggregate_parts. * gimple-fold.c (gimple_get_virt_method_for_vtable): Break out from ... (gimple_get_virt_method_for_binfo): ... here; simplify using vtable_pointer_value_to_vtable. * gimple-fold.h (gimple_get_virt_method_for_vtable): Declare. * ipa-devirt.c (subbinfo_with_vtable_at_offset): Turn OFFSET parameter to unsigned HOST_WIDE_INT; Use vtable_pointer_value_to_vtable. (vtable_pointer_value_to_vtable): Break out from ...; handle also POINTER_PLUS_EXPR. (vtable_pointer_value_to_binfo): ... here. * ipa-utils.h (vtable_pointer_value_to_vtable): Declare. From-SVN: r207439 --- gcc/ChangeLog | 24 +++++++++ gcc/gimple-fold.c | 54 ++++++++++--------- gcc/gimple-fold.h | 2 + gcc/ipa-cp.c | 43 ++++++++++++++- gcc/ipa-devirt.c | 81 ++++++++++++++++++++-------- gcc/ipa-prop.c | 51 +++++++++++++----- gcc/ipa-utils.h | 1 + gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/g++.dg/ipa/devirt-24.C | 42 +++++++++++++++ 9 files changed, 241 insertions(+), 62 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-24.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c86daa88403..118c42ee83a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2014-02-03 Jan Hubicka + + PR ipa/59831 + * ipa-cp.c (ipa_get_indirect_edge_target_1): Give up on -fno-devirtualize; + Try to devirtualize by the knowledge of virtual table pointer given by + aggregate propagation. + * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. + ipa_print_node_jump_functions): Dump also offset that + is relevant for polymorphic calls. + (determine_known_aggregate_parts): Add arg_type parameter; use it + instead of determining the type from pointer type. + (ipa_compute_jump_functions_for_edge): Update call of + determine_known_aggregate_parts. + * gimple-fold.c (gimple_get_virt_method_for_vtable): Break out from ... + (gimple_get_virt_method_for_binfo): ... here; simplify using + vtable_pointer_value_to_vtable. + * gimple-fold.h (gimple_get_virt_method_for_vtable): Declare. + * ipa-devirt.c (subbinfo_with_vtable_at_offset): Turn OFFSET parameter + to unsigned HOST_WIDE_INT; Use vtable_pointer_value_to_vtable. + (vtable_pointer_value_to_vtable): Break out from ...; handle also + POINTER_PLUS_EXPR. + (vtable_pointer_value_to_binfo): ... here. + * ipa-utils.h (vtable_pointer_value_to_vtable): Declare. + 2014-02-03 Teresa Johnson * tree-vect-slp.c (vect_supported_load_permutation_p): Avoid diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 569b49225b6..faea68e457b 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -3236,33 +3236,16 @@ fold_const_aggregate_ref (tree t) return fold_const_aggregate_ref_1 (t, NULL); } -/* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN - is integer form of OBJ_TYPE_REF_TOKEN of the reference expression. - KNOWN_BINFO carries the binfo describing the true type of - OBJ_TYPE_REF_OBJECT(REF). */ +/* Lookup virtual method with index TOKEN in a virtual table V + at OFFSET. */ tree -gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) +gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, + tree v, + unsigned HOST_WIDE_INT offset) { - unsigned HOST_WIDE_INT offset, size; - tree v, fn, vtable, init; - - vtable = v = BINFO_VTABLE (known_binfo); - /* If there is no virtual methods table, leave the OBJ_TYPE_REF alone. */ - if (!v) - return NULL_TREE; - - if (TREE_CODE (v) == POINTER_PLUS_EXPR) - { - offset = tree_to_uhwi (TREE_OPERAND (v, 1)) * BITS_PER_UNIT; - v = TREE_OPERAND (v, 0); - } - else - offset = 0; - - if (TREE_CODE (v) != ADDR_EXPR) - return NULL_TREE; - v = TREE_OPERAND (v, 0); + tree vtable = v, init, fn; + unsigned HOST_WIDE_INT size; if (TREE_CODE (v) != VAR_DECL || !DECL_VIRTUAL_P (v)) @@ -3281,6 +3264,7 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) } gcc_checking_assert (TREE_CODE (TREE_TYPE (v)) == ARRAY_TYPE); size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (TREE_TYPE (v)))); + offset *= BITS_PER_UNIT; offset += token * size; fn = fold_ctor_reference (TREE_TYPE (TREE_TYPE (v)), init, offset, size, v); @@ -3306,6 +3290,28 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) return fn; } +/* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN + is integer form of OBJ_TYPE_REF_TOKEN of the reference expression. + KNOWN_BINFO carries the binfo describing the true type of + OBJ_TYPE_REF_OBJECT(REF). */ + +tree +gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) +{ + unsigned HOST_WIDE_INT offset; + tree v; + + v = BINFO_VTABLE (known_binfo); + /* If there is no virtual methods table, leave the OBJ_TYPE_REF alone. */ + if (!v) + return NULL_TREE; + + if (!vtable_pointer_value_to_vtable (v, &v, &offset)) + return NULL_TREE; + + return gimple_get_virt_method_for_vtable (token, v, offset); +} + /* Return true iff VAL is a gimple expression that is known to be non-negative. Restricted to floating-point inputs. */ diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 886cd02c4dd..3dbce9cf415 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -38,6 +38,8 @@ extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree)); extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree)); extern tree fold_const_aggregate_ref (tree); extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); +extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree, + unsigned HOST_WIDE_INT); extern bool gimple_val_nonnegative_real_p (tree); extern tree gimple_fold_indirect_ref (tree); extern bool arith_code_with_undefined_signed_overflow (tree_code); diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 10fa4b6c236..18a5a03a5e1 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1479,7 +1479,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, HOST_WIDE_INT token, anc_offset; tree otr_type; tree t; - tree target; + tree target = NULL; if (param_index == -1 || known_vals.length () <= (unsigned int) param_index) @@ -1527,14 +1527,53 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, return NULL_TREE; } + if (!flag_devirtualize) + return NULL_TREE; + gcc_assert (!ie->indirect_info->agg_contents); token = ie->indirect_info->otr_token; anc_offset = ie->indirect_info->offset; otr_type = ie->indirect_info->otr_type; - t = known_vals[param_index]; + t = NULL; + + /* Try to work out value of virtual table pointer value in replacemnets. */ + if (!t && agg_reps && !ie->indirect_info->by_ref) + { + while (agg_reps) + { + if (agg_reps->index == param_index + && agg_reps->offset == ie->indirect_info->offset + && agg_reps->by_ref) + { + t = agg_reps->value; + break; + } + agg_reps = agg_reps->next; + } + } + + /* Try to work out value of virtual table pointer value in known + aggregate values. */ + if (!t && known_aggs.length () > (unsigned int) param_index + && !ie->indirect_info->by_ref) + { + struct ipa_agg_jump_function *agg; + agg = known_aggs[param_index]; + t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset, + true); + } + + /* If we found the virtual table pointer, lookup the binfo. */ + if (t) + t = vtable_pointer_value_to_binfo (t); + + /* Did we work out BINFO via type propagation? */ if (!t && known_binfos.length () > (unsigned int) param_index) t = known_binfos[param_index]; + /* Or do we know the constant value of pointer? */ + if (!t) + t = known_vals[param_index]; if (!t) return NULL_TREE; diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c index dcaebdf8659..fb9c666b12d 100644 --- a/gcc/ipa-devirt.c +++ b/gcc/ipa-devirt.c @@ -975,17 +975,24 @@ contains_type_p (tree outer_type, HOST_WIDE_INT offset, /* Lookup base of BINFO that has virtual table VTABLE with OFFSET. */ static tree -subbinfo_with_vtable_at_offset (tree binfo, tree offset, tree vtable) +subbinfo_with_vtable_at_offset (tree binfo, unsigned HOST_WIDE_INT offset, + tree vtable) { tree v = BINFO_VTABLE (binfo); int i; tree base_binfo; + unsigned HOST_WIDE_INT this_offset; - gcc_assert (!v || TREE_CODE (v) == POINTER_PLUS_EXPR); + if (v) + { + if (!vtable_pointer_value_to_vtable (v, &v, &this_offset)) + gcc_unreachable (); + + if (offset == this_offset + && DECL_ASSEMBLER_NAME (v) == DECL_ASSEMBLER_NAME (vtable)) + return binfo; + } - if (v && tree_int_cst_equal (TREE_OPERAND (v, 1), offset) - && TREE_OPERAND (TREE_OPERAND (v, 0), 0) == vtable) - return binfo; for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) if (polymorphic_type_binfo_p (base_binfo)) { @@ -996,11 +1003,12 @@ subbinfo_with_vtable_at_offset (tree binfo, tree offset, tree vtable) return NULL; } -/* T is known constant value of virtual table pointer. Return BINFO of the - instance type. */ +/* T is known constant value of virtual table pointer. + Store virtual table to V and its offset to OFFSET. + Return false if T does not look like virtual table reference. */ -tree -vtable_pointer_value_to_binfo (tree t) +bool +vtable_pointer_value_to_vtable (tree t, tree *v, unsigned HOST_WIDE_INT *offset) { /* We expect &MEM[(void *)&virtual_table + 16B]. We obtain object's BINFO from the context of the virtual table. @@ -1011,7 +1019,7 @@ vtable_pointer_value_to_binfo (tree t) In the case of virtual inheritance, the virtual tables may be nested, i.e. the offset may be different from 16 and we may need to dive into the type representation. */ - if (t && TREE_CODE (t) == ADDR_EXPR + if (TREE_CODE (t) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF && TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) == INTEGER_CST @@ -1020,20 +1028,47 @@ vtable_pointer_value_to_binfo (tree t) && DECL_VIRTUAL_P (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0))) { - tree vtable = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0); - tree offset = TREE_OPERAND (TREE_OPERAND (t, 0), 1); - tree binfo = TYPE_BINFO (DECL_CONTEXT (vtable)); - - binfo = subbinfo_with_vtable_at_offset (binfo, offset, vtable); - - /* FIXME: for stores of construction vtables we return NULL, - because we do not have BINFO for those. Eventually we should fix - our representation to allow this case to be handled, too. - In the case we see store of BINFO we however may assume - that standard folding will be ale to cope with it. */ - return binfo; + *v = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0); + *offset = tree_to_uhwi (TREE_OPERAND (TREE_OPERAND (t, 0), 1)); + return true; } - return NULL; + + /* Alternative representation, used by C++ frontend is POINTER_PLUS_EXPR. + We need to handle it when T comes from static variable initializer or + BINFO. */ + if (TREE_CODE (t) == POINTER_PLUS_EXPR) + { + *offset = tree_to_uhwi (TREE_OPERAND (t, 1)); + t = TREE_OPERAND (t, 0); + } + else + *offset = 0; + + if (TREE_CODE (t) != ADDR_EXPR) + return false; + *v = TREE_OPERAND (t, 0); + return true; +} + +/* T is known constant value of virtual table pointer. Return BINFO of the + instance type. */ + +tree +vtable_pointer_value_to_binfo (tree t) +{ + tree vtable; + unsigned HOST_WIDE_INT offset; + + if (!vtable_pointer_value_to_vtable (t, &vtable, &offset)) + return NULL_TREE; + + /* FIXME: for stores of construction vtables we return NULL, + because we do not have BINFO for those. Eventually we should fix + our representation to allow this case to be handled, too. + In the case we see store of BINFO we however may assume + that standard folding will be ale to cope with it. */ + return subbinfo_with_vtable_at_offset (TYPE_BINFO (DECL_CONTEXT (vtable)), + offset, vtable); } /* Given REF call in FNDECL, determine class of the polymorphic diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 3024414ab8a..37ddbde4621 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -355,8 +355,10 @@ ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node) ii->param_index, ii->offset, ii->by_ref ? "by reference" : "by_value"); else - fprintf (f, " indirect %s callsite, calling param %i", - ii->polymorphic ? "polymorphic" : "simple", ii->param_index); + fprintf (f, " indirect %s callsite, calling param %i, " + "offset " HOST_WIDE_INT_PRINT_DEC, + ii->polymorphic ? "polymorphic" : "simple", ii->param_index, + ii->offset); if (cs->call_stmt) { @@ -1332,11 +1334,11 @@ struct ipa_known_agg_contents_list /* Traverse statements from CALL backwards, scanning whether an aggregate given in ARG is filled in with constant values. ARG can either be an aggregate - expression or a pointer to an aggregate. JFUNC is the jump function into - which the constants are subsequently stored. */ + 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 subsequently stored. */ static void -determine_known_aggregate_parts (gimple call, tree arg, +determine_known_aggregate_parts (gimple call, tree arg, tree arg_type, struct ipa_jump_func *jfunc) { struct ipa_known_agg_contents_list *list = NULL; @@ -1351,18 +1353,18 @@ determine_known_aggregate_parts (gimple call, tree arg, arg_base and arg_offset based on what is actually passed as an actual argument. */ - if (POINTER_TYPE_P (TREE_TYPE (arg))) + if (POINTER_TYPE_P (arg_type)) { by_ref = true; if (TREE_CODE (arg) == SSA_NAME) { tree type_size; - if (!tree_fits_uhwi_p (TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))))) + if (!tree_fits_uhwi_p (TYPE_SIZE (TREE_TYPE (arg_type)))) return; check_ref = true; arg_base = arg; arg_offset = 0; - type_size = TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))); + type_size = TYPE_SIZE (TREE_TYPE (arg_type)); arg_size = tree_to_uhwi (type_size); ao_ref_init_from_ptr_and_size (&r, arg_base, NULL_TREE); } @@ -1645,13 +1647,22 @@ ipa_compute_jump_functions_for_edge (struct param_analysis_info *parms_ainfo, ? TREE_TYPE (param_type) : NULL); + /* If ARG is pointer, we can not use its type to determine the type of aggregate + passed (because type conversions are ignored in gimple). Usually we can + safely get type from function declaration, but in case of K&R prototypes or + variadic functions we can try our luck with type of the pointer passed. + TODO: Since we look for actual initialization of the memory object, we may better + work out the type based on the memory stores we find. */ + if (!param_type) + param_type = TREE_TYPE (arg); + if ((jfunc->type != IPA_JF_PASS_THROUGH || !ipa_get_jf_pass_through_agg_preserved (jfunc)) && (jfunc->type != IPA_JF_ANCESTOR || !ipa_get_jf_ancestor_agg_preserved (jfunc)) && (AGGREGATE_TYPE_P (TREE_TYPE (arg)) - || (POINTER_TYPE_P (TREE_TYPE (arg))))) - determine_known_aggregate_parts (call, arg, jfunc); + || POINTER_TYPE_P (param_type))) + determine_known_aggregate_parts (call, arg, param_type, jfunc); } } @@ -2676,9 +2687,23 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { - tree binfo, target; + tree binfo = NULL, target; - binfo = ipa_value_from_jfunc (new_root_info, jfunc); + if (!flag_devirtualize) + return NULL; + + /* First try to do lookup binfo via known virtual table pointer value. */ + if (!ie->indirect_info->by_ref) + { + tree t = ipa_find_agg_cst_for_param (&jfunc->agg, + ie->indirect_info->offset, + true); + if (t) + binfo = vtable_pointer_value_to_binfo (t); + } + + if (!binfo) + binfo = ipa_value_from_jfunc (new_root_info, jfunc); if (!binfo) return NULL; @@ -2688,7 +2713,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, binfo = gimple_extract_devirt_binfo_from_cst (binfo, ie->indirect_info->otr_type); if (!binfo) - return NULL; + return NULL; } binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index 3212f38cc4c..3f9be1795bf 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -88,6 +88,7 @@ tree get_polymorphic_call_info (tree, tree, tree *, HOST_WIDE_INT *, ipa_polymorphic_call_context *); tree vtable_pointer_value_to_binfo (tree t); +bool vtable_pointer_value_to_vtable (tree, tree *, unsigned HOST_WIDE_INT *); /* Return vector containing possible targets of polymorphic call E. If FINALP is non-NULL, store true if the list is complette. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b7c6252962a..e14976ba0e6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2014-02-03 Jan Hubicka + + PR ipa/59831 + * g++.dg/ipa/devirt-24.C: New testcase. + 2014-02-03 Marc Glisse PR c++/53017 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-24.C b/gcc/testsuite/g++.dg/ipa/devirt-24.C new file mode 100644 index 00000000000..01bfdc2ec50 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-24.C @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-inline -fdump-ipa-cp" } */ +void pad(void); +class A {}; +class B { +public: + A &operator[](int); +}; +class C : B { +public: + virtual int m_fn1() { return 0; } + inline A &operator[](int p1) { + int a; + a = m_fn1(); + static_cast(__builtin_expect(a, 0) ?: 0); + return B::operator[](p1); + } +}; + +int *e; +static void sort(C &p1, C &p2) { + for (int i=0;; i++) { + A c, d = p2[0]; + pad(); + pad(); + pad(); + } +} + +int test (); + +void update_sources() { +while (test()) { + C f; +C *b = new (C); + sort(f, *b); + } +} +/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */ +/* { dg-final { cleanup-ipa-dump "inline" } } */ +/* { dg-final { scan-ipa-dump-times "Aggregate passed by reference" 1 "cp" } } */ +/* { dg-final { cleanup-ipa-dump "cp" } } */