From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21403 invoked by alias); 13 Feb 2010 18:12:50 -0000 Received: (qmail 21390 invoked by uid 22791); 13 Feb 2010 18:12:48 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mail-pz0-f203.google.com (HELO mail-pz0-f203.google.com) (209.85.222.203) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 13 Feb 2010 18:12:43 +0000 Received: by pzk41 with SMTP id 41so7942259pzk.0 for ; Sat, 13 Feb 2010 10:12:42 -0800 (PST) MIME-Version: 1.0 Received: by 10.141.124.15 with SMTP id b15mr2036858rvn.135.1266084762135; Sat, 13 Feb 2010 10:12:42 -0800 (PST) In-Reply-To: <20100213180157.270677864@alvy.suse.cz> References: <20100213180136.555197900@alvy.suse.cz> <20100213180157.270677864@alvy.suse.cz> Date: Sat, 13 Feb 2010 18:12:00 -0000 Message-ID: <84fc9c001002131012r2b82283fw2a895eb429ccd037@mail.gmail.com> Subject: Re: [PATCH 3/6] Folding of virtual calls From: Richard Guenther To: Martin Jambor Cc: GCC Patches , Jan Hubicka Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2010-02/txt/msg00519.txt.bz2 On Sat, Feb 13, 2010 at 7:01 PM, Martin Jambor wrote: > Hi, > > this patch has not changed at all since I posted it last time in > January but needs to be applied before the next one is so I send it > along again. > > The purpose of the patch is to allow folding of more complex > OBJ_TYPE_REFs, as opposed to only the simple class we are able to fold > today. =A0This patch only implements devirtualization through statement > folding (which is also greatly improved, for example we can now fold > when early inlining makes type information available). =A0However, it > also facilitates interface necessary for IPA devirtualization > including indirect inlining of virtual calls. > > The juggling with binfos is a bit bizarre and perhaps a C++ maintainer > should have a look at it but it has worked seamlessly in my tests for > a few months now so I'm becoming quite confident it is in fact > correct. May I ask you to start populating new gimple-fold.[ch] files instead of adding to gimple.c? Eventually all of fold_stmt should move there from tree-ssa-ccp.c, but of course not as part of this patch. Thanks, Richard. > Thanks, > > Martin > > > 2010-02-10 =A0Martin Jambor =A0 > > =A0 =A0 =A0 =A0* gimple.c (get_base_binfo_for_type): New function. > =A0 =A0 =A0 =A0(get_relevant_ref_binfo_single_inh): New function. > =A0 =A0 =A0 =A0(get_relevant_ref_binfo_multi_inh): New function. > =A0 =A0 =A0 =A0(gimple_fold_obj_type_ref_known_binfo): New function. > =A0 =A0 =A0 =A0(gimple_fold_obj_type_ref): Get binfo from > =A0 =A0 =A0 =A0get_relevant_ref_binfo_single_inh and > =A0 =A0 =A0 =A0get_relevant_ref_binfo_multi_inh and use > =A0 =A0 =A0 =A0gimple_fold_obj_type_ref_known_binfo. > =A0 =A0 =A0 =A0* gimple.h (gimple_fold_obj_type_ref): Declare. > =A0 =A0 =A0 =A0(gimple_fold_obj_type_ref_known_binfo): Declare. > =A0 =A0 =A0 =A0* tree-ssa-ccp.c (fold_gimple_call): Simplify condition for > =A0 =A0 =A0 =A0folding virtual calls and call gimple_fold_obj_type_ref. > > > Index: icln/gcc/gimple.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- icln.orig/gcc/gimple.c > +++ icln/gcc/gimple.c > @@ -4632,27 +4632,167 @@ gimple_decl_printable_name (tree decl, i > =A0 return IDENTIFIER_POINTER (DECL_NAME (decl)); > =A0} > > +/* Search for a base binfo of BINFO that corresponds to TYPE and return = it if > + =A0 it is found or NULL_TREE if it is not. */ > + > +static tree > +get_base_binfo_for_type (tree binfo, tree type) > +{ > + =A0int i; > + =A0tree base_binfo; > + =A0tree res =3D NULL_TREE; > + > + =A0for (i =3D 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) > + =A0 =A0if (TREE_TYPE (base_binfo) =3D=3D type) > + =A0 =A0 =A0{ > + =A0 =A0 =A0 gcc_assert (!res); > + =A0 =A0 =A0 res =3D base_binfo; > + =A0 =A0 =A0} > + > + =A0return res; > +} > + > +/* Return a binfo describing the true type of object referenced by expre= ssion > + =A0 REF if all component references access first ancestors. =A0REF can = consist of > + =A0 a series of COMPONENT_REFs of with a declaration or an INDIREC_REF.= =A0It can > + =A0 also be just a simple declaration, indirect reference or an SSA_NAM= E. =A0If > + =A0 the function discoveres an INIDRECT_REF or an SSA_NAME, it will ass= ume that > + =A0 the encapsulating type is described by KNOWN_BINFO or return NULL_T= REE if > + =A0 KNOWN_BINFO is NULL_TREE. =A0Otherwise the first nonartifical field= declaration > + =A0 or the base declaration will be examined to get the encapsulating t= ype. =A0If > + =A0 a COMPONENT_REF referencing an ancestor which is not the first one,= this > + =A0 function returns NULL_TREE and sets *try_multi to true. =A0*/ > + > +static tree > +get_relevant_ref_binfo_single_inh (tree ref, tree known_binfo, bool *try= _multi) > +{ > + =A0while (true) > + =A0 =A0{ > + =A0 =A0 =A0if (TREE_CODE (ref) =3D=3D COMPONENT_REF) > + =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 tree par_type; > + =A0 =A0 =A0 =A0 tree binfo, base_binfo; > + =A0 =A0 =A0 =A0 tree field =3D TREE_OPERAND (ref, 1); > + > + =A0 =A0 =A0 =A0 if (!DECL_ARTIFICIAL (field)) > + =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 tree type =3D TREE_TYPE (field); > + =A0 =A0 =A0 =A0 =A0 =A0 if (TREE_CODE (type) =3D=3D RECORD_TYPE) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return TYPE_BINFO (type); > + =A0 =A0 =A0 =A0 =A0 =A0 else > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0 =A0 =A0 =A0 } > + > + =A0 =A0 =A0 =A0 par_type =3D TREE_TYPE (TREE_OPERAND (ref, 0)); > + =A0 =A0 =A0 =A0 binfo =3D TYPE_BINFO (par_type); > + =A0 =A0 =A0 =A0 if (!binfo > + =A0 =A0 =A0 =A0 =A0 =A0 || BINFO_N_BASE_BINFOS (binfo) =3D=3D 0) > + =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 if (try_multi) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *try_multi =3D 1; > + =A0 =A0 =A0 =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 =A0 base_binfo =3D BINFO_BASE_BINFO (binfo, 0); > + =A0 =A0 =A0 =A0 if (TREE_TYPE (base_binfo) !=3D TREE_TYPE (field)) > + =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 if (try_multi) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *try_multi =3D 1; > + =A0 =A0 =A0 =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0 =A0 =A0 =A0 } > + > + =A0 =A0 =A0 =A0 ref =3D TREE_OPERAND (ref, 0); > + =A0 =A0 =A0 } > + =A0 =A0 =A0else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) =3D=3D = RECORD_TYPE) > + =A0 =A0 =A0 return TYPE_BINFO (TREE_TYPE (ref)); > + =A0 =A0 =A0else if (known_binfo > + =A0 =A0 =A0 =A0 =A0 =A0 =A0&& (TREE_CODE (ref) =3D=3D SSA_NAME > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|| TREE_CODE (ref) =3D=3D INDIRECT_R= EF)) > + =A0 =A0 =A0 return known_binfo; > + =A0 =A0 =A0else > + =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0} > +} > > -/* Fold a OBJ_TYPE_REF expression to the address of a function. > - =A0 KNOWN_TYPE carries the true type of OBJ_TYPE_REF_OBJECT(REF). =A0Ad= apted > - =A0 from cp_fold_obj_type_ref, but it tolerates types with no binfo > - =A0 data. =A0*/ > + > +/* Return a binfo describing the part of object referenced by expression= REF. > + =A0 This can and often is a base_binfo of a descendatn binfo. REF can c= onsist of > + =A0 a series of COMPONENT_REFs of with a declaration or an INDIREC_REF.= =A0It can > + =A0 also be just a simple declaration, indirect reference or an SSA_NAM= E. =A0If > + =A0 the function discoveres an INIDRECT_REF or an SSA_NAME, it will ass= ume that > + =A0 the encapsulating type is described by KNOWN_BINFO or return NULL_T= REE if > + =A0 KNOWN_BINFO is NULL_TREE. =A0Otherwise the first nonartifical field > + =A0 declaration or the base declaration will be examined to get the > + =A0 encapsulating type. =A0*/ > + > +static tree > +get_relevant_ref_binfo_multi_inh (tree ref, tree known_binfo) > +{ > + =A0if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) =3D=3D RECORD_TYPE) > + =A0 =A0return TYPE_BINFO (TREE_TYPE (ref)); > + =A0else if (TREE_CODE (ref) =3D=3D COMPONENT_REF) > + =A0 =A0{ > + =A0 =A0 =A0tree desc_binfo; > + =A0 =A0 =A0tree field =3D TREE_OPERAND (ref, 1); > + > + =A0 =A0 =A0if (!DECL_ARTIFICIAL (field)) > + =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 tree type =3D TREE_TYPE (field); > + =A0 =A0 =A0 =A0 if (TREE_CODE (type) =3D=3D RECORD_TYPE) > + =A0 =A0 =A0 =A0 =A0 return TYPE_BINFO (type); > + =A0 =A0 =A0 =A0 else > + =A0 =A0 =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0 =A0 } > + > + =A0 =A0 =A0desc_binfo =3D get_relevant_ref_binfo_multi_inh (TREE_OPERAN= D (ref, 0), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0known_binfo); > + =A0 =A0 =A0if (!desc_binfo) > + =A0 =A0 =A0 return NULL_TREE; > + =A0 =A0 =A0return get_base_binfo_for_type (desc_binfo, TREE_TYPE (field= )); > + =A0 =A0} > + =A0else if (known_binfo > + =A0 =A0 =A0 =A0 =A0&& (TREE_CODE (ref) =3D=3D SSA_NAME > + =A0 =A0 =A0 =A0 =A0 =A0 =A0|| TREE_CODE (ref) =3D=3D INDIRECT_REF)) > + =A0 =A0return known_binfo; > + =A0else > + =A0 =A0return NULL_TREE; > +} > + > +/* Return a binfo describing the part of object referenced by expression= REF > + =A0 using both get_relevant_ref_binfo_single_inh and > + =A0 get_relevant_ref_binfo_multi_inh in this particular order. =A0*/ > > =A0tree > -gimple_fold_obj_type_ref (tree ref, tree known_type) > +gimple_get_relevant_ref_binfo (tree ref, tree known_binfo) > +{ > + =A0bool try_multi =3D false; > + =A0tree binfo; > + > + =A0binfo =3D get_relevant_ref_binfo_single_inh (ref, known_binfo, &try_= multi); > + =A0if (!binfo && try_multi) > + =A0 =A0binfo =3D get_relevant_ref_binfo_multi_inh (ref, known_binfo); > + =A0return binfo; > +} > + > + > +/* Fold a OBJ_TYPE_REF expression to the address of a function. TOKEN is > + =A0 integer form of OBJ_TYPE_REF_TOKEN of the reference expression. =A0= KNOWN_BINFO > + =A0 carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(R= EF). =A0*/ > + > +tree > +gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_bi= nfo) > =A0{ > - =A0HOST_WIDE_INT index; > =A0 HOST_WIDE_INT i; > - =A0tree v; > - =A0tree fndecl; > + =A0tree v, fndecl; > > - =A0if (TYPE_BINFO (known_type) =3D=3D NULL_TREE) > + =A0/* If binfo flag 2 is not set, this binfo does not "have its own vir= tual > + =A0 =A0 table" (according to cp/cp-tree.h) and cannot be safely used for > + =A0 =A0 devirtualization. =A0Purely empirical experience also shows tha= t we can also > + =A0 =A0 bail out if flag 5 is set. =A0This test also probably works in = lto. =A0*/ > + =A0if (BINFO_FLAG_5 (known_binfo)) > =A0 =A0 return NULL_TREE; > - > - =A0v =3D BINFO_VIRTUALS (TYPE_BINFO (known_type)); > - =A0index =3D tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1); > + =A0v =3D BINFO_VIRTUALS (known_binfo); > =A0 i =3D 0; > - =A0while (i !=3D index) > + =A0while (i !=3D token) > =A0 =A0 { > =A0 =A0 =A0 i +=3D (TARGET_VTABLE_USES_DESCRIPTORS > =A0 =A0 =A0 =A0 =A0 =A0? TARGET_VTABLE_USES_DESCRIPTORS : 1); > @@ -4660,15 +4800,34 @@ gimple_fold_obj_type_ref (tree ref, tree > =A0 =A0 } > > =A0 fndecl =3D TREE_VALUE (v); > + =A0return build_fold_addr_expr (fndecl); > +} > > -#ifdef ENABLE_CHECKING > - =A0gcc_assert (tree_int_cst_equal (OBJ_TYPE_REF_TOKEN (ref), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 DECL_VI= NDEX (fndecl))); > -#endif > > - =A0cgraph_node (fndecl)->local.vtable_method =3D true; > +/* Fold a OBJ_TYPE_REF expression to the address of a function. =A0If KN= OWN_TYPE > + =A0 is not NULL_TREE, it is the true type of the outmost encapsulating = object if > + =A0 that comes from a pointer SSA_NAME. =A0If the true outmost encapsul= ating type > + =A0 can be determined from a declaration OBJ_TYPE_REF_OBJECT(REF), it i= s used > + =A0 regardless of KNOWN_TYPE (which thuc can be NULL_TREE). =A0*/ > > - =A0return build_fold_addr_expr (fndecl); > +tree > +gimple_fold_obj_type_ref (tree ref, tree known_type) > +{ > + =A0tree obj =3D OBJ_TYPE_REF_OBJECT (ref); > + =A0tree known_binfo =3D known_type ? TYPE_BINFO (known_type) : NULL_TRE= E; > + =A0tree binfo; > + > + =A0if (TREE_CODE (obj) =3D=3D ADDR_EXPR) > + =A0 =A0obj =3D TREE_OPERAND (obj, 0); > + > + =A0binfo =3D gimple_get_relevant_ref_binfo (obj, known_binfo); > + =A0if (binfo) > + =A0 =A0{ > + =A0 =A0 =A0HOST_WIDE_INT token =3D tree_low_cst (OBJ_TYPE_REF_TOKEN (re= f), 1); > + =A0 =A0 =A0return gimple_fold_obj_type_ref_known_binfo (token, binfo); > + =A0 =A0} > + =A0else > + =A0 =A0return NULL_TREE; > =A0} > > =A0#include "gt-gimple.h" > Index: icln/gcc/tree-ssa-ccp.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- icln.orig/gcc/tree-ssa-ccp.c > +++ icln/gcc/tree-ssa-ccp.c > @@ -3007,9 +3007,6 @@ fold_gimple_call (gimple_stmt_iterator * > =A0 =A0 } > =A0 else > =A0 =A0 { > - =A0 =A0 =A0/* Check for resolvable OBJ_TYPE_REF. =A0The only sorts we c= an resolve > - =A0 =A0 =A0 =A0 here are when we've propagated the address of a decl in= to the > - =A0 =A0 =A0 =A0 object slot. =A0*/ > =A0 =A0 =A0 /* ??? Should perhaps do this in fold proper. =A0However, doi= ng it > =A0 =A0 =A0 =A0 =A0there requires that we create a new CALL_EXPR, and tha= t requires > =A0 =A0 =A0 =A0 =A0copying EH region info to the new node. =A0Easier to j= ust do it > @@ -3017,19 +3014,11 @@ fold_gimple_call (gimple_stmt_iterator * > =A0 =A0 =A0 /* ??? Is there a good reason not to do this in fold_stmt_inp= lace? =A0*/ > =A0 =A0 =A0 callee =3D gimple_call_fn (stmt); > =A0 =A0 =A0 if (TREE_CODE (callee) =3D=3D OBJ_TYPE_REF > - =A0 =A0 =A0 =A0 =A0&& lang_hooks.fold_obj_type_ref > - =A0 =A0 =A0 =A0 =A0&& TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) =3D=3D A= DDR_EXPR > - =A0 =A0 =A0 =A0 =A0&& DECL_P (TREE_OPERAND > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (OBJ_TYPE_REF_OBJECT (callee), = 0))) > + =A0 =A0 =A0 =A0 =A0&& TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) =3D=3D A= DDR_EXPR) > =A0 =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 =A0 tree t; > > - =A0 =A0 =A0 =A0 =A0/* ??? Caution: Broken ADDR_EXPR semantics means that > - =A0 =A0 =A0 =A0 =A0 =A0 looking at the type of the operand of the addr_= expr > - =A0 =A0 =A0 =A0 =A0 =A0 can yield an array type. =A0See silly exception= in > - =A0 =A0 =A0 =A0 =A0 =A0 check_pointer_types_r. =A0*/ > - =A0 =A0 =A0 =A0 =A0t =3D TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (cal= lee))); > - =A0 =A0 =A0 =A0 =A0t =3D lang_hooks.fold_obj_type_ref (callee, t); > + =A0 =A0 =A0 =A0 =A0t =3D gimple_fold_obj_type_ref (callee, NULL_TREE); > =A0 =A0 =A0 =A0 =A0 if (t) > =A0 =A0 =A0 =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 =A0 =A0 =A0 gimple_call_set_fn (stmt, t); > Index: icln/gcc/gimple.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- icln.orig/gcc/gimple.h > +++ icln/gcc/gimple.h > @@ -864,7 +864,9 @@ unsigned get_gimple_rhs_num_ops (enum tr > =A0#define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO) > =A0gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); > =A0const char *gimple_decl_printable_name (tree, int); > +tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo); > =A0tree gimple_fold_obj_type_ref (tree, tree); > +tree gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT, tree); > > =A0/* Returns true iff T is a valid GIMPLE statement. =A0*/ > =A0extern bool is_gimple_stmt (tree); > Index: icln/gcc/testsuite/g++.dg/otr-fold-1.C > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- /dev/null > +++ icln/gcc/testsuite/g++.dg/otr-fold-1.C > @@ -0,0 +1,77 @@ > +/* Verify that simple virtual calls are inlined even without early > + =A0 inlining, even when a typecast to an ancestor is involved along the > + =A0 way. =A0*/ > +/* { dg-do run } */ > +/* { dg-options "-O -fdump-tree-optimized-slim" =A0} */ > + > +extern "C" void abort (void); > + > +class Distraction > +{ > +public: > + =A0float f; > + =A0double d; > + =A0Distraction () > + =A0{ > + =A0 =A0f =3D 8.3; > + =A0 =A0d =3D 10.2; > + =A0} > + =A0virtual float bar (float z); > +}; > + > +class A > +{ > +public: > + =A0int data; > + =A0virtual int foo (int i); > +}; > + > + > +class B : public Distraction, public A > +{ > +public: > + =A0virtual int foo (int i); > +}; > + > +float Distraction::bar (float z) > +{ > + =A0f +=3D z; > + =A0return f/2; > +} > + > +int A::foo (int i) > +{ > + =A0return i + 1; > +} > + > +int B::foo (int i) > +{ > + =A0return i + 2; > +} > + > +int __attribute__ ((noinline,noclone)) get_input(void) > +{ > + =A0return 1; > +} > + > +static int middleman_1 (class A *obj, int i) > +{ > + =A0return obj->foo (i); > +} > + > +static int middleman_2 (class B *obj, int i) > +{ > + =A0return middleman_1 (obj, i); > +} > + > +int main (int argc, char *argv[]) > +{ > + =A0class B b; > + > + =A0if (middleman_2 (&b, get_input ()) !=3D 3) > + =A0 =A0abort (); > + =A0return 0; > +} > + > +/* { dg-final { scan-tree-dump "=3D B::foo" =A0"optimized" =A0} } */ > +/* { dg-final { cleanup-tree-dump "optimized" } } */ > >