On Tue, Nov 1, 2011 at 10:02 PM, Martin Jambor <mjam...@suse.cz> wrote: > Hi, > > On Tue, Nov 01, 2011 at 10:37:10AM +0100, Richard Guenther wrote: >> On Mon, Oct 31, 2011 at 5:58 PM, Martin Jambor <mjam...@suse.cz> wrote: >> > On Fri, Oct 28, 2011 at 11:21:23AM +0200, Richard Guenther wrote: >> >> On Thu, Oct 27, 2011 at 9:54 PM, Martin Jambor <mjam...@suse.cz> wrote: >> >> > Hi, >> >> > >> >> > On Thu, Oct 27, 2011 at 11:06:02AM +0200, Richard Guenther wrote: >> >> >> On Thu, Oct 27, 2011 at 1:22 AM, Martin Jambor <mjam...@suse.cz> wrote: >> >> >> > Hi, >> >> >> > >> >> >> > I've been asked by Maxim Kuvyrkov to revive the following patch which >> >> >> > has not made it to 4.6. Currently, when type based devirtualization >> >> >> > detects a potential type change, it simply gives up on gathering any >> >> >> > information on the object in question. This patch adds an attempt to >> >> >> > actually detect the new type after the change. >> >> >> > >> >> >> > Maxim claimed this (and another patch I'll post tomorrow) noticeably >> >> >> > improved performance of some real code. I can only offer a rather >> >> >> > artificial example in the attachment. When the constructors are >> >> >> > inlined but the function multiply_matrices is not, this patch makes >> >> >> > the produced executable run for only 7 seconds instead of about 20 on >> >> >> > my 4 year old i686 desktop (with -Ofast). >> >> >> > >> >> >> > Anyway, the patch passes bootstrap and testsuite on x86_64-linux. >> >> >> > What do you think, is it a good idea for trunk now? >> >> >> > >> >> >> > Thanks, >> >> >> > >> >> >> > Martin >> >> >> > >> >> >> > >> >> >> > 2011-10-21 Martin Jambor <mjam...@suse.cz> >> >> >> > >> >> >> > * ipa-prop.c (type_change_info): New fields object, >> >> >> > known_current_type >> >> >> > and multiple_types_encountered. >> >> >> > (extr_type_from_vtbl_ptr_store): New function. >> >> >> > (check_stmt_for_type_change): Use it, set >> >> >> > multiple_types_encountered if >> >> >> > the result is different from the previous one. >> >> >> > (detect_type_change): Renamed to detect_type_change_1. New >> >> >> > parameter >> >> >> > comp_type. Set up new fields in tci, build known type jump >> >> >> > functions if the new type can be identified. >> >> >> > (detect_type_change): New function. >> >> >> > * tree.h (DECL_CONTEXT): Comment new use. >> >> >> > >> >> >> > * testsuite/g++.dg/ipa/devirt-c-1.C: Add dump scans. >> >> >> > * testsuite/g++.dg/ipa/devirt-c-2.C: Likewise. >> >> >> > * testsuite/g++.dg/ipa/devirt-c-7.C: New test. >> >> >> > >> >> >> > >> >> >> > Index: src/gcc/ipa-prop.c >> >> >> > =================================================================== >> >> >> > --- src.orig/gcc/ipa-prop.c >> >> >> > +++ src/gcc/ipa-prop.c >> >> >> > @@ -271,8 +271,17 @@ ipa_print_all_jump_functions (FILE *f) >> >> >> > >> >> >> > struct type_change_info >> >> >> > { >> >> >> > + /* The declaration or SSA_NAME pointer of the base that we are >> >> >> > checking for >> >> >> > + type change. */ >> >> >> > + tree object; >> >> >> > + /* If we actually can tell the type that the object has changed >> >> >> > to, it is >> >> >> > + stored in this field. Otherwise it remains NULL_TREE. */ >> >> >> > + tree known_current_type; >> >> >> > /* Set to true if dynamic type change has been detected. */ >> >> >> > bool type_maybe_changed; >> >> >> > + /* Set to true if multiple types have been encountered. >> >> >> > known_current_type >> >> >> > + must be disregarded in that case. */ >> >> >> > + bool multiple_types_encountered; >> >> >> > }; >> >> >> > >> >> >> > /* Return true if STMT can modify a virtual method table pointer. >> >> >> > @@ -338,6 +347,49 @@ stmt_may_be_vtbl_ptr_store (gimple stmt) >> >> >> > return true; >> >> >> > } >> >> >> > >> >> >> > +/* If STMT can be proved to be an assignment to the virtual method >> >> >> > table >> >> >> > + pointer of ANALYZED_OBJ and the type associated with the new >> >> >> > table >> >> >> > + identified, return the type. Otherwise return NULL_TREE. */ >> >> >> > + >> >> >> > +static tree >> >> >> > +extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj) >> >> >> > +{ >> >> >> > + tree lhs, t, obj; >> >> >> > + >> >> >> > + if (!is_gimple_assign (stmt)) >> >> >> >> >> >> gimple_assign_single_p (stmt) >> >> > >> >> > OK. >> >> > >> >> >> >> >> >> > + return NULL_TREE; >> >> >> > + >> >> >> > + lhs = gimple_assign_lhs (stmt); >> >> >> > + >> >> >> > + if (TREE_CODE (lhs) != COMPONENT_REF) >> >> >> > + return NULL_TREE; >> >> >> > + obj = lhs; >> >> >> > + >> >> >> > + if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) >> >> >> > + return NULL_TREE; >> >> >> > + >> >> >> > + do >> >> >> > + { >> >> >> > + obj = TREE_OPERAND (obj, 0); >> >> >> > + } >> >> >> > + while (TREE_CODE (obj) == COMPONENT_REF); >> >> >> >> >> >> You do not allow other components than component-refs (thus, for >> >> >> example an ARRAY_REF - that is for a reason?). Please add >> >> >> a comment why. Otherwise this whole sequence would look like >> >> >> it should be replaceable by get_base_address (obj). >> >> >> >> >> > >> >> > I guess I might have been overly conservative here, ARRAY_REFs are >> >> > fine. get_base_address only digs into MEM_REFs if they are based on >> >> > an ADDR_EXPR while I do so always. But I can check that either both >> >> > obj and analyzed_obj are a MEM_REF of the same SSA_NAME or they are >> >> > the same thing (i.e. the same decl)... which even feels a bit cleaner, >> >> > so I did that. >> >> >> >> Well, as you are looking for a must-change-type pattern I think you cannot >> >> simply ignore offsets. Consider >> >> >> >> T a[10]; >> >> >> >> new (T') (&a[9]); >> >> a[8]->foo(); >> >> >> >> where the must-type-change on a[9] is _not_ changing the type of a[8]! >> >> >> >> Similar cases might happen with >> >> >> >> class Compound { T a; T b; }; >> >> >> >> no? >> >> >> >> Please think about the difference must vs. may-type-change for these >> >> cases. I'm not convinced that the must-type-change code is correct. >> >> >> > >> > I did, even though it's not always easy because the patch is so old. >> > However, the reasoning is as follows: >> > >> > 1) The callback will never report a known type if the "base address" >> > of the RHS of the assignment is not exactly the same declaration or >> > a MEM_REF of exactly the same SSA name. >> > >> > 2) The aliasing machinery is initialized to exactly the offset we are >> > interested in and the size of one pointer: >> > >> > ao.offset = offset; >> > ao.size = POINTER_SIZE; >> > >> > So, either the access which can alter the virtual table pointer is >> > based on a different pointer and then we will not attempt to claim we >> > know the type or the aliasing machinery invokes the callback only for >> > the correct offset, because it can disambiguate between those based on >> > the same thing. Does that make sense? >> >> Not for the latter - the alias machinery reports may-aliases, thus >> if you ask for [32, 36] then the alias machinery will invoke the >> callback for a[i].vptr as well (the access with a variable offset >> makes it [0, -1U], thus coverting [32, 46]). You have to check for >> a must-alias relationship explicitely, which you cannot with the >> information you have, once a possibly variable offset component >> is visited. Note that the alias machinery may validly invoke your >> callback for all stores, even those that trivially do not alias - saying >> they may-alias is still correct. >> >> > (One further reason why I did not think about arrays that much is that >> > we will currently never attempt to search for BINFOs if there are >> > arrays involved in any way, though I understand we may change that in >> > the future. BTW, I spent quite some time attempting to break this >> > code on Thursday but did not succeed even when I removed the check 1 >> > and the analysis produced clearly wrong results because of how we >> > store the known type jump functions today and search for BINFOs only >> > very much later on, checking the expected type is there at the >> > expected offset in the type hierarchy of the type of the whole >> > declaration. Of course, I understand the analysis has to be correct >> > nevertheless and I tried to make it conservative.) >> >> So I think you should explicitely not handle all variable-offset components >> and pass down the actual offset of the pointer to the walker callback so >> it can verify that the offsets match. >> > > Fair enough, I did not take into account variable offsets. This is > the updated patch, re-bootstrapped and re-tested on x86_64-linux > without any issues.
Ok with ... > Thanks, > > Martin > > > 2011-11-01 Martin Jambor <mjam...@suse.cz> > > * ipa-prop.c (type_change_info): New fields offset, object, > known_current_type and multiple_types_encountered. > (extr_type_from_vtbl_ptr_store): New function. > (check_stmt_for_type_change): Use it, set multiple_types_encountered if > the result is different from the previous one. > (detect_type_change): Renamed to detect_type_change_1. New parameter > comp_type. Set up new fields in tci, build known type jump > functions if the new type can be identified. > (detect_type_change): New function. > * tree.h (DECL_CONTEXT): Comment new use. > > * testsuite/g++.dg/ipa/devirt-c-1.C: Add dump scans. > * testsuite/g++.dg/ipa/devirt-c-2.C: Likewise. > * testsuite/g++.dg/ipa/devirt-c-7.C: New test. > * testsuite/g++.dg/ipa/devirt-c-8.C: Likewise. > > > Index: src/gcc/ipa-prop.c > =================================================================== > --- src.orig/gcc/ipa-prop.c > +++ src/gcc/ipa-prop.c > @@ -271,8 +271,20 @@ ipa_print_all_jump_functions (FILE *f) > > struct type_change_info > { > + /* Offset into the object where there is the virtual method pointer we are > + looking for. */ > + HOST_WIDE_INT offset; > + /* The declaration or SSA_NAME pointer of the base that we are checking for > + type change. */ > + tree object; > + /* If we actually can tell the type that the object has changed to, it is > + stored in this field. Otherwise it remains NULL_TREE. */ > + tree known_current_type; > /* Set to true if dynamic type change has been detected. */ > bool type_maybe_changed; > + /* Set to true if multiple types have been encountered. known_current_type > + must be disregarded in that case. */ > + bool multiple_types_encountered; > }; > > /* Return true if STMT can modify a virtual method table pointer. > @@ -338,6 +350,48 @@ stmt_may_be_vtbl_ptr_store (gimple stmt) > return true; > } > > +/* If STMT can be proved to be an assignment to the virtual method table > + pointer of ANALYZED_OBJ and the type associated with the new table > + identified, return the type. Otherwise return NULL_TREE. */ > + > +static tree > +extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci) > +{ > + HOST_WIDE_INT offset, size, max_size; > + tree lhs, rhs, base; > + > + if (!gimple_assign_single_p (stmt)) > + return NULL_TREE; > + > + lhs = gimple_assign_lhs (stmt); > + rhs = gimple_assign_rhs1 (stmt); > + if (TREE_CODE (lhs) != COMPONENT_REF > + || !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)) > + return NULL_TREE; > + > + base = get_ref_base_and_extent (lhs, &offset, &size, &max_size); > + if (offset != tci->offset > + || size != POINTER_SIZE > + || max_size != POINTER_SIZE) > + return NULL_TREE; > + if (TREE_CODE (base) == MEM_REF) > + { > + if (TREE_CODE (tci->object) != MEM_REF > + || TREE_OPERAND (tci->object, 0) != TREE_OPERAND (base, 0)) || !tree_int_cst_equal (TREE_OPERAND (tci->object, 1), TREE_OPERAND (base, 1)) Thanks, Richard.