Message ID | 20130817214457.GA7313@kam.mff.cuni.cz |
---|---|
State | New |
Headers | show |
On 08/17/2013 05:44 PM, Jan Hubicka wrote: > 1) we want the type to not have base because we may have inlined the constructor. > During construction the vtables are filled by base's vtable and thus we can > not simply devirtualize based on the final virtual table without proving that > constructor was not (partially) inlined. If the constructor is inlined into the current function, then we can see the most recent assignment to the vptr and use that for devirtualization. > I do not know if one can do > something like having automatic variable of class A and use placement new > to change it to class B. This is something of a grey area in the standard, with a few defect reports yet to be resolved. I think it should be undefined behavior. > Finally I decided to replace the lookup of base binfo by call to > get_binfo_at_offset. This is not possible. For example my base > variable can be: > > struct {class A a; class B b} var; > > and offset 10 may point to class B. Here I get an ICE since TYPE_BINFO > of the structure is NULL (it is not class). I don't understand. In C++ a struct is a class. > I wonder if we can track functions that return pointers/values of objects > exactly of given type (i.e. no derivations). If the function returns by value, it's always a value of that exact type. > Is the dynamic type upon return from constructor always known to be of > constructor's type? Yes. > We may want to introduce assert_type_expr use like: > > obj_ptr = assert_type_expr<class A> obj_ptr; > > that can be dropped by FE for us to drive those more interesting cases, like > partial construction. I guess we would need to be careful to insert those after vptr assignments within the constructor body, as well. Jason
On Sat, Aug 17, 2013 at 7:56 PM, Jason Merrill <jason@redhat.com> wrote: >> I do not know if one can do >> something like having automatic variable of class A and use placement new >> to change it to class B. > > > This is something of a grey area in the standard, with a few defect reports > yet to be resolved. I think it should be undefined behavior. Jason, could you remind me of the CWG issue numbers for these? It is also of interest for SG12, even if it isn't going to come withe a different answer. -- Gaby
Jason Merrill <jason@redhat.com> wrote: >On 08/17/2013 05:44 PM, Jan Hubicka wrote: >> 1) we want the type to not have base because we may have inlined >the constructor. >> During construction the vtables are filled by base's vtable and >thus we can >> not simply devirtualize based on the final virtual table without >proving that >> constructor was not (partially) inlined. > >If the constructor is inlined into the current function, then we can >see >the most recent assignment to the vptr and use that for >devirtualization. > >> I do not know if one can do >> something like having automatic variable of class A and use placement >new >> to change it to class B. > >This is something of a grey area in the standard, with a few defect >reports yet to be resolved. I think it should be undefined behavior. I'm curious if special.casing the automatic non-pod type case is worth the trouble. At least you need to support placement new on pod class members, in which case you need to be careful with which cases you can reliably detectat the gimple level. As always - look at past dynamic type bugs we had with boost. Richard. >> Finally I decided to replace the lookup of base binfo by call to >> get_binfo_at_offset. This is not possible. For example my base >> variable can be: >> >> struct {class A a; class B b} var; >> >> and offset 10 may point to class B. Here I get an ICE since >TYPE_BINFO >> of the structure is NULL (it is not class). > >I don't understand. In C++ a struct is a class. > >> I wonder if we can track functions that return pointers/values of >objects >> exactly of given type (i.e. no derivations). > >If the function returns by value, it's always a value of that exact >type. > >> Is the dynamic type upon return from constructor always known to be >of >> constructor's type? > >Yes. > >> We may want to introduce assert_type_expr use like: >> >> obj_ptr = assert_type_expr<class A> obj_ptr; >> >> that can be dropped by FE for us to drive those more interesting >cases, like >> partial construction. > >I guess we would need to be careful to insert those after vptr >assignments within the constructor body, as well. > >Jason
> On 08/17/2013 05:44 PM, Jan Hubicka wrote: > > 1) we want the type to not have base because we may have inlined the constructor. > > During construction the vtables are filled by base's vtable and thus we can > > not simply devirtualize based on the final virtual table without proving that > > constructor was not (partially) inlined. > > If the constructor is inlined into the current function, then we can > see the most recent assignment to the vptr and use that for > devirtualization. In most cases, yes. There are some special issues, like partial inlining (where the "wrong" assignment gets inlined, but the final assignment not) But I think tracking inlining those (it will need to make middle end aware of constructors that I think is good idea anyway) and leaving those to the constant propagation should work well enough in practice. > > >I do not know if one can do > >something like having automatic variable of class A and use placement new > >to change it to class B. > > This is something of a grey area in the standard, with a few defect > reports yet to be resolved. I think it should be undefined > behavior. I would preffer it being so ;) > > >Finally I decided to replace the lookup of base binfo by call to > >get_binfo_at_offset. This is not possible. For example my base > >variable can be: > > > >struct {class A a; class B b} var; > > > >and offset 10 may point to class B. Here I get an ICE since TYPE_BINFO > >of the structure is NULL (it is not class). > > I don't understand. In C++ a struct is a class. You are right. I get the ICE here only why I handle arrays and unions, too. (as I do in my tree, but not in the patches posted). In the example above the struct seems to have TYPE_BINFO even though it is useless. Since handling arrays and union seems to make sense and resolves some real testcases from firefox, I think we could keep the tree structured in a way making this possible. I.e. having the type walk as proposed in the patch instead of calling get_binfo_at_offset. > > >I wonder if we can track functions that return pointers/values of objects > > exactly of given type (i.e. no derivations). > > If the function returns by value, it's always a value of that exact type. OK, good! > > >Is the dynamic type upon return from constructor always known to be of > > constructor's type? > > Yes. > > > We may want to introduce assert_type_expr use like: > > > > obj_ptr = assert_type_expr<class A> obj_ptr; > > > > that can be dropped by FE for us to drive those more interesting cases, like > > partial construction. > > I guess we would need to be careful to insert those after vptr > assignments within the constructor body, as well. Yes, if we go this way, we will need C++ FE to produce the asserts. The original patch seems resonable? Honza > > Jason
> > On 08/17/2013 05:44 PM, Jan Hubicka wrote: > > > 1) we want the type to not have base because we may have inlined the constructor. > > > During construction the vtables are filled by base's vtable and thus we can > > > not simply devirtualize based on the final virtual table without proving that > > > constructor was not (partially) inlined. > > > > If the constructor is inlined into the current function, then we can > > see the most recent assignment to the vptr and use that for > > devirtualization. > > In most cases, yes. There are some special issues, like partial inlining > (where the "wrong" assignment gets inlined, but the final assignment not) But I > think tracking inlining those (it will need to make middle end aware of ^^^^^ of constructors, partial or not. > constructors that I think is good idea anyway) and leaving those to the > constant propagation should work well enough in practice. Basicaly if ctor of given type was inlined, we will make the assumption that the type can be partially constructed. > > >Finally I decided to replace the lookup of base binfo by call to > > >get_binfo_at_offset. This is not possible. For example my base > > >variable can be: > > > > > >struct {class A a; class B b} var; > > > > > >and offset 10 may point to class B. Here I get an ICE since TYPE_BINFO > > >of the structure is NULL (it is not class). > > > > I don't understand. In C++ a struct is a class. > > You are right. I get the ICE here only why I handle arrays and unions, too. ^^^ when > (as I do in my tree, but not in the patches posted). In the example above > the struct seems to have TYPE_BINFO even though it is useless. > > Since handling arrays and union seems to make sense and resolves some real > testcases from firefox, I think we could keep the tree structured in a way > making this possible. I.e. having the type walk as proposed in the patch > instead of calling get_binfo_at_offset. Honza
Hi, here is variant of patch that drops the field walking from gimple_extract_devirt_binfo_from_cst completely. As pointed out by Jason, it is pointless since all structures have BINFO in C++ and thus get_binfo_at_offset will do the job. I would like to return the code back eventually to handle arrays&unions but that can be done incrementally (and this is not the only place that sufers from the problem) Martin: I am still not quite certain about the dynamic type changing logic. if this is the case ipa-prop needs to deal with and it handles only 0 offsets within the outer type, I guess it can just test the offset by itself? Honza Bootstrapped/regtested x86_64-linux, OK? * ipa-cp.c (ipa_get_indirect_edge_target_1): Update use of gimple_extract_devirt_binfo_from_cst. * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): Rework. (gimple_fold_call): Update use of gimple_extract_devirt_binfo_from_cst. * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. * gimple.h (gimple_extract_devirt_binfo_from_cst): Update. Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 201814) --- ipa-cp.c (working copy) *************** ipa_get_indirect_edge_target_1 (struct c *** 1541,1554 **** if (TREE_CODE (t) != TREE_BINFO) { tree binfo; binfo = gimple_extract_devirt_binfo_from_cst ! (t, ie->indirect_info->otr_type); if (!binfo) return NULL_TREE; ! binfo = get_binfo_at_offset (binfo, anc_offset, otr_type); ! if (!binfo) return NULL_TREE; ! return gimple_get_virt_method_for_binfo (token, binfo); } else { --- 1541,1564 ---- if (TREE_CODE (t) != TREE_BINFO) { tree binfo; + tree target, base_target; binfo = gimple_extract_devirt_binfo_from_cst ! (t, ie->indirect_info->otr_type, ! ie->indirect_info->offset); if (!binfo) return NULL_TREE; ! target = gimple_get_virt_method_for_binfo (token, binfo); ! if (!target) ! return NULL_TREE; ! /* Constructors may be partially inlined. We do not track ! if type is in construction and during that time the ! virtual table may correspond to virtual table of the ! base type. */ ! base_target = gimple_get_virt_method_for_binfo ! (token, TYPE_BINFO (ie->indirect_info->otr_type)); ! if (base_target != target) return NULL_TREE; ! return target; } else { Index: gimple-fold.c =================================================================== *** gimple-fold.c (revision 201814) --- gimple-fold.c (working copy) *************** gimple_fold_builtin (gimple stmt) *** 1006,1021 **** /* 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 --- 1006,1025 ---- /* 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. ! ! It is up to the caller to check for absence of dynamic type changes. ! Because constructors may be partially inlined and the virtual tables ! during construction may be overwritten by virtual tables by base types, ! it is also up to caller to verify that either all base types have ! the same virtual method or that this does not happen. */ tree ! gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type, ! HOST_WIDE_INT otr_offset) { HOST_WIDE_INT offset, size, max_size; ! tree base, type; if (!flag_devirtualize || TREE_CODE (cst) != ADDR_EXPR *************** gimple_extract_devirt_binfo_from_cst (tr *** 1028,1074 **** 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_low_cst (DECL_SIZE (fld), 1); ! 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. --- 1032,1042 ---- if (!DECL_P (base) || max_size == -1 || max_size != size ! || TREE_CODE (type) != RECORD_TYPE ! || !TYPE_BINFO (type)) return NULL_TREE; ! return get_binfo_at_offset (TYPE_BINFO (type), ! offset + otr_offset, expected_type); } /* Attempt to fold a call statement referenced by the statement iterator GSI. *************** gimple_fold_call (gimple_stmt_iterator * *** 1108,1121 **** else { tree obj = OBJ_TYPE_REF_OBJECT (callee); tree binfo = gimple_extract_devirt_binfo_from_cst ! (obj, obj_type_ref_class (callee)); if (binfo) { HOST_WIDE_INT token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); ! if (fndecl) { gimple_call_set_fndecl (stmt, fndecl); changed = true; --- 1076,1095 ---- else { tree obj = OBJ_TYPE_REF_OBJECT (callee); + tree class_type = obj_type_ref_class (callee); tree binfo = gimple_extract_devirt_binfo_from_cst ! (obj, class_type, 0); if (binfo) { HOST_WIDE_INT token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); ! tree base_fndecl = gimple_get_virt_method_for_binfo (token, TYPE_BINFO (class_type)); ! /* Constructors may be partially inlined. We do not track ! if type is in construction and during that time the ! virtual table may correspond to virtual table of the ! base type. */ ! if (fndecl && base_fndecl == fndecl) { gimple_call_set_fndecl (stmt, fndecl); changed = true; Index: ipa-prop.c =================================================================== *** ipa-prop.c (revision 201814) --- ipa-prop.c (working copy) *************** try_make_edge_direct_virtual_call (struc *** 2444,2450 **** struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { ! tree binfo, target; binfo = ipa_value_from_jfunc (new_root_info, jfunc); --- 2444,2450 ---- struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { ! tree binfo, target, base_target = NULL; binfo = ipa_value_from_jfunc (new_root_info, jfunc); *************** try_make_edge_direct_virtual_call (struc *** 2454,2472 **** if (TREE_CODE (binfo) != TREE_BINFO) { binfo = gimple_extract_devirt_binfo_from_cst ! (binfo, ie->indirect_info->otr_type); if (!binfo) 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) return ipa_make_edge_direct_to_target (ie, target); else --- 2454,2484 ---- if (TREE_CODE (binfo) != TREE_BINFO) { binfo = gimple_extract_devirt_binfo_from_cst ! (binfo, ie->indirect_info->otr_type, ! ie->indirect_info->offset); if (!binfo) return NULL; + /* Constructors may be partially inlined. We do not track + if type is in construction and during that time the + virtual table may correspond to virtual table of the + base type. */ + base_target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, + TYPE_BINFO (ie->indirect_info->otr_type)); + if (!base_target) + return NULL; } ! else ! 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 (base_target && target != base_target) + return NULL; + if (target) return ipa_make_edge_direct_to_target (ie, target); else Index: gimple.h =================================================================== *** gimple.h (revision 201814) --- gimple.h (working copy) *************** unsigned get_gimple_rhs_num_ops (enum tr *** 854,860 **** gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); ! tree gimple_extract_devirt_binfo_from_cst (tree, tree); /* Returns true iff T is a scalar register variable. */ extern bool is_gimple_reg (tree); --- 854,860 ---- gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); ! tree gimple_extract_devirt_binfo_from_cst (tree, tree, HOST_WIDE_INT); /* Returns true iff T is a scalar register variable. */ extern bool is_gimple_reg (tree);
Hi, I went through some statistics on firefox build (it is a source combining many coding styles). I was basically curious where we do devirtualization. The result is: Before inline (i.e. important devirtualization) 624 ssa-pre devirt 0 this is interaprocedural devirutalization happening during early FRE by propagating constant accesses into vtables entries. (i.e. not type based at all) 10 ssa-pre devirt2 0 this is type based intraprocedural analysis during early optimizations 177 ipa-prop devirt devirtualization in ipa-cp 243 ipa-cp inline devirt this is devirtualization happening at IPA stage during inlining After inline (i.e. devirtualization where we missed the chance to do something useful) 82 gimple-fold devirt 1 this is the gimple-fold function in question (it also run pre-inline but apparently always fail. I will try the proposed patch and sed updated stats tomorrow) 27 ipa-prop intra devirt 1 intraprocedural type based analysis 1569 ssa-pre devirt 1 this is interaprocedural devirutalization happening during late FRE (not type based) So overall type based analyssi accounts 430 devirtualizations pre-inline and 109 post inline. Low level propagation of vtable accesses gets 624 pre-inline and 1569 post inline. Obviously the post inline numbers shows that we miss majority of interesting cases. I hope we can noticeably improve this. I am re-building with the proposed change now. Honza
> Hi, > I went through some statistics on firefox build (it is a source combining many coding styles). > I was basically curious where we do devirtualization. The result is: > > Before inline (i.e. important devirtualization) > 624 ssa-pre devirt 0 > this is interaprocedural devirutalization happening during early FRE by propagating > constant accesses into vtables entries. (i.e. not type based at all) > > 10 ssa-pre devirt2 0 > this is type based intraprocedural analysis during early optimizations > 177 ipa-prop devirt > devirtualization in ipa-cp > 243 ipa-cp inline devirt > this is devirtualization happening at IPA stage during inlining The promesed stats with this change. 59 Single type devirt this is my new code devirtualizing types in anonymous namespaces when there is only one node. I will send separate patch for this. 696 ssa-pre devirt 0 11% better than w/o patch (probably because of more early inline) 537 ipa-cp inline devirt 120% better than w/o patch 5 ssa-pre devirt2 0 50% less probably because I handle it earlier 309 ipa-prop devirt 74% more 10 gimple-fold devirt2 0 type based code in gimple-fold. It now does something. > > After inline (i.e. devirtualization where we missed the chance to do something useful) > 82 gimple-fold devirt 1 > this is the gimple-fold function in question (it also run pre-inline but apparently > always fail. I will try the proposed patch and sed updated stats tomorrow) I was having trakcing bug here. This is standard folding based propagation. The type based path never suceeded. > > 27 ipa-prop intra devirt 1 > intraprocedural type based analysis > 1569 ssa-pre devirt 1 > this is interaprocedural devirutalization happening during late FRE (not type based) 83 gimple-fold devirt 1 One more. 7 gimple-fold devirt2 1 type based path now does something 27 ssa-pre devirt2 1 type based path, no change. 1847 ssa-pre devirt 1 17% up. > > So overall type based analyssi accounts 430 devirtualizations pre-inline and 109 post inline. > Low level propagation of vtable accesses gets 624 pre-inline and 1569 post inline. > > Obviously the post inline numbers shows that we miss majority of interesting cases. > I hope we can noticeably improve this. > > I am re-building with the proposed change now. > > Honza
Hi, On Mon, Aug 19, 2013 at 11:10:31AM +0200, Jan Hubicka wrote: > Hi, > here is variant of patch that drops the field walking from > gimple_extract_devirt_binfo_from_cst completely. As pointed out > by Jason, it is pointless since all structures have BINFO in C++ > and thus get_binfo_at_offset will do the job. > > I would like to return the code back eventually to handle arrays&unions > but that can be done incrementally (and this is not the only place that > sufers from the problem) > > Martin: I am still not quite certain about the dynamic type changing logic. > if this is the case ipa-prop needs to deal with and it handles only 0 offsets > within the outer type, I guess it can just test the offset by itself? In these cases, devirtualization is done according ipa invariants, no dynamic type checks are in place, we did away with requiring the artificial-ness of the field we devirtualize according to. I believe your new check that the function has not been overridden is sufficient and superior. Thanks, Martin > > Honza > > Bootstrapped/regtested x86_64-linux, OK? > > * ipa-cp.c (ipa_get_indirect_edge_target_1): Update use > of gimple_extract_devirt_binfo_from_cst. > * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): Rework. > (gimple_fold_call): Update use of gimple_extract_devirt_binfo_from_cst. > * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. > * gimple.h (gimple_extract_devirt_binfo_from_cst): Update.
=================================== gimple_extract_devirt_binfo_from_cst looks for address expression that object used for virtual method comes from. It sees something like &var + 10; It looks at the type of VAR and tries to look for a field at offset 10. If it suceeds it verify that the field is not artificial (i.e. it is not base of some outer type present in var) and that its BINFO has no derived types (it basically has to be the original type defining the virtual method and the type can not be derived at all). After this happen the caller proceeds by calling get_binfo_at_offset and by lookup of virtual call from vtable found. Why it does so ============== I was always confused on why gimple_extract_devirt_binfo_from_cst apparently duplicates logic of get_binfo_at_offset and why it has the limitations. Disussing this with Martin, it is about dynamic type changes. My understanding is that 1) we want the type to not have base because we may have inlined the constructor. During construction the vtables are filled by base's vtable and thus we can not simply devirtualize based on the final virtual table without proving that constructor was not (partially) inlined. This is ensured by the exisitng code refusing artifical types and by the check for number of bases being 0. 2) apparently the code is supposed to refuse non-0 offset because ipa-prop is tracking possibly dynamic type changes only for beggining of the objects. What is wrong with 1 ==================== I think 1) is unnecesarily restrictive. a) when tyhe bases of type found define no virtual methods (or do not define virtual method in question) then partially constructed types are not harmful. b) when the type found has a base type that defines given method, but do not overwrite it, we are safe too. Less conservative solution for 1 ================================ I think 1) can be also ignored in gimple_extract_devirt_binfo_from_cst pushing the need to check for effects of partial construction into users. I think I can track constructors inlined into given function, but I am not doing it right now. Intead I do two lookups - I find the method by the mechanizm above and I compare it with the first definition of the method. If they are same, I devirtualize. What seems worng with 2 ======================= For 2) i actually found no code checking it. All I can notice that current implementation will always fail to find anything in ipa-prop when indirect_info->offset is non-0. This is because the conditions implementing 1) prevents gimple_extract_devirt_binfo_from_cst to return anything than the final type and it will never match on any derived type (making the whole thing bit pointless, since devirtualization is mostly about derived types I think). Solution for 2 ============== It does not seem to break the testcases, but if check_stmt_for_type_change really can not detect changes outside the main object, then I think all the code above is wrong anyway and it needs to test for zero the combined offset (i.e. offset given by get_ref_base_and_extent on the constant as well as offset of the basetype given by indirect_info->offset). This can be done easily and will disable any transformations on derived types, but I suppose I can extend it incrementally. I am also confused why ipa-prop needs to track dynamic changes, while gimple-fold is happy without doing so. I do not know if one can do something like having automatic variable of class A and use placement new to change it to class B. I tried to change devirt-6.C testcase to do so but did not suceed in producing anything that would seem to have sane construction and destruction order. The local variable is destructed at the end of sope block, so I would at least need to placement new it to something class B, do somethjing and then placement new it back so the destructor can be called. I would hope this is invalid. Is there any sane summary of restriction of placement new use? If such changing of dyanmic type is possible, we will need to track in gimple-fold the changes, too. My secret plan is to track these cases and devirtualize speculatively - the dynamic type changes are rare and often they can be detected by the local constant propagation on memory locations and optimized out anyway (at least for the inlined constructors). Why I can not use get_binfo_at_offset ===================================== Finally I decided to replace the lookup of base binfo by call to get_binfo_at_offset. This is not possible. For example my base variable can be: struct {class A a; class B b} var; and offset 10 may point to class B. Here I get an ICE since TYPE_BINFO of the structure is NULL (it is not class). So we need to A) walk type fields until we find an field we look for (containing the base defining virtual method in question) B) walk binfos of that field to find correct one. A) is now done by new field walking in gimple_extract_devirt_binfo_from_cst and then it dispatch to get_binfo_at_offset that implements B. What is wrong with get_binfo_at_offset ====================================== From observation above I think get_binfo_at_offset code is wrong. It has code that attempts to walk non-artifical fields (i.e. non-base types). It bails out on NULL BINFO that is likely to happen. In fact get_binfo_at_offset probably should use it through the loop in gimple_extract_devirt_binfo_from_cst but I decided to leave this for incremental step. Of course we can also handle array and union containers. unions are bit harder since we will need to collect all binfos and update callers to be ready to receive multitude of them. The proposed patch bootstraps®tests x86_64-linux and ppc64-linux. If it seems to make sense I will proceed with 1) mentioned get_binfo_at_offset reorg 2) using speculative call trick to handle cases where virtual method was overwritten 3) trakcing of partial inlining of constructors to avoid need in speculation in most cases (I hope) 4) I wonder if we can track functions that return pointers/values of objects exactly of given type (i.e. no derivations). This seems to be implementable and also may allow to add user attribute returns_no_derived_types. The devirtualization machinery then can use the type knowledge as if it was the type of container var 5) Is the dynamic type upon return from constructor always known to be of constructor's type? We may want to introduce assert_type_expr use like: obj_ptr = assert_type_expr<class A> obj_ptr; that can be dropped by FE for us to drive those more interesting cases, like partial construction. Like for bulitin_expect we can drop this one after IPA passes. Honza * ipa-cp.c (ipa_get_indirect_edge_target_1): Update use of gimple_extract_devirt_binfo_from_cst. * gimple-fold.c (gimple_extract_devirt_binfo_from_cst): Rework. (gimple_fold_call): Update use of gimple_extract_devirt_binfo_from_cst. * ipa-prop.c (try_make_edge_direct_virtual_call): Likewise. * gimple.h (gimple_extract_devirt_binfo_from_cst): Update. Index: ipa-cp.c =================================================================== --- ipa-cp.c (revision 201814) +++ ipa-cp.c (working copy) @@ -1541,14 +1541,24 @@ ipa_get_indirect_edge_target_1 (struct c if (TREE_CODE (t) != TREE_BINFO) { tree binfo; + tree target, base_target; binfo = gimple_extract_devirt_binfo_from_cst - (t, ie->indirect_info->otr_type); + (t, ie->indirect_info->otr_type, + ie->indirect_info->offset); if (!binfo) return NULL_TREE; - binfo = get_binfo_at_offset (binfo, anc_offset, otr_type); - if (!binfo) + target = gimple_get_virt_method_for_binfo (token, binfo); + if (!target) + return NULL_TREE; + /* Constructors may be partially inlined. We do not track + if type is in construction and during that time the + virtual table may correspond to virtual table of the + base type. */ + base_target = gimple_get_virt_method_for_binfo + (token, TYPE_BINFO (ie->indirect_info->otr_type)); + if (base_target != target) return NULL_TREE; - return gimple_get_virt_method_for_binfo (token, binfo); + return target; } else { Index: gimple-fold.c =================================================================== --- gimple-fold.c (revision 201814) +++ gimple-fold.c (working copy) @@ -1006,16 +1006,20 @@ 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. */ + ADDR_EXPR of such object or the function will return NULL. + + It is up to the caller to check for absence of dynamic type changes. + Because constructors may be partially inlined and the virtual tables + during construction may be overwritten by virtual tables by base types, + it is also up to caller to verify that either all base types have + the same virtual method or that this does not happen. */ tree -gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type) +gimple_extract_devirt_binfo_from_cst (tree cst, tree expected_type, + HOST_WIDE_INT otr_offset) { - HOST_WIDE_INT offset, size, max_size; - tree base, type, binfo; - bool last_artificial = false; + HOST_WIDE_INT offset, size, max_size, base_offset; + tree base, type, last_nonartifical; if (!flag_devirtualize || TREE_CODE (cst) != ADDR_EXPR @@ -1024,15 +1028,19 @@ gimple_extract_devirt_binfo_from_cst (tr cst = TREE_OPERAND (cst, 0); base = get_ref_base_and_extent (cst, &offset, &size, &max_size); + base_offset = offset; type = TREE_TYPE (base); + last_nonartifical = type; 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). */ + /* Find the sub-object the constant actually refers to. + We are interested in the innermost non-artifical one, since that + actually represent the basetype of the field. + Rest of walking is done by get_binfo_at_offset. */ while (true) { HOST_WIDE_INT pos, size; @@ -1056,19 +1064,19 @@ gimple_extract_devirt_binfo_from_cst (tr if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE) return NULL_TREE; - last_artificial = DECL_ARTIFICIAL (fld); type = TREE_TYPE (fld); + if (!DECL_ARTIFICIAL (fld)) + { + last_nonartifical = type; + base_offset = offset; + } 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) + + if (!TYPE_BINFO (last_nonartifical)) return NULL_TREE; - else - return binfo; + return get_binfo_at_offset (TYPE_BINFO (last_nonartifical), + base_offset + otr_offset, expected_type); } /* Attempt to fold a call statement referenced by the statement iterator GSI. @@ -1108,14 +1116,20 @@ gimple_fold_call (gimple_stmt_iterator * else { tree obj = OBJ_TYPE_REF_OBJECT (callee); + tree class_type = obj_type_ref_class (callee); tree binfo = gimple_extract_devirt_binfo_from_cst - (obj, obj_type_ref_class (callee)); + (obj, class_type, 0); if (binfo) { HOST_WIDE_INT token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); - if (fndecl) + tree base_fndecl = gimple_get_virt_method_for_binfo (token, TYPE_BINFO (class_type)); + /* Constructors may be partially inlined. We do not track + if type is in construction and during that time the + virtual table may correspond to virtual table of the + base type. */ + if (fndecl && base_fndecl == fndecl) { gimple_call_set_fndecl (stmt, fndecl); changed = true; Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 201814) +++ ipa-prop.c (working copy) @@ -2444,7 +2444,7 @@ try_make_edge_direct_virtual_call (struc struct ipa_jump_func *jfunc, struct ipa_node_params *new_root_info) { - tree binfo, target; + tree binfo, target, base_target = NULL; binfo = ipa_value_from_jfunc (new_root_info, jfunc); @@ -2454,19 +2454,31 @@ try_make_edge_direct_virtual_call (struc if (TREE_CODE (binfo) != TREE_BINFO) { binfo = gimple_extract_devirt_binfo_from_cst - (binfo, ie->indirect_info->otr_type); + (binfo, ie->indirect_info->otr_type, + ie->indirect_info->offset); if (!binfo) return NULL; + /* Constructors may be partially inlined. We do not track + if type is in construction and during that time the + virtual table may correspond to virtual table of the + base type. */ + base_target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, + TYPE_BINFO (ie->indirect_info->otr_type)); + if (!base_target) + return NULL; } - - binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset, - ie->indirect_info->otr_type); + else + 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 (base_target && target != base_target) + return NULL; + if (target) return ipa_make_edge_direct_to_target (ie, target); else Index: gimple.h =================================================================== --- gimple.h (revision 201814) +++ gimple.h (working copy) @@ -854,7 +854,7 @@ unsigned get_gimple_rhs_num_ops (enum tr gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); -tree gimple_extract_devirt_binfo_from_cst (tree, tree); +tree gimple_extract_devirt_binfo_from_cst (tree, tree, HOST_WIDE_INT); /* Returns true iff T is a scalar register variable. */ extern bool is_gimple_reg (tree);