public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Martin Jambor <mjambor@suse.cz>
To: Richard Guenther <rguenther@suse.de>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH 4/4] Devirtualization based on global objects
Date: Mon, 18 Apr 2011 16:32:00 -0000	[thread overview]
Message-ID: <20110418155722.GC3555@virgil.arch.suse.de> (raw)
In-Reply-To: <alpine.LNX.2.00.1104151732260.810@zhemvz.fhfr.qr>

Hi,

On Fri, Apr 15, 2011 at 05:38:50PM +0200, Richard Guenther wrote:
> On Fri, 15 Apr 2011, Martin Jambor wrote:
> 
> > Hi,
> > 
> > this is the patch that actually speeds up astar (together with the
> > previous one).  It implements devirtualization based on global
> > objects.  In the past we have refrained from doing this because in
> > general it is difficult to say whether the object is currently being
> > constructed and so it might have a dynamic type of one of its
> > ancestors.  However, if the object's class does not have any ancestors
> > that obviously cannot happen.
> > 
> > Devirtualizing in such conditions is enough to change a virtual call
> > to regwayobj::isaddtobound in 473.astar to a direct one which can and
> > should be inlined.  That seemed a good justification to implement this
> > and so the patch below does so and brings about 3.1% speedup for that
> > benchmark with LTO.
> > 
> > I acknowledge that instead of discarding all classes with ancestors it
> > would be better to check that the called virtual method has the same
> > implementation in all ancestors instead.  That is perhaps something
> > for later.
> > 
> > It took me surprisingly long to realize that this technique can be
> > used for folding virtual calls based on local automatically allocated
> > objexts too and so can be used to un-XFAIL g++.dg/opt/devirt1.c that
> > regressed in 4.6.
> > 
> > Bootstrapped and tested on x86_64-linux.  OK for trunk?
> > 
> > Thanks,
> > 
> > Martin
> > 
> > 
> > 2011-04-15  Martin Jambor  <mjambor@suse.cz>
> > 
> > 	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize
> > 	also according to actual contants.
> > 	* gimple-fold.c (gimple_extract_devirt_binfo_from_cst): New function.
> > 	(gimple_fold_obj_type_ref_call): New function.
> > 	(gimple_fold_call): Call gimple_fold_obj_type_ref_call on
> > 	OBJ_TYPE_REFs.
> > 	* gimple.h (gimple_extract_devirt_binfo_from_cst): Declare.
> > 
> > 	* testsuite/g++.dg/opt/devirt1.C: Bump to -O2, remove XFAIL.
> > 	* testsuite/g++.dg/opt/devirt2.C: New test.
> > 	* testsuite/g++.dg/ipa/devirt-g-1.C: Likewise.
> > 
> > 

...

> > Index: src/gcc/gimple-fold.c
> > ===================================================================
> > --- src.orig/gcc/gimple-fold.c
> > +++ src/gcc/gimple-fold.c
> > @@ -1438,6 +1438,95 @@ gimple_adjust_this_by_delta (gimple_stmt
> >    gimple_call_set_arg (call_stmt, 0, tmp);
> >  }
> >  
> > +/* 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).  */
> > +
> > +tree
> > +gimple_extract_devirt_binfo_from_cst (tree cst)
> > +{
> > +  HOST_WIDE_INT offset, size, max_size;
> > +  tree base, type, expected_type, binfo;
> > +  bool last_artificial = false;
> > +
> > +  if (!flag_devirtualize
> > +      || TREE_CODE (cst) != ADDR_EXPR
> > +      || TREE_CODE (TREE_TYPE (TREE_TYPE (cst))) != RECORD_TYPE)
> > +    return NULL_TREE;
> > +
> > +  cst = TREE_OPERAND (cst, 0);
> > +  expected_type = TREE_TYPE (cst);
> > +  base = get_ref_base_and_extent (cst, &offset, &size, &max_size);
> > +  type = TREE_TYPE (base);
> > +  if (!DECL_P (base)
> > +      || max_size == -1
> > +      || max_size != size
> > +      || TREE_CODE (type) != RECORD_TYPE)
> > +    return NULL_TREE;
> > +
> > +  while (true)
> > +    {
> > +      HOST_WIDE_INT pos, size;
> > +      tree fld;
> > +
> > +      if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (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;
> > +    }
> 
> Please add come comments on what this loop does.  It looks like
> it searches for the FIELD_DECL that corresponds to the incoming
> constant address (but it doesn't have to exactly point to its
> beginning?). 

Yes, an object can be within another.  Like S within R in the added
testcase g++/opt/devirt2.C.  Then it checks it is not a representative
of an ancestor but a user-defined object by looking at DECL_ARTIFICIAL.

> Note that you miss to handle the case where the
> declaration is view-converted to a different type and you
> instead use the declared type.  Which ISTR was correct as
> placement new on decls is kind of invalid.

Yes, I know I'm assuming that.

> 
> > +  if (last_artificial)
> > +    return NULL_TREE;
> > +  binfo = TYPE_BINFO (type);
> > +  if (!binfo || BINFO_N_BASE_BINFOS (binfo) > 0)
> > +    return NULL_TREE;
> > +  else
> > +    return binfo;
> > +}
> > +
> > +/* Fold a call statement to OBJ_TYPE_REF to a direct call if possible.  Return
> > +   true iff the statement was changed.  GSI determines the statement.  */
> > +
> > +static bool
> > +gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi)
> > +{
> > +  gimple stmt = gsi_stmt (*gsi);
> > +  tree ref = gimple_call_fn (stmt);
> > +  tree binfo, fndecl, delta;
> > +  HOST_WIDE_INT token;
> > +
> > +  binfo = gimple_extract_devirt_binfo_from_cst (OBJ_TYPE_REF_OBJECT (ref));
> > +  if (!binfo)
> > +    return false;
> > +
> > +  token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> 
> Either use TREE_INT_CST_LOW or first check with host_integerp.
> 
> Ok with that change.
> 

OK.

Unfortunately I have conflicted with your patch folding OBJ_TYPE_REFs
and since you did not introduce a special function for OBJ_TYPE_REFs,
I kept the functionality within gimple_fold_call too.

So this is what I am testing and what I intend to commit if all goes
well.

Thanks a lot,

Martin


2011-04-18  Martin Jambor  <mjambor@suse.cz>

	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize
	also according to actual contants.
	* gimple-fold.c (gimple_extract_devirt_binfo_from_cst): New function.
	(gimple_fold_call): Use it.
	* gimple.h (gimple_extract_devirt_binfo_from_cst): Declare.

	* testsuite/g++.dg/opt/devirt1.C: Bump to -O2, remove XFAIL.
	* testsuite/g++.dg/opt/devirt2.C: New test.
	* testsuite/g++.dg/ipa/devirt-g-1.C: Likewise.

Index: src/gcc/ipa-cp.c
===================================================================
*** src.orig/gcc/ipa-cp.c
--- src/gcc/ipa-cp.c
*************** ipcp_process_devirtualization_opportunit
*** 1246,1296 ****
  
    for (ie = node->indirect_calls; ie; ie = next_ie)
      {
!       int param_index, types_count, j;
        HOST_WIDE_INT token, anc_offset;
        tree target, delta, otr_type;
  
        next_ie = ie->next_callee;
        if (!ie->indirect_info->polymorphic)
  	continue;
        param_index = ie->indirect_info->param_index;
!       if (param_index == -1
! 	  || ipa_param_cannot_devirtualize_p (info, param_index)
! 	  || ipa_param_types_vec_empty (info, param_index))
  	continue;
  
        token = ie->indirect_info->otr_token;
        anc_offset = ie->indirect_info->anc_offset;
        otr_type = ie->indirect_info->otr_type;
        target = NULL_TREE;
!       types_count = VEC_length (tree, info->params[param_index].types);
!       for (j = 0; j < types_count; j++)
  	{
! 	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
! 	  tree d, t;
! 
  	  binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
  	  if (!binfo)
! 	    {
! 	      target = NULL_TREE;
! 	      break;
! 	    }
  
! 	  t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
! 	  if (!t)
! 	    {
! 	      target = NULL_TREE;
! 	      break;
! 	    }
! 	  else if (!target)
! 	    {
! 	      target = t;
! 	      delta = d;
! 	    }
! 	  else if (target != t || !tree_int_cst_equal (delta, d))
  	    {
! 	      target = NULL_TREE;
! 	      break;
  	    }
  	}
  
--- 1246,1316 ----
  
    for (ie = node->indirect_calls; ie; ie = next_ie)
      {
!       int param_index;
        HOST_WIDE_INT token, anc_offset;
        tree target, delta, otr_type;
+       struct ipcp_lattice *lat;
  
        next_ie = ie->next_callee;
        if (!ie->indirect_info->polymorphic)
  	continue;
        param_index = ie->indirect_info->param_index;
!       if (param_index == -1)
  	continue;
  
+       lat = ipcp_get_lattice (info, param_index);
        token = ie->indirect_info->otr_token;
        anc_offset = ie->indirect_info->anc_offset;
        otr_type = ie->indirect_info->otr_type;
        target = NULL_TREE;
!       if (lat->type == IPA_CONST_VALUE)
  	{
! 	  tree binfo = gimple_extract_devirt_binfo_from_cst (lat->constant);
! 	  if (!binfo)
! 	    continue;
  	  binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
  	  if (!binfo)
! 	    continue;
! 	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
! 						     false);
! 	}
!       else
! 	{
! 	  int  types_count, j;
  
! 	  if (ipa_param_cannot_devirtualize_p (info, param_index)
! 	      || ipa_param_types_vec_empty (info, param_index))
! 	    continue;
! 
! 	  types_count = VEC_length (tree, info->params[param_index].types);
! 	  for (j = 0; j < types_count; j++)
  	    {
! 	      tree binfo = VEC_index (tree, info->params[param_index].types, j);
! 	      tree d, t;
! 
! 	      binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
! 	      if (!binfo)
! 		{
! 		  target = NULL_TREE;
! 		  break;
! 		}
! 
! 	      t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
! 	      if (!t)
! 		{
! 		  target = NULL_TREE;
! 		  break;
! 		}
! 	      else if (!target)
! 		{
! 		  target = t;
! 		  delta = d;
! 		}
! 	      else if (target != t || !tree_int_cst_equal (delta, d))
! 		{
! 		  target = NULL_TREE;
! 		  break;
! 		}
  	    }
  	}
  
Index: src/gcc/gimple-fold.c
===================================================================
*** src.orig/gcc/gimple-fold.c
--- src/gcc/gimple-fold.c
*************** gimple_adjust_this_by_delta (gimple_stmt
*** 1441,1446 ****
--- 1441,1514 ----
    gimple_call_set_arg (call_stmt, 0, tmp);
  }
  
+ /* 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).  */
+ 
+ tree
+ gimple_extract_devirt_binfo_from_cst (tree cst)
+ {
+   HOST_WIDE_INT offset, size, max_size;
+   tree base, type, expected_type, binfo;
+   bool last_artificial = false;
+ 
+   if (!flag_devirtualize
+       || TREE_CODE (cst) != ADDR_EXPR
+       || TREE_CODE (TREE_TYPE (TREE_TYPE (cst))) != RECORD_TYPE)
+     return NULL_TREE;
+ 
+   cst = TREE_OPERAND (cst, 0);
+   expected_type = TREE_TYPE (cst);
+   base = get_ref_base_and_extent (cst, &offset, &size, &max_size);
+   type = TREE_TYPE (base);
+   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 (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (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;
+     }
+   /* Artifical 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.
     The statement may be replaced by another statement, e.g., if the call
     simplifies to a constant value. Return true if any changes were made.
*************** gimple_fold_call (gimple_stmt_iterator *
*** 1469,1478 ****
  
    /* Check for virtual calls that became direct calls.  */
    callee = gimple_call_fn (stmt);
!   if (TREE_CODE (callee) == OBJ_TYPE_REF
!       && gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
      {
!       gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
        return true;
      }
  
--- 1537,1563 ----
  
    /* Check for virtual calls that became direct calls.  */
    callee = gimple_call_fn (stmt);
!   if (TREE_CODE (callee) == OBJ_TYPE_REF)
      {
!       tree binfo, fndecl, delta, obj;
!       HOST_WIDE_INT token;
! 
!       if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
! 	{
! 	  gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
! 	  return true;
! 	}
! 
!       obj = OBJ_TYPE_REF_OBJECT (callee);
!       binfo = gimple_extract_devirt_binfo_from_cst (obj);
!       if (!binfo)
! 	return false;
!       token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee));
!       fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta, false);
!       if (!fndecl)
! 	return false;
!       gcc_assert (integer_zerop (delta));
!       gimple_call_set_fndecl (stmt, fndecl);
        return true;
      }
  
Index: src/gcc/gimple.h
===================================================================
*** src.orig/gcc/gimple.h
--- src/gcc/gimple.h
*************** const char *gimple_decl_printable_name (
*** 898,903 ****
--- 898,904 ----
  bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
  tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
  void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
+ tree gimple_extract_devirt_binfo_from_cst (tree);
  /* Returns true iff T is a valid GIMPLE statement.  */
  extern bool is_gimple_stmt (tree);
  
Index: src/gcc/testsuite/g++.dg/opt/devirt2.C
===================================================================
*** /dev/null
--- src/gcc/testsuite/g++.dg/opt/devirt2.C
***************
*** 0 ****
--- 1,11 ----
+ // { dg-do compile }
+ // { dg-options "-O2" }
+ // { dg-final { scan-assembler-times "xyzzy" 2 } }
+ 
+ struct S { S(); virtual void xyzzy(); };
+ struct R { int a; S s; R(); };
+ S s;
+ R r;
+ inline void foo(S *p) { p->xyzzy(); }
+ void bar() {foo(&s);}
+ void bah() {foo(&r.s);}
Index: src/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
===================================================================
*** /dev/null
--- src/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
***************
*** 0 ****
--- 1,24 ----
+ // { dg-do compile }
+ // { dg-options "-O2 -fdump-ipa-cp -fdump-tree-optimized" }
+ 
+ struct S { S(); virtual void xyzzy(); void otherstuff(); };
+ struct R { int a; S s; R(); };
+ S s;
+ R r;
+ 
+ void S::xyzzy ()
+ {
+   otherstuff ();
+   otherstuff ();
+ }
+ 
+ static void __attribute__ ((noinline)) foo(S *p) { p->xyzzy(); }
+ void bar() {foo(&s); }
+ 
+ static void __attribute__ ((noinline)) foh(S *p) { p->xyzzy(); }
+ void bah() {foh(&r.s); }
+ 
+ /* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*S::xyzzy" "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/opt/devirt1.C
===================================================================
*** src.orig/gcc/testsuite/g++.dg/opt/devirt1.C
--- src/gcc/testsuite/g++.dg/opt/devirt1.C
***************
*** 1,6 ****
  // { dg-do compile }
! // { dg-options "-O" }
! // { dg-final { scan-assembler "xyzzy" { xfail *-*-* } } }
  
  struct S { S(); virtual void xyzzy(); };
  inline void foo(S *s) { s->xyzzy(); }
--- 1,6 ----
  // { dg-do compile }
! // { dg-options "-O2" }
! // { dg-final { scan-assembler "xyzzy" } }
  
  struct S { S(); virtual void xyzzy(); };
  inline void foo(S *s) { s->xyzzy(); }

  reply	other threads:[~2011-04-18 15:57 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-04-15 13:00 [PATCH 0/4] Devirtualization fix and improvements Martin Jambor
2011-04-15 13:00 ` [PATCH 4/4] Devirtualization based on global objects Martin Jambor
2011-04-15 15:41   ` Richard Guenther
2011-04-18 16:32     ` Martin Jambor [this message]
2011-04-15 13:00 ` [PATCH 2/4] Handle calls to ancestor objects in IPA-CP devirtualization Martin Jambor
2011-04-15 15:29   ` Richard Guenther
2011-04-18 15:57     ` Martin Jambor
2011-04-15 13:06 ` [PATCH 1/4] Remove usesess and wrong code from ipa_analyze_virtual_call_uses Martin Jambor
2011-04-15 15:21   ` Richard Guenther
2011-04-15 13:06 ` [PATCH 3/4] Simple relaxation of dynamic type change detection routine Martin Jambor
2011-04-15 15:40   ` Richard Guenther
2011-04-18 15:57     ` Martin Jambor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110418155722.GC3555@virgil.arch.suse.de \
    --to=mjambor@suse.cz \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=rguenther@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).