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. Richard. > Thanks, > > Martin > > > >> Richard. >> >> >> > >> >> > + if (TREE_CODE (obj) == MEM_REF) >> >> > + obj = TREE_OPERAND (obj, 0); >> >> > + if (TREE_CODE (obj) == ADDR_EXPR) >> >> > + obj = TREE_OPERAND (obj, 0); >> >> > + if (obj != analyzed_obj) >> >> > + return NULL_TREE; >> >> > + >> >> > + t = gimple_assign_rhs1 (stmt); >> >> > + if (TREE_CODE (t) != ADDR_EXPR) >> >> > + return NULL_TREE; >> >> > + t = get_base_address (TREE_OPERAND (t, 0)); >> >> >> >> Do we actually ever store sth else than the plain address of a >> >> virtual table (thus, &decl)? If so, what difference makes the offset >> >> into the virtual table we get in this way? I ask, because ... >> >> >> >> > + if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t)) >> >> > + return NULL_TREE; >> >> > + >> >> > + return DECL_CONTEXT (t); >> >> >> >> ... DECL_CONTEXT (t) is of course independent of any offset. >> >> >> >> Thus I think you should drop the call to get_base_address () and >> >> instead just look at TREE_OPERAND (t, 0). >> > >> > Even the simplest cases of assignments have basically a form like >> > this: >> > >> > this_1(D)->_vptr.A = &MEM[(void *)&_ZTV1A + 8B]; >> > >> > so I need to extract the actual VAR_DECL somehow. But of course I can >> > do that by looking through the MEM_REF and ADDR_EXPR by hand, if >> > preferable. >> > >> >> >> >> > +} >> >> > + >> >> > /* Callback of walk_aliased_vdefs and a helper function for >> >> > detect_type_change to check whether a particular statement may modify >> >> > the virtual table pointer, and if possible also determine the new >> >> > type of >> >> > @@ -352,6 +404,12 @@ check_stmt_for_type_change (ao_ref *ao A >> >> > >> >> > if (stmt_may_be_vtbl_ptr_store (stmt)) >> >> > { >> >> > + tree type; >> >> > + type = extr_type_from_vtbl_ptr_store (stmt, tci->object); >> >> > + if (tci->type_maybe_changed >> >> > + && type != tci->known_current_type) >> >> > + tci->multiple_types_encountered = true; >> >> > + tci->known_current_type = type; >> >> > tci->type_maybe_changed = true; >> >> > return true; >> >> > } >> >> > @@ -359,19 +417,19 @@ check_stmt_for_type_change (ao_ref *ao A >> >> > return false; >> >> > } >> >> > >> >> > -/* Detect whether the dynamic type of ARG has changed (before callsite >> >> > CALL) by >> >> > - looking for assignments to its virtual table pointer. If it is, >> >> > return true >> >> > - and fill in the jump function JFUNC with relevant type information >> >> > or set it >> >> > - to unknown. ARG is the object itself (not a pointer to it, unless >> >> > - dereferenced). BASE is the base of the memory access as returned by >> >> > - get_ref_base_and_extent, as is the offset. */ >> >> > + >> >> > + >> >> > +/* Like detect_type_change but with extra argument COMP_TYPE which >> >> > will become >> >> > + the component type part of new JFUNC of dynamic type change is >> >> > detected and >> >> > + the new base type is identified. */ >> >> > >> >> > static bool >> >> > -detect_type_change (tree arg, tree base, gimple call, >> >> > - struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) >> >> > +detect_type_change_1 (tree arg, tree base, tree comp_type, gimple call, >> >> > + struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) >> >> > { >> >> > struct type_change_info tci; >> >> > ao_ref ao; >> >> > + tree t; >> >> > >> >> > gcc_checking_assert (DECL_P (arg) >> >> > || TREE_CODE (arg) == MEM_REF >> >> > @@ -381,8 +439,6 @@ detect_type_change (tree arg, tree base, >> >> > if (!flag_devirtualize || !gimple_vuse (call)) >> >> > return false; >> >> > >> >> > - tci.type_maybe_changed = false; >> >> > - >> >> > ao.ref = arg; >> >> > ao.base = base; >> >> > ao.offset = offset; >> >> > @@ -391,15 +447,51 @@ detect_type_change (tree arg, tree base, >> >> > ao.ref_alias_set = -1; >> >> > ao.base_alias_set = -1; >> >> > >> >> > + t = arg; >> >> > + while (handled_component_p (t)) >> >> > + t = TREE_OPERAND (t, 0); >> >> >> >> Here you are using handled_component_p, thus allow ARRAY_REFs ... >> >> but you are not trying to distinguish a[1] from a[2] anywhere, so this >> >> looks bogus and instead should look like the case above (with a comment >> >> as to why looking at ARRAY_REFs would be bogus). >> > >> > I changed both to get_base_address as described above. >> > >> > This is the new version of the patch which I'm currently bootstrapping >> > and testing on an x86_64-linux. OK for trunk if it passes? Thanks. >> > >> > Martin >> > >> > >> > 2011-10-27 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,44 @@ 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 (!gimple_assign_single_p (stmt)) >> > + return NULL_TREE; >> > + >> > + lhs = gimple_assign_lhs (stmt); >> > + >> > + if (TREE_CODE (lhs) != COMPONENT_REF >> > + || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) >> > + return NULL_TREE; >> > + >> > + obj = get_base_address (lhs); >> > + if (TREE_CODE (obj) == MEM_REF) >> > + { >> > + if (TREE_CODE (analyzed_obj) != MEM_REF >> > + || TREE_OPERAND (analyzed_obj, 0) != TREE_OPERAND (obj, 0)) >> > + return NULL_TREE; >> > + } >> > + else if (analyzed_obj != obj) >> > + return NULL_TREE; >> > + >> > + t = gimple_assign_rhs1 (stmt); >> > + if (TREE_CODE (t) != ADDR_EXPR) >> > + return NULL_TREE; >> > + t = get_base_address (TREE_OPERAND (t, 0)); >> > + if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t)) >> > + return NULL_TREE; >> > + >> > + return DECL_CONTEXT (t); >> > +} >> > + >> > /* Callback of walk_aliased_vdefs and a helper function for >> > detect_type_change to check whether a particular statement may modify >> > the virtual table pointer, and if possible also determine the new type >> > of >> > @@ -352,6 +399,12 @@ check_stmt_for_type_change (ao_ref *ao A >> > >> > if (stmt_may_be_vtbl_ptr_store (stmt)) >> > { >> > + tree type; >> > + type = extr_type_from_vtbl_ptr_store (stmt, tci->object); >> > + if (tci->type_maybe_changed >> > + && type != tci->known_current_type) >> > + tci->multiple_types_encountered = true; >> > + tci->known_current_type = type; >> > tci->type_maybe_changed = true; >> > return true; >> > } >> > @@ -359,16 +412,15 @@ check_stmt_for_type_change (ao_ref *ao A >> > return false; >> > } >> > >> > -/* Detect whether the dynamic type of ARG has changed (before callsite >> > CALL) by >> > - looking for assignments to its virtual table pointer. If it is, >> > return true >> > - and fill in the jump function JFUNC with relevant type information or >> > set it >> > - to unknown. ARG is the object itself (not a pointer to it, unless >> > - dereferenced). BASE is the base of the memory access as returned by >> > - get_ref_base_and_extent, as is the offset. */ >> > + >> > + >> > +/* Like detect_type_change but with extra argument COMP_TYPE which will >> > become >> > + the component type part of new JFUNC of dynamic type change is >> > detected and >> > + the new base type is identified. */ >> > >> > static bool >> > -detect_type_change (tree arg, tree base, gimple call, >> > - struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) >> > +detect_type_change_1 (tree arg, tree base, tree comp_type, gimple call, >> > + struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) >> > { >> > struct type_change_info tci; >> > ao_ref ao; >> > @@ -381,8 +433,6 @@ detect_type_change (tree arg, tree base, >> > if (!flag_devirtualize || !gimple_vuse (call)) >> > return false; >> > >> > - tci.type_maybe_changed = false; >> > - >> > ao.ref = arg; >> > ao.base = base; >> > ao.offset = offset; >> > @@ -391,15 +441,44 @@ detect_type_change (tree arg, tree base, >> > ao.ref_alias_set = -1; >> > ao.base_alias_set = -1; >> > >> > + tci.object = get_base_address (arg); >> > + tci.known_current_type = NULL_TREE; >> > + tci.type_maybe_changed = false; >> > + tci.multiple_types_encountered = false; >> > + >> > walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change, >> > &tci, NULL); >> > if (!tci.type_maybe_changed) >> > return false; >> > >> > - jfunc->type = IPA_JF_UNKNOWN; >> > + if (!tci.known_current_type >> > + || tci.multiple_types_encountered >> > + || offset != 0) >> > + jfunc->type = IPA_JF_UNKNOWN; >> > + else >> > + { >> > + jfunc->type = IPA_JF_KNOWN_TYPE; >> > + jfunc->value.known_type.base_type = tci.known_current_type; >> > + jfunc->value.known_type.component_type = comp_type; >> > + } >> > + >> > return true; >> > } >> > >> > +/* Detect whether the dynamic type of ARG has changed (before callsite >> > CALL) by >> > + looking for assignments to its virtual table pointer. If it is, >> > return true >> > + and fill in the jump function JFUNC with relevant type information or >> > set it >> > + to unknown. ARG is the object itself (not a pointer to it, unless >> > + dereferenced). BASE is the base of the memory access as returned by >> > + get_ref_base_and_extent, as is the offset. */ >> > + >> > +static bool >> > +detect_type_change (tree arg, tree base, gimple call, >> > + struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) >> > +{ >> > + return detect_type_change_1 (arg, base, TREE_TYPE (arg), call, jfunc, >> > offset); >> > +} >> > + >> > /* Like detect_type_change but ARG is supposed to be a non-dereferenced >> > pointer >> > SSA name (its dereference will become the base and the offset is >> > assumed to >> > be zero). */ >> > @@ -407,16 +486,19 @@ detect_type_change (tree arg, tree base, >> > static bool >> > detect_type_change_ssa (tree arg, gimple call, struct ipa_jump_func >> > *jfunc) >> > { >> > + tree comp_type; >> > + >> > gcc_checking_assert (TREE_CODE (arg) == SSA_NAME); >> > if (!flag_devirtualize >> > || !POINTER_TYPE_P (TREE_TYPE (arg)) >> > || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE) >> > return false; >> > >> > + comp_type = TREE_TYPE (TREE_TYPE (arg)); >> > arg = build2 (MEM_REF, ptr_type_node, arg, >> > - build_int_cst (ptr_type_node, 0)); >> > + build_int_cst (ptr_type_node, 0)); >> > >> > - return detect_type_change (arg, arg, call, jfunc, 0); >> > + return detect_type_change_1 (arg, arg, comp_type, call, jfunc, 0); >> > } >> > >> > /* Callback of walk_aliased_vdefs. Flags that it has been invoked to the >> > Index: src/gcc/testsuite/g++.dg/ipa/devirt-c-1.C >> > =================================================================== >> > --- src.orig/gcc/testsuite/g++.dg/ipa/devirt-c-1.C >> > +++ src/gcc/testsuite/g++.dg/ipa/devirt-c-1.C >> > @@ -1,7 +1,7 @@ >> > /* Verify that ipa-cp correctly detects the dynamic type of an object >> > under construction when doing devirtualization. */ >> > /* { dg-do run } */ >> > -/* { dg-options "-O3 -fno-early-inlining -fno-inline" } */ >> > +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp >> > -fdump-tree-optimized" } */ >> > >> > extern "C" void abort (void); >> > >> > @@ -69,3 +69,8 @@ int main (int argc, char *argv[]) >> > bah (); >> > return 0; >> > } >> > + >> > +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known >> > target.*A::foo" "cp" } } */ >> > +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ >> > +/* { dg-final { cleanup-ipa-dump "cp" } } */ >> > +/* { dg-final { cleanup-tree-dump "optimized" } } */ >> > Index: src/gcc/testsuite/g++.dg/ipa/devirt-c-2.C >> > =================================================================== >> > --- src.orig/gcc/testsuite/g++.dg/ipa/devirt-c-2.C >> > +++ src/gcc/testsuite/g++.dg/ipa/devirt-c-2.C >> > @@ -1,7 +1,7 @@ >> > /* Verify that ipa-cp correctly detects the dynamic type of an object >> > under construction when doing devirtualization. */ >> > /* { dg-do run } */ >> > -/* { dg-options "-O3 -fno-early-inlining -fno-inline" } */ >> > +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp >> > -fdump-tree-optimized" } */ >> > >> > extern "C" void abort (void); >> > >> > @@ -77,3 +77,8 @@ int main (int argc, char *argv[]) >> > bah (); >> > return 0; >> > } >> > + >> > +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known >> > target.*A::foo" "cp" } } */ >> > +/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */ >> > +/* { dg-final { cleanup-ipa-dump "cp" } } */ >> > +/* { dg-final { cleanup-tree-dump "optimized" } } */ >> > Index: src/gcc/tree.h >> > =================================================================== >> > --- src.orig/gcc/tree.h >> > +++ src/gcc/tree.h >> > @@ -2678,7 +2678,9 @@ struct function; >> > nodes, this points to either the FUNCTION_DECL for the containing >> > function, the RECORD_TYPE or UNION_TYPE for the containing type, or >> > NULL_TREE or a TRANSLATION_UNIT_DECL if the given decl has "file >> > - scope". */ >> > + scope". In particular, for VAR_DECLs which are virtual table pointers >> > + (they have DECL_VIRTUAL set), we use DECL_CONTEXT to determine the >> > type >> > + they belong to. */ >> > #define DECL_CONTEXT(NODE) (DECL_MINIMAL_CHECK >> > (NODE)->decl_minimal.context) >> > #define DECL_FIELD_CONTEXT(NODE) \ >> > (FIELD_DECL_CHECK (NODE)->decl_minimal.context) >> > Index: src/gcc/testsuite/g++.dg/ipa/devirt-c-7.C >> > =================================================================== >> > --- /dev/null >> > +++ src/gcc/testsuite/g++.dg/ipa/devirt-c-7.C >> > @@ -0,0 +1,87 @@ >> > +/* Verify that ipa-cp will not get confused by placement new constructing >> > an >> > + object within another one when looking for dynamic type change . */ >> > +/* { dg-do run } */ >> > +/* { dg-options "-O3 -Wno-attributes" } */ >> > + >> > +extern "C" void abort (void); >> > +namespace std { >> > + typedef __SIZE_TYPE__ size_t; >> > +} >> > +inline void* __attribute__ ((always_inline)) >> > +operator new(std::size_t, void* __p) throw() >> > +{ >> > + return __p; >> > +} >> > + >> > +class A >> > +{ >> > +public: >> > + char data[256]; >> > + A(); >> > + virtual int foo (int i); >> > +}; >> > + >> > +class B : public A >> > +{ >> > +public: >> > + virtual int foo (int i); >> > +}; >> > + >> > +class C >> > +{ >> > +public: >> > + C(); >> > + virtual double foo (double i); >> > +}; >> > + >> > +int A::foo (int i) >> > +{ >> > + return i + 1; >> > +} >> > + >> > +int B::foo (int i) >> > +{ >> > + return i + 2; >> > +} >> > + >> > +double C::foo (double i) >> > +{ >> > + return i + 3.5; >> > +} >> > + >> > +static int __attribute__ ((noinline)) middleman (class A *obj, int i) >> > +{ >> > + return obj->foo (i); >> > +} >> > + >> > +int __attribute__ ((noinline,noclone)) get_input(void) >> > +{ >> > + return 1; >> > +} >> > + >> > +__attribute__ ((always_inline)) C::C () >> > +{ >