From patchwork Fri Jan 14 23:07:02 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 79018 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 883A2B6EEB for ; Sat, 15 Jan 2011 10:07:18 +1100 (EST) Received: (qmail 27963 invoked by alias); 14 Jan 2011 23:07:17 -0000 Received: (qmail 27953 invoked by uid 22791); 14 Jan 2011 23:07:14 -0000 X-SWARE-Spam-Status: No, hits=-2.1 required=5.0 tests=AWL, BAYES_50, TW_JF, TW_TM, TW_WH X-Spam-Check-By: sourceware.org Received: from cantor2.suse.de (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 14 Jan 2011 23:07:06 +0000 Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.221.2]) by mx2.suse.de (Postfix) with ESMTP id 796EA8730A for ; Sat, 15 Jan 2011 00:07:03 +0100 (CET) Date: Sat, 15 Jan 2011 00:07:02 +0100 From: Martin Jambor To: gcc-patches@gcc.gnu.org Subject: Re: [PATCH, PR 45934 2/5] Dynamic type change detection Message-ID: <20110114230702.GA14818@virgil.arch.suse.de> Mail-Followup-To: gcc-patches@gcc.gnu.org References: <20101215164902.438513649@virgil.suse.cz> <20101215164917.094470982@virgil.suse.cz> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.20 (2009-06-14) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org On Thu, Jan 13, 2011 at 12:56:24PM +0100, Richard Guenther wrote: > On Wed, 15 Dec 2010, Martin Jambor wrote: > > Iff we indeed only deal with statically allocated objects patch 2/5 > is ok with the minor adjustments I requested and the TBAA issue > fixed for detect_type_change_ssa. Thanks, this is what I have committed as revision 168825 (after re-testing). Thanks, Martin 2011-01-14 Martin Jambor PR tree-optimization/45934 PR tree-optimization/46302 * ipa-prop.c (type_change_info): New type. (stmt_may_be_vtbl_ptr_store): New function. (check_stmt_for_type_change): Likewise. (detect_type_change): Likewise. (detect_type_change_ssa): Likewise. (compute_complex_assign_jump_func): Check for dynamic type change. (compute_complex_ancestor_jump_func): Likewise. (compute_known_type_jump_func): Likewise. (compute_scalar_jump_functions): Likewise. (ipa_analyze_virtual_call_uses): Likewise. (ipa_analyze_node): Push and pop cfun, set current_function_decl. * testsuite/g++.dg/ipa/devirt-c-1.C: New test. * testsuite/g++.dg/ipa/devirt-c-2.C: Likewise. * testsuite/g++.dg/ipa/devirt-c-3.C: Likewise. * testsuite/g++.dg/ipa/devirt-c-4.C: Likewise. * testsuite/g++.dg/ipa/devirt-c-5.C: Likewise. * testsuite/g++.dg/ipa/devirt-c-6.C: Likewise. * testsuite/g++.dg/ipa/devirt-6.C: Likewise. * testsuite/g++.dg/ipa/devirt-d-1.C: Likewise. * testsuite/g++.dg/torture/pr45934.C: Likewise. Index: icln/gcc/ipa-prop.c =================================================================== --- icln.orig/gcc/ipa-prop.c +++ icln/gcc/ipa-prop.c @@ -350,6 +350,153 @@ ipa_print_all_jump_functions (FILE *f) } } +/* Structure to be passed in between detect_type_change and + check_stmt_for_type_change. */ + +struct type_change_info +{ + /* Set to true if dynamic type change has been detected. */ + bool type_maybe_changed; +}; + +/* Return true if STMT can modify a virtual method table pointer. + + This function makes special assumptions about both constructors and + destructors which are all the functions that are allowed to alter the VMT + pointers. It assumes that destructors begin with assignment into all VMT + pointers and that constructors essentially look in the following way: + + 1) The very first thing they do is that they call constructors of ancestor + sub-objects that have them. + + 2) Then VMT pointers of this and all its ancestors is set to new values + corresponding to the type corresponding to the constructor. + + 3) Only afterwards, other stuff such as constructor of member sub-objects + and the code written by the user is run. Only this may include calling + virtual functions, directly or indirectly. + + There is no way to call a constructor of an ancestor sub-object in any + other way. + + This means that we do not have to care whether constructors get the correct + type information because they will always change it (in fact, if we define + the type to be given by the VMT pointer, it is undefined). + + The most important fact to derive from the above is that if, for some + statement in the section 3, we try to detect whether the dynamic type has + changed, we can safely ignore all calls as we examine the function body + backwards until we reach statements in section 2 because these calls cannot + be ancestor constructors or destructors (if the input is not bogus) and so + do not change the dynamic type (this holds true only for automatically + allocated objects but at the moment we devirtualize only these). We then + must detect that statements in section 2 change the dynamic type and can try + to derive the new type. That is enough and we can stop, we will never see + the calls into constructors of sub-objects in this code. Therefore we can + safely ignore all call statements that we traverse. + */ + +static bool +stmt_may_be_vtbl_ptr_store (gimple stmt) +{ + if (is_gimple_call (stmt)) + return false; + else if (is_gimple_assign (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + + if (TREE_CODE (lhs) == COMPONENT_REF + && !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)) + && !AGGREGATE_TYPE_P (TREE_TYPE (lhs))) + return false; + /* In the future we might want to use get_base_ref_and_offset to find + if there is a field corresponding to the offset and if so, proceed + almost like if it was a component ref. */ + } + return true; +} + +/* Callbeck 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 + the (sub-)object. It stores its result into DATA, which points to a + type_change_info structure. */ + +static bool +check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data) +{ + gimple stmt = SSA_NAME_DEF_STMT (vdef); + struct type_change_info *tci = (struct type_change_info *) data; + + if (stmt_may_be_vtbl_ptr_store (stmt)) + { + tci->type_maybe_changed = true; + return true; + } + else + 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. */ + +static bool +detect_type_change (tree arg, tree base, gimple call, + struct ipa_jump_func *jfunc, HOST_WIDE_INT offset) +{ + struct type_change_info tci; + ao_ref ao; + + gcc_checking_assert (DECL_P (arg) + || TREE_CODE (arg) == MEM_REF + || handled_component_p (arg)); + /* Const calls cannot call virtual methods through VMT and so type changes do + not matter. */ + if (!gimple_vuse (call)) + return false; + + tci.type_maybe_changed = false; + + ao.ref = arg; + ao.base = base; + ao.offset = offset; + ao.size = POINTER_SIZE; + ao.max_size = ao.size; + ao.ref_alias_set = -1; + ao.base_alias_set = -1; + + 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; + return true; +} + +/* 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). */ + +static bool +detect_type_change_ssa (tree arg, gimple call, struct ipa_jump_func *jfunc) +{ + gcc_checking_assert (TREE_CODE (arg) == SSA_NAME); + if (!POINTER_TYPE_P (TREE_TYPE (arg)) + || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE) + return false; + + arg = build2 (MEM_REF, ptr_type_node, arg, + build_int_cst (ptr_type_node, 0)); + + return detect_type_change (arg, arg, call, jfunc, 0); +} + + /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result of an assignment statement STMT, try to find out whether NAME can be described by a (possibly polynomial) pass-through jump-function or an @@ -359,10 +506,10 @@ ipa_print_all_jump_functions (FILE *f) static void compute_complex_assign_jump_func (struct ipa_node_params *info, struct ipa_jump_func *jfunc, - gimple stmt, tree name) + gimple call, gimple stmt, tree name) { HOST_WIDE_INT offset, size, max_size; - tree op1, op2, base, type; + tree op1, op2, base, ssa; int index; op1 = gimple_assign_rhs1 (stmt); @@ -388,7 +535,8 @@ compute_complex_assign_jump_func (struct jfunc->value.pass_through.operation = gimple_assign_rhs_code (stmt); jfunc->value.pass_through.operand = op2; } - else if (gimple_assign_unary_nop_p (stmt)) + else if (gimple_assign_unary_nop_p (stmt) + && !detect_type_change_ssa (op1, call, jfunc)) { jfunc->type = IPA_JF_PASS_THROUGH; jfunc->value.pass_through.formal_id = index; @@ -399,10 +547,8 @@ compute_complex_assign_jump_func (struct if (TREE_CODE (op1) != ADDR_EXPR) return; - op1 = TREE_OPERAND (op1, 0); - type = TREE_TYPE (op1); - if (TREE_CODE (type) != RECORD_TYPE) + if (TREE_CODE (TREE_TYPE (op1)) != RECORD_TYPE) return; base = get_ref_base_and_extent (op1, &offset, &size, &max_size); if (TREE_CODE (base) != MEM_REF @@ -411,20 +557,21 @@ compute_complex_assign_jump_func (struct || max_size != size) return; offset += mem_ref_offset (base).low * BITS_PER_UNIT; - base = TREE_OPERAND (base, 0); - if (TREE_CODE (base) != SSA_NAME - || !SSA_NAME_IS_DEFAULT_DEF (base) + ssa = TREE_OPERAND (base, 0); + if (TREE_CODE (ssa) != SSA_NAME + || !SSA_NAME_IS_DEFAULT_DEF (ssa) || offset < 0) return; /* Dynamic types are changed only in constructors and destructors and */ - index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base)); - if (index >= 0) + index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa)); + if (index >= 0 + && !detect_type_change (op1, base, call, jfunc, offset)) { jfunc->type = IPA_JF_ANCESTOR; jfunc->value.ancestor.formal_id = index; jfunc->value.ancestor.offset = offset; - jfunc->value.ancestor.type = type; + jfunc->value.ancestor.type = TREE_TYPE (op1); } } @@ -453,12 +600,12 @@ compute_complex_assign_jump_func (struct static void compute_complex_ancestor_jump_func (struct ipa_node_params *info, struct ipa_jump_func *jfunc, - gimple phi) + gimple call, gimple phi) { HOST_WIDE_INT offset, size, max_size; gimple assign, cond; basic_block phi_bb, assign_bb, cond_bb; - tree tmp, parm, expr; + tree tmp, parm, expr, obj; int index, i; if (gimple_phi_num_args (phi) != 2) @@ -486,6 +633,7 @@ compute_complex_ancestor_jump_func (stru if (TREE_CODE (expr) != ADDR_EXPR) return; expr = TREE_OPERAND (expr, 0); + obj = expr; expr = get_ref_base_and_extent (expr, &offset, &size, &max_size); if (TREE_CODE (expr) != MEM_REF @@ -513,7 +661,6 @@ compute_complex_ancestor_jump_func (stru || !integer_zerop (gimple_cond_rhs (cond))) return; - phi_bb = gimple_bb (phi); for (i = 0; i < 2; i++) { @@ -522,10 +669,13 @@ compute_complex_ancestor_jump_func (stru return; } - jfunc->type = IPA_JF_ANCESTOR; - jfunc->value.ancestor.formal_id = index; - jfunc->value.ancestor.offset = offset; - jfunc->value.ancestor.type = TREE_TYPE (TREE_TYPE (tmp)); + if (!detect_type_change (obj, expr, call, jfunc, offset)) + { + jfunc->type = IPA_JF_ANCESTOR; + jfunc->value.ancestor.formal_id = index; + jfunc->value.ancestor.offset = offset; + jfunc->value.ancestor.type = TREE_TYPE (obj);; + } } /* Given OP whch is passed as an actual argument to a called function, @@ -533,7 +683,8 @@ compute_complex_ancestor_jump_func (stru and if so, create one and store it to JFUNC. */ static void -compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc) +compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc, + gimple call) { HOST_WIDE_INT offset, size, max_size; tree base, binfo; @@ -551,6 +702,9 @@ compute_known_type_jump_func (tree op, s || is_global_var (base)) return; + if (detect_type_change (op, base, call, jfunc, offset)) + return; + binfo = TYPE_BINFO (TREE_TYPE (base)); if (!binfo) return; @@ -592,7 +746,8 @@ compute_scalar_jump_functions (struct ip { int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg)); - if (index >= 0) + if (index >= 0 + && !detect_type_change_ssa (arg, call, &functions[num])) { functions[num].type = IPA_JF_PASS_THROUGH; functions[num].value.pass_through.formal_id = index; @@ -604,14 +759,14 @@ compute_scalar_jump_functions (struct ip gimple stmt = SSA_NAME_DEF_STMT (arg); if (is_gimple_assign (stmt)) compute_complex_assign_jump_func (info, &functions[num], - stmt, arg); + call, stmt, arg); else if (gimple_code (stmt) == GIMPLE_PHI) compute_complex_ancestor_jump_func (info, &functions[num], - stmt); + call, stmt); } } else - compute_known_type_jump_func (arg, &functions[num]); + compute_known_type_jump_func (arg, &functions[num], call); } } @@ -1218,6 +1373,7 @@ ipa_analyze_virtual_call_uses (struct cg struct ipa_node_params *info, gimple call, tree target) { + struct ipa_jump_func jfunc; tree obj = OBJ_TYPE_REF_OBJECT (target); tree var; int index; @@ -1241,7 +1397,8 @@ ipa_analyze_virtual_call_uses (struct cg var = SSA_NAME_VAR (obj); index = ipa_get_param_decl_index (info, var); - if (index >= 0) + if (index >= 0 + && !detect_type_change_ssa (obj, call, &jfunc)) ipa_note_param_call (node, index, call, true); } @@ -1364,6 +1521,8 @@ ipa_analyze_node (struct cgraph_node *no struct param_analysis_info *parms_info; int i, param_count; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + current_function_decl = node->decl; ipa_initialize_node_params (node); param_count = ipa_get_param_count (info); @@ -1376,6 +1535,9 @@ ipa_analyze_node (struct cgraph_node *no for (i = 0; i < param_count; i++) if (parms_info[i].visited_statements) BITMAP_FREE (parms_info[i].visited_statements); + + current_function_decl = NULL; + pop_cfun (); } Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C @@ -0,0 +1,71 @@ +/* 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" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + A(); + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static int middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +A::A () +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +static void bah () +{ + class B b; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C @@ -0,0 +1,79 @@ +/* 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" } */ + +extern "C" void abort (void); + +class Distraction +{ +public: + float f; + double d; + Distraction () + { + f = 8.3; + d = 10.2; + } + virtual float bar (float z); +}; + +class A +{ +public: + int data; + A(); + virtual int foo (int i); +}; + +class B : public Distraction, public A +{ +public: + virtual int foo (int i); +}; + +float Distraction::bar (float z) +{ + f += z; + return f/2; +} + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +static int middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +A::A() +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +static void bah () +{ + class B b; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C @@ -0,0 +1,80 @@ +/* Verify that ipa-cp correctly detects the dynamic type of an object + under construction when doing devirtualization. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-inline" } */ + +extern "C" void abort (void); + +class Distraction +{ +public: + float f; + double d; + Distraction () + { + f = 8.3; + d = 10.2; + } + virtual float bar (float z); +}; + +class A +{ +public: + int data; + A(); + virtual int foo (int i); +}; + +class B : public Distraction, public A +{ +public: + virtual int foo (int i); +}; + +float Distraction::bar (float z) +{ + f += z; + return f/2; +} + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +static int __attribute__ ((noinline)) +middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +inline __attribute__ ((always_inline)) A::A() +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +static void bah () +{ + class B b; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C @@ -0,0 +1,110 @@ +/* Verify that ipa-cp correctly detects the dynamic type of an object + under construction when doing devirtualization. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-inline" } */ + +extern "C" void abort (void); + +class Distraction +{ +public: + float f; + double d; + Distraction () + { + f = 8.3; + d = 10.2; + } + virtual float bar (float z); +}; + +class A +{ +public: + int data; + A(); + virtual int foo (int i); +}; + +class B : public Distraction, public A +{ +public: + B(); + virtual int foo (int i); +}; + +class C : public B +{ +public: + virtual int foo (int i); +}; + + +float Distraction::bar (float z) +{ + f += z; + return f/2; +} + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +static int __attribute__ ((noinline)) +middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +static void __attribute__ ((noinline)) +sth2 (A *a) +{ + if (a->foo (get_input ()) != 3) + abort (); +} + +inline void __attribute__ ((always_inline)) sth1 (B *b) +{ + sth2 (b); +} + +inline __attribute__ ((always_inline)) A::A() +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +B::B() : Distraction(), A() +{ + sth1 (this); +} + +static void bah () +{ + class C c; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-d-1.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-d-1.C @@ -0,0 +1,71 @@ +/* Verify that ipa-cp correctly detects the dynamic type of an object + under destruction when doing devirtualization. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-early-inlining -fno-inline" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + ~A(); + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static int middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +A::~A () +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +static void bah () +{ + class B b; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/torture/pr45934.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/torture/pr45934.C @@ -0,0 +1,23 @@ +/* { dg-do run } */ + +extern "C" void abort (); + +struct B *b; + +struct B +{ + virtual void f () { } + ~B() { b->f(); } +}; + +struct D : public B +{ + virtual void f () { abort (); } +}; + +int main () +{ + D d; + b = &d; + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-5.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-5.C @@ -0,0 +1,79 @@ +/* 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" } */ + +extern "C" void abort (void); + +class B; + +class A +{ +public: + int data; + A(); + A(B *b); + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static int middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +A::A () +{ +} + +A::A (B *b) +{ + if (middleman (b, get_input ()) != 3) + abort (); +} + +static void bah () +{ + B b; + A a(&b); +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-6.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-6.C @@ -0,0 +1,72 @@ +/* Verify that ipa-cp correctly detects the dynamic type of an object + under construction when doing devirtualization. */ +/* { dg-do run } */ +/* { dg-options "-O3 -fno-inline" } */ + +extern "C" void abort (void); + +class A +{ +public: + int data; + A(); + virtual int foo (int i); +}; + +class B : public A +{ +public: + virtual int foo (int i); +}; + +class C : public A +{ +public: + virtual int foo (int i); +}; + +int A::foo (int i) +{ + return i + 1; +} + +int B::foo (int i) +{ + return i + 2; +} + +int C::foo (int i) +{ + return i + 3; +} + +static inline int __attribute__ ((always_inline)) +middleman (class A *obj, int i) +{ + return obj->foo (i); +} + +int __attribute__ ((noinline,noclone)) get_input(void) +{ + return 1; +} + +__attribute__ ((noinline)) A::A () +{ + if (middleman (this, get_input ()) != 2) + abort (); +} + +static void bah () +{ + class B b; +} + +int main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 10; i++) + bah (); + return 0; +} Index: icln/gcc/testsuite/g++.dg/ipa/devirt-6.C =================================================================== --- /dev/null +++ icln/gcc/testsuite/g++.dg/ipa/devirt-6.C @@ -0,0 +1,38 @@ +/* Verify that we either do not do any devirtualization or correctly + spot that foo changes the dynamic type of the passed object. */ + +/* { dg-do run } */ +/* { dg-options "-O3" } */ + +extern "C" void abort (void); +extern "C" void *malloc(__SIZE_TYPE__); + +inline void* operator new(__SIZE_TYPE__, void* __p) throw() { return __p;} + +int x; + +class A { +public: + virtual ~A() { } +}; + +class B : public A { +public: + virtual ~B() { if (x == 1) abort (); x = 1; } +}; + +void __attribute__((noinline,noclone)) foo (void *p) +{ + B *b = reinterpret_cast(p); + b->~B(); + new (p) A; +} + +int main() +{ + void *p = __builtin_malloc (sizeof (B)); + new (p) B; + foo(p); + reinterpret_cast(p)->~A(); + return 0; +}