public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH, PR 45934 5/5] Intraprocedural type-based devirtualization
  2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
@ 2010-12-15 17:09 ` Martin Jambor
  2010-12-15 17:17 ` [PATCH, PR 45934 2/5] Dynamic type change detection Martin Jambor
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:09 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

[-- Attachment #1: direct_devirt_v2.diff --]
[-- Type: text/plain, Size: 14761 bytes --]

This patch does intraprocedural devirtualization as a part of cgraph
rebuilding pass.  I'm now quite confident this is one of the better
places where to put if not the best - as usual I'm opened to
alternative suggestions here.  However, this placement minimizes the
overhead, allows for subsequent early inlining and dealing with
potential eh cfg cleanups is much less ugly.

This patch compensates for not devirtualizing when folding.  It looks
for OBJ_TYPE_REF calls, and tries to deduce their type by watching out
for dynamic type changes.  If the type is known and the call can be
converted to a direct one with that knowledge, it is done so.

This patch is quite simplified since the last time since I realized
some of the more complex code paths just did not run or could never
work to a known type jump function anyway.  Also I deferred some minor
cleanups which were originally part of this for later.

This patch is necessary for (modified variants of) testcases
g++.dg/otr-fold-[12].C (re-introduced as imm-devirt[12].C),
g++.dg/tree-ssa/pr43411.C and g++.dg/tree-ssa/pr45605.C to pass again.
Without it, early inlining can be detrimental to devirtualization by
making it a purely intraprocedural issue and slow down simple
testcases three-fold.
 
This patch also passes bootstrap and testing on x86_64-linux and make
check-c++ on i686.  I'm looking forward to all comments and
suggestions.

Thanks,

Martin



2010-12-13  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (get_ancestor_base_and_offset): New function.
	(compute_complex_assign_jump_func): Use it, check that op2 is NULL
	in the ancestor case.
	(compute_complex_ancestor_jump_func): Likewise.
	(ipa_try_devirtualize_immediately): New fuction.
	* cgraphbuild.c: Include flags.h and ipa-prop.h.
	(rebuild_cgraph_edges): Renamed to rebuild_cgraph_edges_1, new
	parameter devirtualize, call ipa_try_devirtualize_immediately
	if it is true.
	(rebuild_cgraph_edges): New function.
	(rebuild_cgraph_edges_devirtualize): Likewise.
	(pass_rebuild_cgraph_edges_and_devirt): New variable.
	* passes.c (init_optimization_passes): Use it.
	* tree-pass.h (pass_rebuild_cgraph_edges_and_devirt): Declare.
	* ipa-prop.h (ipa_try_devirtualize_immediately): Declare.
	* Makefile.in (cgraphbuild.o): Add FLAGS_H and IPA_PROP_H to
	dependencies.

	* testsuite/g++.dg/ipa/imm-devirt-1.C: New test.
	* testsuite/g++.dg/ipa/imm-devirt-2.C: Likewise.
	* testsuite/g++.dg/tree-ssa/pr45605.C: Compile with O2, scan optimized
	dump. Un-xfail dump scan.
	* g++.dg/tree-ssa/pr43411.C: Un-xfail dump scan.


Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -1447,6 +1447,59 @@ ipa_analyze_indirect_call_uses (struct c
   return;
 }
 
+/* If a call to an OBJ_TYPE_REF given by GSI can be turned into a direct one
+   according to the type of its object right away and if so, do it and return
+   true.  If CFG is changed in the process, *CFG_CHANGED is set to true.  */
+
+tree
+ipa_try_devirtualize_immediately (gimple_stmt_iterator *gsi, bool *cfg_changed)
+{
+  struct ipa_jump_func jfunc;
+  tree fndecl, delta, token;
+  gimple call = gsi_stmt (*gsi);
+  tree target = gimple_call_fn (call);
+
+  if (TREE_CODE (target) != OBJ_TYPE_REF)
+    return NULL_TREE;
+  jfunc.type = IPA_JF_UNKNOWN;
+  compute_known_type_jump_func (OBJ_TYPE_REF_OBJECT (target), &jfunc, call);
+  if (jfunc.type != IPA_JF_KNOWN_TYPE)
+    return NULL_TREE;
+
+  token = OBJ_TYPE_REF_TOKEN (target);
+  fndecl = gimple_get_virt_mehtod_for_binfo (tree_low_cst (token, 1),
+					     jfunc.value.base_binfo,
+					     &delta, true);
+  if (!fndecl)
+    return NULL_TREE;
+  if (cgraph_dump_file)
+    {
+      fprintf (cgraph_dump_file, "ipa-prop: Immediately devirtualizing call ");
+      print_gimple_expr (cgraph_dump_file, call, 0, 0);
+      fprintf (cgraph_dump_file, "\n          in function ");
+      print_generic_expr (cgraph_dump_file, current_function_decl, 0);
+    }
+
+  if (integer_nonzerop (delta))
+    gimple_adjust_this_by_delta (gsi, delta);
+  gimple_call_set_fndecl (call, fndecl);
+  update_stmt (call);
+  if (maybe_clean_eh_stmt (call)
+      && gimple_purge_dead_eh_edges (gimple_bb (call)))
+    *cfg_changed = true;
+
+  if (cgraph_dump_file)
+    {
+      fprintf (cgraph_dump_file, "\n          into ");
+      print_gimple_expr (cgraph_dump_file, call, 0, 0);
+      fprintf (cgraph_dump_file, "\n          with delta ");
+      print_generic_expr (cgraph_dump_file, delta, 0);
+      fprintf (cgraph_dump_file, "\n");
+    }
+
+  return fndecl;
+}
+
 /* Analyze a CALL to an OBJ_TYPE_REF which is passed in TARGET and if the
    object referenced in the expression is a formal parameter of the caller
    (described by INFO), create a call note for the statement. */
Index: icln/gcc/testsuite/g++.dg/ipa/imm-devirt-1.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/imm-devirt-1.C
@@ -0,0 +1,76 @@
+/* Verify that virtual calls are folded even when a typecast to an
+   ancestor is involved along the way.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized-slim"  } */
+
+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;
+  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 __attribute__ ((noinline)) A::foo (int i)
+{
+  return i + 1;
+}
+
+int __attribute__ ((noinline)) B::foo (int i)
+{
+  return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static inline int middleman_1 (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+static inline int middleman_2 (class B *obj, int i)
+{
+  return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+
+  if (middleman_2 (&b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/imm-devirt-2.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/imm-devirt-2.C
@@ -0,0 +1,88 @@
+/* Verify that virtual calls are folded even when a typecast to an
+   ancestor is involved along the way.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized-slim"  } */
+
+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;
+  virtual int foo (int i);
+};
+
+class A_2 : public A
+{
+public:
+  int data_2;
+  virtual int baz (int i);
+};
+
+
+class B : public Distraction, public A_2
+{
+public:
+  virtual int foo (int i);
+};
+
+float __attribute__ ((noinline)) Distraction::bar (float z)
+{
+  f += z;
+  return f/2;
+}
+
+int __attribute__ ((noinline)) A::foo (int i)
+{
+  return i + 1;
+}
+
+int __attribute__ ((noinline)) A_2::baz (int i)
+{
+  return i * 15;
+}
+
+int __attribute__ ((noinline)) B::foo (int i)
+{
+  return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static inline int middleman_1 (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+static inline int middleman_2 (class A *obj, int i)
+{
+  return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+
+  if (middleman_2 (&b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O1 -fdump-tree-ssa" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 extern "C" void abort(); 
 bool destructor_called = false; 
 
@@ -33,5 +33,5 @@ int main() {
 
 
 /* We should devirtualize call to D::Run */
-/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa" { xfail *-*-* } } } */
-/* { dg-final { cleanup-tree-dump "ssa" } } */
+/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/cgraphbuild.c
===================================================================
--- icln.orig/gcc/cgraphbuild.c
+++ icln/gcc/cgraphbuild.c
@@ -33,6 +33,8 @@ along with GCC; see the file COPYING3.
 #include "tree-pass.h"
 #include "ipa-utils.h"
 #include "except.h"
+#include "flags.h"
+#include "ipa-prop.h"
 
 /* Context of record_reference.  */
 struct record_reference_ctx
@@ -426,15 +428,18 @@ record_references_in_initializer (tree d
   pointer_set_destroy (visited_nodes);
 }
 
-/* Rebuild cgraph edges for current function node.  This needs to be run after
-   passes that don't update the cgraph.  */
+/* Remove callees call graph edges for the current function.  Afterwards scan
+   the body of the current function and create them anew for each call
+   statement.  If DEVIRTUALIZE is true, attempt to do turn virtual calls into
+   direct ones in the process.  */
 
-unsigned int
-rebuild_cgraph_edges (void)
+static unsigned int
+rebuild_cgraph_edges_1 (bool devirtualize)
 {
   basic_block bb;
   struct cgraph_node *node = cgraph_node (current_function_decl);
   gimple_stmt_iterator gsi;
+  bool cfg_changed = false;
 
   cgraph_node_remove_callees (node);
   ipa_remove_all_references (&node->ref_list);
@@ -453,6 +458,8 @@ rebuild_cgraph_edges (void)
 	      int freq = compute_call_stmt_bb_frequency (current_function_decl,
 							 bb);
 	      decl = gimple_call_fndecl (stmt);
+	      if (!decl && devirtualize)
+		decl = ipa_try_devirtualize_immediately (&gsi, &cfg_changed);
 	      if (decl)
 		cgraph_create_edge (node, cgraph_node (decl), stmt,
 				    bb->count, freq,
@@ -474,7 +481,28 @@ rebuild_cgraph_edges (void)
   record_eh_tables (node, cfun);
   gcc_assert (!node->global.inlined_to);
 
-  return 0;
+  if (cfg_changed)
+    return TODO_cleanup_cfg;
+  else
+    return 0;
+}
+
+/* Rebuild outgoing cgraph edges for current function node.  This needs to be
+   run after passes that don't update the cgraph.  */
+
+unsigned int
+rebuild_cgraph_edges (void)
+{
+ return rebuild_cgraph_edges_1 (false);
+}
+
+/* Variant of the above pass that also tries to do type-based devirtualization
+   while rebuilding the outgoing call graph edges.  */
+
+static unsigned int
+rebuild_cgraph_edges_devirtualize (void)
+{
+ return rebuild_cgraph_edges_1 (flag_devirtualize);
 }
 
 /* Rebuild cgraph edges for current function node.  This needs to be run after
@@ -518,6 +546,25 @@ struct gimple_opt_pass pass_rebuild_cgra
   NULL,					/* sub */
   NULL,					/* next */
   0,					/* static_pass_number */
+  TV_CGRAPH,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,					/* properties_provided */
+  0,					/* properties_destroyed */
+  0,					/* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+struct gimple_opt_pass pass_rebuild_cgraph_edges_and_devirt =
+{
+ {
+  GIMPLE_PASS,
+  "*rebuild_cgraph_edges_d",		/* name */
+  NULL,					/* gate */
+  rebuild_cgraph_edges_devirtualize,    /* execute */
+  NULL,					/* sub */
+  NULL,					/* next */
+  0,					/* static_pass_number */
   TV_CGRAPH,				/* tv_id */
   PROP_cfg,				/* properties_required */
   0,					/* properties_provided */
Index: icln/gcc/ipa-prop.h
===================================================================
--- icln.orig/gcc/ipa-prop.h
+++ icln/gcc/ipa-prop.h
@@ -429,6 +429,9 @@ void ipa_initialize_node_params (struct
 bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
 					VEC (cgraph_edge_p, heap) **new_edges);
 
+/* Intraprocedural devirtualization. */
+tree ipa_try_devirtualize_immediately (gimple_stmt_iterator *, bool *);
+
 /* Indirect edge and binfo processing.  */
 struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
 						    tree);
Index: icln/gcc/passes.c
===================================================================
--- icln.orig/gcc/passes.c
+++ icln/gcc/passes.c
@@ -781,7 +781,7 @@ init_optimization_passes (void)
           NEXT_PASS (pass_split_functions);
 	}
       NEXT_PASS (pass_release_ssa_names);
-      NEXT_PASS (pass_rebuild_cgraph_edges);
+      NEXT_PASS (pass_rebuild_cgraph_edges_and_devirt);
       NEXT_PASS (pass_inline_parameters);
     }
   NEXT_PASS (pass_ipa_tree_profile);
Index: icln/gcc/tree-pass.h
===================================================================
--- icln.orig/gcc/tree-pass.h
+++ icln/gcc/tree-pass.h
@@ -436,6 +436,7 @@ extern struct gimple_opt_pass pass_uncpr
 extern struct gimple_opt_pass pass_return_slot;
 extern struct gimple_opt_pass pass_reassoc;
 extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
+extern struct gimple_opt_pass pass_rebuild_cgraph_edges_and_devirt;
 extern struct gimple_opt_pass pass_remove_cgraph_callee_edges;
 extern struct gimple_opt_pass pass_build_cgraph_edges;
 extern struct gimple_opt_pass pass_local_pure_const;
Index: icln/gcc/Makefile.in
===================================================================
--- icln.orig/gcc/Makefile.in
+++ icln/gcc/Makefile.in
@@ -3030,7 +3030,8 @@ cgraphunit.o : cgraphunit.c $(CONFIG_H)
    tree-pretty-print.h gimple-pretty-print.h
 cgraphbuild.o : cgraphbuild.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) langhooks.h $(CGRAPH_H) intl.h pointer-set.h $(GIMPLE_H) \
-   $(TREE_FLOW_H) $(TREE_PASS_H) $(IPA_UTILS_H) $(EXCEPT_H)
+   $(TREE_FLOW_H) $(TREE_PASS_H) $(IPA_UTILS_H) $(EXCEPT_H) $(FLAGS_H) \
+   $(IPA_PROP_H)
 varpool.o : varpool.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) $(CGRAPH_H) langhooks.h $(DIAGNOSTIC_CORE_H) $(HASHTAB_H) \
    $(GGC_H) $(TIMEVAR_H) debug.h $(TARGET_H) output.h $(GIMPLE_H) \
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
@@ -25,5 +25,5 @@ void testInlinePsub() {
     sink1 = v(p);
 }
 
-// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" { xfail *-*-* } } }
+// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" } }
 // { dg-final { cleanup-tree-dump "optimized" } }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes
@ 2010-12-15 17:09 Martin Jambor
  2010-12-15 17:09 ` [PATCH, PR 45934 5/5] Intraprocedural type-based devirtualization Martin Jambor
                   ` (4 more replies)
  0 siblings, 5 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:09 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

Hi,

I re-send the whole series of patches aiming to resolve the
dynamic-type-change devirtualization issues so that they are grouped
together, especially because there is a new patch introducing a
command line switch to switch devirtualization on and off.  Other
wise, the patches are new versions of those posted previosuly which
can be found as followups to
http://gcc.gnu.org/ml/gcc-patches/2010-12/msg00085.html

The division into patches is as follows:

1. Disabling devirtualization in folding and that based on global
   variables which do not work because of dynamic type changes.

2. Detecting dynamic type changes of objects with virtual methods and
   disabling devirtualization upon their encounter.

3. Identifying the new dynamic type of an object after such change
   when we can do so and using it to drive devirtualization instead of
   disabling it.

4. A new command line switch to control devirtualization.

5. Intraprocedural BINFO-based devirtualization.  Necessary to perform
   devirtualization when there is no IPA transfer of information
   involved.

More details about individual steps are in the corresponding email
messages.

Thanks for all feedback, including that I have already received as
comments to the previous version of this patch,

Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH, PR 45934 2/5] Dynamic type change detection
  2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
  2010-12-15 17:09 ` [PATCH, PR 45934 5/5] Intraprocedural type-based devirtualization Martin Jambor
@ 2010-12-15 17:17 ` Martin Jambor
  2011-01-13 12:35   ` Richard Guenther
  2010-12-15 17:33 ` [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization Martin Jambor
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:17 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

[-- Attachment #1: dynamic_type_change_detection.diff --]
[-- Type: text/plain, Size: 35495 bytes --]

This is a re-submission of the patch originally posted here:
http://gcc.gnu.org/ml/gcc-patches/2010-12/msg00091.html

The main differences are:

1. No special provisons for builtin functions.  There is a new big
   comment explaining why we ignore all calls when searching for
   statements changing the dynamic type.

2. Aggregate stores to COMPONENT_REFs are also considered as changing
   dynamic the type.

3. The new use of DECL_CONTEXT is briefly documented in tree.h.

4. check_stmt_for_type_change is now ready to be called even after it
   has returned true and checks that if we derive any known new
   dynamic type, it is always the same one.

5. I have renamed the functions a slightly.  There is now a function
   called detect_type_change which should be directly used for
   declarations and their component_refs and dereferenced pointers and
   their component_refs.  Dereferenced pointers are not supplied
   directly from the analyzed IL but are created by
   detect_type_change_ssa which is to be used to detect any dynamic
   type changed of objects pointed to by an SSA_NAME (as it currently
   is, all callers check a PARAM_DECL DEFAULT_DEF
   SSA_NAMEs). detect_type_change_ssa uses detect_type_change
   internally.

   So when we construct a PASS_THROUGH jump function without any
   binary operation (pass through functions with an operation are
   never combined with a known_type ones), we use
   detect_type_change_ssa.

   On the other hand, when we either want to construct an ANCESTOR
   jump function (and therefore are looking at an address of a
   component of a dereferenced pointer) or TYPE_KNOWN jump function
   (looking either at an address of a decl or its component), we use
   directly detect_type_change.  On both occasions we already need to
   invoke get_ref_base_and extent and so we supply this information to
   detect_type_change which uses it to initialize its ao_ref structure
   on its own.  

   I was considering using ao_ref_init_from_ptr_and_size but I would
   then need to adjust ref_alias_set and base_alias_set because I
   really want to utilize TBAA (and therefore either compute the sets
   or set the ref field too) and considered that to be even uglier
   than setting up the whole thing myself.

   The size is set to the size of a pointer.  We really only care
   about components representing ancestors (get_binfo_at_offset is
   smart enough to recognize them and not use encapsulating
   object-type for non-artificial components) which have at least one
   VMT pointer right at the beginning if they have any virtual methods
   at all and cannot be part of arrays or larger structures without a
   VMT at the beginning.  This also prevents us from wrongly deriving
   a type from an object constructed inside another one with placement
   new (there is a new testcase in the next patch for this).

   All of this is for objects passed by reference, there is no
   provision (or intention) to track stuff passed by value.

I hope I answered all comments raised in the previous thread.

As before, the code that does both detection of dynamic type changes
and tries to extract the new dynamic type in some cases is the
following:

----------------------------------------------------------------------

/* Structure to be passed in between detect_type_change and
   check_stmt_for_type_change.  */

struct type_change_info
{
  /* The declaration or SSA_NAME pointer of the base that we are checking for
     type change.  */
  tree object;
  /* If we actually can tell the type that the object has changed to, it is
     stored in this field.  Otherwise it remains NULL_TREE.  */
  tree known_current_type;
  /* Set to true if dynamic type change has been detected.  */
  bool type_maybe_changed;
  /* Set to true if multiple types have been encountered.  known_current_type
     must be disregarded in that case.  */
  bool multiple_types_encountered;
};

/* Return true if STMT can modify a virtual method table pointer.

   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.  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;
}

/* If STMT can be proved to be an assignment to the virtual method table
   pointer of ANALYZED_OBJ and the type associated witht the new table
   identified, return the type.  Otherwise return NULL_TREE.  */

static tree
extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
{
  tree lhs, t, obj;

  if (!is_gimple_assign (stmt))
    return NULL_TREE;

  lhs = gimple_assign_lhs (stmt);

  if (TREE_CODE (lhs) != COMPONENT_REF)
    return NULL_TREE;
   obj = lhs;

   if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
     return NULL_TREE;

   do
     {
       obj = TREE_OPERAND (obj, 0);
     }
   while (TREE_CODE (obj) == COMPONENT_REF);
   if (TREE_CODE (obj) == MEM_REF)
     obj = TREE_OPERAND (obj, 0);
   if (TREE_CODE (obj) == ADDR_EXPR)
     obj = TREE_OPERAND (obj, 0);
   if (obj != analyzed_obj)
     return NULL_TREE;

   t = gimple_assign_rhs1 (stmt);
   if (TREE_CODE (t) != ADDR_EXPR)
     return NULL_TREE;
   t = get_base_address (TREE_OPERAND (t, 0));
   if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
     return NULL_TREE;

   return DECL_CONTEXT (t);
}

/* 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))
    {
      tree type;
      type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
      if (tci->type_maybe_changed
	  && type != tci->known_current_type)
	tci->multiple_types_encountered = true;
      tci->known_current_type = type;
      tci->type_maybe_changed = true;
      return true;
    }
  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;
  tree type;
  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;

  ao.ref = arg;
  ao.base = base;
  ao.offset = offset;
  ao.size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
  ao.max_size = ao.size;
  ao.ref_alias_set = -1;
  ao.base_alias_set = -1;

  type = TREE_TYPE (arg);
  while (handled_component_p (arg))
    arg = TREE_OPERAND (arg, 0);
  if (TREE_CODE (arg) == MEM_REF)
    arg = TREE_OPERAND (arg, 0);
  if (TREE_CODE (arg) == ADDR_EXPR)
    arg = TREE_OPERAND (arg, 0);
  tci.object = arg;
  tci.known_current_type = NULL_TREE;
  tci.type_maybe_changed = false;
  tci.multiple_types_encountered = false;

  walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
		      &tci, NULL);
  if (!tci.type_maybe_changed)
    return false;

 if (!tci.known_current_type || tci.multiple_types_encountered)
   jfunc->type = IPA_JF_UNKNOWN;
 else
   {
     tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
					   offset, type);
     if (new_binfo)
	{
	  jfunc->type = IPA_JF_KNOWN_TYPE;
	  jfunc->value.base_binfo = new_binfo;
	}
     else
       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 = build_simple_mem_ref (arg);

  return detect_type_change (arg, arg, call, jfunc, 0);
}

----------------------------------------------------------------------

The patch implementing just the detection of changes with all the
callers is below, the rest is in the next patch.

I have bootstrapped and tested this patch separately (i.e. with the
previous ones but not the subsequent ones) on x86-64-linux and it has
also passed make check-c++ on i686.

Thanks for any comments,

Martin


2010-11-09  Martin Jambor  <mjambor@suse.cz>

	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-c-7.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,151 @@ 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.  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 = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
+  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 = build_simple_mem_ref (arg);
+
+  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 +504,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 +533,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 +545,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 +555,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 +598,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 +631,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 +659,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 +667,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 +681,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 +700,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 +744,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 +757,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 +1371,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 +1395,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 +1519,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 +1533,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-c-7.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
@@ -0,0 +1,101 @@
+/* Verify that ipa-cp can use TBAA to fingure out that the virtual call to
+   D::foo is not affected by dynamic type change in constructor A::A(D*).  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp"  } */
+
+extern "C" void abort (void);
+
+class D;
+
+class A
+{
+public:
+  int data;
+  A();
+  A(D *d);
+  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);
+};
+
+class D
+{
+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;
+}
+
+int D::foo (int i)
+{
+  return i + 4;
+}
+
+static int middleman (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+static int middleman (class D *obj, int i)
+{
+  return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+A::A ()
+{
+}
+
+A::A (D *d)
+{
+  if (middleman (d, get_input ()) != 5)
+    abort ();
+  if (middleman (this, get_input ()) != 2)
+    abort ();
+}
+
+static void bah ()
+{
+  D d;
+  A a(&d);
+}
+
+int main (int argc, char *argv[])
+{
+  int i;
+
+  for (i = 0; i < 10; i++)
+    bah ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*D::foo"  "cp"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization
  2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
  2010-12-15 17:09 ` [PATCH, PR 45934 5/5] Intraprocedural type-based devirtualization Martin Jambor
  2010-12-15 17:17 ` [PATCH, PR 45934 2/5] Dynamic type change detection Martin Jambor
@ 2010-12-15 17:33 ` Martin Jambor
  2010-12-15 18:55   ` Ralf Wildenhues
  2011-01-13 13:01   ` Richard Guenther
  2010-12-15 17:43 ` [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done Martin Jambor
  2010-12-15 17:44 ` [PATCH, PR 45934 3/5] Identify the new dynamic type after change Martin Jambor
  4 siblings, 2 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:33 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

[-- Attachment #1: option_for_devirt.diff --]
[-- Type: text/plain, Size: 6313 bytes --]

For various reasons it is probably desirable to be able to switch
devirtualization off.  This is to be accomplished by a flag
-fdevirtualize added by this patch.  I am open to suggestions for a
better name, I was considering that we somehow include in the name
that we mean type based devirtualization but then I thought I'd
suggest the simple name first.

For IPA devirtualization, it is enough never to produce TYPE_KNOWN
jump messages in order to avoid any devirtualization when doing IPA-CP
or inlining.  However, I also made the flag disable the type-change
detection mechanism so that it can be used to debug or workaround
problems there.

Intraprocedural devirtualization is another matter.  The main
type-based intraprocedural devirtualization is implemented in the next
patch and the new version has a test included in it.  However, we do
some very simple devirtualization when folding too and I have decided
not to condition that on the new flag because we would regress at -O1.
However, adding the test is very simple if that is not considered a
problem.  Alternatively, we can also decide to run both
intraprocedural devirtualizations at -O1 if we decide to be even
bolder.

I have bootstrapped and tested this patch on x86_64-linux and it has
passed make check-c++ on i686 together with the next patch too.  I
have also verified the documentation changes by make pdf.  I'd be
happy to respond to any comments and/or suggestions.

Thanks,

Martin


2010-12-13  Martin Jambor  <mjambor@suse.cz>

	* common.opt (fdevirtualize): New flag.
	* doc/invoke.texi (Option Summary): Document it.
	* opts.c (default_options_table): Add devirtualize flag.
	* ipa-prop.c (detect_type_change): Return immediately if
	devirtualize flag is not set.
	(detect_type_change_ssa): Likewise.
	(compute_known_type_jump_func): Likewise.
	(ipa_analyze_virtual_call_uses): Likewise.


Index: icln/gcc/common.opt
===================================================================
--- icln.orig/gcc/common.opt
+++ icln/gcc/common.opt
@@ -911,6 +911,10 @@ fdelete-null-pointer-checks
 Common Report Var(flag_delete_null_pointer_checks) Init(1) Optimization
 Delete useless null pointer checks
 
+fdevirtualize
+Common Report Var(flag_devirtualize) Optimization
+Try to convert virtual calls to direct ones.
+
 fdiagnostics-show-location=
 Common Joined RejectNegative Enum(diagnostic_prefixing_rule)
 -fdiagnostics-show-location=[once|every-line]	How often to emit source location at the beginning of line-wrapped diagnostics
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -514,7 +514,7 @@ detect_type_change (tree arg, tree base,
 		       || handled_component_p (arg));
   /* Const calls cannot call virtual methods through VMT and so type changes do
      not matter.  */
-  if (!gimple_vuse (call))
+  if (!flag_devirtualize || !gimple_vuse (call))
     return false;
 
   ao.ref = arg;
@@ -568,7 +568,8 @@ 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))
+  if (!flag_devirtualize
+      || !POINTER_TYPE_P (TREE_TYPE (arg))
       || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE)
     return false;
 
@@ -770,7 +771,8 @@ compute_known_type_jump_func (tree op, s
   HOST_WIDE_INT offset, size, max_size;
   tree base, binfo;
 
-  if (TREE_CODE (op) != ADDR_EXPR
+  if (!flag_devirtualize
+      || TREE_CODE (op) != ADDR_EXPR
       || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
     return;
 
@@ -1459,6 +1461,9 @@ ipa_analyze_virtual_call_uses (struct cg
   tree var;
   int index;
 
+  if (!flag_devirtualize)
+    return;
+
   if (TREE_CODE (obj) == ADDR_EXPR)
     {
       do
Index: icln/gcc/opts.c
===================================================================
--- icln.orig/gcc/opts.c
+++ icln/gcc/opts.c
@@ -485,6 +485,7 @@ static const struct default_options defa
     { OPT_LEVELS_2_PLUS, OPT_ftree_pre, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_ftree_switch_conversion, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fipa_cp, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fdevirtualize, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fipa_sra, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_falign_loops, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_falign_jumps, NULL, 1 },
Index: icln/gcc/doc/invoke.texi
===================================================================
--- icln.orig/gcc/doc/invoke.texi
+++ icln/gcc/doc/invoke.texi
@@ -340,8 +340,8 @@ Objective-C and Objective-C++ Dialects}.
 -fcprop-registers -fcrossjumping @gol
 -fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules @gol
 -fcx-limited-range @gol
--fdata-sections -fdce -fdce @gol
--fdelayed-branch -fdelete-null-pointer-checks -fdse -fdse @gol
+-fdata-sections -fdce -fdce -fdelayed-branch @gol
+-fdelete-null-pointer-checks -fdse -fdevirtualize -fdse @gol
 -fearly-inlining -fipa-sra -fexpensive-optimizations -ffast-math @gol
 -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
 -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol
@@ -5917,6 +5917,7 @@ also turns on the following optimization
 -fcrossjumping @gol
 -fcse-follow-jumps  -fcse-skip-blocks @gol
 -fdelete-null-pointer-checks @gol
+-fdevirtualize @gol
 -fexpensive-optimizations @gol
 -fgcse  -fgcse-lm  @gol
 -finline-small-functions @gol
@@ -6420,6 +6421,14 @@ Otherwise it is enabled at all levels: @
 @option{-O2}, @option{-O3}, @option{-Os}.  Passes that use the information
 are enabled independently at different optimization levels.
 
+@item -fdevirtualize
+@opindex fdevirtualize
+Attempt to convert calls to virtual functions to direct calls.  This
+is done both within a procedure and inteprocedurally as part of
+indirect inlining (@code{-findirect-inlining}) and inteprocedural copy
+propagation (@option{-fipa-cp}).
+Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
+
 @item -fexpensive-optimizations
 @opindex fexpensive-optimizations
 Perform a number of minor optimizations that are relatively expensive.
Index: icln/gcc/gimple-fold.c
===================================================================
--- icln.orig/gcc/gimple-fold.c
+++ icln/gcc/gimple-fold.c

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done
  2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
                   ` (2 preceding siblings ...)
  2010-12-15 17:33 ` [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization Martin Jambor
@ 2010-12-15 17:43 ` Martin Jambor
  2010-12-22 14:53   ` Martin Jambor
  2011-01-13 11:49   ` Richard Guenther
  2010-12-15 17:44 ` [PATCH, PR 45934 3/5] Identify the new dynamic type after change Martin Jambor
  4 siblings, 2 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:43 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

[-- Attachment #1: remove_impossible_devirt.diff --]
[-- Type: text/plain, Size: 20965 bytes --]

I believe this has been basically already approved by Richi but I'd
like to apply it just before the rest of the series so that we don't
regress in functionality for more time than necessary.  The only
important change since the last time is that the hunks pushing and
popping cfun were moved to the subsequent patch where they belong.

To re-state the general description, this patch removes parts of the
current devirtualization machinery that cannot work because of
potential dynamic type change which might be under way during
sub-object construction and destruction.  In the general case we
simply cannot know this about global objects and we do not want to
proceed with the necessary detection when folding.  So
devirtualization based on global decls, global IPA-CP constants is
being removed and devirtualization in folding is dumbed down to never
work on sub-objects.

This patch removes the two testcases that test devirtualization based
on global variables as well as g++.dg/otr-fold-[12].C which are then
however reintroduced in the last patch in the series.  Furthermore,
g++.dg/tree-ssa/pr43411.C and g++.dg/tree-ssa/pr45605.C fail which is
expected and also dealt with in the last patch (which removes the
xfail).

Bootstrapped and tested separately on x86-64-linux, also tested with
make check-c++ on i686.

Thanks,

Martin


2010-12-09  Martin Jambor  <mjambor@suse.cz>

	PR tree-optimization/45934
	PR tree-optimization/46302
	* gimple-fold.c (get_base_binfo_for_type): Removed.
	(gimple_get_relevant_ref_binfo): Likewise.
	(gimple_fold_obj_type_ref_call): Dumb down to 4.5 functionality,
	removed parameter inplace, updated the caller.
	* gimple.h (gimple_get_relevant_ref_binfo): Remove declaration.
	* ipa-cp.c (ipcp_propagate_types): Do not derive types from constants.
	(ipcp_discover_new_direct_edges): Do not do devirtualization based on
	constants.
	* ipa-prop.c (compute_known_type_jump_func): Use
	get_ref_base_and_extent and get_binfo_at_offset instead of
	gimple_get_relevant_ref_binfo.
	(compute_known_type_jump_func): Likewise.
	(update_jump_functions_after_inlining): Do not derive types from
	constants.
	(try_make_edge_direct_virtual_call): Likewise.
	* tree.c (get_binfo_at_offset): Get type from non-artificial fields.
	* testsuite/g++.dg/ipa/ipcp-ivi-1.C: Removed.
	* testsuite/g++.dg/ipa/ivinline-6.C: Likewise.
	* testsuite/g++.dg/otr-fold-1.C: Likewise.
	* testsuite/g++.dg/otr-fold-2.C: Likewise.
	* testsuite/g++.dg/tree-ssa/pr43411.C: Xfail dump scan.
	* testsuite/g++.dg/tree-ssa/pr45605.C: Likewise.

Index: icln/gcc/gimple-fold.c
===================================================================
--- icln.orig/gcc/gimple-fold.c
+++ icln/gcc/gimple-fold.c
@@ -1364,88 +1364,6 @@ gimple_fold_builtin (gimple stmt)
   return result;
 }
 
-/* Search for a base binfo of BINFO that corresponds to TYPE and return it if
-   it is found or NULL_TREE if it is not.  */
-
-static tree
-get_base_binfo_for_type (tree binfo, tree type)
-{
-  int i;
-  tree base_binfo;
-  tree res = NULL_TREE;
-
-  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
-    if (TREE_TYPE (base_binfo) == type)
-      {
-	gcc_assert (!res);
-	res = base_binfo;
-      }
-
-  return res;
-}
-
-/* Return a binfo describing the part of object referenced by expression REF.
-   Return NULL_TREE if it cannot be determined.  REF can consist of a series of
-   COMPONENT_REFs of a declaration or of an INDIRECT_REF or it can also be just
-   a simple declaration, indirect reference or an SSA_NAME.  If the function
-   discovers an INDIRECT_REF or an SSA_NAME, it will assume that the
-   encapsulating type is described by KNOWN_BINFO, if it is not NULL_TREE.
-   Otherwise the first non-artificial field declaration or the base declaration
-   will be examined to get the encapsulating type. */
-
-tree
-gimple_get_relevant_ref_binfo (tree ref, tree known_binfo)
-{
-  while (true)
-    {
-      if (TREE_CODE (ref) == COMPONENT_REF)
-	{
-	  tree par_type;
-	  tree binfo;
-	  tree field = TREE_OPERAND (ref, 1);
-
-	  if (!DECL_ARTIFICIAL (field))
-	    {
-	      tree type = TREE_TYPE (field);
-	      if (TREE_CODE (type) == RECORD_TYPE)
-		return TYPE_BINFO (type);
-	      else
-		return NULL_TREE;
-	    }
-
-	  par_type = TREE_TYPE (TREE_OPERAND (ref, 0));
-	  binfo = TYPE_BINFO (par_type);
-	  if (!binfo
-	      || BINFO_N_BASE_BINFOS (binfo) == 0)
-	    return NULL_TREE;
-
-	  /* Offset 0 indicates the primary base, whose vtable contents are
-	     represented in the binfo for the derived class.  */
-	  if (int_bit_position (field) != 0)
-	    {
-	      tree d_binfo;
-
-	      /* Get descendant binfo. */
-	      d_binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (ref, 0),
-						       known_binfo);
-	      if (!d_binfo)
-		return NULL_TREE;
-	      return get_base_binfo_for_type (d_binfo, TREE_TYPE (field));
-	    }
-
-	  ref = TREE_OPERAND (ref, 0);
-	}
-      else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) == RECORD_TYPE)
-	return TYPE_BINFO (TREE_TYPE (ref));
-      else if (known_binfo
-	       && (TREE_CODE (ref) == SSA_NAME
-		   || TREE_CODE (ref) == MEM_REF))
-	return known_binfo;
-      else
-	return NULL_TREE;
-    }
-}
-
 /* 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
@@ -1529,7 +1447,7 @@ gimple_adjust_this_by_delta (gimple_stmt
    INPLACE is false.  Return true iff the statement was changed.  */
 
 static bool
-gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
+gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi)
 {
   gimple stmt = gsi_stmt (*gsi);
   tree ref = gimple_call_fn (stmt);
@@ -1537,27 +1455,21 @@ gimple_fold_obj_type_ref_call (gimple_st
   tree binfo, fndecl, delta;
   HOST_WIDE_INT token;
 
-  if (TREE_CODE (obj) == ADDR_EXPR)
-    obj = TREE_OPERAND (obj, 0);
-  else
+  if (TREE_CODE (obj) != ADDR_EXPR)
     return false;
-
-  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
+  obj = TREE_OPERAND (obj, 0);
+  if (!DECL_P (obj)
+      || TREE_CODE (TREE_TYPE (obj)) != RECORD_TYPE)
+    return false;
+  binfo = TYPE_BINFO (TREE_TYPE (obj));
   if (!binfo)
     return false;
+
   token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
-  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
-					     !DECL_P (obj));
+  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta, false);
   if (!fndecl)
     return false;
-
-  if (integer_nonzerop (delta))
-    {
-      if (inplace)
-        return false;
-      gimple_adjust_this_by_delta (gsi, delta);
-    }
-
+  gcc_assert (integer_zerop (delta));
   gimple_call_set_fndecl (stmt, fndecl);
   return true;
 }
@@ -1595,7 +1507,7 @@ gimple_fold_call (gimple_stmt_iterator *
          here where we can just smash the call operand.  */
       callee = gimple_call_fn (stmt);
       if (TREE_CODE (callee) == OBJ_TYPE_REF)
-	return gimple_fold_obj_type_ref_call (gsi, inplace);
+	return gimple_fold_obj_type_ref_call (gsi);
     }
 
   return false;
Index: icln/gcc/gimple.h
===================================================================
--- icln.orig/gcc/gimple.h
+++ icln/gcc/gimple.h
@@ -892,7 +892,6 @@ unsigned get_gimple_rhs_num_ops (enum tr
 gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
 const char *gimple_decl_printable_name (tree, int);
 bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
-tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
 tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
 void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
 /* Returns true iff T is a valid GIMPLE statement.  */
Index: icln/gcc/ipa-cp.c
===================================================================
--- icln.orig/gcc/ipa-cp.c
+++ icln/gcc/ipa-cp.c
@@ -781,26 +781,16 @@ ipcp_propagate_types (struct ipa_node_pa
 		      struct ipa_node_params *callee_info,
 		      struct ipa_jump_func *jf, int i)
 {
-  tree cst, binfo;
-
   switch (jf->type)
     {
     case IPA_JF_UNKNOWN:
     case IPA_JF_CONST_MEMBER_PTR:
+    case IPA_JF_CONST:
       break;
 
     case IPA_JF_KNOWN_TYPE:
       return ipcp_add_param_type (callee_info, i, jf->value.base_binfo);
 
-    case IPA_JF_CONST:
-      cst = jf->value.constant;
-      if (TREE_CODE (cst) != ADDR_EXPR)
-	break;
-      binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE);
-      if (!binfo)
-	break;
-      return ipcp_add_param_type (callee_info, i, binfo);
-
     case IPA_JF_PASS_THROUGH:
     case IPA_JF_ANCESTOR:
       return ipcp_copy_types (caller_info, callee_info, i, jf);
@@ -1292,35 +1282,13 @@ ipcp_discover_new_direct_edges (struct c
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
       struct cgraph_indirect_call_info *ici = ie->indirect_info;
-      tree target, delta = NULL_TREE;
 
       next_ie = ie->next_callee;
-      if (ici->param_index != index)
+      if (ici->param_index != index
+	  || ici->polymorphic)
 	continue;
 
-      if (ici->polymorphic)
-	{
-	  tree binfo;
-	  HOST_WIDE_INT token;
-
-	  if (TREE_CODE (cst) != ADDR_EXPR)
-	    continue;
-
-	  binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
-						 NULL_TREE);
-	  if (!binfo)
-	    continue;
-	  gcc_assert (ie->indirect_info->anc_offset == 0);
-	  token = ie->indirect_info->otr_token;
-	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
-						     true);
-	  if (!target)
-	    continue;
-	}
-      else
-	target = cst;
-
-      ipa_make_edge_direct_to_target (ie, target, delta);
+      ipa_make_edge_direct_to_target (ie, cst, NULL_TREE);
     }
 }
 
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -362,7 +362,7 @@ compute_complex_assign_jump_func (struct
 				  gimple stmt, tree name)
 {
   HOST_WIDE_INT offset, size, max_size;
-  tree op1, op2, type;
+  tree op1, op2, base, type;
   int index;
 
   op1 = gimple_assign_rhs1 (stmt);
@@ -404,20 +404,21 @@ compute_complex_assign_jump_func (struct
   type = TREE_TYPE (op1);
   if (TREE_CODE (type) != RECORD_TYPE)
     return;
-  op1 = get_ref_base_and_extent (op1, &offset, &size, &max_size);
-  if (TREE_CODE (op1) != MEM_REF
+  base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
+  if (TREE_CODE (base) != MEM_REF
       /* If this is a varying address, punt.  */
       || max_size == -1
       || max_size != size)
     return;
-  offset += mem_ref_offset (op1).low * BITS_PER_UNIT;
-  op1 = TREE_OPERAND (op1, 0);
-  if (TREE_CODE (op1) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (op1)
+  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)
       || offset < 0)
     return;
 
-  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1));
+  /* Dynamic types are changed only in constructors and destructors and  */
+  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base));
   if (index >= 0)
     {
       jfunc->type = IPA_JF_ANCESTOR;
@@ -534,13 +535,26 @@ compute_complex_ancestor_jump_func (stru
 static void
 compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc)
 {
-  tree binfo;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base, binfo;
 
-  if (TREE_CODE (op) != ADDR_EXPR)
+  if (TREE_CODE (op) != ADDR_EXPR
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
     return;
 
   op = TREE_OPERAND (op, 0);
-  binfo = gimple_get_relevant_ref_binfo (op, NULL_TREE);
+  base = get_ref_base_and_extent (op, &offset, &size, &max_size);
+  if (!DECL_P (base)
+      || max_size == -1
+      || max_size != size
+      || TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE
+      || is_global_var (base))
+    return;
+
+  binfo = TYPE_BINFO (TREE_TYPE (base));
+  if (!binfo)
+    return;
+  binfo = get_binfo_at_offset (binfo, offset, TREE_TYPE (op));
   if (binfo)
     {
       jfunc->type = IPA_JF_KNOWN_TYPE;
@@ -1420,17 +1434,6 @@ update_jump_functions_after_inlining (st
 	  src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
 	  if (src->type == IPA_JF_KNOWN_TYPE)
 	    combine_known_type_and_ancestor_jfs (src, dst);
-	  else if (src->type == IPA_JF_CONST)
-	    {
-	      struct ipa_jump_func kt_func;
-
-	      kt_func.type = IPA_JF_UNKNOWN;
-	      compute_known_type_jump_func (src->value.constant, &kt_func);
-	      if (kt_func.type == IPA_JF_KNOWN_TYPE)
-		combine_known_type_and_ancestor_jfs (&kt_func, dst);
-	      else
-		dst->type = IPA_JF_UNKNOWN;
-	    }
 	  else if (src->type == IPA_JF_PASS_THROUGH
 		   && src->value.pass_through.operation == NOP_EXPR)
 	    dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
@@ -1543,15 +1546,6 @@ try_make_edge_direct_virtual_call (struc
 
   if (jfunc->type == IPA_JF_KNOWN_TYPE)
     binfo = jfunc->value.base_binfo;
-  else if (jfunc->type == IPA_JF_CONST)
-    {
-      tree cst = jfunc->value.constant;
-      if (TREE_CODE (cst) == ADDR_EXPR)
-	binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
-					       NULL_TREE);
-      else
-  	return NULL;
-    }
   else
     return NULL;
 
Index: icln/gcc/tree.c
===================================================================
--- icln.orig/gcc/tree.c
+++ icln/gcc/tree.c
@@ -10945,8 +10945,7 @@ get_binfo_at_offset (tree binfo, HOST_WI
 
       if (type == expected_type)
 	  return binfo;
-      if (TREE_CODE (type) != RECORD_TYPE
-	  || offset < 0)
+      if (offset < 0)
 	return NULL_TREE;
 
       for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
@@ -10959,12 +10958,18 @@ get_binfo_at_offset (tree binfo, HOST_WI
 	  if (pos <= offset && (pos + size) > offset)
 	    break;
 	}
-      if (!fld || !DECL_ARTIFICIAL (fld))
+      if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE)
 	return NULL_TREE;
 
+      if (!DECL_ARTIFICIAL (fld))
+	{
+	  binfo = TYPE_BINFO (TREE_TYPE (fld));
+	  if (!binfo)
+	    return NULL_TREE;
+	}
       /* Offset 0 indicates the primary base, whose vtable contents are
 	 represented in the binfo for the derived class.  */
-      if (offset != 0)
+      else if (offset != 0)
 	{
 	  tree base_binfo, found_binfo = NULL_TREE;
 	  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
Index: icln/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Verify that simple virtual calls are inlined even without early
-   inlining.  */
-/* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
-
-extern "C" void abort (void);
-
-class A
-{
-public:
-  int data;
-  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;
-}
-
-int __attribute__ ((noinline)) middleman (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-class B b;
-
-int main (int argc, char *argv[])
-{
-  int i;
-
-  for (i = 0; i < get_input (); i++)
-    if (middleman (&b, get_input ()) != 3)
-      abort ();
-  return 0;
-}
-
-/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int.*middleman"  "inline"  } } */
-/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/ivinline-6.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/ivinline-6.C
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Verify that virtual call inlining works also when it has to get the
-   type from an ipa invariant and that even in this case it does not
-   pick a wrong method when there is a user defined ancestor in an
-   object.  */
-/* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
-
-extern "C" void abort (void);
-
-class A
-{
-public:
-  int data;
-  virtual int foo (int i);
-};
-
-class B : public A
-{
-public:
-  class A confusion;
-  virtual int foo (int i);
-};
-
-int A::foo (int i)
-{
-  return i + 1;
-}
-
-int B::foo (int i)
-{
-  return i + 2;
-}
-
-int middleman (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-class B b;
-
-int main (int argc, char *argv[])
-{
-  int i, j = get_input ();
-
-  for (i = 0; i < j; i++)
-    if ((middleman (&b, j) + 100 * middleman (&b.confusion, j)) != 203)
-      abort ();
-  return 0;
-}
-
-/* { dg-final { scan-ipa-dump "A::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
-/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
-/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
@@ -25,5 +25,5 @@ void testInlinePsub() {
     sink1 = v(p);
 }
 
-// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" } }
+// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" { xfail *-*-* } } }
 // { dg-final { cleanup-tree-dump "optimized" } }
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
@@ -33,5 +33,5 @@ int main() {
 
 
 /* We should devirtualize call to D::Run */
-/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa"} } */
+/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa" { xfail *-*-* } } } */
 /* { dg-final { cleanup-tree-dump "ssa" } } */
Index: icln/gcc/testsuite/g++.dg/otr-fold-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/otr-fold-1.C
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Verify that virtual calls are folded even when a typecast to an
-   ancestor is involved along the way.  */
-/* { dg-do run } */
-/* { dg-options "-O -fdump-tree-optimized-slim"  } */
-
-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;
-  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 inline int middleman_1 (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-static inline int middleman_2 (class B *obj, int i)
-{
-  return middleman_1 (obj, i);
-}
-
-int main (int argc, char *argv[])
-{
-  class B b;
-
-  if (middleman_2 (&b, get_input ()) != 3)
-    abort ();
-  return 0;
-}
-
-/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
-/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/otr-fold-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/otr-fold-2.C
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Verify that virtual calls are folded even when a typecast to an
-   ancestor is involved along the way.  */
-/* { dg-do run } */
-/* { dg-options "-O -fdump-tree-optimized-slim"  } */
-
-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;
-  virtual int foo (int i);
-};
-
-class A_2 : public A
-{
-public:
-  int data_2;
-  virtual int baz (int i);
-};
-
-
-class B : public Distraction, public A_2
-{
-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 A_2::baz (int i)
-{
-  return i * 15;
-}
-
-int B::foo (int i)
-{
-  return i + 2;
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-static inline int middleman_1 (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-static inline int middleman_2 (class A *obj, int i)
-{
-  return middleman_1 (obj, i);
-}
-
-int main (int argc, char *argv[])
-{
-  class B b;
-
-  if (middleman_2 (&b, get_input ()) != 3)
-    abort ();
-  return 0;
-}
-
-/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
-/* { dg-final { cleanup-tree-dump "optimized" } } */

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH, PR 45934 3/5] Identify the new dynamic type after change
  2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
                   ` (3 preceding siblings ...)
  2010-12-15 17:43 ` [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done Martin Jambor
@ 2010-12-15 17:44 ` Martin Jambor
  2011-01-15  9:08   ` Martin Jambor
  4 siblings, 1 reply; 16+ messages in thread
From: Martin Jambor @ 2010-12-15 17:44 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

[-- Attachment #1: identify_new_dynamic_type.diff --]
[-- Type: text/plain, Size: 10969 bytes --]

This patch adds code attempting to identify of the new dynamic type
after it has been changed, which has been missing in the previous
patch.  All the details are in the previous email.

Just for the record, this has been also bootstrapped and tested
(without the last patch applied) on x86-64-linux and has passed make
check-c++ on i686.

Thanks,

Martin


2010-12-09  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (type_change_info): New fields object, known_current_type
	and multiple_types_encountered.
	(extr_type_from_vtbl_ptr_store): New function.
	(check_stmt_for_type_change): Use it, set multiple_types_encountered if
        the result is different from the prvious one.
	(detect_type_change): Set up new fields in tci, build knonw type
	jump functions if the new type chan be identified.

	* testsuite/g++.dg/ipa/devirt-c-1.C: Add dump scans.
	* testsuite/g++.dg/ipa/devirt-c-2.C: Likewise.
	* testsuite/g++.dg/ipa/devirt-c-3.C: Likewise.
	* testsuite/g++.dg/ipa/devirt-c-4.C: Likewise.
	* testsuite/g++.dg/ipa/devirt-c-8.C: New test.


Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -355,8 +355,17 @@ ipa_print_all_jump_functions (FILE *f)
 
 struct type_change_info
 {
+  /* The declaration or SSA_NAME pointer of the base that we are checking for
+     type change.  */
+  tree object;
+  /* If we actually can tell the type that the object has changed to, it is
+     stored in this field.  Otherwise it remains NULL_TREE.  */
+  tree known_current_type;
   /* Set to true if dynamic type change has been detected.  */
   bool type_maybe_changed;
+  /* Set to true if multiple types have been encountered.  known_current_type
+     must be disregarded in that case.  */
+  bool multiple_types_encountered;
 };
 
 /* Return true if STMT can modify a virtual method table pointer.
@@ -415,6 +424,49 @@ stmt_may_be_vtbl_ptr_store (gimple stmt)
   return true;
 }
 
+/* If STMT can be proved to be an assignment to the virtual method table
+   pointer of ANALYZED_OBJ and the type associated witht the new table
+   identified, return the type.  Otherwise return NULL_TREE.  */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
+{
+  tree lhs, t, obj;
+
+  if (!is_gimple_assign (stmt))
+    return NULL_TREE;
+
+  lhs = gimple_assign_lhs (stmt);
+
+  if (TREE_CODE (lhs) != COMPONENT_REF)
+    return NULL_TREE;
+   obj = lhs;
+
+   if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+     return NULL_TREE;
+
+   do
+     {
+       obj = TREE_OPERAND (obj, 0);
+     }
+   while (TREE_CODE (obj) == COMPONENT_REF);
+   if (TREE_CODE (obj) == MEM_REF)
+     obj = TREE_OPERAND (obj, 0);
+   if (TREE_CODE (obj) == ADDR_EXPR)
+     obj = TREE_OPERAND (obj, 0);
+   if (obj != analyzed_obj)
+     return NULL_TREE;
+
+   t = gimple_assign_rhs1 (stmt);
+   if (TREE_CODE (t) != ADDR_EXPR)
+     return NULL_TREE;
+   t = get_base_address (TREE_OPERAND (t, 0));
+   if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
+     return NULL_TREE;
+
+   return DECL_CONTEXT (t);
+}
+
 /* 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
@@ -429,6 +481,12 @@ check_stmt_for_type_change (ao_ref *ao A
 
   if (stmt_may_be_vtbl_ptr_store (stmt))
     {
+      tree type;
+      type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
+      if (tci->type_maybe_changed
+	  && type != tci->known_current_type)
+	tci->multiple_types_encountered = true;
+      tci->known_current_type = type;
       tci->type_maybe_changed = true;
       return true;
     }
@@ -448,6 +506,7 @@ detect_type_change (tree arg, tree base,
 		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
 {
   struct type_change_info tci;
+  tree type;
   ao_ref ao;
 
   gcc_checking_assert (DECL_P (arg)
@@ -458,8 +517,6 @@ detect_type_change (tree arg, tree base,
   if (!gimple_vuse (call))
     return false;
 
-  tci.type_maybe_changed = false;
-
   ao.ref = arg;
   ao.base = base;
   ao.offset = offset;
@@ -468,12 +525,38 @@ detect_type_change (tree arg, tree base,
   ao.ref_alias_set = -1;
   ao.base_alias_set = -1;
 
+  type = TREE_TYPE (arg);
+  while (handled_component_p (arg))
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == MEM_REF)
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == ADDR_EXPR)
+    arg = TREE_OPERAND (arg, 0);
+  tci.object = arg;
+  tci.known_current_type = NULL_TREE;
+  tci.type_maybe_changed = false;
+  tci.multiple_types_encountered = false;
+
   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
 		      &tci, NULL);
   if (!tci.type_maybe_changed)
     return false;
 
-  jfunc->type = IPA_JF_UNKNOWN;
+ if (!tci.known_current_type || tci.multiple_types_encountered)
+   jfunc->type = IPA_JF_UNKNOWN;
+ else
+   {
+     tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
+					   offset, type);
+     if (new_binfo)
+	{
+	  jfunc->type = IPA_JF_KNOWN_TYPE;
+	  jfunc->value.base_binfo = new_binfo;
+	}
+     else
+       jfunc->type = IPA_JF_UNKNOWN;
+   }
+
   return true;
 }
 
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -69,3 +69,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -77,3 +77,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-inline"  } */
+/* { dg-options "-O3 -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -78,3 +78,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-inline"  } */
+/* { dg-options "-O3 -fno-inline -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -108,3 +108,6 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/tree.h
===================================================================
--- icln.orig/gcc/tree.h
+++ icln/gcc/tree.h
@@ -2565,7 +2565,9 @@ struct function;
     nodes, this points to either the FUNCTION_DECL for the containing
     function, the RECORD_TYPE or UNION_TYPE for the containing type, or
     NULL_TREE or a TRANSLATION_UNIT_DECL if the given decl has "file
-    scope".  */
+    scope".  In particular, for VAR_DECLs which are virtual table pointers
+    (they have DECL_VIRTUAL set), we use DECL_CONTEXT to determine the type
+    they belong to.  */
 #define DECL_CONTEXT(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.context)
 #define DECL_FIELD_CONTEXT(NODE) \
   (FIELD_DECL_CHECK (NODE)->decl_minimal.context)
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-8.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-8.C
@@ -0,0 +1,87 @@
+/* Verify that ipa-cp will not get confused by placement new constructing an
+   object within another one when looking for dynamic type change .  */
+/* { dg-do run } */
+/* { dg-options "-O3"  } */
+
+extern "C" void abort (void);
+namespace std {
+  typedef __SIZE_TYPE__ size_t;
+}
+inline void* __attribute__ ((always_inline))
+operator new(std::size_t, void* __p) throw()
+{
+  return __p;
+}
+
+class A
+{
+public:
+  char data[256];
+  A();
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C
+{
+public:
+  C();
+  virtual double foo (double i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+double C::foo (double i)
+{
+  return i + 3.5;
+}
+
+static int __attribute__ ((noinline)) middleman (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+__attribute__ ((always_inline)) C::C ()
+{
+}
+
+A::A ()
+{
+}
+
+static  __attribute__ ((noinline)) void bah ()
+{
+  class B b;
+
+  C *c = new ((void *) &b.data) C;
+
+  if (middleman (&b, get_input ()) != 3)
+    abort ();
+}
+
+int main (int argc, char *argv[])
+{
+  int i;
+
+  for (i = 0; i < 10; i++)
+    bah ();
+  return 0;
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization
  2010-12-15 17:33 ` [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization Martin Jambor
@ 2010-12-15 18:55   ` Ralf Wildenhues
  2010-12-16 11:59     ` Martin Jambor
  2011-01-13 13:01   ` Richard Guenther
  1 sibling, 1 reply; 16+ messages in thread
From: Ralf Wildenhues @ 2010-12-15 18:55 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka, Richard Guenther

* Martin Jambor wrote on Wed, Dec 15, 2010 at 05:49:06PM CET:
> 	* common.opt (fdevirtualize): New flag.
> 	* doc/invoke.texi (Option Summary): Document it.
> 	* opts.c (default_options_table): Add devirtualize flag.
> 	* ipa-prop.c (detect_type_change): Return immediately if
> 	devirtualize flag is not set.
> 	(detect_type_change_ssa): Likewise.
> 	(compute_known_type_jump_func): Likewise.
> 	(ipa_analyze_virtual_call_uses): Likewise.

> --- icln.orig/gcc/doc/invoke.texi
> +++ icln/gcc/doc/invoke.texi

> @@ -6420,6 +6421,14 @@ Otherwise it is enabled at all levels: @
>  @option{-O2}, @option{-O3}, @option{-Os}.  Passes that use the information
>  are enabled independently at different optimization levels.
>  
> +@item -fdevirtualize
> +@opindex fdevirtualize
> +Attempt to convert calls to virtual functions to direct calls.  This
> +is done both within a procedure and inteprocedurally as part of

interprocedurally

> +indirect inlining (@code{-findirect-inlining}) and inteprocedural copy

interprocedural

> +propagation (@option{-fipa-cp}).
> +Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.

Cheers,
Ralf

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization
  2010-12-15 18:55   ` Ralf Wildenhues
@ 2010-12-16 11:59     ` Martin Jambor
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-16 11:59 UTC (permalink / raw)
  To: Ralf Wildenhues, GCC Patches

Hi,

On Wed, Dec 15, 2010 at 07:13:39PM +0100, Ralf Wildenhues wrote:
> * Martin Jambor wrote on Wed, Dec 15, 2010 at 05:49:06PM CET:
> > 	* common.opt (fdevirtualize): New flag.
> > 	* doc/invoke.texi (Option Summary): Document it.
> > 	* opts.c (default_options_table): Add devirtualize flag.
> > 	* ipa-prop.c (detect_type_change): Return immediately if
> > 	devirtualize flag is not set.
> > 	(detect_type_change_ssa): Likewise.
> > 	(compute_known_type_jump_func): Likewise.
> > 	(ipa_analyze_virtual_call_uses): Likewise.
> 
> > --- icln.orig/gcc/doc/invoke.texi
> > +++ icln/gcc/doc/invoke.texi
> 
> > @@ -6420,6 +6421,14 @@ Otherwise it is enabled at all levels: @
> >  @option{-O2}, @option{-O3}, @option{-Os}.  Passes that use the information
> >  are enabled independently at different optimization levels.
> >  
> > +@item -fdevirtualize
> > +@opindex fdevirtualize
> > +Attempt to convert calls to virtual functions to direct calls.  This
> > +is done both within a procedure and inteprocedurally as part of
> 
> interprocedurally
> 
> > +indirect inlining (@code{-findirect-inlining}) and inteprocedural copy
> 
> interprocedural

... and constant propagation, not copy propagation.

Thanks, I must have forgotten to switch on my spell checker.  All of
the above is corrected in my version of the patch now.

Martin

> 
> > +propagation (@option{-fipa-cp}).
> > +Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
> 
> Cheers,
> Ralf

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done
  2010-12-15 17:43 ` [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done Martin Jambor
@ 2010-12-22 14:53   ` Martin Jambor
  2011-01-13 11:49   ` Richard Guenther
  1 sibling, 0 replies; 16+ messages in thread
From: Martin Jambor @ 2010-12-22 14:53 UTC (permalink / raw)
  To: GCC Patches

Hi,

On Wed, Dec 15, 2010 at 05:49:03PM +0100, Martin Jambor wrote:
> I believe this has been basically already approved by Richi but I'd
> like to apply it just before the rest of the series so that we don't
> regress in functionality for more time than necessary.  The only
> important change since the last time is that the hunks pushing and
> popping cfun were moved to the subsequent patch where they belong.
> 
> To re-state the general description, this patch removes parts of the
> current devirtualization machinery that cannot work because of
> potential dynamic type change which might be under way during
> sub-object construction and destruction.  In the general case we
> simply cannot know this about global objects and we do not want to
> proceed with the necessary detection when folding.  So
> devirtualization based on global decls, global IPA-CP constants is
> being removed and devirtualization in folding is dumbed down to never
> work on sub-objects.
> 
> This patch removes the two testcases that test devirtualization based
> on global variables as well as g++.dg/otr-fold-[12].C which are then
> however reintroduced in the last patch in the series.  Furthermore,
> g++.dg/tree-ssa/pr43411.C and g++.dg/tree-ssa/pr45605.C fail which is
> expected and also dealt with in the last patch (which removes the
> xfail).
> 

because this patch also fixes a rather ugly PR 46987, I have committed
it today as revision 168168. instead of waiting for an approval of the
followups.

The patch has been approved by Richi some time ago
(http://gcc.gnu.org/ml/gcc-patches/2010-12/msg00149.html), since then
I have basically only fiddled with the testcases, xfailing some as
Richi suggested and added a new one for PR 46987.  I hope that is OK,
the exact committed patch is below.

This patch also makes PR 46823 go away, however at this point I have
reasons to believe it merely hides the real problem.  I'm still in the
process of trying to figure out what that is.

I re-bootstrapped and retested separately on x86-64-linux, also
re-tested with make check-c++ on i686 (all of it this Monday).

Thanks,

Martin


2010-12-22  Martin Jambor  <mjambor@suse.cz>

	PR tree-optimization/45934
	PR tree-optimization/46302
	PR tree-optimization/46987
	* gimple-fold.c (get_base_binfo_for_type): Removed.
	(gimple_get_relevant_ref_binfo): Likewise.
	(gimple_fold_obj_type_ref_call): Dumb down to 4.5 functionality,
	removed parameter inplace, updated the caller.
	* gimple.h (gimple_get_relevant_ref_binfo): Remove declaration.
	* ipa-cp.c (ipcp_propagate_types): Do not derive types from constants.
	(ipcp_discover_new_direct_edges): Do not do devirtualization based on
	constants.
	* ipa-prop.c (compute_known_type_jump_func): Use
	get_ref_base_and_extent and get_binfo_at_offset instead of
	gimple_get_relevant_ref_binfo.
	(compute_known_type_jump_func): Likewise.
	(update_jump_functions_after_inlining): Do not derive types from
	constants.
	(try_make_edge_direct_virtual_call): Likewise.
	* tree.c (get_binfo_at_offset): Get type from non-artificial fields.

	* testsuite/g++.dg/ipa/ipcp-ivi-1.C: Removed.
	* testsuite/g++.dg/ipa/ivinline-6.C: Likewise.
	* testsuite/g++.dg/otr-fold-1.C: Likewise.
	* testsuite/g++.dg/otr-fold-2.C: Likewise.
	* testsuite/g++.dg/tree-ssa/pr43411.C: Xfail dump scan.
	* testsuite/g++.dg/tree-ssa/pr45605.C: Likewise.
	* testsuite/g++.dg/tree-ssa/pr46987.C: New test.

Index: icln/gcc/gimple-fold.c
===================================================================
--- icln.orig/gcc/gimple-fold.c
+++ icln/gcc/gimple-fold.c
@@ -1364,88 +1364,6 @@ gimple_fold_builtin (gimple stmt)
   return result;
 }
 
-/* Search for a base binfo of BINFO that corresponds to TYPE and return it if
-   it is found or NULL_TREE if it is not.  */
-
-static tree
-get_base_binfo_for_type (tree binfo, tree type)
-{
-  int i;
-  tree base_binfo;
-  tree res = NULL_TREE;
-
-  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
-    if (TREE_TYPE (base_binfo) == type)
-      {
-	gcc_assert (!res);
-	res = base_binfo;
-      }
-
-  return res;
-}
-
-/* Return a binfo describing the part of object referenced by expression REF.
-   Return NULL_TREE if it cannot be determined.  REF can consist of a series of
-   COMPONENT_REFs of a declaration or of an INDIRECT_REF or it can also be just
-   a simple declaration, indirect reference or an SSA_NAME.  If the function
-   discovers an INDIRECT_REF or an SSA_NAME, it will assume that the
-   encapsulating type is described by KNOWN_BINFO, if it is not NULL_TREE.
-   Otherwise the first non-artificial field declaration or the base declaration
-   will be examined to get the encapsulating type. */
-
-tree
-gimple_get_relevant_ref_binfo (tree ref, tree known_binfo)
-{
-  while (true)
-    {
-      if (TREE_CODE (ref) == COMPONENT_REF)
-	{
-	  tree par_type;
-	  tree binfo;
-	  tree field = TREE_OPERAND (ref, 1);
-
-	  if (!DECL_ARTIFICIAL (field))
-	    {
-	      tree type = TREE_TYPE (field);
-	      if (TREE_CODE (type) == RECORD_TYPE)
-		return TYPE_BINFO (type);
-	      else
-		return NULL_TREE;
-	    }
-
-	  par_type = TREE_TYPE (TREE_OPERAND (ref, 0));
-	  binfo = TYPE_BINFO (par_type);
-	  if (!binfo
-	      || BINFO_N_BASE_BINFOS (binfo) == 0)
-	    return NULL_TREE;
-
-	  /* Offset 0 indicates the primary base, whose vtable contents are
-	     represented in the binfo for the derived class.  */
-	  if (int_bit_position (field) != 0)
-	    {
-	      tree d_binfo;
-
-	      /* Get descendant binfo. */
-	      d_binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (ref, 0),
-						       known_binfo);
-	      if (!d_binfo)
-		return NULL_TREE;
-	      return get_base_binfo_for_type (d_binfo, TREE_TYPE (field));
-	    }
-
-	  ref = TREE_OPERAND (ref, 0);
-	}
-      else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) == RECORD_TYPE)
-	return TYPE_BINFO (TREE_TYPE (ref));
-      else if (known_binfo
-	       && (TREE_CODE (ref) == SSA_NAME
-		   || TREE_CODE (ref) == MEM_REF))
-	return known_binfo;
-      else
-	return NULL_TREE;
-    }
-}
-
 /* 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
@@ -1529,7 +1447,7 @@ gimple_adjust_this_by_delta (gimple_stmt
    INPLACE is false.  Return true iff the statement was changed.  */
 
 static bool
-gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
+gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi)
 {
   gimple stmt = gsi_stmt (*gsi);
   tree ref = gimple_call_fn (stmt);
@@ -1537,27 +1455,21 @@ gimple_fold_obj_type_ref_call (gimple_st
   tree binfo, fndecl, delta;
   HOST_WIDE_INT token;
 
-  if (TREE_CODE (obj) == ADDR_EXPR)
-    obj = TREE_OPERAND (obj, 0);
-  else
+  if (TREE_CODE (obj) != ADDR_EXPR)
     return false;
-
-  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
+  obj = TREE_OPERAND (obj, 0);
+  if (!DECL_P (obj)
+      || TREE_CODE (TREE_TYPE (obj)) != RECORD_TYPE)
+    return false;
+  binfo = TYPE_BINFO (TREE_TYPE (obj));
   if (!binfo)
     return false;
+
   token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
-  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
-					     !DECL_P (obj));
+  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta, false);
   if (!fndecl)
     return false;
-
-  if (integer_nonzerop (delta))
-    {
-      if (inplace)
-        return false;
-      gimple_adjust_this_by_delta (gsi, delta);
-    }
-
+  gcc_assert (integer_zerop (delta));
   gimple_call_set_fndecl (stmt, fndecl);
   return true;
 }
@@ -1595,7 +1507,7 @@ gimple_fold_call (gimple_stmt_iterator *
          here where we can just smash the call operand.  */
       callee = gimple_call_fn (stmt);
       if (TREE_CODE (callee) == OBJ_TYPE_REF)
-	return gimple_fold_obj_type_ref_call (gsi, inplace);
+	return gimple_fold_obj_type_ref_call (gsi);
     }
 
   return false;
Index: icln/gcc/gimple.h
===================================================================
--- icln.orig/gcc/gimple.h
+++ icln/gcc/gimple.h
@@ -892,7 +892,6 @@ unsigned get_gimple_rhs_num_ops (enum tr
 gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
 const char *gimple_decl_printable_name (tree, int);
 bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
-tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
 tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
 void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
 /* Returns true iff T is a valid GIMPLE statement.  */
Index: icln/gcc/ipa-cp.c
===================================================================
--- icln.orig/gcc/ipa-cp.c
+++ icln/gcc/ipa-cp.c
@@ -781,26 +781,16 @@ ipcp_propagate_types (struct ipa_node_pa
 		      struct ipa_node_params *callee_info,
 		      struct ipa_jump_func *jf, int i)
 {
-  tree cst, binfo;
-
   switch (jf->type)
     {
     case IPA_JF_UNKNOWN:
     case IPA_JF_CONST_MEMBER_PTR:
+    case IPA_JF_CONST:
       break;
 
     case IPA_JF_KNOWN_TYPE:
       return ipcp_add_param_type (callee_info, i, jf->value.base_binfo);
 
-    case IPA_JF_CONST:
-      cst = jf->value.constant;
-      if (TREE_CODE (cst) != ADDR_EXPR)
-	break;
-      binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE);
-      if (!binfo)
-	break;
-      return ipcp_add_param_type (callee_info, i, binfo);
-
     case IPA_JF_PASS_THROUGH:
     case IPA_JF_ANCESTOR:
       return ipcp_copy_types (caller_info, callee_info, i, jf);
@@ -1292,35 +1282,13 @@ ipcp_discover_new_direct_edges (struct c
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
       struct cgraph_indirect_call_info *ici = ie->indirect_info;
-      tree target, delta = NULL_TREE;
 
       next_ie = ie->next_callee;
-      if (ici->param_index != index)
+      if (ici->param_index != index
+	  || ici->polymorphic)
 	continue;
 
-      if (ici->polymorphic)
-	{
-	  tree binfo;
-	  HOST_WIDE_INT token;
-
-	  if (TREE_CODE (cst) != ADDR_EXPR)
-	    continue;
-
-	  binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
-						 NULL_TREE);
-	  if (!binfo)
-	    continue;
-	  gcc_assert (ie->indirect_info->anc_offset == 0);
-	  token = ie->indirect_info->otr_token;
-	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
-						     true);
-	  if (!target)
-	    continue;
-	}
-      else
-	target = cst;
-
-      ipa_make_edge_direct_to_target (ie, target, delta);
+      ipa_make_edge_direct_to_target (ie, cst, NULL_TREE);
     }
 }
 
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -362,7 +362,7 @@ compute_complex_assign_jump_func (struct
 				  gimple stmt, tree name)
 {
   HOST_WIDE_INT offset, size, max_size;
-  tree op1, op2, type;
+  tree op1, op2, base, type;
   int index;
 
   op1 = gimple_assign_rhs1 (stmt);
@@ -404,20 +404,21 @@ compute_complex_assign_jump_func (struct
   type = TREE_TYPE (op1);
   if (TREE_CODE (type) != RECORD_TYPE)
     return;
-  op1 = get_ref_base_and_extent (op1, &offset, &size, &max_size);
-  if (TREE_CODE (op1) != MEM_REF
+  base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
+  if (TREE_CODE (base) != MEM_REF
       /* If this is a varying address, punt.  */
       || max_size == -1
       || max_size != size)
     return;
-  offset += mem_ref_offset (op1).low * BITS_PER_UNIT;
-  op1 = TREE_OPERAND (op1, 0);
-  if (TREE_CODE (op1) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (op1)
+  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)
       || offset < 0)
     return;
 
-  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1));
+  /* Dynamic types are changed only in constructors and destructors and  */
+  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base));
   if (index >= 0)
     {
       jfunc->type = IPA_JF_ANCESTOR;
@@ -534,13 +535,26 @@ compute_complex_ancestor_jump_func (stru
 static void
 compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc)
 {
-  tree binfo;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base, binfo;
 
-  if (TREE_CODE (op) != ADDR_EXPR)
+  if (TREE_CODE (op) != ADDR_EXPR
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
     return;
 
   op = TREE_OPERAND (op, 0);
-  binfo = gimple_get_relevant_ref_binfo (op, NULL_TREE);
+  base = get_ref_base_and_extent (op, &offset, &size, &max_size);
+  if (!DECL_P (base)
+      || max_size == -1
+      || max_size != size
+      || TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE
+      || is_global_var (base))
+    return;
+
+  binfo = TYPE_BINFO (TREE_TYPE (base));
+  if (!binfo)
+    return;
+  binfo = get_binfo_at_offset (binfo, offset, TREE_TYPE (op));
   if (binfo)
     {
       jfunc->type = IPA_JF_KNOWN_TYPE;
@@ -1420,17 +1434,6 @@ update_jump_functions_after_inlining (st
 	  src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
 	  if (src->type == IPA_JF_KNOWN_TYPE)
 	    combine_known_type_and_ancestor_jfs (src, dst);
-	  else if (src->type == IPA_JF_CONST)
-	    {
-	      struct ipa_jump_func kt_func;
-
-	      kt_func.type = IPA_JF_UNKNOWN;
-	      compute_known_type_jump_func (src->value.constant, &kt_func);
-	      if (kt_func.type == IPA_JF_KNOWN_TYPE)
-		combine_known_type_and_ancestor_jfs (&kt_func, dst);
-	      else
-		dst->type = IPA_JF_UNKNOWN;
-	    }
 	  else if (src->type == IPA_JF_PASS_THROUGH
 		   && src->value.pass_through.operation == NOP_EXPR)
 	    dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
@@ -1543,15 +1546,6 @@ try_make_edge_direct_virtual_call (struc
 
   if (jfunc->type == IPA_JF_KNOWN_TYPE)
     binfo = jfunc->value.base_binfo;
-  else if (jfunc->type == IPA_JF_CONST)
-    {
-      tree cst = jfunc->value.constant;
-      if (TREE_CODE (cst) == ADDR_EXPR)
-	binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
-					       NULL_TREE);
-      else
-  	return NULL;
-    }
   else
     return NULL;
 
Index: icln/gcc/tree.c
===================================================================
--- icln.orig/gcc/tree.c
+++ icln/gcc/tree.c
@@ -10949,8 +10949,7 @@ get_binfo_at_offset (tree binfo, HOST_WI
 
       if (type == expected_type)
 	  return binfo;
-      if (TREE_CODE (type) != RECORD_TYPE
-	  || offset < 0)
+      if (offset < 0)
 	return NULL_TREE;
 
       for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
@@ -10963,12 +10962,18 @@ get_binfo_at_offset (tree binfo, HOST_WI
 	  if (pos <= offset && (pos + size) > offset)
 	    break;
 	}
-      if (!fld || !DECL_ARTIFICIAL (fld))
+      if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE)
 	return NULL_TREE;
 
+      if (!DECL_ARTIFICIAL (fld))
+	{
+	  binfo = TYPE_BINFO (TREE_TYPE (fld));
+	  if (!binfo)
+	    return NULL_TREE;
+	}
       /* Offset 0 indicates the primary base, whose vtable contents are
 	 represented in the binfo for the derived class.  */
-      if (offset != 0)
+      else if (offset != 0)
 	{
 	  tree base_binfo, found_binfo = NULL_TREE;
 	  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
Index: icln/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Verify that simple virtual calls are inlined even without early
-   inlining.  */
-/* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
-
-extern "C" void abort (void);
-
-class A
-{
-public:
-  int data;
-  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;
-}
-
-int __attribute__ ((noinline)) middleman (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-class B b;
-
-int main (int argc, char *argv[])
-{
-  int i;
-
-  for (i = 0; i < get_input (); i++)
-    if (middleman (&b, get_input ()) != 3)
-      abort ();
-  return 0;
-}
-
-/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int.*middleman"  "inline"  } } */
-/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/ivinline-6.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/ivinline-6.C
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Verify that virtual call inlining works also when it has to get the
-   type from an ipa invariant and that even in this case it does not
-   pick a wrong method when there is a user defined ancestor in an
-   object.  */
-/* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
-
-extern "C" void abort (void);
-
-class A
-{
-public:
-  int data;
-  virtual int foo (int i);
-};
-
-class B : public A
-{
-public:
-  class A confusion;
-  virtual int foo (int i);
-};
-
-int A::foo (int i)
-{
-  return i + 1;
-}
-
-int B::foo (int i)
-{
-  return i + 2;
-}
-
-int middleman (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-class B b;
-
-int main (int argc, char *argv[])
-{
-  int i, j = get_input ();
-
-  for (i = 0; i < j; i++)
-    if ((middleman (&b, j) + 100 * middleman (&b.confusion, j)) != 203)
-      abort ();
-  return 0;
-}
-
-/* { dg-final { scan-ipa-dump "A::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
-/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
-/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
@@ -25,5 +25,5 @@ void testInlinePsub() {
     sink1 = v(p);
 }
 
-// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" } }
+// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" { xfail *-*-* } } }
 // { dg-final { cleanup-tree-dump "optimized" } }
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
@@ -33,5 +33,5 @@ int main() {
 
 
 /* We should devirtualize call to D::Run */
-/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa"} } */
+/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa" { xfail *-*-* } } } */
 /* { dg-final { cleanup-tree-dump "ssa" } } */
Index: icln/gcc/testsuite/g++.dg/otr-fold-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/otr-fold-1.C
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Verify that virtual calls are folded even when a typecast to an
-   ancestor is involved along the way.  */
-/* { dg-do run } */
-/* { dg-options "-O -fdump-tree-optimized-slim"  } */
-
-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;
-  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 inline int middleman_1 (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-static inline int middleman_2 (class B *obj, int i)
-{
-  return middleman_1 (obj, i);
-}
-
-int main (int argc, char *argv[])
-{
-  class B b;
-
-  if (middleman_2 (&b, get_input ()) != 3)
-    abort ();
-  return 0;
-}
-
-/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
-/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/otr-fold-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/otr-fold-2.C
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Verify that virtual calls are folded even when a typecast to an
-   ancestor is involved along the way.  */
-/* { dg-do run } */
-/* { dg-options "-O -fdump-tree-optimized-slim"  } */
-
-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;
-  virtual int foo (int i);
-};
-
-class A_2 : public A
-{
-public:
-  int data_2;
-  virtual int baz (int i);
-};
-
-
-class B : public Distraction, public A_2
-{
-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 A_2::baz (int i)
-{
-  return i * 15;
-}
-
-int B::foo (int i)
-{
-  return i + 2;
-}
-
-int __attribute__ ((noinline,noclone)) get_input(void)
-{
-  return 1;
-}
-
-static inline int middleman_1 (class A *obj, int i)
-{
-  return obj->foo (i);
-}
-
-static inline int middleman_2 (class A *obj, int i)
-{
-  return middleman_1 (obj, i);
-}
-
-int main (int argc, char *argv[])
-{
-  class B b;
-
-  if (middleman_2 (&b, get_input ()) != 3)
-    abort ();
-  return 0;
-}
-
-/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
-/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr46987.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/tree-ssa/pr46987.C
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+struct A {
+  virtual A *getThis();
+};
+
+struct B {
+  virtual B *getThis();
+};
+
+struct AB : public A, public B {
+  virtual AB *getThis() { return 0; }
+};
+
+void foo ()
+{
+  AB ab;
+  B *b = &ab;
+  b->getThis();
+}
+

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done
  2010-12-15 17:43 ` [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done Martin Jambor
  2010-12-22 14:53   ` Martin Jambor
@ 2011-01-13 11:49   ` Richard Guenther
  1 sibling, 0 replies; 16+ messages in thread
From: Richard Guenther @ 2011-01-13 11:49 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

On Wed, 15 Dec 2010, Martin Jambor wrote:

> I believe this has been basically already approved by Richi but I'd
> like to apply it just before the rest of the series so that we don't
> regress in functionality for more time than necessary.  The only
> important change since the last time is that the hunks pushing and
> popping cfun were moved to the subsequent patch where they belong.
> 
> To re-state the general description, this patch removes parts of the
> current devirtualization machinery that cannot work because of
> potential dynamic type change which might be under way during
> sub-object construction and destruction.  In the general case we
> simply cannot know this about global objects and we do not want to
> proceed with the necessary detection when folding.  So
> devirtualization based on global decls, global IPA-CP constants is
> being removed and devirtualization in folding is dumbed down to never
> work on sub-objects.
> 
> This patch removes the two testcases that test devirtualization based
> on global variables as well as g++.dg/otr-fold-[12].C which are then
> however reintroduced in the last patch in the series.  Furthermore,
> g++.dg/tree-ssa/pr43411.C and g++.dg/tree-ssa/pr45605.C fail which is
> expected and also dealt with in the last patch (which removes the
> xfail).
> 
> Bootstrapped and tested separately on x86-64-linux, also tested with
> make check-c++ on i686.

Please apply this patch now.  It's already late and this patch
fixes bugs.  I'm going over the other patches now.

Thanks,
Richard.

> Thanks,
> 
> Martin
> 
> 
> 2010-12-09  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR tree-optimization/45934
> 	PR tree-optimization/46302
> 	* gimple-fold.c (get_base_binfo_for_type): Removed.
> 	(gimple_get_relevant_ref_binfo): Likewise.
> 	(gimple_fold_obj_type_ref_call): Dumb down to 4.5 functionality,
> 	removed parameter inplace, updated the caller.
> 	* gimple.h (gimple_get_relevant_ref_binfo): Remove declaration.
> 	* ipa-cp.c (ipcp_propagate_types): Do not derive types from constants.
> 	(ipcp_discover_new_direct_edges): Do not do devirtualization based on
> 	constants.
> 	* ipa-prop.c (compute_known_type_jump_func): Use
> 	get_ref_base_and_extent and get_binfo_at_offset instead of
> 	gimple_get_relevant_ref_binfo.
> 	(compute_known_type_jump_func): Likewise.
> 	(update_jump_functions_after_inlining): Do not derive types from
> 	constants.
> 	(try_make_edge_direct_virtual_call): Likewise.
> 	* tree.c (get_binfo_at_offset): Get type from non-artificial fields.
> 	* testsuite/g++.dg/ipa/ipcp-ivi-1.C: Removed.
> 	* testsuite/g++.dg/ipa/ivinline-6.C: Likewise.
> 	* testsuite/g++.dg/otr-fold-1.C: Likewise.
> 	* testsuite/g++.dg/otr-fold-2.C: Likewise.
> 	* testsuite/g++.dg/tree-ssa/pr43411.C: Xfail dump scan.
> 	* testsuite/g++.dg/tree-ssa/pr45605.C: Likewise.
> 
> Index: icln/gcc/gimple-fold.c
> ===================================================================
> --- icln.orig/gcc/gimple-fold.c
> +++ icln/gcc/gimple-fold.c
> @@ -1364,88 +1364,6 @@ gimple_fold_builtin (gimple stmt)
>    return result;
>  }
>  
> -/* Search for a base binfo of BINFO that corresponds to TYPE and return it if
> -   it is found or NULL_TREE if it is not.  */
> -
> -static tree
> -get_base_binfo_for_type (tree binfo, tree type)
> -{
> -  int i;
> -  tree base_binfo;
> -  tree res = NULL_TREE;
> -
> -  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
> -    if (TREE_TYPE (base_binfo) == type)
> -      {
> -	gcc_assert (!res);
> -	res = base_binfo;
> -      }
> -
> -  return res;
> -}
> -
> -/* Return a binfo describing the part of object referenced by expression REF.
> -   Return NULL_TREE if it cannot be determined.  REF can consist of a series of
> -   COMPONENT_REFs of a declaration or of an INDIRECT_REF or it can also be just
> -   a simple declaration, indirect reference or an SSA_NAME.  If the function
> -   discovers an INDIRECT_REF or an SSA_NAME, it will assume that the
> -   encapsulating type is described by KNOWN_BINFO, if it is not NULL_TREE.
> -   Otherwise the first non-artificial field declaration or the base declaration
> -   will be examined to get the encapsulating type. */
> -
> -tree
> -gimple_get_relevant_ref_binfo (tree ref, tree known_binfo)
> -{
> -  while (true)
> -    {
> -      if (TREE_CODE (ref) == COMPONENT_REF)
> -	{
> -	  tree par_type;
> -	  tree binfo;
> -	  tree field = TREE_OPERAND (ref, 1);
> -
> -	  if (!DECL_ARTIFICIAL (field))
> -	    {
> -	      tree type = TREE_TYPE (field);
> -	      if (TREE_CODE (type) == RECORD_TYPE)
> -		return TYPE_BINFO (type);
> -	      else
> -		return NULL_TREE;
> -	    }
> -
> -	  par_type = TREE_TYPE (TREE_OPERAND (ref, 0));
> -	  binfo = TYPE_BINFO (par_type);
> -	  if (!binfo
> -	      || BINFO_N_BASE_BINFOS (binfo) == 0)
> -	    return NULL_TREE;
> -
> -	  /* Offset 0 indicates the primary base, whose vtable contents are
> -	     represented in the binfo for the derived class.  */
> -	  if (int_bit_position (field) != 0)
> -	    {
> -	      tree d_binfo;
> -
> -	      /* Get descendant binfo. */
> -	      d_binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (ref, 0),
> -						       known_binfo);
> -	      if (!d_binfo)
> -		return NULL_TREE;
> -	      return get_base_binfo_for_type (d_binfo, TREE_TYPE (field));
> -	    }
> -
> -	  ref = TREE_OPERAND (ref, 0);
> -	}
> -      else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) == RECORD_TYPE)
> -	return TYPE_BINFO (TREE_TYPE (ref));
> -      else if (known_binfo
> -	       && (TREE_CODE (ref) == SSA_NAME
> -		   || TREE_CODE (ref) == MEM_REF))
> -	return known_binfo;
> -      else
> -	return NULL_TREE;
> -    }
> -}
> -
>  /* 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
> @@ -1529,7 +1447,7 @@ gimple_adjust_this_by_delta (gimple_stmt
>     INPLACE is false.  Return true iff the statement was changed.  */
>  
>  static bool
> -gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
> +gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi)
>  {
>    gimple stmt = gsi_stmt (*gsi);
>    tree ref = gimple_call_fn (stmt);
> @@ -1537,27 +1455,21 @@ gimple_fold_obj_type_ref_call (gimple_st
>    tree binfo, fndecl, delta;
>    HOST_WIDE_INT token;
>  
> -  if (TREE_CODE (obj) == ADDR_EXPR)
> -    obj = TREE_OPERAND (obj, 0);
> -  else
> +  if (TREE_CODE (obj) != ADDR_EXPR)
>      return false;
> -
> -  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
> +  obj = TREE_OPERAND (obj, 0);
> +  if (!DECL_P (obj)
> +      || TREE_CODE (TREE_TYPE (obj)) != RECORD_TYPE)
> +    return false;
> +  binfo = TYPE_BINFO (TREE_TYPE (obj));
>    if (!binfo)
>      return false;
> +
>    token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> -  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
> -					     !DECL_P (obj));
> +  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta, false);
>    if (!fndecl)
>      return false;
> -
> -  if (integer_nonzerop (delta))
> -    {
> -      if (inplace)
> -        return false;
> -      gimple_adjust_this_by_delta (gsi, delta);
> -    }
> -
> +  gcc_assert (integer_zerop (delta));
>    gimple_call_set_fndecl (stmt, fndecl);
>    return true;
>  }
> @@ -1595,7 +1507,7 @@ gimple_fold_call (gimple_stmt_iterator *
>           here where we can just smash the call operand.  */
>        callee = gimple_call_fn (stmt);
>        if (TREE_CODE (callee) == OBJ_TYPE_REF)
> -	return gimple_fold_obj_type_ref_call (gsi, inplace);
> +	return gimple_fold_obj_type_ref_call (gsi);
>      }
>  
>    return false;
> Index: icln/gcc/gimple.h
> ===================================================================
> --- icln.orig/gcc/gimple.h
> +++ icln/gcc/gimple.h
> @@ -892,7 +892,6 @@ unsigned get_gimple_rhs_num_ops (enum tr
>  gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
>  const char *gimple_decl_printable_name (tree, int);
>  bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
> -tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
>  tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
>  void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
>  /* Returns true iff T is a valid GIMPLE statement.  */
> Index: icln/gcc/ipa-cp.c
> ===================================================================
> --- icln.orig/gcc/ipa-cp.c
> +++ icln/gcc/ipa-cp.c
> @@ -781,26 +781,16 @@ ipcp_propagate_types (struct ipa_node_pa
>  		      struct ipa_node_params *callee_info,
>  		      struct ipa_jump_func *jf, int i)
>  {
> -  tree cst, binfo;
> -
>    switch (jf->type)
>      {
>      case IPA_JF_UNKNOWN:
>      case IPA_JF_CONST_MEMBER_PTR:
> +    case IPA_JF_CONST:
>        break;
>  
>      case IPA_JF_KNOWN_TYPE:
>        return ipcp_add_param_type (callee_info, i, jf->value.base_binfo);
>  
> -    case IPA_JF_CONST:
> -      cst = jf->value.constant;
> -      if (TREE_CODE (cst) != ADDR_EXPR)
> -	break;
> -      binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE);
> -      if (!binfo)
> -	break;
> -      return ipcp_add_param_type (callee_info, i, binfo);
> -
>      case IPA_JF_PASS_THROUGH:
>      case IPA_JF_ANCESTOR:
>        return ipcp_copy_types (caller_info, callee_info, i, jf);
> @@ -1292,35 +1282,13 @@ ipcp_discover_new_direct_edges (struct c
>    for (ie = node->indirect_calls; ie; ie = next_ie)
>      {
>        struct cgraph_indirect_call_info *ici = ie->indirect_info;
> -      tree target, delta = NULL_TREE;
>  
>        next_ie = ie->next_callee;
> -      if (ici->param_index != index)
> +      if (ici->param_index != index
> +	  || ici->polymorphic)
>  	continue;
>  
> -      if (ici->polymorphic)
> -	{
> -	  tree binfo;
> -	  HOST_WIDE_INT token;
> -
> -	  if (TREE_CODE (cst) != ADDR_EXPR)
> -	    continue;
> -
> -	  binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
> -						 NULL_TREE);
> -	  if (!binfo)
> -	    continue;
> -	  gcc_assert (ie->indirect_info->anc_offset == 0);
> -	  token = ie->indirect_info->otr_token;
> -	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
> -						     true);
> -	  if (!target)
> -	    continue;
> -	}
> -      else
> -	target = cst;
> -
> -      ipa_make_edge_direct_to_target (ie, target, delta);
> +      ipa_make_edge_direct_to_target (ie, cst, NULL_TREE);
>      }
>  }
>  
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -362,7 +362,7 @@ compute_complex_assign_jump_func (struct
>  				  gimple stmt, tree name)
>  {
>    HOST_WIDE_INT offset, size, max_size;
> -  tree op1, op2, type;
> +  tree op1, op2, base, type;
>    int index;
>  
>    op1 = gimple_assign_rhs1 (stmt);
> @@ -404,20 +404,21 @@ compute_complex_assign_jump_func (struct
>    type = TREE_TYPE (op1);
>    if (TREE_CODE (type) != RECORD_TYPE)
>      return;
> -  op1 = get_ref_base_and_extent (op1, &offset, &size, &max_size);
> -  if (TREE_CODE (op1) != MEM_REF
> +  base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
> +  if (TREE_CODE (base) != MEM_REF
>        /* If this is a varying address, punt.  */
>        || max_size == -1
>        || max_size != size)
>      return;
> -  offset += mem_ref_offset (op1).low * BITS_PER_UNIT;
> -  op1 = TREE_OPERAND (op1, 0);
> -  if (TREE_CODE (op1) != SSA_NAME
> -      || !SSA_NAME_IS_DEFAULT_DEF (op1)
> +  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)
>        || offset < 0)
>      return;
>  
> -  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1));
> +  /* Dynamic types are changed only in constructors and destructors and  */
> +  index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base));
>    if (index >= 0)
>      {
>        jfunc->type = IPA_JF_ANCESTOR;
> @@ -534,13 +535,26 @@ compute_complex_ancestor_jump_func (stru
>  static void
>  compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc)
>  {
> -  tree binfo;
> +  HOST_WIDE_INT offset, size, max_size;
> +  tree base, binfo;
>  
> -  if (TREE_CODE (op) != ADDR_EXPR)
> +  if (TREE_CODE (op) != ADDR_EXPR
> +      || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
>      return;
>  
>    op = TREE_OPERAND (op, 0);
> -  binfo = gimple_get_relevant_ref_binfo (op, NULL_TREE);
> +  base = get_ref_base_and_extent (op, &offset, &size, &max_size);
> +  if (!DECL_P (base)
> +      || max_size == -1
> +      || max_size != size
> +      || TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE
> +      || is_global_var (base))
> +    return;
> +
> +  binfo = TYPE_BINFO (TREE_TYPE (base));
> +  if (!binfo)
> +    return;
> +  binfo = get_binfo_at_offset (binfo, offset, TREE_TYPE (op));
>    if (binfo)
>      {
>        jfunc->type = IPA_JF_KNOWN_TYPE;
> @@ -1420,17 +1434,6 @@ update_jump_functions_after_inlining (st
>  	  src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
>  	  if (src->type == IPA_JF_KNOWN_TYPE)
>  	    combine_known_type_and_ancestor_jfs (src, dst);
> -	  else if (src->type == IPA_JF_CONST)
> -	    {
> -	      struct ipa_jump_func kt_func;
> -
> -	      kt_func.type = IPA_JF_UNKNOWN;
> -	      compute_known_type_jump_func (src->value.constant, &kt_func);
> -	      if (kt_func.type == IPA_JF_KNOWN_TYPE)
> -		combine_known_type_and_ancestor_jfs (&kt_func, dst);
> -	      else
> -		dst->type = IPA_JF_UNKNOWN;
> -	    }
>  	  else if (src->type == IPA_JF_PASS_THROUGH
>  		   && src->value.pass_through.operation == NOP_EXPR)
>  	    dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
> @@ -1543,15 +1546,6 @@ try_make_edge_direct_virtual_call (struc
>  
>    if (jfunc->type == IPA_JF_KNOWN_TYPE)
>      binfo = jfunc->value.base_binfo;
> -  else if (jfunc->type == IPA_JF_CONST)
> -    {
> -      tree cst = jfunc->value.constant;
> -      if (TREE_CODE (cst) == ADDR_EXPR)
> -	binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
> -					       NULL_TREE);
> -      else
> -  	return NULL;
> -    }
>    else
>      return NULL;
>  
> Index: icln/gcc/tree.c
> ===================================================================
> --- icln.orig/gcc/tree.c
> +++ icln/gcc/tree.c
> @@ -10945,8 +10945,7 @@ get_binfo_at_offset (tree binfo, HOST_WI
>  
>        if (type == expected_type)
>  	  return binfo;
> -      if (TREE_CODE (type) != RECORD_TYPE
> -	  || offset < 0)
> +      if (offset < 0)
>  	return NULL_TREE;
>  
>        for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
> @@ -10959,12 +10958,18 @@ get_binfo_at_offset (tree binfo, HOST_WI
>  	  if (pos <= offset && (pos + size) > offset)
>  	    break;
>  	}
> -      if (!fld || !DECL_ARTIFICIAL (fld))
> +      if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE)
>  	return NULL_TREE;
>  
> +      if (!DECL_ARTIFICIAL (fld))
> +	{
> +	  binfo = TYPE_BINFO (TREE_TYPE (fld));
> +	  if (!binfo)
> +	    return NULL_TREE;
> +	}
>        /* Offset 0 indicates the primary base, whose vtable contents are
>  	 represented in the binfo for the derived class.  */
> -      if (offset != 0)
> +      else if (offset != 0)
>  	{
>  	  tree base_binfo, found_binfo = NULL_TREE;
>  	  for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
> Index: icln/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C
> +++ /dev/null
> @@ -1,65 +0,0 @@
> -/* Verify that simple virtual calls are inlined even without early
> -   inlining.  */
> -/* { dg-do run } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
> -
> -extern "C" void abort (void);
> -
> -class A
> -{
> -public:
> -  int data;
> -  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;
> -}
> -
> -int __attribute__ ((noinline)) middleman (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -int __attribute__ ((noinline,noclone)) get_input(void)
> -{
> -  return 1;
> -}
> -
> -class B b;
> -
> -int main (int argc, char *argv[])
> -{
> -  int i;
> -
> -  for (i = 0; i < get_input (); i++)
> -    if (middleman (&b, get_input ()) != 3)
> -      abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int.*middleman"  "inline"  } } */
> -/* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: icln/gcc/testsuite/g++.dg/ipa/ivinline-6.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/ipa/ivinline-6.C
> +++ /dev/null
> @@ -1,58 +0,0 @@
> -/* Verify that virtual call inlining works also when it has to get the
> -   type from an ipa invariant and that even in this case it does not
> -   pick a wrong method when there is a user defined ancestor in an
> -   object.  */
> -/* { dg-do run } */
> -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
> -
> -extern "C" void abort (void);
> -
> -class A
> -{
> -public:
> -  int data;
> -  virtual int foo (int i);
> -};
> -
> -class B : public A
> -{
> -public:
> -  class A confusion;
> -  virtual int foo (int i);
> -};
> -
> -int A::foo (int i)
> -{
> -  return i + 1;
> -}
> -
> -int B::foo (int i)
> -{
> -  return i + 2;
> -}
> -
> -int middleman (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -int __attribute__ ((noinline,noclone)) get_input(void)
> -{
> -  return 1;
> -}
> -
> -class B b;
> -
> -int main (int argc, char *argv[])
> -{
> -  int i, j = get_input ();
> -
> -  for (i = 0; i < j; i++)
> -    if ((middleman (&b, j) + 100 * middleman (&b.confusion, j)) != 203)
> -      abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-ipa-dump "A::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
> -/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int main"  "inline"  } } */
> -/* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
> +++ icln/gcc/testsuite/g++.dg/tree-ssa/pr43411.C
> @@ -25,5 +25,5 @@ void testInlinePsub() {
>      sink1 = v(p);
>  }
>  
> -// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" } }
> +// { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "optimized" { xfail *-*-* } } }
>  // { dg-final { cleanup-tree-dump "optimized" } }
> Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> +++ icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> @@ -33,5 +33,5 @@ int main() {
>  
>  
>  /* We should devirtualize call to D::Run */
> -/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa"} } */
> +/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa" { xfail *-*-* } } } */
>  /* { dg-final { cleanup-tree-dump "ssa" } } */
> Index: icln/gcc/testsuite/g++.dg/otr-fold-1.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/otr-fold-1.C
> +++ /dev/null
> @@ -1,76 +0,0 @@
> -/* Verify that virtual calls are folded even when a typecast to an
> -   ancestor is involved along the way.  */
> -/* { dg-do run } */
> -/* { dg-options "-O -fdump-tree-optimized-slim"  } */
> -
> -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;
> -  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 inline int middleman_1 (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -static inline int middleman_2 (class B *obj, int i)
> -{
> -  return middleman_1 (obj, i);
> -}
> -
> -int main (int argc, char *argv[])
> -{
> -  class B b;
> -
> -  if (middleman_2 (&b, get_input ()) != 3)
> -    abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> -/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/testsuite/g++.dg/otr-fold-2.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/otr-fold-2.C
> +++ /dev/null
> @@ -1,88 +0,0 @@
> -/* Verify that virtual calls are folded even when a typecast to an
> -   ancestor is involved along the way.  */
> -/* { dg-do run } */
> -/* { dg-options "-O -fdump-tree-optimized-slim"  } */
> -
> -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;
> -  virtual int foo (int i);
> -};
> -
> -class A_2 : public A
> -{
> -public:
> -  int data_2;
> -  virtual int baz (int i);
> -};
> -
> -
> -class B : public Distraction, public A_2
> -{
> -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 A_2::baz (int i)
> -{
> -  return i * 15;
> -}
> -
> -int B::foo (int i)
> -{
> -  return i + 2;
> -}
> -
> -int __attribute__ ((noinline,noclone)) get_input(void)
> -{
> -  return 1;
> -}
> -
> -static inline int middleman_1 (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -static inline int middleman_2 (class A *obj, int i)
> -{
> -  return middleman_1 (obj, i);
> -}
> -
> -int main (int argc, char *argv[])
> -{
> -  class B b;
> -
> -  if (middleman_2 (&b, get_input ()) != 3)
> -    abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> -/* { dg-final { cleanup-tree-dump "optimized" } } */
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 2/5] Dynamic type change detection
  2010-12-15 17:17 ` [PATCH, PR 45934 2/5] Dynamic type change detection Martin Jambor
@ 2011-01-13 12:35   ` Richard Guenther
  2011-01-13 18:06     ` Martin Jambor
  2011-01-15  0:41     ` Martin Jambor
  0 siblings, 2 replies; 16+ messages in thread
From: Richard Guenther @ 2011-01-13 12:35 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

On Wed, 15 Dec 2010, Martin Jambor wrote:

> This is a re-submission of the patch originally posted here:
> http://gcc.gnu.org/ml/gcc-patches/2010-12/msg00091.html
> 
> The main differences are:
> 
> 1. No special provisons for builtin functions.  There is a new big
>    comment explaining why we ignore all calls when searching for
>    statements changing the dynamic type.
> 
> 2. Aggregate stores to COMPONENT_REFs are also considered as changing
>    dynamic the type.
> 
> 3. The new use of DECL_CONTEXT is briefly documented in tree.h.
> 
> 4. check_stmt_for_type_change is now ready to be called even after it
>    has returned true and checks that if we derive any known new
>    dynamic type, it is always the same one.
> 
> 5. I have renamed the functions a slightly.  There is now a function
>    called detect_type_change which should be directly used for
>    declarations and their component_refs and dereferenced pointers and
>    their component_refs.  Dereferenced pointers are not supplied
>    directly from the analyzed IL but are created by
>    detect_type_change_ssa which is to be used to detect any dynamic
>    type changed of objects pointed to by an SSA_NAME (as it currently
>    is, all callers check a PARAM_DECL DEFAULT_DEF
>    SSA_NAMEs). detect_type_change_ssa uses detect_type_change
>    internally.
> 
>    So when we construct a PASS_THROUGH jump function without any
>    binary operation (pass through functions with an operation are
>    never combined with a known_type ones), we use
>    detect_type_change_ssa.
> 
>    On the other hand, when we either want to construct an ANCESTOR
>    jump function (and therefore are looking at an address of a
>    component of a dereferenced pointer) or TYPE_KNOWN jump function
>    (looking either at an address of a decl or its component), we use
>    directly detect_type_change.  On both occasions we already need to
>    invoke get_ref_base_and extent and so we supply this information to
>    detect_type_change which uses it to initialize its ao_ref structure
>    on its own.  
> 
>    I was considering using ao_ref_init_from_ptr_and_size but I would
>    then need to adjust ref_alias_set and base_alias_set because I
>    really want to utilize TBAA (and therefore either compute the sets
>    or set the ref field too) and considered that to be even uglier
>    than setting up the whole thing myself.
> 
>    The size is set to the size of a pointer.  We really only care
>    about components representing ancestors (get_binfo_at_offset is
>    smart enough to recognize them and not use encapsulating
>    object-type for non-artificial components) which have at least one
>    VMT pointer right at the beginning if they have any virtual methods
>    at all and cannot be part of arrays or larger structures without a
>    VMT at the beginning.  This also prevents us from wrongly deriving
>    a type from an object constructed inside another one with placement
>    new (there is a new testcase in the next patch for this).
> 
>    All of this is for objects passed by reference, there is no
>    provision (or intention) to track stuff passed by value.
> 
> I hope I answered all comments raised in the previous thread.
> 
> As before, the code that does both detection of dynamic type changes
> and tries to extract the new dynamic type in some cases is the
> following:

I'm commenting in the plain text below, not in the patch.

> ----------------------------------------------------------------------
> 
> /* Structure to be passed in between detect_type_change and
>    check_stmt_for_type_change.  */
> 
> struct type_change_info
> {
>   /* The declaration or SSA_NAME pointer of the base that we are checking for
>      type change.  */
>   tree object;
>   /* If we actually can tell the type that the object has changed to, it is
>      stored in this field.  Otherwise it remains NULL_TREE.  */
>   tree known_current_type;
>   /* Set to true if dynamic type change has been detected.  */
>   bool type_maybe_changed;
>   /* Set to true if multiple types have been encountered.  known_current_type
>      must be disregarded in that case.  */
>   bool multiple_types_encountered;
> };
> 
> /* Return true if STMT can modify a virtual method table pointer.
> 
>    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.  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;
> }

Hm.  Does this address

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<B *>(p);
 b->~B();
 new (p) A;
}
int main()
{
 void *p = __builtin_malloc (sizeof (B));
 new (p) B;
 foo(p);
 reinterpret_cast<A *>(p)->~A();
 return 0;
}

where we have a call (foo) that changes the dynamic type of *p?

Or is this a non-issue with the current patch because we only
ever devirtualize calls when we eventually see a statically
allocated object?  Can you still add the above testcase please?
Can you add a comment to the above function that ignoring
calls is not possible if we start to devirtualize for
allocated objects?  Or rather, mention that we only deal
with statically allocated objects?

You said you could make a failing testcase out of the
array testcase sketch - I can't see if that is included in
the set of testcases you add.  Is it?

> /* If STMT can be proved to be an assignment to the virtual method table
>    pointer of ANALYZED_OBJ and the type associated witht the new table
>    identified, return the type.  Otherwise return NULL_TREE.  */
> 
> static tree
> extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
> {
>   tree lhs, t, obj;
> 
>   if (!is_gimple_assign (stmt))
>     return NULL_TREE;
> 
>   lhs = gimple_assign_lhs (stmt);
> 
>   if (TREE_CODE (lhs) != COMPONENT_REF)
>     return NULL_TREE;
>    obj = lhs;
> 
>    if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
>      return NULL_TREE;
> 
>    do
>      {
>        obj = TREE_OPERAND (obj, 0);
>      }
>    while (TREE_CODE (obj) == COMPONENT_REF);
>    if (TREE_CODE (obj) == MEM_REF)
>      obj = TREE_OPERAND (obj, 0);
>    if (TREE_CODE (obj) == ADDR_EXPR)
>      obj = TREE_OPERAND (obj, 0);
>    if (obj != analyzed_obj)
>      return NULL_TREE;
> 
>    t = gimple_assign_rhs1 (stmt);
>    if (TREE_CODE (t) != ADDR_EXPR)
>      return NULL_TREE;
>    t = get_base_address (TREE_OPERAND (t, 0));
>    if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
>      return NULL_TREE;
> 
>    return DECL_CONTEXT (t);
> }
> 
> /* 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))
>     {
>       tree type;
>       type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
>       if (tci->type_maybe_changed
> 	  && type != tci->known_current_type)
> 	tci->multiple_types_encountered = true;
>       tci->known_current_type = type;
>       tci->type_maybe_changed = true;
>       return true;
>     }
>   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;
>   tree type;
>   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.  */

But as there won't be calls we are interested in in the callee why do
we end up here at all?  Well, I guess this can be cleaned up in
a followup.

>   if (!gimple_vuse (call))
>     return false;
> 
>   ao.ref = arg;
>   ao.base = base;
>   ao.offset = offset;
>   ao.size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;

POINTER_SIZE

>   ao.max_size = ao.size;
>   ao.ref_alias_set = -1;
>   ao.base_alias_set = -1;
> 
>   type = TREE_TYPE (arg);
>   while (handled_component_p (arg))
>     arg = TREE_OPERAND (arg, 0);
>   if (TREE_CODE (arg) == MEM_REF)
>     arg = TREE_OPERAND (arg, 0);
>   if (TREE_CODE (arg) == ADDR_EXPR)
>     arg = TREE_OPERAND (arg, 0);
>   tci.object = arg;
>   tci.known_current_type = NULL_TREE;
>   tci.type_maybe_changed = false;
>   tci.multiple_types_encountered = false;
> 
>   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
> 		      &tci, NULL);
>   if (!tci.type_maybe_changed)
>     return false;
> 
>  if (!tci.known_current_type || tci.multiple_types_encountered)
>    jfunc->type = IPA_JF_UNKNOWN;
>  else
>    {
>      tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
> 					   offset, type);
>      if (new_binfo)
> 	{
> 	  jfunc->type = IPA_JF_KNOWN_TYPE;
> 	  jfunc->value.base_binfo = new_binfo;
> 	}
>      else
>        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 = build_simple_mem_ref (arg);

This still yields to wrong TBAA disambiguations.  I suggest you
pass a flag to detect_type_change and drop back to basic tbaa:

  ao.base_alias_set = get_alias_set (ptr_type_node);
  ao.ref_alias_set = get_alias_set (ptr_type_node);

(you could do the ref_alias_set initialization unconditionally
and the base_alias_set one if the call is from detect_type_change_ssa
in which case you should leave ao.ref as NULL_TREE.  Basically
do a ao_ref_init_from_ptr_and_size but provide some TBAA
information).  Or you can simply do

  arg = build2 (MEM_REF, ptr_type_node, arg,
	        build_int_cst (ptr_type_node, 0));

which will magically do the same things.

I think I have reviewed patch 3/5 with the above as well as far
as I can see.

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,
Richard.

>   return detect_type_change (arg, arg, call, jfunc, 0);
> }
> 
> ----------------------------------------------------------------------
> 
> The patch implementing just the detection of changes with all the
> callers is below, the rest is in the next patch.
> 
> I have bootstrapped and tested this patch separately (i.e. with the
> previous ones but not the subsequent ones) on x86-64-linux and it has
> also passed make check-c++ on i686.
> 
> Thanks for any comments,
> 
> Martin
> 
> 
> 2010-11-09  Martin Jambor  <mjambor@suse.cz>
> 
> 	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-c-7.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,151 @@ 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.  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 = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
> +  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 = build_simple_mem_ref (arg);
> +
> +  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 +504,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 +533,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 +545,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 +555,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 +598,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 +631,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 +659,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 +667,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 +681,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 +700,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 +744,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 +757,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 +1371,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 +1395,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 +1519,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 +1533,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-c-7.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
> @@ -0,0 +1,101 @@
> +/* Verify that ipa-cp can use TBAA to fingure out that the virtual call to
> +   D::foo is not affected by dynamic type change in constructor A::A(D*).  */
> +
> +/* { dg-do run } */
> +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp"  } */
> +
> +extern "C" void abort (void);
> +
> +class D;
> +
> +class A
> +{
> +public:
> +  int data;
> +  A();
> +  A(D *d);
> +  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);
> +};
> +
> +class D
> +{
> +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;
> +}
> +
> +int D::foo (int i)
> +{
> +  return i + 4;
> +}
> +
> +static int middleman (class A *obj, int i)
> +{
> +  return obj->foo (i);
> +}
> +
> +static int middleman (class D *obj, int i)
> +{
> +  return obj->foo (i);
> +}
> +
> +int __attribute__ ((noinline,noclone)) get_input(void)
> +{
> +  return 1;
> +}
> +
> +A::A ()
> +{
> +}
> +
> +A::A (D *d)
> +{
> +  if (middleman (d, get_input ()) != 5)
> +    abort ();
> +  if (middleman (this, get_input ()) != 2)
> +    abort ();
> +}
> +
> +static void bah ()
> +{
> +  D d;
> +  A a(&d);
> +}
> +
> +int main (int argc, char *argv[])
> +{
> +  int i;
> +
> +  for (i = 0; i < 10; i++)
> +    bah ();
> +  return 0;
> +}
> +
> +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*D::foo"  "cp"  } } */
> +/* { dg-final { cleanup-ipa-dump "cp" } } */
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization
  2010-12-15 17:33 ` [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization Martin Jambor
  2010-12-15 18:55   ` Ralf Wildenhues
@ 2011-01-13 13:01   ` Richard Guenther
  1 sibling, 0 replies; 16+ messages in thread
From: Richard Guenther @ 2011-01-13 13:01 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

On Wed, 15 Dec 2010, Martin Jambor wrote:

> For various reasons it is probably desirable to be able to switch
> devirtualization off.  This is to be accomplished by a flag
> -fdevirtualize added by this patch.  I am open to suggestions for a
> better name, I was considering that we somehow include in the name
> that we mean type based devirtualization but then I thought I'd
> suggest the simple name first.
> 
> For IPA devirtualization, it is enough never to produce TYPE_KNOWN
> jump messages in order to avoid any devirtualization when doing IPA-CP
> or inlining.  However, I also made the flag disable the type-change
> detection mechanism so that it can be used to debug or workaround
> problems there.
> 
> Intraprocedural devirtualization is another matter.  The main
> type-based intraprocedural devirtualization is implemented in the next
> patch and the new version has a test included in it.  However, we do
> some very simple devirtualization when folding too and I have decided
> not to condition that on the new flag because we would regress at -O1.
> However, adding the test is very simple if that is not considered a
> problem.  Alternatively, we can also decide to run both
> intraprocedural devirtualizations at -O1 if we decide to be even
> bolder.
> 
> I have bootstrapped and tested this patch on x86_64-linux and it has
> passed make check-c++ on i686 together with the next patch too.  I
> have also verified the documentation changes by make pdf.  I'd be
> happy to respond to any comments and/or suggestions.

Ok with the suggested typo changes.

Thanks,
Richard.

> Thanks,
> 
> Martin
> 
> 
> 2010-12-13  Martin Jambor  <mjambor@suse.cz>
> 
> 	* common.opt (fdevirtualize): New flag.
> 	* doc/invoke.texi (Option Summary): Document it.
> 	* opts.c (default_options_table): Add devirtualize flag.
> 	* ipa-prop.c (detect_type_change): Return immediately if
> 	devirtualize flag is not set.
> 	(detect_type_change_ssa): Likewise.
> 	(compute_known_type_jump_func): Likewise.
> 	(ipa_analyze_virtual_call_uses): Likewise.
> 
> 
> Index: icln/gcc/common.opt
> ===================================================================
> --- icln.orig/gcc/common.opt
> +++ icln/gcc/common.opt
> @@ -911,6 +911,10 @@ fdelete-null-pointer-checks
>  Common Report Var(flag_delete_null_pointer_checks) Init(1) Optimization
>  Delete useless null pointer checks
>  
> +fdevirtualize
> +Common Report Var(flag_devirtualize) Optimization
> +Try to convert virtual calls to direct ones.
> +
>  fdiagnostics-show-location=
>  Common Joined RejectNegative Enum(diagnostic_prefixing_rule)
>  -fdiagnostics-show-location=[once|every-line]	How often to emit source location at the beginning of line-wrapped diagnostics
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -514,7 +514,7 @@ detect_type_change (tree arg, tree base,
>  		       || handled_component_p (arg));
>    /* Const calls cannot call virtual methods through VMT and so type changes do
>       not matter.  */
> -  if (!gimple_vuse (call))
> +  if (!flag_devirtualize || !gimple_vuse (call))
>      return false;
>  
>    ao.ref = arg;
> @@ -568,7 +568,8 @@ 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))
> +  if (!flag_devirtualize
> +      || !POINTER_TYPE_P (TREE_TYPE (arg))
>        || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE)
>      return false;
>  
> @@ -770,7 +771,8 @@ compute_known_type_jump_func (tree op, s
>    HOST_WIDE_INT offset, size, max_size;
>    tree base, binfo;
>  
> -  if (TREE_CODE (op) != ADDR_EXPR
> +  if (!flag_devirtualize
> +      || TREE_CODE (op) != ADDR_EXPR
>        || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
>      return;
>  
> @@ -1459,6 +1461,9 @@ ipa_analyze_virtual_call_uses (struct cg
>    tree var;
>    int index;
>  
> +  if (!flag_devirtualize)
> +    return;
> +
>    if (TREE_CODE (obj) == ADDR_EXPR)
>      {
>        do
> Index: icln/gcc/opts.c
> ===================================================================
> --- icln.orig/gcc/opts.c
> +++ icln/gcc/opts.c
> @@ -485,6 +485,7 @@ static const struct default_options defa
>      { OPT_LEVELS_2_PLUS, OPT_ftree_pre, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_ftree_switch_conversion, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_fipa_cp, NULL, 1 },
> +    { OPT_LEVELS_2_PLUS, OPT_fdevirtualize, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_fipa_sra, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_falign_loops, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_falign_jumps, NULL, 1 },
> Index: icln/gcc/doc/invoke.texi
> ===================================================================
> --- icln.orig/gcc/doc/invoke.texi
> +++ icln/gcc/doc/invoke.texi
> @@ -340,8 +340,8 @@ Objective-C and Objective-C++ Dialects}.
>  -fcprop-registers -fcrossjumping @gol
>  -fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules @gol
>  -fcx-limited-range @gol
> --fdata-sections -fdce -fdce @gol
> --fdelayed-branch -fdelete-null-pointer-checks -fdse -fdse @gol
> +-fdata-sections -fdce -fdce -fdelayed-branch @gol
> +-fdelete-null-pointer-checks -fdse -fdevirtualize -fdse @gol
>  -fearly-inlining -fipa-sra -fexpensive-optimizations -ffast-math @gol
>  -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
>  -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol
> @@ -5917,6 +5917,7 @@ also turns on the following optimization
>  -fcrossjumping @gol
>  -fcse-follow-jumps  -fcse-skip-blocks @gol
>  -fdelete-null-pointer-checks @gol
> +-fdevirtualize @gol
>  -fexpensive-optimizations @gol
>  -fgcse  -fgcse-lm  @gol
>  -finline-small-functions @gol
> @@ -6420,6 +6421,14 @@ Otherwise it is enabled at all levels: @
>  @option{-O2}, @option{-O3}, @option{-Os}.  Passes that use the information
>  are enabled independently at different optimization levels.
>  
> +@item -fdevirtualize
> +@opindex fdevirtualize
> +Attempt to convert calls to virtual functions to direct calls.  This
> +is done both within a procedure and inteprocedurally as part of
> +indirect inlining (@code{-findirect-inlining}) and inteprocedural copy
> +propagation (@option{-fipa-cp}).
> +Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
> +
>  @item -fexpensive-optimizations
>  @opindex fexpensive-optimizations
>  Perform a number of minor optimizations that are relatively expensive.
> Index: icln/gcc/gimple-fold.c
> ===================================================================
> --- icln.orig/gcc/gimple-fold.c
> +++ icln/gcc/gimple-fold.c
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 2/5] Dynamic type change detection
  2011-01-13 12:35   ` Richard Guenther
@ 2011-01-13 18:06     ` Martin Jambor
  2011-01-14  9:51       ` Richard Guenther
  2011-01-15  0:41     ` Martin Jambor
  1 sibling, 1 reply; 16+ messages in thread
From: Martin Jambor @ 2011-01-13 18:06 UTC (permalink / raw)
  To: Richard Guenther; +Cc: GCC Patches, Jan Hubicka

Hi,

On Thu, Jan 13, 2011 at 12:56:24PM +0100, Richard Guenther wrote:
> On Wed, 15 Dec 2010, Martin Jambor wrote:
> 
> 
> I'm commenting in the plain text below, not in the patch.
> 
> > ----------------------------------------------------------------------
> > 
> > /* Structure to be passed in between detect_type_change and
> >    check_stmt_for_type_change.  */
> > 
> > struct type_change_info
> > {
> >   /* The declaration or SSA_NAME pointer of the base that we are checking for
> >      type change.  */
> >   tree object;
> >   /* If we actually can tell the type that the object has changed to, it is
> >      stored in this field.  Otherwise it remains NULL_TREE.  */
> >   tree known_current_type;
> >   /* Set to true if dynamic type change has been detected.  */
> >   bool type_maybe_changed;
> >   /* Set to true if multiple types have been encountered.  known_current_type
> >      must be disregarded in that case.  */
> >   bool multiple_types_encountered;
> > };
> > 
> > /* Return true if STMT can modify a virtual method table pointer.
> > 
> >    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.  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;
> > }
> 
> Hm.  Does this address
> 
> 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<B *>(p);
>  b->~B();
>  new (p) A;
> }
> int main()
> {
>  void *p = __builtin_malloc (sizeof (B));
>  new (p) B;
>  foo(p);
>  reinterpret_cast<A *>(p)->~A();
>  return 0;
> }
> 
> where we have a call (foo) that changes the dynamic type of *p?
> 
> Or is this a non-issue with the current patch because we only
> ever devirtualize calls when we eventually see a statically
> allocated object?  Can you still add the above testcase please?
> Can you add a comment to the above function that ignoring
> calls is not possible if we start to devirtualize for
> allocated objects?  Or rather, mention that we only deal
> with statically allocated objects?

Exactly.  I've added the above as another testcase and added a comment
saying that we only care for automatically allocated objects at this
point.

> 
> You said you could make a failing testcase out of the
> array testcase sketch - I can't see if that is included in
> the set of testcases you add.  Is it?

It is the last one in the third patch in the series.

> 
> > /* If STMT can be proved to be an assignment to the virtual method table
> >    pointer of ANALYZED_OBJ and the type associated witht the new table
> >    identified, return the type.  Otherwise return NULL_TREE.  */
> > 
> > static tree
> > extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
> > {
> >   tree lhs, t, obj;
> > 
> >   if (!is_gimple_assign (stmt))
> >     return NULL_TREE;
> > 
> >   lhs = gimple_assign_lhs (stmt);
> > 
> >   if (TREE_CODE (lhs) != COMPONENT_REF)
> >     return NULL_TREE;
> >    obj = lhs;
> > 
> >    if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
> >      return NULL_TREE;
> > 
> >    do
> >      {
> >        obj = TREE_OPERAND (obj, 0);
> >      }
> >    while (TREE_CODE (obj) == COMPONENT_REF);
> >    if (TREE_CODE (obj) == MEM_REF)
> >      obj = TREE_OPERAND (obj, 0);
> >    if (TREE_CODE (obj) == ADDR_EXPR)
> >      obj = TREE_OPERAND (obj, 0);
> >    if (obj != analyzed_obj)
> >      return NULL_TREE;
> > 
> >    t = gimple_assign_rhs1 (stmt);
> >    if (TREE_CODE (t) != ADDR_EXPR)
> >      return NULL_TREE;
> >    t = get_base_address (TREE_OPERAND (t, 0));
> >    if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
> >      return NULL_TREE;
> > 
> >    return DECL_CONTEXT (t);
> > }
> > 
> > /* 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))
> >     {
> >       tree type;
> >       type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
> >       if (tci->type_maybe_changed
> > 	  && type != tci->known_current_type)
> > 	tci->multiple_types_encountered = true;
> >       tci->known_current_type = type;
> >       tci->type_maybe_changed = true;
> >       return true;
> >     }
> >   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;
> >   tree type;
> >   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.  */
> 
> But as there won't be calls we are interested in in the callee why do
> we end up here at all?  Well, I guess this can be cleaned up in
> a followup.

The call here represents the point at which we need to figure out
whether the type has changed, not the calls we skip when walking.  We
take the virtual SSA name from it.  If we can, that is.  The comment
just states that if we cannot, it does not matter since the function
does not use memory including any VMTs.

> 
> >   if (!gimple_vuse (call))
> >     return false;
> > 
> >   ao.ref = arg;
> >   ao.base = base;
> >   ao.offset = offset;
> >   ao.size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
> 
> POINTER_SIZE

OK

> 
> >   ao.max_size = ao.size;
> >   ao.ref_alias_set = -1;
> >   ao.base_alias_set = -1;
> > 
> >   type = TREE_TYPE (arg);
> >   while (handled_component_p (arg))
> >     arg = TREE_OPERAND (arg, 0);
> >   if (TREE_CODE (arg) == MEM_REF)
> >     arg = TREE_OPERAND (arg, 0);
> >   if (TREE_CODE (arg) == ADDR_EXPR)
> >     arg = TREE_OPERAND (arg, 0);
> >   tci.object = arg;
> >   tci.known_current_type = NULL_TREE;
> >   tci.type_maybe_changed = false;
> >   tci.multiple_types_encountered = false;
> > 
> >   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
> > 		      &tci, NULL);
> >   if (!tci.type_maybe_changed)
> >     return false;
> > 
> >  if (!tci.known_current_type || tci.multiple_types_encountered)
> >    jfunc->type = IPA_JF_UNKNOWN;
> >  else
> >    {
> >      tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
> > 					   offset, type);
> >      if (new_binfo)
> > 	{
> > 	  jfunc->type = IPA_JF_KNOWN_TYPE;
> > 	  jfunc->value.base_binfo = new_binfo;
> > 	}
> >      else
> >        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 = build_simple_mem_ref (arg);
> 
> This still yields to wrong TBAA disambiguations.  I suggest you
> pass a flag to detect_type_change and drop back to basic tbaa:
> 
>   ao.base_alias_set = get_alias_set (ptr_type_node);
>   ao.ref_alias_set = get_alias_set (ptr_type_node);
> 
> (you could do the ref_alias_set initialization unconditionally
> and the base_alias_set one if the call is from detect_type_change_ssa
> in which case you should leave ao.ref as NULL_TREE.  Basically
> do a ao_ref_init_from_ptr_and_size but provide some TBAA
> information).  Or you can simply do
> 
>   arg = build2 (MEM_REF, ptr_type_node, arg,
> 	        build_int_cst (ptr_type_node, 0));

I did the above and the testcase devirt-c-7.C below started to fail.
Maybe I ask too much from the TBAA.  My idea was that objects with
virtual methods should only be accessed with their or their ancestors'
types and so decided to rely on the provided pointer type.

Nevertheless, at the moment I decided to simply remove the testcase
and keep it aside until I have a better idea of how much this matters
in practice.  Maybe I'll revisit this in the future.

> 
> which will magically do the same things.
> 
> I think I have reviewed patch 3/5 with the above as well as far
> as I can see.

Yes.

> 
> 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.

And the 3/5?

I am now in the process of doing the final touches and re-testing this
and the two subsequent patches and would like to commit it (them?)
tomorrow.

Thanks a lot,

Martin


> 
> Thanks,
> Richard.
> 
> >   return detect_type_change (arg, arg, call, jfunc, 0);
> > }
> > 
> > ----------------------------------------------------------------------
> > 
> > The patch implementing just the detection of changes with all the
> > callers is below, the rest is in the next patch.
> > 
> > I have bootstrapped and tested this patch separately (i.e. with the
> > previous ones but not the subsequent ones) on x86-64-linux and it has
> > also passed make check-c++ on i686.
> > 
> > Thanks for any comments,
> > 
> > Martin
> > 
> > 
> > 2010-11-09  Martin Jambor  <mjambor@suse.cz>
> > 
> > 	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-c-7.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,151 @@ 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.  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 = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
> > +  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 = build_simple_mem_ref (arg);
> > +
> > +  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 +504,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 +533,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 +545,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 +555,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 +598,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 +631,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 +659,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 +667,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 +681,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 +700,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 +744,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 +757,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 +1371,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 +1395,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 +1519,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 +1533,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-c-7.C
> > ===================================================================
> > --- /dev/null
> > +++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
> > @@ -0,0 +1,101 @@
> > +/* Verify that ipa-cp can use TBAA to fingure out that the virtual call to
> > +   D::foo is not affected by dynamic type change in constructor A::A(D*).  */
> > +
> > +/* { dg-do run } */
> > +/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp"  } */
> > +
> > +extern "C" void abort (void);
> > +
> > +class D;
> > +
> > +class A
> > +{
> > +public:
> > +  int data;
> > +  A();
> > +  A(D *d);
> > +  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);
> > +};
> > +
> > +class D
> > +{
> > +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;
> > +}
> > +
> > +int D::foo (int i)
> > +{
> > +  return i + 4;
> > +}
> > +
> > +static int middleman (class A *obj, int i)
> > +{
> > +  return obj->foo (i);
> > +}
> > +
> > +static int middleman (class D *obj, int i)
> > +{
> > +  return obj->foo (i);
> > +}
> > +
> > +int __attribute__ ((noinline,noclone)) get_input(void)
> > +{
> > +  return 1;
> > +}
> > +
> > +A::A ()
> > +{
> > +}
> > +
> > +A::A (D *d)
> > +{
> > +  if (middleman (d, get_input ()) != 5)
> > +    abort ();
> > +  if (middleman (this, get_input ()) != 2)
> > +    abort ();
> > +}
> > +
> > +static void bah ()
> > +{
> > +  D d;
> > +  A a(&d);
> > +}
> > +
> > +int main (int argc, char *argv[])
> > +{
> > +  int i;
> > +
> > +  for (i = 0; i < 10; i++)
> > +    bah ();
> > +  return 0;
> > +}
> > +
> > +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*D::foo"  "cp"  } } */
> > +/* { dg-final { cleanup-ipa-dump "cp" } } */
> > 
> > 
> 
> -- 
> Richard Guenther <rguenther@suse.de>
> Novell / SUSE Labs
> SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 2/5] Dynamic type change detection
  2011-01-13 18:06     ` Martin Jambor
@ 2011-01-14  9:51       ` Richard Guenther
  0 siblings, 0 replies; 16+ messages in thread
From: Richard Guenther @ 2011-01-14  9:51 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

On Thu, 13 Jan 2011, Martin Jambor wrote:

> Hi,
> 
> On Thu, Jan 13, 2011 at 12:56:24PM +0100, Richard Guenther wrote:
> > On Wed, 15 Dec 2010, Martin Jambor wrote:
> > 
> > 
> > I'm commenting in the plain text below, not in the patch.
> > 
> > > ----------------------------------------------------------------------
> > > 
> > > /* Structure to be passed in between detect_type_change and
> > >    check_stmt_for_type_change.  */
> > > 
> > > struct type_change_info
> > > {
> > >   /* The declaration or SSA_NAME pointer of the base that we are checking for
> > >      type change.  */
> > >   tree object;
> > >   /* If we actually can tell the type that the object has changed to, it is
> > >      stored in this field.  Otherwise it remains NULL_TREE.  */
> > >   tree known_current_type;
> > >   /* Set to true if dynamic type change has been detected.  */
> > >   bool type_maybe_changed;
> > >   /* Set to true if multiple types have been encountered.  known_current_type
> > >      must be disregarded in that case.  */
> > >   bool multiple_types_encountered;
> > > };
> > > 
> > > /* Return true if STMT can modify a virtual method table pointer.
> > > 
> > >    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.  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;
> > > }
> > 
> > Hm.  Does this address
> > 
> > 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<B *>(p);
> >  b->~B();
> >  new (p) A;
> > }
> > int main()
> > {
> >  void *p = __builtin_malloc (sizeof (B));
> >  new (p) B;
> >  foo(p);
> >  reinterpret_cast<A *>(p)->~A();
> >  return 0;
> > }
> > 
> > where we have a call (foo) that changes the dynamic type of *p?
> > 
> > Or is this a non-issue with the current patch because we only
> > ever devirtualize calls when we eventually see a statically
> > allocated object?  Can you still add the above testcase please?
> > Can you add a comment to the above function that ignoring
> > calls is not possible if we start to devirtualize for
> > allocated objects?  Or rather, mention that we only deal
> > with statically allocated objects?
> 
> Exactly.  I've added the above as another testcase and added a comment
> saying that we only care for automatically allocated objects at this
> point.
> 
> > 
> > You said you could make a failing testcase out of the
> > array testcase sketch - I can't see if that is included in
> > the set of testcases you add.  Is it?
> 
> It is the last one in the third patch in the series.
> 
> > 
> > > /* If STMT can be proved to be an assignment to the virtual method table
> > >    pointer of ANALYZED_OBJ and the type associated witht the new table
> > >    identified, return the type.  Otherwise return NULL_TREE.  */
> > > 
> > > static tree
> > > extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
> > > {
> > >   tree lhs, t, obj;
> > > 
> > >   if (!is_gimple_assign (stmt))
> > >     return NULL_TREE;
> > > 
> > >   lhs = gimple_assign_lhs (stmt);
> > > 
> > >   if (TREE_CODE (lhs) != COMPONENT_REF)
> > >     return NULL_TREE;
> > >    obj = lhs;
> > > 
> > >    if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
> > >      return NULL_TREE;
> > > 
> > >    do
> > >      {
> > >        obj = TREE_OPERAND (obj, 0);
> > >      }
> > >    while (TREE_CODE (obj) == COMPONENT_REF);
> > >    if (TREE_CODE (obj) == MEM_REF)
> > >      obj = TREE_OPERAND (obj, 0);
> > >    if (TREE_CODE (obj) == ADDR_EXPR)
> > >      obj = TREE_OPERAND (obj, 0);
> > >    if (obj != analyzed_obj)
> > >      return NULL_TREE;
> > > 
> > >    t = gimple_assign_rhs1 (stmt);
> > >    if (TREE_CODE (t) != ADDR_EXPR)
> > >      return NULL_TREE;
> > >    t = get_base_address (TREE_OPERAND (t, 0));
> > >    if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
> > >      return NULL_TREE;
> > > 
> > >    return DECL_CONTEXT (t);
> > > }
> > > 
> > > /* 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))
> > >     {
> > >       tree type;
> > >       type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
> > >       if (tci->type_maybe_changed
> > > 	  && type != tci->known_current_type)
> > > 	tci->multiple_types_encountered = true;
> > >       tci->known_current_type = type;
> > >       tci->type_maybe_changed = true;
> > >       return true;
> > >     }
> > >   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;
> > >   tree type;
> > >   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.  */
> > 
> > But as there won't be calls we are interested in in the callee why do
> > we end up here at all?  Well, I guess this can be cleaned up in
> > a followup.
> 
> The call here represents the point at which we need to figure out
> whether the type has changed, not the calls we skip when walking.  We
> take the virtual SSA name from it.  If we can, that is.  The comment
> just states that if we cannot, it does not matter since the function
> does not use memory including any VMTs.
> 
> > 
> > >   if (!gimple_vuse (call))
> > >     return false;
> > > 
> > >   ao.ref = arg;
> > >   ao.base = base;
> > >   ao.offset = offset;
> > >   ao.size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node)) * BITS_PER_UNIT;
> > 
> > POINTER_SIZE
> 
> OK
> 
> > 
> > >   ao.max_size = ao.size;
> > >   ao.ref_alias_set = -1;
> > >   ao.base_alias_set = -1;
> > > 
> > >   type = TREE_TYPE (arg);
> > >   while (handled_component_p (arg))
> > >     arg = TREE_OPERAND (arg, 0);
> > >   if (TREE_CODE (arg) == MEM_REF)
> > >     arg = TREE_OPERAND (arg, 0);
> > >   if (TREE_CODE (arg) == ADDR_EXPR)
> > >     arg = TREE_OPERAND (arg, 0);
> > >   tci.object = arg;
> > >   tci.known_current_type = NULL_TREE;
> > >   tci.type_maybe_changed = false;
> > >   tci.multiple_types_encountered = false;
> > > 
> > >   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
> > > 		      &tci, NULL);
> > >   if (!tci.type_maybe_changed)
> > >     return false;
> > > 
> > >  if (!tci.known_current_type || tci.multiple_types_encountered)
> > >    jfunc->type = IPA_JF_UNKNOWN;
> > >  else
> > >    {
> > >      tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
> > > 					   offset, type);
> > >      if (new_binfo)
> > > 	{
> > > 	  jfunc->type = IPA_JF_KNOWN_TYPE;
> > > 	  jfunc->value.base_binfo = new_binfo;
> > > 	}
> > >      else
> > >        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 = build_simple_mem_ref (arg);
> > 
> > This still yields to wrong TBAA disambiguations.  I suggest you
> > pass a flag to detect_type_change and drop back to basic tbaa:
> > 
> >   ao.base_alias_set = get_alias_set (ptr_type_node);
> >   ao.ref_alias_set = get_alias_set (ptr_type_node);
> > 
> > (you could do the ref_alias_set initialization unconditionally
> > and the base_alias_set one if the call is from detect_type_change_ssa
> > in which case you should leave ao.ref as NULL_TREE.  Basically
> > do a ao_ref_init_from_ptr_and_size but provide some TBAA
> > information).  Or you can simply do
> > 
> >   arg = build2 (MEM_REF, ptr_type_node, arg,
> > 	        build_int_cst (ptr_type_node, 0));
> 
> I did the above and the testcase devirt-c-7.C below started to fail.
> Maybe I ask too much from the TBAA.  My idea was that objects with
> virtual methods should only be accessed with their or their ancestors'
> types and so decided to rely on the provided pointer type.
> 
> Nevertheless, at the moment I decided to simply remove the testcase
> and keep it aside until I have a better idea of how much this matters
> in practice.  Maybe I'll revisit this in the future.
> 
> > 
> > which will magically do the same things.
> > 
> > I think I have reviewed patch 3/5 with the above as well as far
> > as I can see.
> 
> Yes.
> 
> > 
> > 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.
> 
> And the 3/5?

Yes, including 3/5.

> I am now in the process of doing the final touches and re-testing this
> and the two subsequent patches and would like to commit it (them?)
> tomorrow.

Thanks,
Richard.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 2/5] Dynamic type change detection
  2011-01-13 12:35   ` Richard Guenther
  2011-01-13 18:06     ` Martin Jambor
@ 2011-01-15  0:41     ` Martin Jambor
  1 sibling, 0 replies; 16+ messages in thread
From: Martin Jambor @ 2011-01-15  0:41 UTC (permalink / raw)
  To: gcc-patches

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  <mjambor@suse.cz>

	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<B *>(p);
+ b->~B();
+ new (p) A;
+}
+
+int main()
+{
+ void *p = __builtin_malloc (sizeof (B));
+ new (p) B;
+ foo(p);
+ reinterpret_cast<A *>(p)->~A();
+ return 0;
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH, PR 45934 3/5] Identify the new dynamic type after change
  2010-12-15 17:44 ` [PATCH, PR 45934 3/5] Identify the new dynamic type after change Martin Jambor
@ 2011-01-15  9:08   ` Martin Jambor
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Jambor @ 2011-01-15  9:08 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka, Richard Guenther

On Wed, Dec 15, 2010 at 05:49:05PM +0100, Martin Jambor wrote:
> This patch adds code attempting to identify of the new dynamic type
> after it has been changed, which has been missing in the previous
> patch.  All the details are in the previous email.

While trying to modify this patch to accommodate changes in the
previous one I realized that currently offset parameter of
dynamic_type_change gets as a parameter is not necessarily the same
offset that get_binfo_at offset expects (if the binfo belongs to a
sub-object within base).

This afternoon I could not think of anything better than punting if
offset is not zero - if it is I used the same binfo even before,
except for when looking through a non-artificial field but that was
most probably a mistake.  I will think about that more on Monday
(especially if I can make a testcase with a non-artificial field on
offset zero) but the final version is very likely to be the following
(there are only changes in detect_type_change).

Thanks,

Martin


2011-01-14  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (type_change_info): New fields object, known_current_type
	and multiple_types_encountered.
	(extr_type_from_vtbl_ptr_store): New function.
	(check_stmt_for_type_change): Use it, set multiple_types_encountered if
        the result is different from the prvious one.
	(detect_type_change): Set up new fields in tci, build knonw type
	jump functions if the new type chan be identified.

	* testsuite/g++.dg/ipa/devirt-c-1.C: Add dump scans.
	* testsuite/g++.dg/ipa/devirt-c-2.C: Likewise.
	* testsuite/g++.dg/ipa/devirt-c-7.C: New test.


Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -355,8 +355,17 @@ ipa_print_all_jump_functions (FILE *f)
 
 struct type_change_info
 {
+  /* The declaration or SSA_NAME pointer of the base that we are checking for
+     type change.  */
+  tree object;
+  /* If we actually can tell the type that the object has changed to, it is
+     stored in this field.  Otherwise it remains NULL_TREE.  */
+  tree known_current_type;
   /* Set to true if dynamic type change has been detected.  */
   bool type_maybe_changed;
+  /* Set to true if multiple types have been encountered.  known_current_type
+     must be disregarded in that case.  */
+  bool multiple_types_encountered;
 };
 
 /* Return true if STMT can modify a virtual method table pointer.
@@ -416,6 +425,49 @@ stmt_may_be_vtbl_ptr_store (gimple stmt)
   return true;
 }
 
+/* If STMT can be proved to be an assignment to the virtual method table
+   pointer of ANALYZED_OBJ and the type associated witht the new table
+   identified, return the type.  Otherwise return NULL_TREE.  */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
+{
+  tree lhs, t, obj;
+
+  if (!is_gimple_assign (stmt))
+    return NULL_TREE;
+
+  lhs = gimple_assign_lhs (stmt);
+
+  if (TREE_CODE (lhs) != COMPONENT_REF)
+    return NULL_TREE;
+  obj = lhs;
+
+  if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+    return NULL_TREE;
+
+  do
+    {
+      obj = TREE_OPERAND (obj, 0);
+    }
+  while (TREE_CODE (obj) == COMPONENT_REF);
+  if (TREE_CODE (obj) == MEM_REF)
+    obj = TREE_OPERAND (obj, 0);
+  if (TREE_CODE (obj) == ADDR_EXPR)
+    obj = TREE_OPERAND (obj, 0);
+  if (obj != analyzed_obj)
+    return NULL_TREE;
+
+  t = gimple_assign_rhs1 (stmt);
+  if (TREE_CODE (t) != ADDR_EXPR)
+    return NULL_TREE;
+  t = get_base_address (TREE_OPERAND (t, 0));
+  if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
+    return NULL_TREE;
+
+  return DECL_CONTEXT (t);
+}
+
 /* 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
@@ -430,6 +482,12 @@ check_stmt_for_type_change (ao_ref *ao A
 
   if (stmt_may_be_vtbl_ptr_store (stmt))
     {
+      tree type;
+      type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
+      if (tci->type_maybe_changed
+	  && type != tci->known_current_type)
+	tci->multiple_types_encountered = true;
+      tci->known_current_type = type;
       tci->type_maybe_changed = true;
       return true;
     }
@@ -449,6 +507,7 @@ detect_type_change (tree arg, tree base,
 		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
 {
   struct type_change_info tci;
+  tree type;
   ao_ref ao;
 
   gcc_checking_assert (DECL_P (arg)
@@ -459,8 +518,6 @@ detect_type_change (tree arg, tree base,
   if (!flag_devirtualize || !gimple_vuse (call))
     return false;
 
-  tci.type_maybe_changed = false;
-
   ao.ref = arg;
   ao.base = base;
   ao.offset = offset;
@@ -469,12 +526,33 @@ detect_type_change (tree arg, tree base,
   ao.ref_alias_set = -1;
   ao.base_alias_set = -1;
 
+  type = TREE_TYPE (arg);
+  while (handled_component_p (arg))
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == MEM_REF)
+    arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == ADDR_EXPR)
+    arg = TREE_OPERAND (arg, 0);
+  tci.object = arg;
+  tci.known_current_type = NULL_TREE;
+  tci.type_maybe_changed = false;
+  tci.multiple_types_encountered = false;
+
   walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
 		      &tci, NULL);
   if (!tci.type_maybe_changed)
     return false;
 
-  jfunc->type = IPA_JF_UNKNOWN;
+  if (!tci.known_current_type
+      || tci.multiple_types_encountered
+      || offset != 0)
+    jfunc->type = IPA_JF_UNKNOWN;
+  else
+    {
+      jfunc->type = IPA_JF_KNOWN_TYPE;
+      jfunc->value.base_binfo = TYPE_BINFO (tci.known_current_type);
+    }
+
   return true;
 }
 
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -69,3 +69,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -77,3 +77,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/tree.h
===================================================================
--- icln.orig/gcc/tree.h
+++ icln/gcc/tree.h
@@ -2565,7 +2565,9 @@ struct function;
     nodes, this points to either the FUNCTION_DECL for the containing
     function, the RECORD_TYPE or UNION_TYPE for the containing type, or
     NULL_TREE or a TRANSLATION_UNIT_DECL if the given decl has "file
-    scope".  */
+    scope".  In particular, for VAR_DECLs which are virtual table pointers
+    (they have DECL_VIRTUAL set), we use DECL_CONTEXT to determine the type
+    they belong to.  */
 #define DECL_CONTEXT(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.context)
 #define DECL_FIELD_CONTEXT(NODE) \
   (FIELD_DECL_CHECK (NODE)->decl_minimal.context)
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-7.C
@@ -0,0 +1,87 @@
+/* Verify that ipa-cp will not get confused by placement new constructing an
+   object within another one when looking for dynamic type change .  */
+/* { dg-do run } */
+/* { dg-options "-O3"  } */
+
+extern "C" void abort (void);
+namespace std {
+  typedef __SIZE_TYPE__ size_t;
+}
+inline void* __attribute__ ((always_inline))
+operator new(std::size_t, void* __p) throw()
+{
+  return __p;
+}
+
+class A
+{
+public:
+  char data[256];
+  A();
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C
+{
+public:
+  C();
+  virtual double foo (double i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+double C::foo (double i)
+{
+  return i + 3.5;
+}
+
+static int __attribute__ ((noinline)) middleman (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+__attribute__ ((always_inline)) C::C ()
+{
+}
+
+A::A ()
+{
+}
+
+static  __attribute__ ((noinline)) void bah ()
+{
+  class B b;
+
+  C *c = new ((void *) &b.data) C;
+
+  if (middleman (&b, get_input ()) != 3)
+    abort ();
+}
+
+int main (int argc, char *argv[])
+{
+  int i;
+
+  for (i = 0; i < 10; i++)
+    bah ();
+  return 0;
+}


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2011-01-15  0:41 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-15 17:09 [PATCH, PR 45934 0/5] Devirtualization aware of dynamic type changes Martin Jambor
2010-12-15 17:09 ` [PATCH, PR 45934 5/5] Intraprocedural type-based devirtualization Martin Jambor
2010-12-15 17:17 ` [PATCH, PR 45934 2/5] Dynamic type change detection Martin Jambor
2011-01-13 12:35   ` Richard Guenther
2011-01-13 18:06     ` Martin Jambor
2011-01-14  9:51       ` Richard Guenther
2011-01-15  0:41     ` Martin Jambor
2010-12-15 17:33 ` [PATCH, PR 45934 4/5] Introduce a flag to control devirtualization Martin Jambor
2010-12-15 18:55   ` Ralf Wildenhues
2010-12-16 11:59     ` Martin Jambor
2011-01-13 13:01   ` Richard Guenther
2010-12-15 17:43 ` [PATCH, PR 45934 1/5] Remove devirtualizations that cannot be done Martin Jambor
2010-12-22 14:53   ` Martin Jambor
2011-01-13 11:49   ` Richard Guenther
2010-12-15 17:44 ` [PATCH, PR 45934 3/5] Identify the new dynamic type after change Martin Jambor
2011-01-15  9:08   ` Martin Jambor

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).