diff mbox

Introduce constructors for polymorphic_call_info

Message ID 20140920163331.GB18043@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka Sept. 20, 2014, 4:33 p.m. UTC
Hi,
this patch makes ipa_polymorphic_call_context construction to be useful for
forward propagation passes (such as ipa-devirt).  My original implementation
takes polymorphic call statment as a parameter and walks backward the SSA
graph.

Now one can construct the polymorphic call info for any constant or SSA name
and do forward propagation to the call statement himself.

Martin, this is how I expect this to be used by ipa-prop.

1) You will want to have polymorphic call contextes for polymorphic call (saved
   to edges), function parameters (in jump functoins) on each call site and
   to function parameters themselves (or handle that specially - the point here
   is that method functions will have context specifying type of THIS pointer)

   You can use the new constructors to build these. I.e.:
     ipa_polymorphic_call_context context (decl, param, call_stmt, &instance);
   on parameters of function calls to get polymorphic call info for values passed by pointer
   to PARAM.

You can also build contetes for invariants (&var) and function parameters (parm_decl).

2) The optional instance parameter to constructor may be used to let ipa-prop to
   track from where the instance is comming and build jump functions (i.e. if
   INSTANCE of a context is PARM_DECL).

   You will need to store this info yourself for polymorphic calls and jump functions.
   I guess it all boils down to PARAM_INDEX, but you do not want to share this with
   PARAM_INDEX used for normal data jump functions as in some cases the context
   may be fully specified (i.e. PARAM_INDEX -1) even though the PARAM_INDEX
   of data function is non-NULL

   I really think that unlike what we do now, we basically want to have two sets of
   data - i.e. data jump functions (what we have now minus KNOWN_TYPE)
   and type jump functions (contextes + param_index). While propagating we can use one
   to feed the other, but these are generally two dataflow problems.

3) In addition to be able to build contextes for parameters, we will need methods
   for forward propagation.  I expect we will need AND/OR/OFFSET_BY methods for
   contextes to implement the forward dataflow on them as opposed to binfos we
   do now.

   offset_by is trivial to do - just add given offset to context->offset and
   context->speculative_offset (if speculation is present). And/or will be bit
   more fun - we need to check if one context is more restricted than the other
   that can be done via containst_type_p, but we can start with dummy
   operations.
4) LTO will also need stream_out/stream_in methods for polymorphic contextes.

Bootstrapped/regtested x86_64-linux, comitted.

	* ipa-utils.h (ipa_polymorphic_call_context): Turn into class; add ctors.
	(possible_polymorphic_call_targets, dump_possible_polymorphic_call_targets,
	possible_polymorphic_call_target_p, possible_polymorphic_call_target_p): Simplify.
	(get_dynamic_type): Remove.
	* ipa-devirt.c (ipa_dummy_polymorphic_call_context): Remove.
	(clear_speculation): Bring to ipa-deivrt.h
	(get_class_context): Rename to ...
	(ipa_polymorphic_call_context::restrict_to_inner_class): ... this one.
	(contains_type_p): Update.
	(get_dynamic_type): Rename to ...
	ipa_polymorphic_call_context::get_dynamic_type(): ... this one.
	(possible_polymorphic_call_targets): UPdate.
	* tree-ssa-pre.c (eliminate_dom_walker::before_dom_children): Update.
	* ipa-prop.c (ipa_analyze_call_uses): Update.
diff mbox

Patch

Index: cgraph.c
===================================================================
--- cgraph.c	(revision 215391)
+++ cgraph.c	(working copy)
@@ -884,21 +884,15 @@ 
       && (target = gimple_call_fn (call_stmt))
       && virtual_method_call_p (target))
     {
-      tree otr_type;
-      HOST_WIDE_INT otr_token;
-      ipa_polymorphic_call_context context;
+      ipa_polymorphic_call_context context (decl, target, call_stmt);
 
-      get_polymorphic_call_info (decl,
-				 target,
-				 &otr_type, &otr_token,
-				 &context, call_stmt);
-
       /* Only record types can have virtual calls.  */
-      gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);
       edge->indirect_info->polymorphic = true;
       edge->indirect_info->param_index = -1;
-      edge->indirect_info->otr_token = otr_token;
-      edge->indirect_info->otr_type = otr_type;
+      edge->indirect_info->otr_token
+	 = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (target));
+      edge->indirect_info->otr_type = obj_type_ref_class (target);
+      gcc_assert (TREE_CODE (edge->indirect_info->otr_type) == RECORD_TYPE);
       edge->indirect_info->outer_type = context.outer_type;
       edge->indirect_info->speculative_outer_type
 	 = context.speculative_outer_type;
Index: gimple-fold.c
===================================================================
--- gimple-fold.c	(revision 215391)
+++ gimple-fold.c	(working copy)
@@ -2563,8 +2563,8 @@ 
 	{
           if (dump_file && virtual_method_call_p (callee)
 	      && !possible_polymorphic_call_target_p
-		    (callee, cgraph_node::get (gimple_call_addr_fndecl
-					       (OBJ_TYPE_REF_EXPR (callee)))))
+		    (callee, stmt, cgraph_node::get (gimple_call_addr_fndecl
+						     (OBJ_TYPE_REF_EXPR (callee)))))
 	    {
 	      fprintf (dump_file,
 		       "Type inheritance inconsistent devirtualization of ");
Index: ipa-cp.c
===================================================================
--- ipa-cp.c	(revision 215391)
+++ ipa-cp.c	(working copy)
@@ -1618,14 +1618,11 @@ 
 
   if (TREE_CODE (t) != TREE_BINFO)
     {
-      ipa_polymorphic_call_context context;
+      ipa_polymorphic_call_context context (t, ie->indirect_info->otr_type,
+					    anc_offset);
       vec <cgraph_node *>targets;
       bool final;
 
-      if (!get_polymorphic_call_info_from_invariant
-	     (&context, t, ie->indirect_info->otr_type,
-	      anc_offset))
-	return NULL_TREE;
       targets = possible_polymorphic_call_targets
 		 (ie->indirect_info->otr_type,
 		  ie->indirect_info->otr_token,
Index: ipa-devirt.c
===================================================================
--- ipa-devirt.c	(revision 215391)
+++ ipa-devirt.c	(working copy)
@@ -2400,41 +2400,43 @@ 
 }
 
 /* Proudce polymorphic call context for call method of instance
-   that is located within BASE (that is assumed to be a decl) at OFFSET. */
+   that is located within BASE (that is assumed to be a decl) at offset OFF. */
 
-static void
-get_polymorphic_call_info_for_decl (ipa_polymorphic_call_context *context,
-				    tree base, HOST_WIDE_INT offset)
+void
+ipa_polymorphic_call_context::set_by_decl (tree base, HOST_WIDE_INT off)
 {
   gcc_assert (DECL_P (base));
 
-  context->outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (base));
-  context->offset = offset;
-  context->speculative_outer_type = NULL;
-  context->speculative_offset = 0;
-  context->speculative_maybe_derived_type = true;
+  outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (base));
+  offset = off;
+  clear_speculation ();
   /* Make very conservative assumption that all objects
      may be in construction. 
      TODO: ipa-prop already contains code to tell better. 
      merge it later.  */
-  context->maybe_in_construction = true;
-  context->maybe_derived_type = false;
+  maybe_in_construction = true;
+  maybe_derived_type = false;
 }
 
 /* CST is an invariant (address of decl), try to get meaningful
    polymorphic call context for polymorphic call of method 
-   if instance of OTR_TYPE that is located at OFFSET of this invariant.
+   if instance of OTR_TYPE that is located at offset OFF of this invariant.
    Return FALSE if nothing meaningful can be found.  */
 
 bool
-get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context,
-				          tree cst,
-				          tree otr_type,
-				          HOST_WIDE_INT offset)
+ipa_polymorphic_call_context::set_by_invariant (tree cst,
+						tree otr_type,
+						HOST_WIDE_INT off)
 {
   HOST_WIDE_INT offset2, size, max_size;
   tree base;
 
+  invalid = false;
+  off = 0;
+  outer_type = NULL;
+  maybe_in_construction = true;
+  maybe_derived_type = true;
+
   if (TREE_CODE (cst) != ADDR_EXPR)
     return false;
 
@@ -2445,10 +2447,13 @@ 
 
   /* Only type inconsistent programs can have otr_type that is
      not part of outer type.  */
-  if (!contains_type_p (TREE_TYPE (base), offset, otr_type))
-    return false;
+  if (otr_type && !contains_type_p (TREE_TYPE (base), off, otr_type))
+    {
+      invalid = true;
+      return false;
+    }
 
-  get_polymorphic_call_info_for_decl (context, base, offset);
+  set_by_decl (base, off);
   return true;
 }
 
@@ -2472,34 +2477,46 @@ 
   return op;
 }
 
-/* Given REF call in FNDECL, determine class of the polymorphic
-   call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT.
-   CALL is optional argument giving the actual statement (usually call) where
-   the context is used.
-   Return pointer to object described by the context or an declaration if
-   we found the instance to be stored in the static storage.  */
+/* Create polymorphic call context from IP invariant CST.
+   This is typically &global_var.
+   OTR_TYPE specify type of polymorphic call or NULL if unknown, OFF
+   is offset of call.  */
 
-tree
-get_polymorphic_call_info (tree fndecl,
-			   tree ref,
-			   tree *otr_type,
-			   HOST_WIDE_INT *otr_token,
-			   ipa_polymorphic_call_context *context,
-			   gimple call)
+ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree cst,
+							    tree otr_type,
+							    HOST_WIDE_INT off)
 {
+  clear_speculation ();
+  set_by_invariant (cst, otr_type, off);
+}
+
+/* Build context for pointer REF contained in FNDECL at statement STMT.
+   if INSTANCE is non-NULL, return pointer to the object described by
+   the context or DECL where context is contained in.  */
+
+ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
+							    tree ref,
+							    gimple stmt,
+							    tree *instance)
+{
+  tree otr_type = NULL;
   tree base_pointer;
-  *otr_type = obj_type_ref_class (ref);
-  *otr_token = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (ref));
 
+  if (TREE_CODE (ref) == OBJ_TYPE_REF)
+    {
+      otr_type = obj_type_ref_class (ref);
+      base_pointer = OBJ_TYPE_REF_OBJECT (ref);
+    }
+  else
+    base_pointer = ref;
+
   /* Set up basic info in case we find nothing interesting in the analysis.  */
-  context->speculative_outer_type = NULL;
-  context->speculative_offset = 0;
-  context->speculative_maybe_derived_type = true;
-  context->outer_type = TYPE_MAIN_VARIANT (*otr_type);
-  context->offset = 0;
-  base_pointer = OBJ_TYPE_REF_OBJECT (ref);
-  context->maybe_derived_type = true;
-  context->maybe_in_construction = true;
+  clear_speculation ();
+  outer_type = TYPE_MAIN_VARIANT (otr_type);
+  offset = 0;
+  maybe_derived_type = true;
+  maybe_in_construction = true;
+  invalid = false;
 
   /* Walk SSA for outer object.  */
   do 
@@ -2522,9 +2539,9 @@ 
 	      if (TREE_CODE (base) == MEM_REF)
 		{
 		  base_pointer = TREE_OPERAND (base, 0);
-		  context->offset
+		  offset
 		    += offset2 + mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT;
-		  context->outer_type = NULL;
+		  outer_type = NULL;
 		}
 	      /* We found base object.  In this case the outer_type
 		 is known.  */
@@ -2534,24 +2551,25 @@ 
 
 		  /* Only type inconsistent programs can have otr_type that is
 		     not part of outer type.  */
-		  if (!contains_type_p (TREE_TYPE (base),
-					context->offset + offset2, *otr_type))
+		  if (otr_type
+		      && !contains_type_p (TREE_TYPE (base),
+					   offset + offset2, otr_type))
 		    {
-		      /* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent
-			 code sequences; we arrange the calls to be builtin_unreachable
-			 later.  */
-		      *otr_token = INT_MAX;
-		      return base_pointer;
+		      invalid = true;
+		      if (instance)
+			*instance = base_pointer;
+		      return;
 		    }
-		  get_polymorphic_call_info_for_decl (context, base,
-						      context->offset + offset2);
-		  if (context->maybe_in_construction && call)
-		    context->maybe_in_construction
+		  set_by_decl (base, offset + offset2);
+		  if (maybe_in_construction && stmt)
+		    maybe_in_construction
 		     = decl_maybe_in_construction_p (base,
-						     context->outer_type,
-						     call,
+						     outer_type,
+						     stmt,
 						     fndecl);
-		  return base;
+		  if (instance)
+		    *instance = base;
+		  return;
 		}
 	      else
 		break;
@@ -2562,7 +2580,7 @@ 
       else if (TREE_CODE (base_pointer) == POINTER_PLUS_EXPR
 	       && tree_fits_uhwi_p (TREE_OPERAND (base_pointer, 1)))
 	{
-	  context->offset += tree_to_shwi (TREE_OPERAND (base_pointer, 1))
+	  offset += tree_to_shwi (TREE_OPERAND (base_pointer, 1))
 		    * BITS_PER_UNIT;
 	  base_pointer = TREE_OPERAND (base_pointer, 0);
 	}
@@ -2580,19 +2598,22 @@ 
       if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE
 	  && SSA_NAME_VAR (base_pointer) == DECL_ARGUMENTS (fndecl))
 	{
-	  context->outer_type
+	  outer_type
 	     = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer)));
-	  gcc_assert (TREE_CODE (context->outer_type) == RECORD_TYPE);
+	  gcc_assert (TREE_CODE (outer_type) == RECORD_TYPE);
 
 	  /* Dynamic casting has possibly upcasted the type
 	     in the hiearchy.  In this case outer type is less
 	     informative than inner type and we should forget
 	     about it.  */
-	  if (!contains_type_p (context->outer_type, context->offset,
-				*otr_type))
+	  if (otr_type
+	      && !contains_type_p (outer_type, offset,
+				   otr_type))
 	    {
-	      context->outer_type = NULL;
-	      return base_pointer;
+	      outer_type = NULL;
+	      if (instance)
+		*instance = base_pointer;
+	      return;
 	    }
 
 	  /* If the function is constructor or destructor, then
@@ -2601,15 +2622,17 @@ 
 	  if (DECL_CXX_CONSTRUCTOR_P (fndecl)
 	      || DECL_CXX_DESTRUCTOR_P (fndecl))
 	    {
-	      context->maybe_in_construction = true;
-	      context->maybe_derived_type = false;
+	      maybe_in_construction = true;
+	      maybe_derived_type = false;
 	    }
 	  else
 	    {
-	      context->maybe_derived_type = true;
-	      context->maybe_in_construction = false;
+	      maybe_derived_type = true;
+	      maybe_in_construction = false;
 	    }
-	  return base_pointer;
+	  if (instance)
+	    *instance = base_pointer;
+	  return;
 	}
       /* Non-PODs passed by value are really passed by invisible
 	 reference.  In this case we also know the type of the
@@ -2616,23 +2639,24 @@ 
 	 object.  */
       if (DECL_BY_REFERENCE (SSA_NAME_VAR (base_pointer)))
 	{
-	  context->outer_type
+	  outer_type
 	     = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer)));
-	  gcc_assert (!POINTER_TYPE_P (context->outer_type));
+	  gcc_assert (!POINTER_TYPE_P (outer_type));
 	  /* Only type inconsistent programs can have otr_type that is
 	     not part of outer type.  */
-	  if (!contains_type_p (context->outer_type, context->offset,
-			        *otr_type))
+	  if (!contains_type_p (outer_type, offset,
+			        otr_type))
 	    { 
-	      /* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent
-		 code sequences; we arrange the calls to be builtin_unreachable
-		 later.  */
-	      *otr_token = INT_MAX;
-	      return base_pointer;
+	      invalid = true;
+	      if (instance)
+		*instance = base_pointer;
+	      return;
 	    }
-	  context->maybe_derived_type = false;
-	  context->maybe_in_construction = false;
-          return base_pointer;
+	  maybe_derived_type = false;
+	  maybe_in_construction = false;
+	  if (instance)
+	    *instance = base_pointer;
+	  return;
 	}
     }
 
@@ -2642,11 +2666,10 @@ 
       && SSA_NAME_IS_DEFAULT_DEF (base_pointer)
       && TREE_CODE (SSA_NAME_VAR (base_pointer)) != PARM_DECL)
     {
-      /* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent
-	 code sequences; we arrange the calls to be builtin_unreachable
-	 later.  */
-      *otr_token = INT_MAX;
-      return base_pointer;
+      invalid = true;
+      if (instance)
+	*instance = base_pointer;
+      return;
     }
   if (TREE_CODE (base_pointer) == SSA_NAME
       && SSA_NAME_DEF_STMT (base_pointer)
@@ -2655,19 +2678,22 @@ 
 			    (SSA_NAME_DEF_STMT (base_pointer)));
  
   if (POINTER_TYPE_P (base_type)
-      && contains_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)),
-			  context->offset,
-			  *otr_type))
+      && (otr_type
+	  || !contains_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)),
+			       offset,
+			       otr_type)))
     {
-      context->speculative_outer_type = TYPE_MAIN_VARIANT
+      speculative_outer_type = TYPE_MAIN_VARIANT
 					  (TREE_TYPE (base_type));
-      context->speculative_offset = context->offset;
-      context->speculative_maybe_derived_type = true;
+      speculative_offset = offset;
+      speculative_maybe_derived_type = true;
     }
   /* TODO: There are multiple ways to derive a type.  For instance
      if BASE_POINTER is passed to an constructor call prior our refernece.
      We do not make this type of flow sensitive analysis yet.  */
-  return base_pointer;
+  if (instance)
+    *instance = base_pointer;
+  return;
 }
 
 /* Structure to be passed in between detect_type_change and
@@ -3404,9 +3430,6 @@ 
    temporarily change to one of base types.  INCLUDE_DERIVER_TYPES make
    us to walk the inheritance graph for all derivations.
 
-   OTR_TOKEN == INT_MAX is used to mark calls that are provably
-   undefined and should be redirected to unreachable.
-
    If COMPLETEP is non-NULL, store true if the list is complete. 
    CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry
    in the target cache.  If user needs to visit every target list
@@ -3443,11 +3466,12 @@ 
 
   otr_type = TYPE_MAIN_VARIANT (otr_type);
 
-  /* If ODR is not initialized, return empty incomplete list.  */
-  if (!odr_hash)
+  /* If ODR is not initialized or the constext is invalid, return empty
+     incomplete list.  */
+  if (!odr_hash || context.invalid)
     {
       if (completep)
-	*completep = false;
+	*completep = context.invalid;
       if (cache_token)
 	*cache_token = NULL;
       if (speculative_targetsp)
@@ -3455,18 +3479,6 @@ 
       return nodes;
     }
 
-  /* If we hit type inconsistency, just return empty list of targets.  */
-  if (otr_token == INT_MAX)
-    {
-      if (completep)
-	*completep = true;
-      if (cache_token)
-	*cache_token = NULL;
-      if (speculative_targetsp)
-	*speculative_targetsp = 0;
-      return nodes;
-    }
-
   /* Do not bother to compute speculative info when user do not asks for it.  */
   if (!speculative_targetsp || !context.speculative_outer_type)
     context.clear_speculation ();
@@ -3853,6 +3865,26 @@ 
 }
 
 
+
+/* Return true if N can be possibly target of a polymorphic call of
+   OBJ_TYPE_REF expression REF in STMT.  */
+
+bool
+possible_polymorphic_call_target_p (tree ref,
+				    gimple stmt,
+				    struct cgraph_node *n)
+{
+  ipa_polymorphic_call_context context (current_function_decl, ref, stmt);
+  tree call_fn = gimple_call_fn (stmt);
+
+  return possible_polymorphic_call_target_p (obj_type_ref_class (call_fn),
+					     tree_to_uhwi
+					       (OBJ_TYPE_REF_TOKEN (call_fn)),
+					     context,
+					     n);
+}
+
+
 /* After callgraph construction new external nodes may appear.
    Add them into the graph.  */
 
Index: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 215391)
+++ ipa-prop.c	(working copy)
@@ -2347,14 +2347,13 @@ 
     {
       tree otr_type;
       HOST_WIDE_INT otr_token;
-      ipa_polymorphic_call_context context;
       tree instance;
       tree target = gimple_call_fn (call);
+      ipa_polymorphic_call_context context (current_function_decl,
+					    target, call, &instance);
 
-      instance = get_polymorphic_call_info (current_function_decl,
-					    target,
-					    &otr_type, &otr_token,
-					    &context, call);
+      otr_type = obj_type_ref_class (target);
+      otr_token = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (target));
 
       if (context.get_dynamic_type (instance,
 				    OBJ_TYPE_REF_OBJECT (target),
@@ -2609,7 +2608,7 @@ 
 #ifdef ENABLE_CHECKING
   if (fndecl)
     gcc_assert (possible_polymorphic_call_target_p
-		  (otr, cgraph_node::get (fndecl)));
+		  (otr, call, cgraph_node::get (fndecl)));
 #endif
   return fndecl;
 }
@@ -3121,14 +3120,12 @@ 
 
   if (TREE_CODE (binfo) != TREE_BINFO)
     {
-      ipa_polymorphic_call_context context;
+      ipa_polymorphic_call_context context (binfo,
+					    ie->indirect_info->otr_type,
+					    ie->indirect_info->offset);
       vec <cgraph_node *>targets;
       bool final;
 
-      if (!get_polymorphic_call_info_from_invariant
-	     (&context, binfo, ie->indirect_info->otr_type,
-	      ie->indirect_info->offset))
-	return NULL;
       targets = possible_polymorphic_call_targets
 		 (ie->indirect_info->otr_type,
 		  ie->indirect_info->otr_token,
Index: ipa-utils.h
===================================================================
--- ipa-utils.h	(revision 215391)
+++ ipa-utils.h	(working copy)
@@ -53,13 +53,29 @@ 
   /* True if speculative outer object may be of derived type.  We always
      speculate that construction does not happen.  */
   bool speculative_maybe_derived_type;
+  /* True if the context is invalid and all calls should be redirected
+     to BUILTIN_UNREACHABLE.  */
+  bool invalid;
 
   /* Build empty "I know nothing" context.  */
   ipa_polymorphic_call_context ();
-
   /* Build polymorphic call context for indirect call E.  */
   ipa_polymorphic_call_context (cgraph_edge *e);
+  /* Build polymorphic call context for IP invariant CST.
+     If specified, OTR_TYPE specify the type of polymorphic call
+     that takes CST+OFFSET as a prameter.  */
+  ipa_polymorphic_call_context (tree cst, tree otr_type = NULL,
+				HOST_WIDE_INT offset = 0);
+  /* Build context for pointer REF contained in FNDECL at statement STMT.
+     if INSTANCE is non-NULL, return pointer to the object described by
+     the context.  */
+  ipa_polymorphic_call_context (tree fndecl, tree ref, gimple stmt,
+				tree *instance = NULL);
 
+  /* Look for vtable stores or constructor calls to work out dynamic type
+     of memory location.  */
+  bool get_dynamic_type (tree, tree, tree, gimple);
+
   /* Make context non-speculative.  */
   void clear_speculation ();
 
@@ -67,9 +83,9 @@ 
      containing EXPECTED_TYPE as base class.  */
   bool restrict_to_inner_class (tree expected_type);
 
-  /* Look for vtable stores or constructor calls to work out dynamic type
-     of memory location.  */
-  bool get_dynamic_type (tree, tree, tree, gimple);
+private:
+  void set_by_decl (tree, HOST_WIDE_INT);
+  bool set_by_invariant (tree, tree, HOST_WIDE_INT);
 };
 
 /* Build polymorphic call context for indirect call E.  */
@@ -77,6 +93,8 @@ 
 inline
 ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e)
 {
+  gcc_checking_assert (e->indirect_info->polymorphic);
+
   offset = e->indirect_info->offset;
   speculative_offset = e->indirect_info->speculative_offset;
   outer_type = e->indirect_info->outer_type;
@@ -84,6 +102,7 @@ 
   maybe_in_construction = e->indirect_info->maybe_in_construction;
   maybe_derived_type = e->indirect_info->maybe_derived_type;
   speculative_maybe_derived_type = e->indirect_info->speculative_maybe_derived_type;
+  invalid = false;
 }
 
 /* Build empty "I know nothing" context.  */
@@ -90,10 +109,15 @@ 
 
 inline
 ipa_polymorphic_call_context::ipa_polymorphic_call_context ()
- : offset(0), speculative_offset(0), outer_type(NULL),
-   speculative_outer_type(NULL), maybe_in_construction(false),
-   maybe_derived_type(false), speculative_maybe_derived_type(false)
 {
+  offset = 0;
+  speculative_offset = 0;
+  outer_type = NULL;
+  speculative_outer_type = NULL;
+  maybe_in_construction = true;
+  maybe_derived_type = true;
+  speculative_maybe_derived_type = false;
+  invalid = false;
 }
 
 /* Make context non-speculative.  */
@@ -131,10 +155,11 @@ 
 vec <cgraph_node *>
 possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
 				   ipa_polymorphic_call_context,
-				   bool *final = NULL,
+				   bool *copletep = NULL,
 				   void **cache_token = NULL,
 				   int *nonconstruction_targets = NULL);
 odr_type get_odr_type (tree, bool insert = false);
+bool possible_polymorphic_call_target_p (tree ref, gimple stmt, struct cgraph_node *n);
 void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT,
 					     const ipa_polymorphic_call_context &);
 bool possible_polymorphic_call_target_p (tree, HOST_WIDE_INT,
@@ -141,12 +166,6 @@ 
 				         const ipa_polymorphic_call_context &,
 					 struct cgraph_node *);
 tree method_class_type (const_tree);
-tree get_polymorphic_call_info (tree, tree, tree *,
-				HOST_WIDE_INT *,
-				ipa_polymorphic_call_context *,
-				gimple call = NULL);
-bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *,
-					       tree, tree, HOST_WIDE_INT);
 bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
 tree vtable_pointer_value_to_binfo (const_tree);
 bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
@@ -155,7 +174,7 @@ 
 void register_odr_type (tree);
 
 /* Return vector containing possible targets of polymorphic call E.
-   If FINALP is non-NULL, store true if the list is complette. 
+   If COMPLETEP is non-NULL, store true if the list is complette. 
    CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry
    in the target cache.  If user needs to visit every target list
    just once, it can memoize them.
@@ -166,16 +185,16 @@ 
 
 inline vec <cgraph_node *>
 possible_polymorphic_call_targets (struct cgraph_edge *e,
-				   bool *final = NULL,
+				   bool *completep = NULL,
 				   void **cache_token = NULL,
 				   int *nonconstruction_targets = NULL)
 {
-  gcc_checking_assert (e->indirect_info->polymorphic);
   ipa_polymorphic_call_context context(e);
+
   return possible_polymorphic_call_targets (e->indirect_info->otr_type,
 					    e->indirect_info->otr_token,
 					    context,
-					    final, cache_token,
+					    completep, cache_token,
 					    nonconstruction_targets);
 }
 
@@ -184,21 +203,16 @@ 
 inline vec <cgraph_node *>
 possible_polymorphic_call_targets (tree ref,
 				   gimple call,
-				   bool *final = NULL,
+				   bool *completep = NULL,
 				   void **cache_token = NULL)
 {
-  tree otr_type;
-  HOST_WIDE_INT otr_token;
-  ipa_polymorphic_call_context context;
+  ipa_polymorphic_call_context context (current_function_decl, ref, call);
 
-  get_polymorphic_call_info (current_function_decl,
-			     ref,
-			     &otr_type, &otr_token, &context, call);
   return possible_polymorphic_call_targets (obj_type_ref_class (ref),
 					    tree_to_uhwi
 					      (OBJ_TYPE_REF_TOKEN (ref)),
 					    context,
-					    final, cache_token);
+					    completep, cache_token);
 }
 
 /* Dump possible targets of a polymorphic call E into F.  */
@@ -206,8 +220,8 @@ 
 inline void
 dump_possible_polymorphic_call_targets (FILE *f, struct cgraph_edge *e)
 {
-  gcc_checking_assert (e->indirect_info->polymorphic);
   ipa_polymorphic_call_context context(e);
+
   dump_possible_polymorphic_call_targets (f, e->indirect_info->otr_type,
 					  e->indirect_info->otr_token,
 					  context);
@@ -221,26 +235,12 @@ 
 				    struct cgraph_node *n)
 {
   ipa_polymorphic_call_context context(e);
+
   return possible_polymorphic_call_target_p (e->indirect_info->otr_type,
 					     e->indirect_info->otr_token,
 					     context, n);
 }
 
-/* Return true if N can be possibly target of a polymorphic call of
-   OBJ_TYPE_REF expression CALL.  */
-
-inline bool
-possible_polymorphic_call_target_p (tree call,
-				    struct cgraph_node *n)
-{
-  ipa_polymorphic_call_context context;
-  return possible_polymorphic_call_target_p (obj_type_ref_class (call),
-					     tree_to_uhwi
-					       (OBJ_TYPE_REF_TOKEN (call)),
-					     context,
-					     n);
-}
-
 /* Return true of T is type with One Definition Rule info attached. 
    It means that either it is anonymous type or it has assembler name
    set.  */
Index: tree-ssa-pre.c
===================================================================
--- tree-ssa-pre.c	(revision 215391)
+++ tree-ssa-pre.c	(working copy)
@@ -4277,16 +4277,11 @@ 
 	      && flag_devirtualize
 	      && virtual_method_call_p (fn))
 	    {
-	      tree otr_type;
-	      HOST_WIDE_INT otr_token;
-	      ipa_polymorphic_call_context context;
+	      tree otr_type = obj_type_ref_class (fn);
 	      tree instance;
+	      ipa_polymorphic_call_context context (current_function_decl, fn, stmt, &instance);
 	      bool final;
 
-	      instance = get_polymorphic_call_info (current_function_decl,
-						    fn,
-						    &otr_type, &otr_token, &context, stmt);
-
 	      context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt);
 
 	      vec <cgraph_node *>targets