Message ID | 20140131062254.GA28026@kam.mff.cuni.cz |
---|---|
State | New |
Headers | show |
On Fri, Jan 31, 2014 at 07:22:55AM +0100, Jan Hubicka wrote: > --- ipa-devirt.c (revision 207287) > +++ ipa-devirt.c (working copy) > @@ -972,6 +972,120 @@ contains_type_p (tree outer_type, HOST_W > return get_class_context (&context, otr_type); > } > > +/* Proudce polymorphic call context for call method of instance > + that is located within BASE (that is assumed to be a decl) at OFFSET. */ > + > +static void > +get_poymorphic_call_info_for_decl (ipa_polymorphic_call_context *context, > + tree base, HOST_WIDE_INT offset) Missing l in polymorphic. Jakub
On 2014.01.31 at 07:22 +0100, Jan Hubicka wrote: > +tree > +vtable_pointer_value_to_binfo (tree t) > +{ > + /* We expect &MEM[(void *)&virtual_table + 16B]. > + We obtain object's BINFO from the context of the virtual table. > + This one contains pointer to virtual table represented via > + POINTER_PLUS_EXPR. Verify that this pointer match to what > + we propagated through. > + > + 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 > + && 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 > + && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0)) > + == VAR_DECL) > + && 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); > + gcc_assert (binfo); > + return binfo; > + } > + return NULL; > +} I've tested your patch a little bit and hit the gcc_assert above: markus@x4 tmp % cat test.ii class A { public: unsigned length; }; class B {}; class MultiTermDocs : public virtual B { protected: A readerTermDocs; A subReaders; virtual B *m_fn1(int *); virtual ~MultiTermDocs(); }; class C : MultiTermDocs { B *m_fn1(int *); }; MultiTermDocs::~MultiTermDocs() { if (&readerTermDocs) { B *a; for (unsigned i = 0; i < subReaders.length; i++) (a != 0); } } B *C::m_fn1(int *) { return 0; } markus@x4 tmp % g++ -O2 test.ii test.ii: In destructor ‘virtual C::~C()’: test.ii:24:32: internal compiler error: in vtable_pointer_value_to_binfo, at ipa-devirt.c:1082 B *C::m_fn1(int *) { return 0; } ^ 0x9ca774 vtable_pointer_value_to_binfo(tree_node*) ../../gcc/gcc/ipa-devirt.c:1082 0x9e22b9 extr_type_from_vtbl_ptr_store ../../gcc/gcc/ipa-prop.c:606 0x9e22b9 check_stmt_for_type_change ../../gcc/gcc/ipa-prop.c:648 0xc18343 walk_aliased_vdefs_1 ../../gcc/gcc/tree-ssa-alias.c:2472 0xc1846d walk_aliased_vdefs(ao_ref*, tree_node*, bool (*)(ao_ref*, tree_node*, void*), void*, bitmap_head**) ../../gcc/gcc/tree-ssa-alias.c:2491 0x9e1cba detect_type_change ../../gcc/gcc/ipa-prop.c:702 0x9e8b63 compute_complex_assign_jump_func ../../gcc/gcc/ipa-prop.c:1089 0x9e8b63 ipa_compute_jump_functions_for_edge ../../gcc/gcc/ipa-prop.c:1635 0x9eb882 ipa_compute_jump_functions ../../gcc/gcc/ipa-prop.c:1675 0x9eb882 ipa_analyze_node(cgraph_node*) ../../gcc/gcc/ipa-prop.c:2212 0x103ea7f ipcp_generate_summary ../../gcc/gcc/ipa-cp.c:3709 0xaa4906 execute_ipa_summary_passes(ipa_opt_pass_d*) ../../gcc/gcc/passes.c:2027 0x833dbb ipa_passes ../../gcc/gcc/cgraphunit.c:2070 0x833dbb compile() ../../gcc/gcc/cgraphunit.c:2174 0x834304 finalize_compilation_unit() ../../gcc/gcc/cgraphunit.c:2329 0x62efce cp_write_global_declarations() ../../gcc/gcc/cp/decl2.c:4447 Please submit a full bug report, with preprocessed source if appropriate. Please include the complete backtrace with any bug report.
> I've tested your patch a little bit and hit the gcc_assert above: > > markus@x4 tmp % cat test.ii > class A { > public: > unsigned length; > }; > class B {}; > class MultiTermDocs : public virtual B { > protected: > A readerTermDocs; > A subReaders; > virtual B *m_fn1(int *); > virtual ~MultiTermDocs(); > }; > class C : MultiTermDocs { > B *m_fn1(int *); > }; > MultiTermDocs::~MultiTermDocs() { > if (&readerTermDocs) { > B *a; > for (unsigned i = 0; i < subReaders.length; i++) > (a != 0); > } > } > > B *C::m_fn1(int *) { return 0; } Thanks! It is the case where we see a construction vtable store. There is no BINFO for that, so we need to give up trying to analyze the type. We will need to improve our representation to handle this eventually, but that is definitely not stage3 material. I actualy wondered about this and tried to construct a testcase that did not trigger for some reason and concluded that I probably don't care when it does not fire for firefox. Your testing (and reduced testcaes) are very useful. I will drop the abort and add your testcase before comitting the patch. I wonder if we can reproduce this as wrong code bug for gcc-4.8. Basically virtual calls in the destructors of the virtual base should get devirtualized the wrong way. Honza > > markus@x4 tmp % g++ -O2 test.ii > test.ii: In destructor ‘virtual C::~C()’: > test.ii:24:32: internal compiler error: in vtable_pointer_value_to_binfo, at ipa-devirt.c:1082 > B *C::m_fn1(int *) { return 0; } > ^ > 0x9ca774 vtable_pointer_value_to_binfo(tree_node*) > ../../gcc/gcc/ipa-devirt.c:1082 > 0x9e22b9 extr_type_from_vtbl_ptr_store > ../../gcc/gcc/ipa-prop.c:606 > 0x9e22b9 check_stmt_for_type_change > ../../gcc/gcc/ipa-prop.c:648 > 0xc18343 walk_aliased_vdefs_1 > ../../gcc/gcc/tree-ssa-alias.c:2472 > 0xc1846d walk_aliased_vdefs(ao_ref*, tree_node*, bool (*)(ao_ref*, tree_node*, void*), void*, bitmap_head**) > ../../gcc/gcc/tree-ssa-alias.c:2491 > 0x9e1cba detect_type_change > ../../gcc/gcc/ipa-prop.c:702 > 0x9e8b63 compute_complex_assign_jump_func > ../../gcc/gcc/ipa-prop.c:1089 > 0x9e8b63 ipa_compute_jump_functions_for_edge > ../../gcc/gcc/ipa-prop.c:1635 > 0x9eb882 ipa_compute_jump_functions > ../../gcc/gcc/ipa-prop.c:1675 > 0x9eb882 ipa_analyze_node(cgraph_node*) > ../../gcc/gcc/ipa-prop.c:2212 > 0x103ea7f ipcp_generate_summary > ../../gcc/gcc/ipa-cp.c:3709 > 0xaa4906 execute_ipa_summary_passes(ipa_opt_pass_d*) > ../../gcc/gcc/passes.c:2027 > 0x833dbb ipa_passes > ../../gcc/gcc/cgraphunit.c:2070 > 0x833dbb compile() > ../../gcc/gcc/cgraphunit.c:2174 > 0x834304 finalize_compilation_unit() > ../../gcc/gcc/cgraphunit.c:2329 > 0x62efce cp_write_global_declarations() > ../../gcc/gcc/cp/decl2.c:4447 > Please submit a full bug report, > with preprocessed source if appropriate. > Please include the complete backtrace with any bug report. > > > -- > Markus
Hi, this is variant of testcase that produces wrong code on Mainline. $ ./xgcc -B ./ -O3 ~/t.C -S -fno-partial-inlining -fno-early-inlining -fdump-ipa-all ; g++ t.s; ./a.out Aborted The bug is that we determine wrong type on call of ~MultiTermDocs within ~C (it is determined as C, while it really should be C in construction). Quite amusingly gcc-4.8 also determine the wrong type but does devirtualize correctly. Martin, any idea why? #include <stdlib.h> class A { public: unsigned length; }; class B {}; class MultiTermDocs : public virtual B { protected: A readerTermDocs; A subReaders; virtual B *m_fn1(int *) {} inline virtual ~MultiTermDocs(); void wrap(void) { m_fn1(NULL); } }; class C : MultiTermDocs { B *m_fn1(int *); }; MultiTermDocs::~MultiTermDocs() { wrap (); if (&readerTermDocs) { B *a; for (unsigned i = 0; i < subReaders.length; i++) (a != 0); } } B *C::m_fn1(int *) { abort (); } main() { class C c; }
Hi, here is even better testcase (in a sense that my patch does not solve the wrong code issue) Compile with ./xgcc -B ./ -O3 ~/t.C -S -fno-partial-inlining -fno-early-inlining -fdump-ipa-all -fdump-tree-all -fipa-cp -fno-ipa-sra Here the sequence is bit different. Here we have contstruction table store done within destructor that sets it accoridng to the second parameter. We hover still produce PASSTHROUGH function for it and we end up propagating the wrong type. Honza #include <stdlib.h> class A { public: unsigned length; }; class B {}; class MultiTermDocs : public virtual B { protected: A readerTermDocs; A subReaders; virtual B *m_fn1(int *) {} virtual inline ~MultiTermDocs(); void wrap(void) { m_fn1(NULL); } }; class C : MultiTermDocs { B *m_fn1(int *); }; MultiTermDocs::~MultiTermDocs() { wrap (); if (&readerTermDocs) { B *a; for (unsigned i = 0; i < subReaders.length; i++) (a != 0); } } B *C::m_fn1(int *) { abort (); } main() { class C c; }
Hi, On Fri, Jan 31, 2014 at 07:22:55AM +0100, Jan Hubicka wrote: ... > PR ipa/59831 > * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): Remove. > * ipa-devirt.c (get_poymorphic_call_info_for_decl): Break out from ... > (get_polymorphic_call_info): ... here. > (get_polymorphic_call_info_from_invariant): New function based on > gimple_extract_devirt_binfo_from_cst. > (try_make_edge_direct_virtual_call): Update to use ipa-devirt. > (ipa_get_indirect_edge_target_1): Likewise. > (get_polymorphic_call_info_from_invariant): New function. > (vtable_pointer_value_to_binfo): New function. > * ipa-utils.h (get_polymorphic_call_info_from_invariant): Declare. > (vtable_pointer_value_to_binfo): Declare. > * ipa-prop.c (extr_type_from_vtbl_ptr_store): Use it. > (try_make_edge_direct_virtual_call): Use aggregate propagation; > rewrite handling of constants. > * ipa-cp.c (ipa_get_indirect_edge_target_1): Likewise. > > * testsuite/g++.dg/ipa/devirt-21.C: New testcase. > * testsuite/g++.dg/ipa/devirt-20.C: Fix template. ... > Index: ipa-cp.c > =================================================================== > --- ipa-cp.c (revision 207287) > +++ ipa-cp.c (working copy) > @@ -117,6 +117,7 @@ along with GCC; see the file COPYING3. > #include "params.h" > #include "ipa-inline.h" > #include "ipa-utils.h" > +#include "print-tree.h" > > struct ipcp_value; > > @@ -1479,9 +1480,9 @@ ipa_get_indirect_edge_target_1 (struct c > HOST_WIDE_INT token, anc_offset; > tree otr_type; > tree t; > - tree target; > + tree target = NULL; > > - if (param_index == -1 > + if (!flag_devirtualize || param_index == -1 > || known_vals.length () <= (unsigned int) param_index) > return NULL_TREE; > > @@ -1532,25 +1533,76 @@ ipa_get_indirect_edge_target_1 (struct c > anc_offset = ie->indirect_info->offset; > otr_type = ie->indirect_info->otr_type; > > - t = known_vals[param_index]; > + t = NULL; > + > + /* Do we know BINFO? */ > if (!t && known_binfos.length () > (unsigned int) param_index) > t = known_binfos[param_index]; > - if (!t) > - return NULL_TREE; > + /* FIXME: ipcp_discover_new_direct_edges makes no difference between > + constants and binfos. This is because ipa-cp currently assumes that known > + value is always better than binfo. Hopefully this will be fixed when > + ipa-cp is turned to propagate polymorphic call contextes. */ > + if (known_vals[param_index] && TREE_CODE (known_vals[param_index]) == TREE_BINFO) > + t = known_vals[param_index]; > > - if (TREE_CODE (t) != TREE_BINFO) > + /* Try to work out BINFO from virtual table pointer value in replacements. */ > + if (!t && agg_reps && !ie->indirect_info->by_ref) At this point you know that !ie->indirect_info->polymorphic is set and thus ie->indirect_info->by_ref is always false because it really has no meaning (it is only meaningful when agg_contents is set which is mutually exclusive with the polymorphic flag). > { > - tree binfo; > - binfo = gimple_extract_devirt_binfo_from_cst > - (t, ie->indirect_info->otr_type); > - if (!binfo) > + while (agg_reps) > + { > + if (agg_reps->index == param_index > + && agg_reps->offset == ie->indirect_info->offset > + && agg_reps->by_ref) > + { > + debug_tree (t); > + t = agg_reps->value; > + t = vtable_pointer_value_to_binfo (t); > + break; > + } > + agg_reps = agg_reps->next; > + } > + } > + > + /* Try to work out BINFO from virtual table pointer value in known > + known aggregate values. */ > + if (!t && known_aggs.length () > (unsigned int) param_index > + && !ie->indirect_info->by_ref) The same here. Thanks, Martin
> > - if (TREE_CODE (t) != TREE_BINFO) > > + /* Try to work out BINFO from virtual table pointer value in replacements. */ > > + if (!t && agg_reps && !ie->indirect_info->by_ref) > > At this point you know that !ie->indirect_info->polymorphic is set and > thus ie->indirect_info->by_ref is always false because it really has > no meaning (it is only meaningful when agg_contents is set which is > mutually exclusive with the polymorphic flag). I was worried here about case where in future we may want to represent call of virtual methods from pointers passed by reference (i.e. in some other object). We don't do that at the moment, but for that would really need better jump functions. If you preffer, I can remove that check. Honza > > > { > > - tree binfo; > > - binfo = gimple_extract_devirt_binfo_from_cst > > - (t, ie->indirect_info->otr_type); > > - if (!binfo) > > + while (agg_reps) > > + { > > + if (agg_reps->index == param_index > > + && agg_reps->offset == ie->indirect_info->offset > > + && agg_reps->by_ref) > > + { > > + debug_tree (t); > > + t = agg_reps->value; > > + t = vtable_pointer_value_to_binfo (t); > > + break; > > + } > > + agg_reps = agg_reps->next; > > + } > > + } > > + > > + /* Try to work out BINFO from virtual table pointer value in known > > + known aggregate values. */ > > + if (!t && known_aggs.length () > (unsigned int) param_index > > + && !ie->indirect_info->by_ref) > > The same here. > > Thanks, > > Martin
Hi, On Wed, Feb 05, 2014 at 12:47:30AM +0100, Jan Hubicka wrote: > > > - if (TREE_CODE (t) != TREE_BINFO) > > > + /* Try to work out BINFO from virtual table pointer value in replacements. */ > > > + if (!t && agg_reps && !ie->indirect_info->by_ref) > > > > At this point you know that !ie->indirect_info->polymorphic is set and > > thus ie->indirect_info->by_ref is always false because it really has > > no meaning (it is only meaningful when agg_contents is set which is > > mutually exclusive with the polymorphic flag). > > I was worried here about case where in future we may want to represent call of > virtual methods from pointers passed by reference (i.e. in some other object). > We don't do that at the moment, but for that would really need better jump > functions. > > If you preffer, I can remove that check. > I think it would be better, yes. IIRC, We want to re-organize indirect_info anyway (I vaguely remember we want to split the overloaded offset field into two but forgot the exact reason why but I have it written somewhere), I suppose we'll be turning it into a union (or class hierarchy?) and this would make it slightly awkward. If we ever support the pointer-by-reference scenario, quite a few more places will need to be adjusted anyway. Thanks, Martin
> > > > I think it would be better, yes. IIRC, We want to re-organize > indirect_info anyway (I vaguely remember we want to split the > overloaded offset field into two but forgot the exact reason why but I > have it written somewhere), I suppose we'll be turning it into a union > (or class hierarchy?) and this would make it slightly awkward. If we > ever support the pointer-by-reference scenario, quite a few more > places will need to be adjusted anyway. OK, I will remove the check. I wanted to split polymorphic call context and its propagation from aggregate analysis. For example struct A { struct B b; struct C c; } when you call method of A.c.foo() its context is not {outer_type=a,offset=offsetof(c)}, since A is not polymorphic type at all. We should instead use {outer_type=c,offset=0,not_derived_type}. In ipa-devirt there is function get_class_context that gets you from first to second. So as briefly discussed last july, I think ipa-prop can simply do two propagations in parallel where the type one is done via functionality that willbe exported from ipa-devirt, since the logic should be shared with a local devirt pass. There are also cases where the parameter is KNOWN_TYPE but also PASS_THROUGH at the same type (as tings passed by invisible refernece, or when the function is constructor and it initialized dynamic type of THIS pointer). Those are cases where current code is losing information because one is not supperset of the other. We want to use KNOWN_TYPE information for devirtualization, but we do not want to forget about PASS_THROUGH for normal constant propagation in case constructor is only called on &decl. Honza > > Thanks, > > Martin
Index: gimple-fold.c =================================================================== --- gimple-fold.c (revision 207287) +++ gimple-fold.c (working copy) @@ -1071,74 +1071,6 @@ gimple_fold_builtin (gimple stmt) } -/* Return a binfo to be used for devirtualization of calls based on an object - represented by a declaration (i.e. a global or automatically allocated one) - or NULL if it cannot be found or is not safe. CST is expected to be an - ADDR_EXPR of such object or the function will return NULL. Currently it is - safe to use such binfo only if it has no base binfo (i.e. no ancestors) - EXPECTED_TYPE is type of the class virtual belongs to. */ - -tree -gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type) -{ - HOST_WIDE_INT offset, size, max_size; - tree base, type, binfo; - bool last_artificial = false; - - if (!flag_devirtualize - || TREE_CODE (cst) != ADDR_EXPR - || TREE_CODE (TREE_TYPE (TREE_TYPE (cst))) != RECORD_TYPE) - return NULL_TREE; - - cst = TREE_OPERAND (cst, 0); - base = get_ref_base_and_extent (cst, &offset, &size, &max_size); - type = TREE_TYPE (base); - if (!DECL_P (base) - || max_size == -1 - || max_size != size - || TREE_CODE (type) != RECORD_TYPE) - return NULL_TREE; - - /* Find the sub-object the constant actually refers to and mark whether it is - an artificial one (as opposed to a user-defined one). */ - while (true) - { - HOST_WIDE_INT pos, size; - tree fld; - - if (types_same_for_odr (type, expected_type)) - break; - if (offset < 0) - return NULL_TREE; - - for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) - { - if (TREE_CODE (fld) != FIELD_DECL) - continue; - - pos = int_bit_position (fld); - size = tree_to_uhwi (DECL_SIZE (fld)); - if (pos <= offset && (pos + size) > offset) - break; - } - if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE) - return NULL_TREE; - - last_artificial = DECL_ARTIFICIAL (fld); - type = TREE_TYPE (fld); - offset -= pos; - } - /* Artificial sub-objects are ancestors, we do not want to use them for - devirtualization, at least not here. */ - if (last_artificial) - return NULL_TREE; - binfo = TYPE_BINFO (type); - if (!binfo || BINFO_N_BASE_BINFOS (binfo) > 0) - return NULL_TREE; - else - return binfo; -} - /* Attempt to fold a call statement referenced by the statement iterator GSI. The statement may be replaced by another statement, e.g., if the call simplifies to a constant value. Return true if any changes were made. Index: gimple-fold.h =================================================================== --- gimple-fold.h (revision 207287) +++ gimple-fold.h (working copy) @@ -26,7 +26,6 @@ extern tree canonicalize_constructor_val extern tree get_symbol_constant_value (tree); extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); extern tree gimple_fold_builtin (gimple); -extern tree gimple_extract_devirt_binfo_from_cst (tree, tree); extern bool fold_stmt (gimple_stmt_iterator *); extern bool fold_stmt_inplace (gimple_stmt_iterator *); extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, Index: ipa-utils.h =================================================================== --- ipa-utils.h (revision 207287) +++ ipa-utils.h (working copy) @@ -87,6 +87,9 @@ tree method_class_type (tree); tree get_polymorphic_call_info (tree, tree, tree *, HOST_WIDE_INT *, ipa_polymorphic_call_context *); +bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *, + tree, tree, HOST_WIDE_INT); +tree vtable_pointer_value_to_binfo (tree t); /* Return vector containing possible targets of polymorphic call E. If FINALP is non-NULL, store true if the list is complette. Index: ipa-devirt.c =================================================================== --- ipa-devirt.c (revision 207287) +++ ipa-devirt.c (working copy) @@ -972,6 +972,120 @@ contains_type_p (tree outer_type, HOST_W return get_class_context (&context, otr_type); } +/* Proudce polymorphic call context for call method of instance + that is located within BASE (that is assumed to be a decl) at OFFSET. */ + +static void +get_poymorphic_call_info_for_decl (ipa_polymorphic_call_context *context, + tree base, HOST_WIDE_INT offset) +{ + gcc_assert (DECL_P (base)); + + context->outer_type = TREE_TYPE (base); + context->offset = offset; + /* Make very conservative assumption that all objects + may be in construction. + TODO: ipa-prop already contains code to tell better. + merge it later. */ + context->maybe_in_construction = true; + context->maybe_derived_type = false; +} + +/* CST is an invariant (address of decl), try to get meaningful + polymorphic call context for polymorphic call of method + if instance of OTR_TYPE that is located at OFFSET of this invariant. + Return FALSE if nothing meaningful can be found. */ + +bool +get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context, + tree cst, + tree otr_type, + HOST_WIDE_INT offset) +{ + HOST_WIDE_INT offset2, size, max_size; + tree base; + + if (TREE_CODE (cst) != ADDR_EXPR) + return NULL_TREE; + + cst = TREE_OPERAND (cst, 0); + base = get_ref_base_and_extent (cst, &offset2, &size, &max_size); + if (!DECL_P (base) + || max_size == -1 + || max_size != size) + return NULL_TREE; + + /* Only type inconsistent programs can have otr_type that is + not part of outer type. */ + if (!contains_type_p (TREE_TYPE (base), + offset, otr_type)) + return NULL_TREE; + + get_poymorphic_call_info_for_decl (context, + base, offset); + return true; +} + +/* Lookup base of BINFO that has virtual table VTABLE with OFFSET. */ + +static tree +subbinfo_with_vtable_at_offset (tree binfo, tree offset, tree vtable) +{ + tree v = BINFO_VTABLE (binfo); + int i; + tree base_binfo; + + gcc_assert (!v || TREE_CODE (v) == POINTER_PLUS_EXPR); + + 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)) + { + base_binfo = subbinfo_with_vtable_at_offset (base_binfo, offset, vtable); + if (base_binfo) + return base_binfo; + } + return NULL; +} + +/* T is known constant value of virtual table pointer. Return BINFO of the + instance type. */ + +tree +vtable_pointer_value_to_binfo (tree t) +{ + /* We expect &MEM[(void *)&virtual_table + 16B]. + We obtain object's BINFO from the context of the virtual table. + This one contains pointer to virtual table represented via + POINTER_PLUS_EXPR. Verify that this pointer match to what + we propagated through. + + 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 + && 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 + && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 0)) + == VAR_DECL) + && 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); + gcc_assert (binfo); + return binfo; + } + return NULL; +} + + /* Given REF call in FNDECL, determine class of the polymorphic call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT. Return pointer to object described by the context */ @@ -1030,21 +1144,13 @@ get_polymorphic_call_info (tree fndecl, is known. */ else if (DECL_P (base)) { - gcc_assert (!POINTER_TYPE_P (TREE_TYPE (base))); - /* Only type inconsistent programs can have otr_type that is not part of outer type. */ if (!contains_type_p (TREE_TYPE (base), context->offset + offset2, *otr_type)) return base_pointer; - context->outer_type = TREE_TYPE (base); - context->offset += offset2; - /* Make very conservative assumption that all objects - may be in construction. - TODO: ipa-prop already contains code to tell better. - merge it later. */ - context->maybe_in_construction = true; - context->maybe_derived_type = false; + get_poymorphic_call_info_for_decl (context, base, + context->offset + offset2); return NULL; } else Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 207287) +++ ipa-prop.c (working copy) @@ -592,6 +592,7 @@ extr_type_from_vtbl_ptr_store (gimple st { HOST_WIDE_INT offset, size, max_size; tree lhs, rhs, base; + tree binfo; if (!gimple_assign_single_p (stmt)) return NULL_TREE; @@ -602,10 +603,12 @@ extr_type_from_vtbl_ptr_store (gimple st || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)) || TREE_CODE (rhs) != ADDR_EXPR) return NULL_TREE; - rhs = get_base_address (TREE_OPERAND (rhs, 0)); - if (!rhs - || TREE_CODE (rhs) != VAR_DECL - || !DECL_VIRTUAL_P (rhs)) + binfo = vtable_pointer_value_to_binfo (rhs); + + /* FIXME: We may end up looking up an binfo that is not + a base type, since the instance is part of a bigger one. + We should handle this in future by working out proper offset. */ + if (!binfo || TYPE_BINFO (BINFO_TYPE (binfo)) != binfo) return NULL_TREE; base = get_ref_base_and_extent (lhs, &offset, &size, &max_size); @@ -624,7 +627,7 @@ extr_type_from_vtbl_ptr_store (gimple st else if (tci->object != base) return NULL_TREE; - return DECL_CONTEXT (rhs); + return BINFO_TYPE (binfo); } /* Callback of walk_aliased_vdefs and a helper function for @@ -2670,28 +2673,58 @@ try_make_edge_direct_virtual_call (struc struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { - tree binfo, target; + tree binfo = NULL, target; + + if (!flag_devirtualize) + return NULL; - binfo = ipa_value_from_jfunc (new_root_info, jfunc); + /* First try to work out binfo from the knowledge of virtual + table pointer. This is always safe. */ + if (!ie->indirect_info->by_ref) + { + tree t = ipa_find_agg_cst_for_param (&jfunc->agg, + ie->indirect_info->offset, + true); + binfo = vtable_pointer_value_to_binfo (t); + } + /* See if we propagated value or binfo. */ + if (!binfo) + binfo = ipa_value_from_jfunc (new_root_info, jfunc); if (!binfo) return NULL; if (TREE_CODE (binfo) != TREE_BINFO) { - binfo = gimple_extract_devirt_binfo_from_cst - (binfo, ie->indirect_info->otr_type); - if (!binfo) - return NULL; + ipa_polymorphic_call_context context; + vec <cgraph_node *>targets; + bool final; + + if (!get_polymorphic_call_info_from_invariant + (&context, binfo, ie->indirect_info->otr_type, + ie->indirect_info->offset)) + return NULL; + targets = possible_polymorphic_call_targets + (ie->indirect_info->otr_type, + ie->indirect_info->otr_token, + context, &final); + if (!final || targets.length () > 1) + return NULL; + if (targets.length () == 1) + target = targets[0]->decl; + else + target = builtin_decl_implicit (BUILT_IN_UNREACHABLE); } - - binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, - ie->indirect_info->otr_type); - if (binfo) - target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, - binfo); else - return NULL; + { + binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, + ie->indirect_info->otr_type); + if (binfo) + target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, + binfo); + else + return NULL; + } if (target) { Index: ipa-cp.c =================================================================== --- ipa-cp.c (revision 207287) +++ ipa-cp.c (working copy) @@ -117,6 +117,7 @@ along with GCC; see the file COPYING3. #include "params.h" #include "ipa-inline.h" #include "ipa-utils.h" +#include "print-tree.h" struct ipcp_value; @@ -1479,9 +1480,9 @@ ipa_get_indirect_edge_target_1 (struct c HOST_WIDE_INT token, anc_offset; tree otr_type; tree t; - tree target; + tree target = NULL; - if (param_index == -1 + if (!flag_devirtualize || param_index == -1 || known_vals.length () <= (unsigned int) param_index) return NULL_TREE; @@ -1532,25 +1533,76 @@ ipa_get_indirect_edge_target_1 (struct c anc_offset = ie->indirect_info->offset; otr_type = ie->indirect_info->otr_type; - t = known_vals[param_index]; + t = NULL; + + /* Do we know BINFO? */ if (!t && known_binfos.length () > (unsigned int) param_index) t = known_binfos[param_index]; - if (!t) - return NULL_TREE; + /* FIXME: ipcp_discover_new_direct_edges makes no difference between + constants and binfos. This is because ipa-cp currently assumes that known + value is always better than binfo. Hopefully this will be fixed when + ipa-cp is turned to propagate polymorphic call contextes. */ + if (known_vals[param_index] && TREE_CODE (known_vals[param_index]) == TREE_BINFO) + t = known_vals[param_index]; - if (TREE_CODE (t) != TREE_BINFO) + /* Try to work out BINFO from virtual table pointer value in replacements. */ + if (!t && agg_reps && !ie->indirect_info->by_ref) { - tree binfo; - binfo = gimple_extract_devirt_binfo_from_cst - (t, ie->indirect_info->otr_type); - if (!binfo) + while (agg_reps) + { + if (agg_reps->index == param_index + && agg_reps->offset == ie->indirect_info->offset + && agg_reps->by_ref) + { + debug_tree (t); + t = agg_reps->value; + t = vtable_pointer_value_to_binfo (t); + break; + } + agg_reps = agg_reps->next; + } + } + + /* Try to work out BINFO from virtual table pointer value in known + 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 (t) + t = vtable_pointer_value_to_binfo (t); + } + + /* Try to work out value of the pointer. This comes last since not all + values leads to devirtualization. */ + if (!t) + { + ipa_polymorphic_call_context context; + vec <cgraph_node *>targets; + bool final; + + t = known_vals[param_index]; + if (!t) + return NULL_TREE; + + if (!get_polymorphic_call_info_from_invariant + (&context, t, ie->indirect_info->otr_type, + anc_offset)) return NULL_TREE; - binfo = get_binfo_at_offset (binfo, anc_offset, otr_type); - if (!binfo) + targets = possible_polymorphic_call_targets + (ie->indirect_info->otr_type, + ie->indirect_info->otr_token, + context, &final); + if (!final || targets.length () > 1) return NULL_TREE; - target = gimple_get_virt_method_for_binfo (token, binfo); + if (targets.length () == 1) + return targets[0]->decl; + return builtin_decl_implicit (BUILT_IN_UNREACHABLE); } - else + if (t) { tree binfo;