This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Harden ipa-devirt for invalid programs, part 1 (and also fix a wrong code issue)


Hi,
this is first part of fix for lto/59468 that is about ipa-devirt ICEs on
invalid C++ programs (violating ODR rule).

This patch hardens it wrt various low-level VTABLE corruptions - the case
virtual tables gets too small, their initalizer is missing or user decided to
rewrite the symbol by a different kind of variable so we no longer understand
its constructor.

While working on it I did some testing and noticed that there is important
bug WRT coplete list.  We may end up not listing a node because we can not
refer it, but we forget to clear the list.  This is sported by the testcases
added. 

To avoid bugs like this, I decided to make can_refer explicit 
when calling gimple_get_virt_method_for_vtable (that function may return NULL
also in other cases when caller is confused - this will not happen for
ipa-devirt but may happen for old devirt code in ipa-prop that I plan to remove
next stage1).  This allows bit more sanity checks about updating complete
flag.

Bootstrapped/regtested x86_64-linux, will commit it after bit of firefox
testing.

As a followup I will add ODR violation warning when virtual tables do not match.

I also noticed that we may end up not shipping virtual tables that matters to
LTO because they are not referenced directly by any of the units. This seems a
bit more important than anticipated.  Have old patch to make this happen but it
seems bit expensive without measurable benefits (i.e. improvements in
devirtualization counts) and it triggers interesting issues with free_lang_data
that I would like to discuss separately.

Will evaulate it bit further.

Honza

	PR lto/59468
	* ipa-utils.h (possible_polymorphic_call_targets): Update prototype
	and wrapper.
	* ipa-devirt.c: Include demangle.h
	(odr_violation_reported): New static variable.
	(add_type_duplicate): Update odr_violations.
	(maybe_record_node): Add completep parameter; update it.
	(record_target_from_binfo): Add COMPLETEP parameter;
	update it as needed.
	(possible_polymorphic_call_targets_1): Likewise.
	(struct polymorphic_call_target_d): Add nonconstruction_targets;
	rename FINAL to COMPLETE.
	(record_targets_from_bases): Sanity check we found the binfo;
	fix COMPLETEP updating.
	(possible_polymorphic_call_targets): Add NONCONSTRUTION_TARGETSP
	parameter, fix computing of COMPLETEP.
	(dump_possible_polymorphic_call_targets): Imrove readability of dump; at
	LTO time do demangling.
	(ipa_devirt): Use nonconstruction_targets; Improve dumps.
	* gimple-fold.c (gimple_get_virt_method_for_vtable): Add can_refer
	parameter.
	(gimple_get_virt_method_for_binfo): Likewise.
	* gimple-fold.h (gimple_get_virt_method_for_binfo,
	gimple_get_virt_method_for_vtable): Update prototypes.

	* g++.dg/ipa/devirt-27.C: New testcase.
	* g++.dg/ipa/devirt-26.C: New testcase.
Index: gimple-fold.c
===================================================================
*** gimple-fold.c	(revision 207649)
--- gimple-fold.c	(working copy)
*************** fold_const_aggregate_ref (tree t)
*** 3170,3191 ****
  }
  
  /* Lookup virtual method with index TOKEN in a virtual table V
!    at OFFSET.  */
  
  tree
  gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
  				   tree v,
! 				   unsigned HOST_WIDE_INT offset)
  {
    tree vtable = v, init, fn;
    unsigned HOST_WIDE_INT size;
    unsigned HOST_WIDE_INT elt_size, access_index;
    tree domain_type;
  
    /* First of all double check we have virtual table.  */
    if (TREE_CODE (v) != VAR_DECL
        || !DECL_VIRTUAL_P (v))
!     return NULL_TREE;
  
    init = ctor_for_folding (v);
  
--- 3170,3204 ----
  }
  
  /* Lookup virtual method with index TOKEN in a virtual table V
!    at OFFSET.  
!    Set CAN_REFER if non-NULL to false if method
!    is not referable or if the virtual table is ill-formed (such as rewriten
!    by non-C++ produced symbol). Otherwise just return NULL in that calse.  */
  
  tree
  gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
  				   tree v,
! 				   unsigned HOST_WIDE_INT offset,
! 				   bool *can_refer)
  {
    tree vtable = v, init, fn;
    unsigned HOST_WIDE_INT size;
    unsigned HOST_WIDE_INT elt_size, access_index;
    tree domain_type;
  
+   if (can_refer)
+     *can_refer = true;
+ 
    /* First of all double check we have virtual table.  */
    if (TREE_CODE (v) != VAR_DECL
        || !DECL_VIRTUAL_P (v))
!     {
!       gcc_assert (in_lto_p);
!       /* Pass down that we lost track of the target.  */
!       if (can_refer)
! 	*can_refer = false;
!       return NULL_TREE;
!     }
  
    init = ctor_for_folding (v);
  
*************** gimple_get_virt_method_for_vtable (HOST_
*** 3197,3202 ****
--- 3210,3218 ----
    if (init == error_mark_node)
      {
        gcc_assert (in_lto_p);
+       /* Pass down that we lost track of the target.  */
+       if (can_refer)
+ 	*can_refer = false;
        return NULL_TREE;
      }
    gcc_checking_assert (TREE_CODE (TREE_TYPE (v)) == ARRAY_TYPE);
*************** gimple_get_virt_method_for_vtable (HOST_
*** 3247,3253 ****
  	 ends up in other partition, because we found devirtualization
  	 possibility too late.  */
        if (!can_refer_decl_in_current_unit_p (fn, vtable))
! 	return NULL_TREE;
      }
  
    /* Make sure we create a cgraph node for functions we'll reference.
--- 3263,3276 ----
  	 ends up in other partition, because we found devirtualization
  	 possibility too late.  */
        if (!can_refer_decl_in_current_unit_p (fn, vtable))
! 	{
! 	  if (can_refer)
! 	    {
! 	      *can_refer = false;
! 	      return fn;
! 	    }
! 	  return NULL_TREE;
! 	}
      }
  
    /* Make sure we create a cgraph node for functions we'll reference.
*************** gimple_get_virt_method_for_vtable (HOST_
*** 3261,3270 ****
  /* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN
     is integer form of OBJ_TYPE_REF_TOKEN of the reference expression.
     KNOWN_BINFO carries the binfo describing the true type of
!    OBJ_TYPE_REF_OBJECT(REF).  */
  
  tree
! gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo)
  {
    unsigned HOST_WIDE_INT offset;
    tree v;
--- 3284,3297 ----
  /* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN
     is integer form of OBJ_TYPE_REF_TOKEN of the reference expression.
     KNOWN_BINFO carries the binfo describing the true type of
!    OBJ_TYPE_REF_OBJECT(REF).
!    Set CAN_REFER if non-NULL to false if method
!    is not referable or if the virtual table is ill-formed (such as rewriten
!    by non-C++ produced symbol). Otherwise just return NULL in that calse.  */
  
  tree
! gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo,
! 				  bool *can_refer)
  {
    unsigned HOST_WIDE_INT offset;
    tree v;
*************** gimple_get_virt_method_for_binfo (HOST_W
*** 3275,3283 ****
      return NULL_TREE;
  
    if (!vtable_pointer_value_to_vtable (v, &v, &offset))
!     return NULL_TREE;
! 
!   return gimple_get_virt_method_for_vtable (token, v, offset);
  }
  
  /* Return true iff VAL is a gimple expression that is known to be
--- 3302,3313 ----
      return NULL_TREE;
  
    if (!vtable_pointer_value_to_vtable (v, &v, &offset))
!     {
!       if (can_refer)
! 	*can_refer = false;
!       return NULL_TREE;
!     }
!   return gimple_get_virt_method_for_vtable (token, v, offset, can_refer);
  }
  
  /* Return true iff VAL is a gimple expression that is known to be
Index: gimple-fold.h
===================================================================
*** gimple-fold.h	(revision 207649)
--- gimple-fold.h	(working copy)
*************** extern tree gimple_fold_stmt_to_constant
*** 36,44 ****
  extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree));
  extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
  extern tree fold_const_aggregate_ref (tree);
! extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree);
  extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree,
! 					       unsigned HOST_WIDE_INT);
  extern bool gimple_val_nonnegative_real_p (tree);
  extern tree gimple_fold_indirect_ref (tree);
  extern bool arith_code_with_undefined_signed_overflow (tree_code);
--- 36,46 ----
  extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree));
  extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
  extern tree fold_const_aggregate_ref (tree);
! extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree,
! 					      bool *can_refer = NULL);
  extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree,
! 					       unsigned HOST_WIDE_INT,
! 					       bool *can_refer = NULL);
  extern bool gimple_val_nonnegative_real_p (tree);
  extern tree gimple_fold_indirect_ref (tree);
  extern bool arith_code_with_undefined_signed_overflow (tree_code);
Index: testsuite/g++.dg/ipa/devirt-27.C
===================================================================
*** testsuite/g++.dg/ipa/devirt-27.C	(revision 0)
--- testsuite/g++.dg/ipa/devirt-27.C	(revision 0)
***************
*** 0 ****
--- 1,27 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O3 -fdump-ipa-devirt -fdump-tree-optimized"  } */
+ struct A
+  {
+    int a;
+  };
+ struct B
+  {
+   __attribute__ ((visibility("default")))
+    virtual int foo(void) {return 42;}
+    int b;
+  };
+ struct C: A,B
+  {
+   __attribute__ ((visibility("hidden")))
+    virtual int foo(void);
+  };
+ 
+ struct C c;
+ int test(void)
+ {
+   struct C *d=&c;
+   struct B *b=d;
+   return d->foo()+b->foo();
+ }
+ /* { dg-final { scan-tree-dump "OBJ_TYPE_REF" "optimized"  } } */
+ /* { dg-final { cleanup-tree-dump "optimized" } } */
Index: testsuite/g++.dg/ipa/devirt-26.C
===================================================================
*** testsuite/g++.dg/ipa/devirt-26.C	(revision 0)
--- testsuite/g++.dg/ipa/devirt-26.C	(revision 0)
***************
*** 0 ****
--- 1,29 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O3 -fdump-ipa-devirt"  } */
+ struct A
+  {
+    int a;
+    virtual int bar(void) {return a;}
+  };
+ struct B
+  {
+    virtual int foo(void) {return b;}
+    int b;
+  };
+ struct C: A,B
+  {
+    virtual int foo(void) {return a;}
+  };
+ 
+ struct C c;
+ int test(void)
+ {
+   struct C *d=&c;
+   struct B *b=d;
+   return d->foo()+b->foo();
+ }
+ /* The call to b->foo() is perfectly devirtualizable because C can not be in construction
+    when &c was used, but we can not analyze that so far.  Test that we at least speculate
+    that type is in the construction.  */
+ /* { dg-final { scan-ipa-dump "Speculatively devirtualizing" "devirt"  } } */
+ /* { dg-final { cleanup-ipa-dump "devirt" } } */
Index: ipa-utils.h
===================================================================
*** ipa-utils.h	(revision 207649)
--- ipa-utils.h	(working copy)
*************** vec <cgraph_node *>
*** 76,82 ****
  possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
  				   ipa_polymorphic_call_context,
  				   bool *final = NULL,
! 				   void **cache_token = NULL);
  odr_type get_odr_type (tree, bool insert = false);
  void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT,
  					     const ipa_polymorphic_call_context &);
--- 76,83 ----
  possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
  				   ipa_polymorphic_call_context,
  				   bool *final = NULL,
! 				   void **cache_token = NULL,
! 				   int *nonconstruction_targets = NULL);
  odr_type get_odr_type (tree, bool insert = false);
  void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT,
  					     const ipa_polymorphic_call_context &);
*************** bool vtable_pointer_value_to_vtable (tre
*** 105,111 ****
  inline vec <cgraph_node *>
  possible_polymorphic_call_targets (struct cgraph_edge *e,
  				   bool *final = NULL,
! 				   void **cache_token = NULL)
  {
    gcc_checking_assert (e->indirect_info->polymorphic);
    ipa_polymorphic_call_context context = {e->indirect_info->offset,
--- 106,113 ----
  inline vec <cgraph_node *>
  possible_polymorphic_call_targets (struct cgraph_edge *e,
  				   bool *final = NULL,
! 				   void **cache_token = NULL,
! 				   int *nonconstruction_targets = NULL)
  {
    gcc_checking_assert (e->indirect_info->polymorphic);
    ipa_polymorphic_call_context context = {e->indirect_info->offset,
*************** possible_polymorphic_call_targets (struc
*** 115,121 ****
    return possible_polymorphic_call_targets (e->indirect_info->otr_type,
  					    e->indirect_info->otr_token,
  					    context,
! 					    final, cache_token);
  }
  
  /* Same as above but taking OBJ_TYPE_REF as an parameter.  */
--- 117,124 ----
    return possible_polymorphic_call_targets (e->indirect_info->otr_type,
  					    e->indirect_info->otr_token,
  					    context,
! 					    final, cache_token,
! 					    nonconstruction_targets);
  }
  
  /* Same as above but taking OBJ_TYPE_REF as an parameter.  */
Index: ipa-devirt.c
===================================================================
*** ipa-devirt.c	(revision 207649)
--- ipa-devirt.c	(working copy)
*************** along with GCC; see the file COPYING3.
*** 128,133 ****
--- 128,136 ----
  #include "ipa-inline.h"
  #include "diagnostic.h"
  #include "tree-dfa.h"
+ #include "demangle.h"
+ 
+ static bool odr_violation_reported = false;
  
  /* Dummy polymorphic call context.  */
  
*************** add_type_duplicate (odr_type val, tree t
*** 297,302 ****
--- 300,306 ----
        if (!types_compatible_p (val->type, type))
  	{
  	  merge = false;
+ 	  odr_violation_reported = true;
  	  if (BINFO_VTABLE (TYPE_BINFO (val->type))
  	      && warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0,
  			     "type %qD violates one definition rule  ",
*************** add_type_duplicate (odr_type val, tree t
*** 332,337 ****
--- 336,342 ----
        if (base_mismatch)
  	{
  	  merge = false;
+ 	  odr_violation_reported = true;
  
  	  if (warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0,
  			  "type %qD violates one definition rule  ",
*************** build_type_inheritance_graph (void)
*** 594,610 ****
  }
  
  /* If TARGET has associated node, record it in the NODES array.
!    if TARGET can not be inserted (for example because its body was
!    already removed and there is no way to refer to it), clear COMPLETEP.  */
  
  static void
  maybe_record_node (vec <cgraph_node *> &nodes,
  		   tree target, pointer_set_t *inserted,
  		   bool *completep)
  {
    struct cgraph_node *target_node;
    enum built_in_function fcode;
  
    if (!target
        /* Those are used to mark impossible scenarios.  */
        || (fcode = DECL_FUNCTION_CODE (target))
--- 599,629 ----
  }
  
  /* If TARGET has associated node, record it in the NODES array.
!    CAN_REFER specify if program can refer to the target directly.
!    if TARGET is unknown (NULL) or it can not be inserted (for example because
!    its body was already removed and there is no way to refer to it), clear
!    COMPLETEP.  */
  
  static void
  maybe_record_node (vec <cgraph_node *> &nodes,
  		   tree target, pointer_set_t *inserted,
+ 		   bool can_refer,
  		   bool *completep)
  {
    struct cgraph_node *target_node;
    enum built_in_function fcode;
  
+   if (!can_refer)
+     {
+       /* The only case when method of anonymous namespace becomes unreferable
+ 	 is when we completely optimized it out.  */
+       if (flag_ltrans
+ 	  || !target 
+           || !type_in_anonymous_namespace_p (DECL_CONTEXT (target)))
+ 	*completep = false;
+       return;
+     }
+ 
    if (!target
        /* Those are used to mark impossible scenarios.  */
        || (fcode = DECL_FUNCTION_CODE (target))
*************** maybe_record_node (vec <cgraph_node *> &
*** 649,654 ****
--- 668,675 ----
     inserted.
  
     ANONYMOUS is true if BINFO is part of anonymous namespace.
+ 
+    Clear COMPLETEP when we hit unreferable target.
    */
  
  static void
*************** record_target_from_binfo (vec <cgraph_no
*** 661,667 ****
  			  HOST_WIDE_INT offset,
  			  pointer_set_t *inserted,
  			  pointer_set_t *matched_vtables,
! 			  bool anonymous)
  {
    tree type = BINFO_TYPE (binfo);
    int i;
--- 682,689 ----
  			  HOST_WIDE_INT offset,
  			  pointer_set_t *inserted,
  			  pointer_set_t *matched_vtables,
! 			  bool anonymous,
! 			  bool *completep)
  {
    tree type = BINFO_TYPE (binfo);
    int i;
*************** record_target_from_binfo (vec <cgraph_no
*** 692,697 ****
--- 714,724 ----
  	return;
        tree inner_binfo = get_binfo_at_offset (type_binfo,
  					      offset, otr_type);
+       if (!inner_binfo)
+ 	{
+ 	  gcc_assert (odr_violation_reported);
+ 	  return;
+ 	}
        /* For types in anonymous namespace first check if the respective vtable
  	 is alive. If not, we know the type can't be called.  */
        if (!flag_ltrans && anonymous)
*************** record_target_from_binfo (vec <cgraph_no
*** 708,716 ****
        gcc_assert (inner_binfo);
        if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (inner_binfo)))
  	{
! 	  tree target = gimple_get_virt_method_for_binfo (otr_token, inner_binfo);
! 	  if (target)
! 	    maybe_record_node (nodes, target, inserted, NULL);
  	}
        return;
      }
--- 735,745 ----
        gcc_assert (inner_binfo);
        if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (inner_binfo)))
  	{
! 	  bool can_refer;
! 	  tree target = gimple_get_virt_method_for_binfo (otr_token,
! 							  inner_binfo,
! 							  &can_refer);
! 	  maybe_record_node (nodes, target, inserted, can_refer, completep);
  	}
        return;
      }
*************** record_target_from_binfo (vec <cgraph_no
*** 722,728 ****
        record_target_from_binfo (nodes, base_binfo, otr_type,
  				type_binfos, 
  				otr_token, outer_type, offset, inserted,
! 				matched_vtables, anonymous);
    if (BINFO_VTABLE (binfo))
      type_binfos.pop ();
  }
--- 751,757 ----
        record_target_from_binfo (nodes, base_binfo, otr_type,
  				type_binfos, 
  				otr_token, outer_type, offset, inserted,
! 				matched_vtables, anonymous, completep);
    if (BINFO_VTABLE (binfo))
      type_binfos.pop ();
  }
*************** record_target_from_binfo (vec <cgraph_no
*** 730,736 ****
  /* Lookup virtual methods matching OTR_TYPE (with OFFSET and OTR_TOKEN)
     of TYPE, insert them to NODES, recurse into derived nodes. 
     INSERTED is used to avoid duplicate insertions of methods into NODES.
!    MATCHED_VTABLES are used to avoid duplicate walking vtables.  */
  
  static void
  possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
--- 759,766 ----
  /* Lookup virtual methods matching OTR_TYPE (with OFFSET and OTR_TOKEN)
     of TYPE, insert them to NODES, recurse into derived nodes. 
     INSERTED is used to avoid duplicate insertions of methods into NODES.
!    MATCHED_VTABLES are used to avoid duplicate walking vtables.
!    Clear COMPLETEP if unreferable target is found.  */
  
  static void
  possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
*************** possible_polymorphic_call_targets_1 (vec
*** 740,746 ****
  				     odr_type type,
  				     HOST_WIDE_INT otr_token,
  				     tree outer_type,
! 				     HOST_WIDE_INT offset)
  {
    tree binfo = TYPE_BINFO (type->type);
    unsigned int i;
--- 770,777 ----
  				     odr_type type,
  				     HOST_WIDE_INT otr_token,
  				     tree outer_type,
! 				     HOST_WIDE_INT offset,
! 				     bool *completep)
  {
    tree binfo = TYPE_BINFO (type->type);
    unsigned int i;
*************** possible_polymorphic_call_targets_1 (vec
*** 749,762 ****
    record_target_from_binfo (nodes, binfo, otr_type, type_binfos, otr_token,
  			    outer_type, offset,
  			    inserted, matched_vtables,
! 			    type->anonymous_namespace);
    type_binfos.release ();
    for (i = 0; i < type->derived_types.length (); i++)
      possible_polymorphic_call_targets_1 (nodes, inserted, 
  					 matched_vtables,
  					 otr_type,
  					 type->derived_types[i],
! 					 otr_token, outer_type, offset);
  }
  
  /* Cache of queries for polymorphic call targets.
--- 780,793 ----
    record_target_from_binfo (nodes, binfo, otr_type, type_binfos, otr_token,
  			    outer_type, offset,
  			    inserted, matched_vtables,
! 			    type->anonymous_namespace, completep);
    type_binfos.release ();
    for (i = 0; i < type->derived_types.length (); i++)
      possible_polymorphic_call_targets_1 (nodes, inserted, 
  					 matched_vtables,
  					 otr_type,
  					 type->derived_types[i],
! 					 otr_token, outer_type, offset, completep);
  }
  
  /* Cache of queries for polymorphic call targets.
*************** struct polymorphic_call_target_d
*** 771,777 ****
    ipa_polymorphic_call_context context;
    odr_type type;
    vec <cgraph_node *> targets;
!   bool final;
  };
  
  /* Polymorphic call target cache helpers.  */
--- 802,809 ----
    ipa_polymorphic_call_context context;
    odr_type type;
    vec <cgraph_node *> targets;
!   int nonconstruction_targets;
!   bool complete;
  };
  
  /* Polymorphic call target cache helpers.  */
*************** record_targets_from_bases (tree otr_type
*** 1286,1292 ****
  			   HOST_WIDE_INT otr_token,
  			   tree outer_type,
  			   HOST_WIDE_INT offset,
! 			   vec <cgraph_node *> nodes,
  			   pointer_set_t *inserted,
  			   pointer_set_t *matched_vtables,
  			   bool *completep)
--- 1318,1324 ----
  			   HOST_WIDE_INT otr_token,
  			   tree outer_type,
  			   HOST_WIDE_INT offset,
! 			   vec <cgraph_node *> &nodes,
  			   pointer_set_t *inserted,
  			   pointer_set_t *matched_vtables,
  			   bool *completep)
*************** record_targets_from_bases (tree otr_type
*** 1307,1313 ****
  
  	  pos = int_bit_position (fld);
  	  size = tree_to_shwi (DECL_SIZE (fld));
! 	  if (pos <= offset && (pos + size) > offset)
  	    break;
  	}
        /* Within a class type we should always find correcponding fields.  */
--- 1339,1347 ----
  
  	  pos = int_bit_position (fld);
  	  size = tree_to_shwi (DECL_SIZE (fld));
! 	  if (pos <= offset && (pos + size) > offset
! 	      /* Do not get confused by zero sized bases.  */
! 	      && polymorphic_type_binfo_p (TYPE_BINFO (TREE_TYPE (fld))))
  	    break;
  	}
        /* Within a class type we should always find correcponding fields.  */
*************** record_targets_from_bases (tree otr_type
*** 1321,1336 ****
  
        base_binfo = get_binfo_at_offset (TYPE_BINFO (outer_type),
  					offset, otr_type);
        gcc_assert (base_binfo);
        if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo)))
  	{
! 	  tree target = gimple_get_virt_method_for_binfo (otr_token, base_binfo);
! 	  if (target)
! 	    maybe_record_node (nodes, target, inserted, completep);
! 	  /* The only way method in anonymous namespace can become unreferable
! 	     is that it has been fully optimized out.  */
! 	  else if (flag_ltrans || !type_in_anonymous_namespace_p (outer_type))
! 	    *completep = false;
  	  pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo));
  	}
      }
--- 1355,1373 ----
  
        base_binfo = get_binfo_at_offset (TYPE_BINFO (outer_type),
  					offset, otr_type);
+       if (!base_binfo)
+ 	{
+ 	  gcc_assert (odr_violation_reported);
+ 	  return;
+ 	}
        gcc_assert (base_binfo);
        if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo)))
  	{
! 	  bool can_refer;
! 	  tree target = gimple_get_virt_method_for_binfo (otr_token,
! 							  base_binfo,
! 							  &can_refer);
! 	  maybe_record_node (nodes, target, inserted, can_refer, completep);
  	  pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo));
  	}
      }
*************** devirt_variable_node_removal_hook (varpo
*** 1361,1366 ****
--- 1398,1407 ----
     in the target cache.  If user needs to visit every target list
     just once, it can memoize them.
  
+    NONCONSTRUCTION_TARGETS specify number of targets with asumption that
+    the type is not in the construction.  Those targets appear first in the
+    vector returned.
+ 
     Returned vector is placed into cache.  It is NOT caller's responsibility
     to free it.  The vector can be freed on cgraph_remove_node call if
     the particular node is a virtual function present in the cache.  */
*************** possible_polymorphic_call_targets (tree
*** 1370,1376 ****
  			           HOST_WIDE_INT otr_token,
  				   ipa_polymorphic_call_context context,
  			           bool *completep,
! 			           void **cache_token)
  {
    static struct cgraph_node_hook_list *node_removal_hook_holder;
    pointer_set_t *inserted;
--- 1411,1418 ----
  			           HOST_WIDE_INT otr_token,
  				   ipa_polymorphic_call_context context,
  			           bool *completep,
! 			           void **cache_token,
! 				   int *nonconstruction_targetsp)
  {
    static struct cgraph_node_hook_list *node_removal_hook_holder;
    pointer_set_t *inserted;
*************** possible_polymorphic_call_targets (tree
*** 1381,1394 ****
    polymorphic_call_target_d **slot;
    unsigned int i;
    tree binfo, target;
!   bool final;
  
!   if (!odr_hash.is_created ())                                                                                                                    
!     {                                                                                                                                             
!       if (completep)                                                                                                                              
! 	*completep = false;                                                                                                                        
!       return nodes;                                                                                                                               
!     }                                                                                                                                             
  
    type = get_odr_type (otr_type, true);
  
--- 1423,1439 ----
    polymorphic_call_target_d **slot;
    unsigned int i;
    tree binfo, target;
!   bool complete;
!   bool can_refer;
  
!   if (!odr_hash.is_created ())
!     {
!       if (completep)
! 	*completep = false;
!       if (nonconstruction_targetsp)
! 	*nonconstruction_targetsp = 0;
!       return nodes;
!     }
  
    type = get_odr_type (otr_type, true);
  
*************** possible_polymorphic_call_targets (tree
*** 1410,1416 ****
    /* If outer and inner type match, there are no bases to see.  */
    if (type == outer_type)
      context.maybe_in_construction = false;
!   /* If the type is final, there are no derivations.  */
    if (TYPE_FINAL_P (outer_type->type))
      context.maybe_derived_type = false;
  
--- 1455,1461 ----
    /* If outer and inner type match, there are no bases to see.  */
    if (type == outer_type)
      context.maybe_in_construction = false;
!   /* If the type is complete, there are no derivations.  */
    if (TYPE_FINAL_P (outer_type->type))
      context.maybe_derived_type = false;
  
*************** possible_polymorphic_call_targets (tree
*** 1438,1448 ****
    if (*slot)
      {
        if (completep)
! 	*completep = (*slot)->final;
        return (*slot)->targets;
      }
  
!   final = true;
  
    /* Do actual search.  */
    timevar_push (TV_IPA_VIRTUAL_CALL);
--- 1483,1495 ----
    if (*slot)
      {
        if (completep)
! 	*completep = (*slot)->complete;
!       if (nonconstruction_targetsp)
! 	*nonconstruction_targetsp = (*slot)->nonconstruction_targets;
        return (*slot)->targets;
      }
  
!   complete = true;
  
    /* Do actual search.  */
    timevar_push (TV_IPA_VIRTUAL_CALL);
*************** possible_polymorphic_call_targets (tree
*** 1460,1505 ****
  
    binfo = get_binfo_at_offset (TYPE_BINFO (outer_type->type),
  			       context.offset, otr_type);
!   target = gimple_get_virt_method_for_binfo (otr_token, binfo);
!   if (target)
      {
!       maybe_record_node (nodes, target, inserted, &final);
  
!       /* In the case we get final method, we don't need 
  	 to walk derivations.  */
        if (DECL_FINAL_P (target))
  	context.maybe_derived_type = false;
      }
!   /* The only way method in anonymous namespace can become unreferable
!      is that it has been fully optimized out.  */
!   else if (flag_ltrans || !type->anonymous_namespace)
!     final = false;
    pointer_set_insert (matched_vtables, BINFO_VTABLE (binfo));
  
!   /* Next walk bases, if asked to.  */
!   if (context.maybe_in_construction)
!     record_targets_from_bases (otr_type, otr_token, outer_type->type,
! 			       context.offset, nodes, inserted,
! 			       matched_vtables, &final);
! 
!   /* Finally walk recursively all derived types.  */
    if (context.maybe_derived_type)
      {
        /* For anonymous namespace types we can attempt to build full type.
  	 All derivations must be in this unit (unless we see partial unit).  */
        if (!type->anonymous_namespace || flag_ltrans)
! 	final = false;
        for (i = 0; i < outer_type->derived_types.length(); i++)
  	possible_polymorphic_call_targets_1 (nodes, inserted,
  					     matched_vtables,
  					     otr_type, outer_type->derived_types[i],
  					     otr_token, outer_type->type,
! 					     context.offset);
      }
    (*slot)->targets = nodes;
!   (*slot)->final = final;
    if (completep)
!     *completep = final;
  
    pointer_set_destroy (inserted);
    pointer_set_destroy (matched_vtables);
--- 1507,1562 ----
  
    binfo = get_binfo_at_offset (TYPE_BINFO (outer_type->type),
  			       context.offset, otr_type);
!   if (binfo)
!     target = gimple_get_virt_method_for_binfo (otr_token, binfo,
! 					       &can_refer);
!   else
      {
!       gcc_assert (odr_violation_reported);
!       target = NULL;
!     }
! 
!   maybe_record_node (nodes, target, inserted, can_refer, &complete);
  
!   if (target)
!     {
!       /* In the case we get complete method, we don't need 
  	 to walk derivations.  */
        if (DECL_FINAL_P (target))
  	context.maybe_derived_type = false;
      }
!   else
!     gcc_assert (!complete);
    pointer_set_insert (matched_vtables, BINFO_VTABLE (binfo));
  
!   /* Next walk recursively all derived types.  */
    if (context.maybe_derived_type)
      {
        /* For anonymous namespace types we can attempt to build full type.
  	 All derivations must be in this unit (unless we see partial unit).  */
        if (!type->anonymous_namespace || flag_ltrans)
! 	complete = false;
        for (i = 0; i < outer_type->derived_types.length(); i++)
  	possible_polymorphic_call_targets_1 (nodes, inserted,
  					     matched_vtables,
  					     otr_type, outer_type->derived_types[i],
  					     otr_token, outer_type->type,
! 					     context.offset, &complete);
      }
+ 
+   /* Finally walk bases, if asked to.  */
+   (*slot)->nonconstruction_targets = nodes.length();
+   if (context.maybe_in_construction)
+     record_targets_from_bases (otr_type, otr_token, outer_type->type,
+ 			       context.offset, nodes, inserted,
+ 			       matched_vtables, &complete);
+ 
    (*slot)->targets = nodes;
!   (*slot)->complete = complete;
    if (completep)
!     *completep = complete;
!   if (nonconstruction_targetsp)
!     *nonconstruction_targetsp = (*slot)->nonconstruction_targets;
  
    pointer_set_destroy (inserted);
    pointer_set_destroy (matched_vtables);
*************** dump_possible_polymorphic_call_targets (
*** 1519,1546 ****
    bool final;
    odr_type type = get_odr_type (otr_type, false);
    unsigned int i;
  
    if (!type)
      return;
    targets = possible_polymorphic_call_targets (otr_type, otr_token,
  					       ctx,
! 					       &final);
    fprintf (f, "  Targets of polymorphic call of type %i:", type->id);
    print_generic_expr (f, type->type, TDF_SLIM);
!   fprintf (f, " token %i\n"
! 	   "    Contained in type:",
! 	   (int)otr_token);
!   print_generic_expr (f, ctx.outer_type, TDF_SLIM);
!   fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n"
! 	   "    %s%s%s\n      ",
! 	   ctx.offset,
! 	   final ? "This is full list." :
  	   "This is partial list; extra targets may be defined in other units.",
  	   ctx.maybe_in_construction ? " (base types included)" : "",
  	   ctx.maybe_derived_type ? " (derived types included)" : "");
    for (i = 0; i < targets.length (); i++)
!     fprintf (f, " %s/%i", targets[i]->name (),
! 	     targets[i]->order);
    fprintf (f, "\n\n");
  }
  
--- 1576,1621 ----
    bool final;
    odr_type type = get_odr_type (otr_type, false);
    unsigned int i;
+   int nonconstruction;
  
    if (!type)
      return;
    targets = possible_polymorphic_call_targets (otr_type, otr_token,
  					       ctx,
! 					       &final, NULL, &nonconstruction);
    fprintf (f, "  Targets of polymorphic call of type %i:", type->id);
    print_generic_expr (f, type->type, TDF_SLIM);
!   fprintf (f, " token %i\n", (int)otr_token);
!   if (ctx.outer_type || ctx.offset)
!     {
!       fprintf (f, "    Contained in type:");
!       print_generic_expr (f, ctx.outer_type, TDF_SLIM);
!       fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n",
! 	       ctx.offset);
!     }
! 
!   fprintf (f, "    %s%s%s\n      ",
! 	   final ? "This is a complete list." :
  	   "This is partial list; extra targets may be defined in other units.",
  	   ctx.maybe_in_construction ? " (base types included)" : "",
  	   ctx.maybe_derived_type ? " (derived types included)" : "");
    for (i = 0; i < targets.length (); i++)
!     {
!       char *name = NULL;
!       if (i == (unsigned)nonconstruction)
! 	fprintf (f, "\n     If the type is in construction,"
! 		 " then additional tarets are:\n"
! 		 "      ");
!       if (in_lto_p)
! 	name = cplus_demangle_v3 (targets[i]->asm_name (), 0);
!       fprintf (f, " %s/%i", name ? name : targets[i]->name (), targets[i]->order);
!       if (in_lto_p)
! 	free (name);
!       if (!targets[i]->definition)
! 	fprintf (f, " (no definition%s)",
! 		 DECL_DECLARED_INLINE_P (targets[i]->decl)
! 		 ? " inline" : "");
!     }
    fprintf (f, "\n\n");
  }
  
*************** ipa_devirt (void)
*** 1652,1660 ****
  	    struct cgraph_node *likely_target = NULL;
  	    void *cache_token;
  	    bool final;
  	    vec <cgraph_node *>targets
  	       = possible_polymorphic_call_targets
! 		    (e, &final, &cache_token);
  	    unsigned int i;
  
  	    if (dump_file)
--- 1727,1736 ----
  	    struct cgraph_node *likely_target = NULL;
  	    void *cache_token;
  	    bool final;
+ 	    int nonconstruction_targets;
  	    vec <cgraph_node *>targets
  	       = possible_polymorphic_call_targets
! 		    (e, &final, &cache_token, &nonconstruction_targets);
  	    unsigned int i;
  
  	    if (dump_file)
*************** ipa_devirt (void)
*** 1666,1679 ****
  	    if (!cgraph_maybe_hot_edge_p (e))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Call is cold\n");
  		ncold++;
  		continue;
  	      }
  	    if (e->speculative)
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Call is aready speculated\n");
  		nspeculated++;
  
  		/* When dumping see if we agree with speculation.  */
--- 1742,1755 ----
  	    if (!cgraph_maybe_hot_edge_p (e))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Call is cold\n\n");
  		ncold++;
  		continue;
  	      }
  	    if (e->speculative)
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Call is aready speculated\n\n");
  		nspeculated++;
  
  		/* When dumping see if we agree with speculation.  */
*************** ipa_devirt (void)
*** 1684,1690 ****
  				      cache_token))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target list is known to be useless\n");
  		nmultiple++;
  		continue;
  	      }
--- 1760,1766 ----
  				      cache_token))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target list is known to be useless\n\n");
  		nmultiple++;
  		continue;
  	      }
*************** ipa_devirt (void)
*** 1693,1702 ****
  		{
  		  if (likely_target)
  		    {
! 		      likely_target = NULL;
! 		      if (dump_file)
! 			fprintf (dump_file, "More than one likely target\n");
! 		      nmultiple++;
  		      break;
  		    }
  		  likely_target = targets[i];
--- 1769,1781 ----
  		{
  		  if (likely_target)
  		    {
! 		      if (i < (unsigned) nonconstruction_targets)
! 			{
! 			  likely_target = NULL;
! 			  if (dump_file)
! 			    fprintf (dump_file, "More than one likely target\n\n");
! 			  nmultiple++;
! 			}
  		      break;
  		    }
  		  likely_target = targets[i];
*************** ipa_devirt (void)
*** 1716,1727 ****
  		if (cgraph_function_or_thunk_node (e2->callee, NULL)
  		    == cgraph_function_or_thunk_node (likely_target, NULL))
  		  {
! 		    fprintf (dump_file, "We agree with speculation\n");
  		    nok++;
  		  }
  		else
  		  {
! 		    fprintf (dump_file, "We disagree with speculation\n");
  		    nwrong++;
  		  }
  		continue;
--- 1795,1806 ----
  		if (cgraph_function_or_thunk_node (e2->callee, NULL)
  		    == cgraph_function_or_thunk_node (likely_target, NULL))
  		  {
! 		    fprintf (dump_file, "We agree with speculation\n\n");
  		    nok++;
  		  }
  		else
  		  {
! 		    fprintf (dump_file, "We disagree with speculation\n\n");
  		    nwrong++;
  		  }
  		continue;
*************** ipa_devirt (void)
*** 1729,1735 ****
  	    if (!likely_target->definition)
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is not an definition\n");
  		nnotdefined++;
  		continue;
  	      }
--- 1808,1814 ----
  	    if (!likely_target->definition)
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is not an definition\n\n");
  		nnotdefined++;
  		continue;
  	      }
*************** ipa_devirt (void)
*** 1740,1746 ****
  	    if (DECL_EXTERNAL (likely_target->decl))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is external\n");
  		nexternal++;
  		continue;
  	      }
--- 1819,1825 ----
  	    if (DECL_EXTERNAL (likely_target->decl))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is external\n\n");
  		nexternal++;
  		continue;
  	      }
*************** ipa_devirt (void)
*** 1749,1755 ****
  		&& symtab_can_be_discarded (likely_target))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is overwritable\n");
  		noverwritable++;
  		continue;
  	      }
--- 1828,1834 ----
  		&& symtab_can_be_discarded (likely_target))
  	      {
  		if (dump_file)
! 		  fprintf (dump_file, "Target is overwritable\n\n");
  		noverwritable++;
  		continue;
  	      }
*************** ipa_devirt (void)
*** 1757,1763 ****
  	      {
  		if (dump_file)
  		  fprintf (dump_file,
! 			   "Speculatively devirtualizing call in %s/%i to %s/%i\n",
  			   n->name (), n->order,
  			   likely_target->name (),
  			   likely_target->order);
--- 1836,1842 ----
  	      {
  		if (dump_file)
  		  fprintf (dump_file,
! 			   "Speculatively devirtualizing call in %s/%i to %s/%i\n\n",
  			   n->name (), n->order,
  			   likely_target->name (),
  			   likely_target->order);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]