diff mbox

[2/3] IPA-CP propagation of polymorphic contexts

Message ID 20141114013210.GB15392@virgil.suse
State New
Headers show

Commit Message

Martin Jambor Nov. 14, 2014, 1:32 a.m. UTC
Hi,

this patch is that main part of the series, implementing propagation
of ipa_polymorphic_call_contexts.  Although I tried to keep its size
checked, it is big because switching from binfos to contexts in ipa-cp
also requires changes in inlining heuristics.  The patch also does not
do any speculative devirtualization as a part of ipa-cp, that is left
for later.

Because contexts are more powerful and work also with multiple
inheritance, the type_preserved flag is no longer sufficient to guard
against dynamic type changes and so the appropriate checks were
changed to use the much more strict agg_preserved flag instead.
Alleviating such conservative treatment is also left fr future work.
As a consequence, I had to xfail a few testcases but I was able to
un-xfail one too.

A very similar patch has passed bootstrap and testing on x86_64-linux,
testing of this exact one is under way.  Honza has privately approved
the patch and asked me to commit it soon, so I will do that if this
testing turns out OK and after some additional tests on ppc64-aix and
32bit i686-linux.

Thanks,

Martin


2014-11-14  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use
	agg_preserved flag instead.
	(ipa_get_jf_ancestor_type_preserved): Likewise.
	(ipa_node_params): Rename known_vals to known_csts, update all users.
	New field known_contexts.
	(ipa_get_indirect_edge_target): Update prototype.
	(ipcp_poly_ctx_values_pool): Declare.
	(ipa_context_from_jfunc): Likewise.
	* ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype.
	* cgraph.h (ipa_polymorphic_call_context): New method equal_to.  New
	parameter newline of method dump.
	* ipa-cp.c (ctxlat): New field.
	(ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users.
	(ipcp_poly_ctx_values_pool):New variable.
	(ipa_get_poly_ctx_lat): New function.
	(print_ipcp_constant_value): New overloaded function for contexts.
	(print_all_lattices): Also print contexts.
	(ipa_topo_info): New field contexts;
	(set_all_contains_variable): Also set the flag in the context lattice.
	(initialize_node_lattices): Likewise for flag bottom.
	(ipa_get_jf_ancestor_result): Removed BINFO handling.
	(ipa_value_from_jfunc): Likewise.
	(ipa_context_from_jfunc): New function.
	(values_equal_for_ipcp_p): New overloaded function for contexts.
	(allocate_and_init_ipcp_value): Construct the value.
	(allocate_and_init_ipcp_value): New overloaded function for contexts.
	(propagate_scalar_accross_jump_function): Removed handling of
	KNOWN_TYPE jump functions.
	(propagate_context_accross_jump_function): New function.
	(propagate_constants_accross_call): Also propagate contexts.
	(ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs.
	(ipa_get_indirect_edge_target): Likewise.
	(devirtualization_time_bonus): Likewise.
	(gather_context_independent_values): Create and populate known_contexts
	vector rather than known_binfos.
	(perform_estimation_of_a_value): Work on contexts rather than BINFOs.
	(estimate_local_effects): Likewise.
	(add_all_node_vals_to_toposort): Also add contexts to teir topological
	sort.
	(ipcp_propagate_stage): Also propagate effects of contexts.
	(ipcp_discover_new_direct_edges): Receive and pass known_contexts to
	ipa_get_indirect_edge_target_1.
	(cgraph_edge_brings_value_p): New overloaded function for contexts.
	(create_specialized_node): Work on contexts rather than BINFOs.
	(find_more_contexts_for_caller_subset): New function.
	(known_contexts_useful_p): New function.
	(copy_useful_known_contexts): Likewise.
	(modify_known_vectors_with_val): Likewise.
	(ipcp_val_in_agg_replacements_p): Renamed to
	ipcp_val_agg_replacement_ok_p, return true for all offset indicating
	non-aggregate.
	(ipcp_val_agg_replacement_ok_p): New overloaded function for contexts.
	(decide_about_value): Work on contexts rather than BINFOs.
	(decide_whether_version_node): Likewise.
	(ipcp_driver): Initialize the new alloc pool.
	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify
	printing of edge contexts.
	(ipa_set_ancestor_jf): Replace assert with conditional setting of
	type_preserved to false.
	(update_jump_functions_after_inlining): Use access function instead of
	reading agg_preserved directly.  Store combined context in the ancestor
	case.
	(try_make_edge_direct_virtual_call): Work on contexts rather than
	BINFOs.
	(update_indirect_edges_after_inlining): Get context from
	ipa_context_from_jfunc.
	(ipa_free_node_params_substructures): Free also known_contexts.
	(ipa_free_all_structures_after_ipa_cp): Free the new alloc pool.
	(ipa_free_all_structures_after_iinln): Likewise.
	* ipa-inline-analysis.c (evaluate_properties_for_edge): Work on
	contexts rather than BINFOs.
	(estimate_edge_devirt_benefit): Likewise.
	(estimate_edge_size_and_time): Likewise.
	(estimate_calls_size_and_time): Likewise.
	(estimate_node_size_and_time): Likewise.
	(estimate_ipcp_clone_size_and_time): Likewise.
	(do_estimate_edge_time): Likewise.
	(do_estimate_edge_size): Likewise.
	(do_estimate_edge_hints): Likewise.
	* ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New
	parameter newline, ouput newline only when it is set.
	(ipa_polymorphic_call_context::equal_to): New method.

testsuite/
	* g++.dg/ipa/devirt-11.C: Dont't run ipa-cp, remove times constraint
	from the dump scan.
	* g++.dg/ipa/devirt-21.C: Xfail.
	* g++.dg/ipa/devirt-24.C: Likewise.
	* g++.dg/ipa/devirt-10.C: Removed times constraint from the dump scan.
	* g++.dg/ipa/devirt-41.C: Updated the dump scan.
	* g++.dg/ipa/devirt-44.C: Likewise.
	* g++.dg/ipa/devirt-43.C: Xfail.

Comments

H.J. Lu Dec. 1, 2014, 5:47 p.m. UTC | #1
On Thu, Nov 13, 2014 at 5:32 PM, Martin Jambor <mjambor@suse.cz> wrote:
> Hi,
>
> this patch is that main part of the series, implementing propagation
> of ipa_polymorphic_call_contexts.  Although I tried to keep its size
> checked, it is big because switching from binfos to contexts in ipa-cp
> also requires changes in inlining heuristics.  The patch also does not
> do any speculative devirtualization as a part of ipa-cp, that is left
> for later.
>
> Because contexts are more powerful and work also with multiple
> inheritance, the type_preserved flag is no longer sufficient to guard
> against dynamic type changes and so the appropriate checks were
> changed to use the much more strict agg_preserved flag instead.
> Alleviating such conservative treatment is also left fr future work.
> As a consequence, I had to xfail a few testcases but I was able to
> un-xfail one too.
>
> A very similar patch has passed bootstrap and testing on x86_64-linux,
> testing of this exact one is under way.  Honza has privately approved
> the patch and asked me to commit it soon, so I will do that if this
> testing turns out OK and after some additional tests on ppc64-aix and
> 32bit i686-linux.
>
> Thanks,
>
> Martin
>
>
> 2014-11-14  Martin Jambor  <mjambor@suse.cz>
>
>         * ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use
>         agg_preserved flag instead.
>         (ipa_get_jf_ancestor_type_preserved): Likewise.
>         (ipa_node_params): Rename known_vals to known_csts, update all users.
>         New field known_contexts.
>         (ipa_get_indirect_edge_target): Update prototype.
>         (ipcp_poly_ctx_values_pool): Declare.
>         (ipa_context_from_jfunc): Likewise.
>         * ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype.
>         * cgraph.h (ipa_polymorphic_call_context): New method equal_to.  New
>         parameter newline of method dump.
>         * ipa-cp.c (ctxlat): New field.
>         (ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users.
>         (ipcp_poly_ctx_values_pool):New variable.
>         (ipa_get_poly_ctx_lat): New function.
>         (print_ipcp_constant_value): New overloaded function for contexts.
>         (print_all_lattices): Also print contexts.
>         (ipa_topo_info): New field contexts;
>         (set_all_contains_variable): Also set the flag in the context lattice.
>         (initialize_node_lattices): Likewise for flag bottom.
>         (ipa_get_jf_ancestor_result): Removed BINFO handling.
>         (ipa_value_from_jfunc): Likewise.
>         (ipa_context_from_jfunc): New function.
>         (values_equal_for_ipcp_p): New overloaded function for contexts.
>         (allocate_and_init_ipcp_value): Construct the value.
>         (allocate_and_init_ipcp_value): New overloaded function for contexts.
>         (propagate_scalar_accross_jump_function): Removed handling of
>         KNOWN_TYPE jump functions.
>         (propagate_context_accross_jump_function): New function.
>         (propagate_constants_accross_call): Also propagate contexts.
>         (ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs.
>         (ipa_get_indirect_edge_target): Likewise.
>         (devirtualization_time_bonus): Likewise.
>         (gather_context_independent_values): Create and populate known_contexts
>         vector rather than known_binfos.
>         (perform_estimation_of_a_value): Work on contexts rather than BINFOs.
>         (estimate_local_effects): Likewise.
>         (add_all_node_vals_to_toposort): Also add contexts to teir topological
>         sort.
>         (ipcp_propagate_stage): Also propagate effects of contexts.
>         (ipcp_discover_new_direct_edges): Receive and pass known_contexts to
>         ipa_get_indirect_edge_target_1.
>         (cgraph_edge_brings_value_p): New overloaded function for contexts.
>         (create_specialized_node): Work on contexts rather than BINFOs.
>         (find_more_contexts_for_caller_subset): New function.
>         (known_contexts_useful_p): New function.
>         (copy_useful_known_contexts): Likewise.
>         (modify_known_vectors_with_val): Likewise.
>         (ipcp_val_in_agg_replacements_p): Renamed to
>         ipcp_val_agg_replacement_ok_p, return true for all offset indicating
>         non-aggregate.
>         (ipcp_val_agg_replacement_ok_p): New overloaded function for contexts.
>         (decide_about_value): Work on contexts rather than BINFOs.
>         (decide_whether_version_node): Likewise.
>         (ipcp_driver): Initialize the new alloc pool.
>         * ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify
>         printing of edge contexts.
>         (ipa_set_ancestor_jf): Replace assert with conditional setting of
>         type_preserved to false.
>         (update_jump_functions_after_inlining): Use access function instead of
>         reading agg_preserved directly.  Store combined context in the ancestor
>         case.
>         (try_make_edge_direct_virtual_call): Work on contexts rather than
>         BINFOs.
>         (update_indirect_edges_after_inlining): Get context from
>         ipa_context_from_jfunc.
>         (ipa_free_node_params_substructures): Free also known_contexts.
>         (ipa_free_all_structures_after_ipa_cp): Free the new alloc pool.
>         (ipa_free_all_structures_after_iinln): Likewise.
>         * ipa-inline-analysis.c (evaluate_properties_for_edge): Work on
>         contexts rather than BINFOs.
>         (estimate_edge_devirt_benefit): Likewise.
>         (estimate_edge_size_and_time): Likewise.
>         (estimate_calls_size_and_time): Likewise.
>         (estimate_node_size_and_time): Likewise.
>         (estimate_ipcp_clone_size_and_time): Likewise.
>         (do_estimate_edge_time): Likewise.
>         (do_estimate_edge_size): Likewise.
>         (do_estimate_edge_hints): Likewise.
>         * ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New
>         parameter newline, ouput newline only when it is set.
>         (ipa_polymorphic_call_context::equal_to): New methods);

This caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64139
diff mbox

Patch

Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-cp.c	2014-11-14 01:56:16.000000000 +0100
@@ -258,6 +258,8 @@  class ipcp_param_lattices
 public:
   /* Lattice describing the value of the parameter itself.  */
   ipcp_lattice<tree> itself;
+  /* Lattice describing the the polymorphic contexts of a parameter.  */
+  ipcp_lattice<ipa_polymorphic_call_context> ctxlat;
   /* Lattices describing aggregate parts.  */
   ipcp_agg_lattice *aggs;
   /* Number of aggregate lattices */
@@ -278,7 +280,8 @@  public:
 
 /* Allocation pools for values and their sources in ipa-cp.  */
 
-alloc_pool ipcp_values_pool;
+alloc_pool ipcp_cst_values_pool;
+alloc_pool ipcp_poly_ctx_values_pool;
 alloc_pool ipcp_sources_pool;
 alloc_pool ipcp_agg_lattice_pool;
 
@@ -310,6 +313,15 @@  ipa_get_scalar_lat (struct ipa_node_para
   return &plats->itself;
 }
 
+/* Return the lattice corresponding to the scalar value of the Ith formal
+   parameter of the function described by INFO.  */
+static inline ipcp_lattice<ipa_polymorphic_call_context> *
+ipa_get_poly_ctx_lat (struct ipa_node_params *info, int i)
+{
+  struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+  return &plats->ctxlat;
+}
+
 /* Return whether LAT is a lattice with a single constant and without an
    undefined value.  */
 
@@ -343,6 +355,14 @@  print_ipcp_constant_value (FILE * f, tre
     print_generic_expr (f, v, 0);
 }
 
+/* Print V which is extracted from a value in a lattice to F.  */
+
+static void
+print_ipcp_constant_value (FILE * f, ipa_polymorphic_call_context v)
+{
+  v.dump(f, false);
+}
+
 /* Print a lattice LAT to F.  */
 
 template <typename valtype>
@@ -427,7 +447,8 @@  print_all_lattices (FILE * f, bool dump_
 	  struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
 	  fprintf (f, "    param [%d]: ", i);
 	  plats->itself.print (f, dump_sources, dump_benefits);
-
+	  fprintf (f, "         ctxs: ");
+	  plats->ctxlat.print (f, dump_sources, dump_benefits);
 	  if (plats->virt_call)
 	    fprintf (f, "        virt_call flag set\n");
 
@@ -639,6 +660,7 @@  public:
   int nnodes, stack_top;
 
   value_topo_info<tree> constants;
+  value_topo_info<ipa_polymorphic_call_context> contexts;
 
   ipa_topo_info () : order(NULL), stack(NULL), nnodes(0), stack_top(0),
     constants ()
@@ -750,9 +772,10 @@  set_agg_lats_contain_variable (struct ip
 static inline bool
 set_all_contains_variable (struct ipcp_param_lattices *plats)
 {
-  bool ret = !plats->itself.contains_variable || !plats->aggs_contain_variable;
-  plats->itself.contains_variable = true;
-  plats->aggs_contain_variable = true;
+  bool ret;
+  ret = plats->itself.set_contains_variable ();
+  ret |= plats->ctxlat.set_contains_variable ();
+  ret |= set_agg_lats_contain_variable (plats);
   return ret;
 }
 
@@ -787,6 +810,7 @@  initialize_node_lattices (struct cgraph_
 	  if (disable)
 	    {
 	      plats->itself.set_to_bottom ();
+	      plats->ctxlat.set_to_bottom ();
 	      set_agg_lats_to_bottom (plats);
 	    }
 	  else
@@ -853,28 +877,8 @@  ipa_get_jf_pass_through_result (struct i
 static tree
 ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input)
 {
-  if (TREE_CODE (input) == TREE_BINFO)
-    {
-      if (!ipa_get_jf_ancestor_type_preserved (jfunc))
-	return NULL;
-      /* FIXME: At LTO we can't propagate to non-polymorphic type, because
-	 we have no ODR equivalency on those.  This should be fixed by
-	 propagating on types rather than binfos that would make type
-	 matching here unnecesary.  */
-      if (in_lto_p
-	  && (TREE_CODE (ipa_get_jf_ancestor_type (jfunc)) != RECORD_TYPE
-	      || !TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc))
-	      || !BINFO_VTABLE (TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc)))))
-	{
-	  if (!ipa_get_jf_ancestor_offset (jfunc))
-	    return input;
-	  return NULL;
-	}
-      return get_binfo_at_offset (input,
-				  ipa_get_jf_ancestor_offset (jfunc),
-				  ipa_get_jf_ancestor_type (jfunc));
-    }
-  else if (TREE_CODE (input) == ADDR_EXPR)
+  gcc_checking_assert (TREE_CODE (input) != TREE_BINFO);
+  if (TREE_CODE (input) == ADDR_EXPR)
     {
       tree t = TREE_OPERAND (input, 0);
       t = build_ref_for_offset (EXPR_LOCATION (t), t,
@@ -888,9 +892,9 @@  ipa_get_jf_ancestor_result (struct ipa_j
     return NULL_TREE;
 }
 
-/* Determine whether JFUNC evaluates to a known value (that is either a
-   constant or a binfo) and if so, return it.  Otherwise return NULL. INFO
-   describes the caller node so that pass-through jump functions can be
+/* Determine whether JFUNC evaluates to a single known constant value and if
+   so, return it.  Otherwise return NULL.  INFO describes the caller node or
+   the one it is inlined to, so that pass-through jump functions can be
    evaluated.  */
 
 tree
@@ -898,8 +902,6 @@  ipa_value_from_jfunc (struct ipa_node_pa
 {
   if (jfunc->type == IPA_JF_CONST)
     return ipa_get_jf_constant (jfunc);
-  else if (jfunc->type == IPA_JF_KNOWN_TYPE)
-    return ipa_binfo_from_known_type_jfunc (jfunc);
   else if (jfunc->type == IPA_JF_PASS_THROUGH
 	   || jfunc->type == IPA_JF_ANCESTOR)
     {
@@ -912,7 +914,7 @@  ipa_value_from_jfunc (struct ipa_node_pa
 	idx = ipa_get_jf_ancestor_formal_id (jfunc);
 
       if (info->ipcp_orig_node)
-	input = info->known_vals[idx];
+	input = info->known_csts[idx];
       else
 	{
 	  ipcp_lattice<tree> *lat;
@@ -940,6 +942,68 @@  ipa_value_from_jfunc (struct ipa_node_pa
     return NULL_TREE;
 }
 
+/* Determie whether JFUNC evaluates to single known polymorphic context, given
+   that INFO describes the caller node or the one it is inlined to, CS is the
+   call graph edge corresponding to JFUNC and CSIDX index of the described
+   parameter.  */
+
+ipa_polymorphic_call_context
+ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
+			ipa_jump_func *jfunc)
+{
+  ipa_edge_args *args = IPA_EDGE_REF (cs);
+  ipa_polymorphic_call_context ctx;
+  ipa_polymorphic_call_context *edge_ctx
+    = cs ? ipa_get_ith_polymorhic_call_context (args, csidx) : NULL;
+
+  if (edge_ctx && !edge_ctx->useless_p ())
+    ctx = *edge_ctx;
+
+  if (jfunc->type == IPA_JF_PASS_THROUGH
+      || jfunc->type == IPA_JF_ANCESTOR)
+    {
+      ipa_polymorphic_call_context srcctx;
+      int srcidx;
+      if (jfunc->type == IPA_JF_PASS_THROUGH)
+	{
+	  if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
+	      || !ipa_get_jf_pass_through_type_preserved (jfunc))
+	    return ctx;
+	  srcidx = ipa_get_jf_pass_through_formal_id (jfunc);
+	}
+      else
+	{
+	  if (!ipa_get_jf_ancestor_type_preserved (jfunc))
+	    return ctx;
+	  srcidx = ipa_get_jf_ancestor_formal_id (jfunc);
+	}
+      if (info->ipcp_orig_node)
+	{
+	  if (info->known_contexts.exists ())
+	    srcctx = info->known_contexts[srcidx];
+	}
+      else
+	{
+	  if (!info->lattices)
+	    {
+	      gcc_checking_assert (!flag_ipa_cp);
+	      return ctx;
+	    }
+	  ipcp_lattice<ipa_polymorphic_call_context> *lat;
+	  lat = ipa_get_poly_ctx_lat (info, srcidx);
+	  if (!lat->is_single_const ())
+	    return ctx;
+	  srcctx = lat->values->value;
+	}
+      if (srcctx.useless_p ())
+	return ctx;
+      if (jfunc->type == IPA_JF_ANCESTOR)
+	srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc));
+      ctx.combine_with (srcctx);
+    }
+
+  return ctx;
+}
 
 /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
    bottom, not containing a variable component and without any known value at
@@ -1000,6 +1064,16 @@  values_equal_for_ipcp_p (tree x, tree y)
     return operand_equal_p (x, y, 0);
 }
 
+/* Return true iff X and Y should be considered equal contexts by IPA-CP.  */
+
+static bool
+values_equal_for_ipcp_p (ipa_polymorphic_call_context x,
+			 ipa_polymorphic_call_context y)
+{
+  return x.equal_to (y);
+}
+
+
 /* Add a new value source to the value represented by THIS, marking that a
    value comes from edge CS and (if the underlying jump function is a
    pass-through or an ancestor one) from a caller value SRC_VAL of a caller
@@ -1031,7 +1105,22 @@  allocate_and_init_ipcp_value (tree sourc
 {
   ipcp_value<tree> *val;
 
-  val = new (pool_alloc (ipcp_values_pool)) ipcp_value<tree>;
+  val = new (pool_alloc (ipcp_cst_values_pool)) ipcp_value<tree>;
+  memset (val, 0, sizeof (*val));
+  val->value = source;
+  return val;
+}
+
+/* Allocate a new ipcp_value holding a polymorphic context, initialize its
+   value to SOURCE and clear all other fields.  */
+
+static ipcp_value<ipa_polymorphic_call_context> *
+allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
+{
+  ipcp_value<ipa_polymorphic_call_context> *val;
+
+  val = new (pool_alloc (ipcp_poly_ctx_values_pool))
+    ipcp_value<ipa_polymorphic_call_context>;
   memset (val, 0, sizeof (*val));
   val->value = source;
   return val;
@@ -1171,19 +1260,9 @@  propagate_scalar_accross_jump_function (
   if (dest_lat->bottom)
     return false;
 
-  if (jfunc->type == IPA_JF_CONST
-      || jfunc->type == IPA_JF_KNOWN_TYPE)
+  if (jfunc->type == IPA_JF_CONST)
     {
-      tree val;
-
-      if (jfunc->type == IPA_JF_KNOWN_TYPE)
-	{
-	  val = ipa_binfo_from_known_type_jfunc (jfunc);
-	  if (!val)
-	    return dest_lat->set_contains_variable ();
-	}
-      else
-	val = ipa_get_jf_constant (jfunc);
+      tree val = ipa_get_jf_constant (jfunc);
       return dest_lat->add_value (val, cs, NULL, 0);
     }
   else if (jfunc->type == IPA_JF_PASS_THROUGH
@@ -1227,6 +1306,93 @@  propagate_scalar_accross_jump_function (
   return dest_lat->set_contains_variable ();
 }
 
+/* Propagate scalar values across jump function JFUNC that is associated with
+   edge CS and describes argument IDX and put the values into DEST_LAT.  */
+
+static bool
+propagate_context_accross_jump_function (cgraph_edge *cs,
+			  ipa_jump_func *jfunc, int idx,
+			  ipcp_lattice<ipa_polymorphic_call_context> *dest_lat)
+{
+  ipa_edge_args *args = IPA_EDGE_REF (cs);
+  if (dest_lat->bottom)
+    return false;
+  bool ret = false;
+  bool added_sth = false;
+
+  ipa_polymorphic_call_context edge_ctx, *edge_ctx_ptr
+    = ipa_get_ith_polymorhic_call_context (args, idx);
+
+  if (edge_ctx_ptr)
+    {
+      edge_ctx = *edge_ctx_ptr;
+      edge_ctx.clear_speculation ();
+    }
+
+  if (jfunc->type == IPA_JF_PASS_THROUGH
+      || jfunc->type == IPA_JF_ANCESTOR)
+    {
+      struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
+      int src_idx;
+      ipcp_lattice<ipa_polymorphic_call_context> *src_lat;
+
+      /* TODO: Once we figure out how to propagate speculations, it will
+	 probably be a good idea to switch to speculation if type_preserved is
+	 not set instead of punting.  */
+      if (jfunc->type == IPA_JF_PASS_THROUGH)
+	{
+	  if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
+	      || !ipa_get_jf_pass_through_type_preserved (jfunc))
+	    goto prop_fail;
+	  src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
+	}
+      else
+	{
+	  if (!ipa_get_jf_ancestor_type_preserved (jfunc))
+	    goto prop_fail;
+	  src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
+	}
+
+      src_lat = ipa_get_poly_ctx_lat (caller_info, src_idx);
+      /* If we would need to clone the caller and cannot, do not propagate.  */
+      if (!ipcp_versionable_function_p (cs->caller)
+	  && (src_lat->contains_variable
+	      || (src_lat->values_count > 1)))
+	goto prop_fail;
+      if (src_lat->contains_variable)
+	  ret |= dest_lat->set_contains_variable ();
+
+      ipcp_value<ipa_polymorphic_call_context> *src_val;
+      for (src_val = src_lat->values; src_val; src_val = src_val->next)
+	{
+	  ipa_polymorphic_call_context cur = src_val->value;
+	  if (jfunc->type == IPA_JF_ANCESTOR)
+	    cur.offset_by (ipa_get_jf_ancestor_offset (jfunc));
+	  /* TODO: Perhaps attempt to look up some used OTR type? */
+	  cur.clear_speculation ();
+	  if (!edge_ctx.useless_p ())
+	    cur.combine_with (edge_ctx);
+	  if (!cur.useless_p ())
+	    {
+	      ret |= dest_lat->add_value (cur, cs, src_val, src_idx);
+	      added_sth = true;
+	    }
+	}
+
+    }
+
+ prop_fail:
+  if (!added_sth)
+    {
+      if (!edge_ctx.useless_p ())
+	ret |= dest_lat->add_value (edge_ctx, cs);
+      else
+	ret |= dest_lat->set_contains_variable ();
+    }
+
+  return ret;
+}
+
 /* If DEST_PLATS already has aggregate items, check that aggs_by_ref matches
    NEW_AGGS_BY_REF and if not, mark all aggs as bottoms and return true (in all
    other cases, return false).  If there are no aggregate items, set
@@ -1561,6 +1727,8 @@  propagate_constants_accross_call (struct
 	{
 	  ret |= propagate_scalar_accross_jump_function (cs, jump_func,
 							 &dest_plats->itself);
+	  ret |= propagate_context_accross_jump_function (cs, jump_func, i,
+							  &dest_plats->ctxlat);
 	  ret |= propagate_aggs_accross_jump_function (cs, jump_func,
 						       dest_plats);
 	}
@@ -1572,25 +1740,24 @@  propagate_constants_accross_call (struct
 }
 
 /* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
-   (which can contain both constants and binfos), KNOWN_BINFOS, KNOWN_AGGS or
+   (which can contain both constants and binfos), KNOWN_CONTEXTS, KNOWN_AGGS or
    AGG_REPS return the destination.  The latter three can be NULL.  If AGG_REPS
    is not NULL, KNOWN_AGGS is ignored.  */
 
 static tree
 ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
-				vec<tree> known_vals,
-				vec<tree> known_binfos,
+				vec<tree> known_csts,
+				vec<ipa_polymorphic_call_context> known_contexts,
 				vec<ipa_agg_jump_function_p> known_aggs,
 				struct ipa_agg_replacement_value *agg_reps)
 {
   int param_index = ie->indirect_info->param_index;
-  HOST_WIDE_INT token, anc_offset;
-  tree otr_type;
+  HOST_WIDE_INT anc_offset;
   tree t;
   tree target = NULL;
 
   if (param_index == -1
-      || known_vals.length () <= (unsigned int) param_index)
+      || known_csts.length () <= (unsigned int) param_index)
     return NULL_TREE;
 
   if (!ie->indirect_info->polymorphic)
@@ -1625,7 +1792,7 @@  ipa_get_indirect_edge_target_1 (struct c
 	    t = NULL;
 	}
       else
-	t = known_vals[param_index];
+	t = known_csts[param_index];
 
       if (t &&
 	  TREE_CODE (t) == ADDR_EXPR
@@ -1639,9 +1806,7 @@  ipa_get_indirect_edge_target_1 (struct c
     return NULL_TREE;
 
   gcc_assert (!ie->indirect_info->agg_contents);
-  token = ie->indirect_info->otr_token;
   anc_offset = ie->indirect_info->offset;
-  otr_type = ie->indirect_info->otr_type;
 
   t = NULL;
 
@@ -1695,42 +1860,43 @@  ipa_get_indirect_edge_target_1 (struct c
 	}
     }
 
-  /* Did we work out BINFO via type propagation?  */
-  if (!t && known_binfos.length () > (unsigned int) param_index)
-    t = known_binfos[param_index];
-  /* Or do we know the constant value of pointer?  */
+  /* Do we know the constant value of pointer?  */
   if (!t)
-    t = known_vals[param_index];
-  if (!t)
-    return NULL_TREE;
+    t = known_csts[param_index];
 
-  if (TREE_CODE (t) != TREE_BINFO)
-    {
-      ipa_polymorphic_call_context context (t, ie->indirect_info->otr_type,
-					    anc_offset);
-      vec <cgraph_node *>targets;
-      bool final;
+  gcc_checking_assert (!t || TREE_CODE (t) != TREE_BINFO);
 
-      targets = possible_polymorphic_call_targets
-		 (ie->indirect_info->otr_type,
-		  ie->indirect_info->otr_token,
-		  context, &final);
-      if (!final || targets.length () > 1)
-	return NULL_TREE;
-      if (targets.length () == 1)
-	target = targets[0]->decl;
-      else
-	target = ipa_impossible_devirt_target (ie, NULL_TREE);
+  ipa_polymorphic_call_context context;
+  if (known_contexts.length () > (unsigned int) param_index)
+    {
+      context = known_contexts[param_index];
+      if (t)
+	{
+	  ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context
+	    (t, ie->indirect_info->otr_type, anc_offset);
+	  if (!ctx2.useless_p ())
+	    context.combine_with (ctx2, ie->indirect_info->otr_type);
+	}
     }
+  else if (t)
+    context = ipa_polymorphic_call_context (t, ie->indirect_info->otr_type,
+					    anc_offset);
   else
-    {
-      tree binfo;
+    return NULL_TREE;
 
-      binfo = get_binfo_at_offset (t, anc_offset, otr_type);
-      if (!binfo)
-	return NULL_TREE;
-      target = gimple_get_virt_method_for_binfo (token, binfo);
-    }
+  vec <cgraph_node *>targets;
+  bool final;
+
+  targets = possible_polymorphic_call_targets
+    (ie->indirect_info->otr_type,
+     ie->indirect_info->otr_token,
+     context, &final);
+  if (!final || targets.length () > 1)
+    return NULL_TREE;
+  if (targets.length () == 1)
+    target = targets[0]->decl;
+  else
+    target = ipa_impossible_devirt_target (ie, NULL_TREE);
 
   if (target && !possible_polymorphic_call_target_p (ie,
 						     cgraph_node::get (target)))
@@ -1740,27 +1906,27 @@  ipa_get_indirect_edge_target_1 (struct c
 }
 
 
-/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
-   (which can contain both constants and binfos), KNOWN_BINFOS (which can be
-   NULL) or KNOWN_AGGS (which also can be NULL) return the destination.  */
+/* If an indirect edge IE can be turned into a direct one based on KNOWN_CSTS,
+   KNOWN_CONTEXTS (which can be vNULL) or KNOWN_AGGS (which also can be vNULL)
+   return the destination.  */
 
 tree
 ipa_get_indirect_edge_target (struct cgraph_edge *ie,
-			      vec<tree> known_vals,
-			      vec<tree> known_binfos,
+			      vec<tree> known_csts,
+			      vec<ipa_polymorphic_call_context> known_contexts,
 			      vec<ipa_agg_jump_function_p> known_aggs)
 {
-  return ipa_get_indirect_edge_target_1 (ie, known_vals, known_binfos,
+  return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
 					 known_aggs, NULL);
 }
 
 /* Calculate devirtualization time bonus for NODE, assuming we know KNOWN_CSTS
-   and KNOWN_BINFOS.  */
+   and KNOWN_CONTEXTS.  */
 
 static int
 devirtualization_time_bonus (struct cgraph_node *node,
 			     vec<tree> known_csts,
-			     vec<tree> known_binfos,
+			     vec<ipa_polymorphic_call_context> known_contexts,
 			     vec<ipa_agg_jump_function_p> known_aggs)
 {
   struct cgraph_edge *ie;
@@ -1773,7 +1939,7 @@  devirtualization_time_bonus (struct cgra
       enum availability avail;
       tree target;
 
-      target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos,
+      target = ipa_get_indirect_edge_target (ie, known_csts, known_contexts,
 					     known_aggs);
       if (!target)
 	continue;
@@ -1890,25 +2056,27 @@  context_independent_aggregate_values (st
   return res;
 }
 
-/* Allocate KNOWN_CSTS, KNOWN_BINFOS and, if non-NULL, KNOWN_AGGS and populate
-   them with values of parameters that are known independent of the context.
-   INFO describes the function.  If REMOVABLE_PARAMS_COST is non-NULL, the
-   movement cost of all removable parameters will be stored in it.  */
+/* Allocate KNOWN_CSTS, KNOWN_CONTEXTS and, if non-NULL, KNOWN_AGGS and
+   populate them with values of parameters that are known independent of the
+   context.  INFO describes the function.  If REMOVABLE_PARAMS_COST is
+   non-NULL, the movement cost of all removable parameters will be stored in
+   it.  */
 
 static bool
 gather_context_independent_values (struct ipa_node_params *info,
-			       vec<tree> *known_csts,
-			       vec<tree> *known_binfos,
-			       vec<ipa_agg_jump_function> *known_aggs,
-			       int *removable_params_cost)
+				   vec<tree> *known_csts,
+				   vec<ipa_polymorphic_call_context>
+				   *known_contexts,
+				   vec<ipa_agg_jump_function> *known_aggs,
+				   int *removable_params_cost)
 {
   int i, count = ipa_get_param_count (info);
   bool ret = false;
 
   known_csts->create (0);
-  known_binfos->create (0);
+  known_contexts->create (0);
   known_csts->safe_grow_cleared (count);
-  known_binfos->safe_grow_cleared (count);
+  known_contexts->safe_grow_cleared (count);
   if (known_aggs)
     {
       known_aggs->create (0);
@@ -1926,28 +2094,25 @@  gather_context_independent_values (struc
       if (lat->is_single_const ())
 	{
 	  ipcp_value<tree> *val = lat->values;
-	  if (TREE_CODE (val->value) != TREE_BINFO)
-	    {
-	      (*known_csts)[i] = val->value;
-	      if (removable_params_cost)
-		*removable_params_cost
-		  += estimate_move_cost (TREE_TYPE (val->value), false);
-	      ret = true;
-	    }
-	  else if (plats->virt_call)
-	    {
-	      (*known_binfos)[i] = val->value;
-	      ret = true;
-	    }
-	  else if (removable_params_cost
-		   && !ipa_is_param_used (info, i))
-	    *removable_params_cost += ipa_get_param_move_cost (info, i);
+	  gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
+	  (*known_csts)[i] = val->value;
+	  if (removable_params_cost)
+	    *removable_params_cost
+	      += estimate_move_cost (TREE_TYPE (val->value), false);
+	  ret = true;
 	}
       else if (removable_params_cost
 	       && !ipa_is_param_used (info, i))
 	*removable_params_cost
 	  += ipa_get_param_move_cost (info, i);
 
+      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
+      if (ctxlat->is_single_const ())
+	{
+	  (*known_contexts)[i] = ctxlat->values->value;
+	  ret = true;
+	}
+
       if (known_aggs)
 	{
 	  vec<ipa_agg_jf_item, va_gc> *agg_items;
@@ -1985,14 +2150,14 @@  agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jum
 }
 
 /* Perform time and size measurement of NODE with the context given in
-   KNOWN_CSTS, KNOWN_BINFOS and KNOWN_AGGS, calculate the benefit and cost
+   KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost
    given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST of
    all context-independent removable parameters and EST_MOVE_COST of estimated
    movement of the considered parameter and store it into VAL.  */
 
 static void
 perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
-			       vec<tree> known_binfos,
+			       vec<ipa_polymorphic_call_context> known_contexts,
 			       vec<ipa_agg_jump_function_p> known_aggs_ptrs,
 			       int base_time, int removable_params_cost,
 			       int est_move_cost, ipcp_value_base *val)
@@ -2000,11 +2165,11 @@  perform_estimation_of_a_value (cgraph_no
   int time, size, time_benefit;
   inline_hints hints;
 
-  estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
+  estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
 				     known_aggs_ptrs, &size, &time,
 				     &hints);
   time_benefit = base_time - time
-    + devirtualization_time_bonus (node, known_csts, known_binfos,
+    + devirtualization_time_bonus (node, known_csts, known_contexts,
 				   known_aggs_ptrs)
     + hint_time_bonus (hints)
     + removable_params_cost + est_move_cost;
@@ -2029,7 +2194,8 @@  estimate_local_effects (struct cgraph_no
 {
   struct ipa_node_params *info = IPA_NODE_REF (node);
   int i, count = ipa_get_param_count (info);
-  vec<tree> known_csts, known_binfos;
+  vec<tree> known_csts;
+  vec<ipa_polymorphic_call_context> known_contexts;
   vec<ipa_agg_jump_function> known_aggs;
   vec<ipa_agg_jump_function_p> known_aggs_ptrs;
   bool always_const;
@@ -2044,7 +2210,7 @@  estimate_local_effects (struct cgraph_no
 	     node->name (), node->order, base_time);
 
   always_const = gather_context_independent_values (info, &known_csts,
-						    &known_binfos, &known_aggs,
+						    &known_contexts, &known_aggs,
 						    &removable_params_cost);
   known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs);
   if (always_const)
@@ -2056,9 +2222,9 @@  estimate_local_effects (struct cgraph_no
       init_caller_stats (&stats);
       node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
 					      false);
-      estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
+      estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
 					 known_aggs_ptrs, &size, &time, &hints);
-      time -= devirtualization_time_bonus (node, known_csts, known_binfos,
+      time -= devirtualization_time_bonus (node, known_csts, known_contexts,
 					   known_aggs_ptrs);
       time -= hint_time_bonus (hints);
       time -= removable_params_cost;
@@ -2104,32 +2270,19 @@  estimate_local_effects (struct cgraph_no
       struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
       ipcp_lattice<tree> *lat = &plats->itself;
       ipcp_value<tree> *val;
-      int emc;
 
       if (lat->bottom
 	  || !lat->values
-	  || known_csts[i]
-	  || known_binfos[i])
+	  || known_csts[i])
 	continue;
 
       for (val = lat->values; val; val = val->next)
 	{
-	  if (TREE_CODE (val->value) != TREE_BINFO)
-	    {
-	      known_csts[i] = val->value;
-	      known_binfos[i] = NULL_TREE;
-	      emc = estimate_move_cost (TREE_TYPE (val->value), true);
-	    }
-	  else if (plats->virt_call)
-	    {
-	      known_csts[i] = NULL_TREE;
-	      known_binfos[i] = val->value;
-	      emc = 0;
-	    }
-	  else
-	    continue;
+	  gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
+	  known_csts[i] = val->value;
 
-	  perform_estimation_of_a_value (node, known_csts, known_binfos,
+	  int emc = estimate_move_cost (TREE_TYPE (val->value), true);
+	  perform_estimation_of_a_value (node, known_csts, known_contexts,
 					 known_aggs_ptrs, base_time,
 					 removable_params_cost, emc, val);
 
@@ -2143,10 +2296,44 @@  estimate_local_effects (struct cgraph_no
 		       val->local_time_benefit, val->local_size_cost);
 	    }
 	}
-      known_binfos[i] = NULL_TREE;
       known_csts[i] = NULL_TREE;
     }
 
+  for (i = 0; i < count; i++)
+    {
+      struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+
+      if (!plats->virt_call)
+	continue;
+
+      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
+      ipcp_value<ipa_polymorphic_call_context> *val;
+
+      if (ctxlat->bottom
+	  || !ctxlat->values
+	  || !known_contexts[i].useless_p ())
+	continue;
+
+      for (val = ctxlat->values; val; val = val->next)
+	{
+	  known_contexts[i] = val->value;
+	  perform_estimation_of_a_value (node, known_csts, known_contexts,
+					 known_aggs_ptrs, base_time,
+					 removable_params_cost, 0, val);
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, " - estimates for polymorphic context ");
+	      print_ipcp_constant_value (dump_file, val->value);
+	      fprintf (dump_file, " for ");
+	      ipa_dump_param (dump_file, info, i);
+	      fprintf (dump_file, ": time_benefit: %i, size: %i\n",
+		       val->local_time_benefit, val->local_size_cost);
+	    }
+	}
+      known_contexts[i] = ipa_polymorphic_call_context ();
+    }
+
   for (i = 0; i < count ; i++)
     {
       struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
@@ -2174,7 +2361,7 @@  estimate_local_effects (struct cgraph_no
 	      item.value = val->value;
 	      vec_safe_push (ajf->items, item);
 
-	      perform_estimation_of_a_value (node, known_csts, known_binfos,
+	      perform_estimation_of_a_value (node, known_csts, known_contexts,
 					     known_aggs_ptrs, base_time,
 					     removable_params_cost, 0, val);
 
@@ -2200,7 +2387,7 @@  estimate_local_effects (struct cgraph_no
     vec_free (known_aggs[i].items);
 
   known_csts.release ();
-  known_binfos.release ();
+  known_contexts.release ();
   known_aggs.release ();
   known_aggs_ptrs.release ();
 }
@@ -2274,17 +2461,30 @@  add_all_node_vals_to_toposort (cgraph_no
       struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
       ipcp_lattice<tree> *lat = &plats->itself;
       struct ipcp_agg_lattice *aglat;
-      ipcp_value<tree> *val;
 
       if (!lat->bottom)
-	for (val = lat->values; val; val = val->next)
-	  topo->constants.add_val (val);
+	{
+	  ipcp_value<tree> *val;
+	  for (val = lat->values; val; val = val->next)
+	    topo->constants.add_val (val);
+	}
 
       if (!plats->aggs_bottom)
 	for (aglat = plats->aggs; aglat; aglat = aglat->next)
 	  if (!aglat->bottom)
-	    for (val = aglat->values; val; val = val->next)
-	      topo->constants.add_val (val);
+	    {
+	      ipcp_value<tree> *val;
+	      for (val = aglat->values; val; val = val->next)
+		topo->constants.add_val (val);
+	    }
+
+      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
+      if (!ctxlat->bottom)
+	{
+	  ipcp_value<ipa_polymorphic_call_context> *ctxval;
+	  for (ctxval = ctxlat->values; ctxval; ctxval = ctxval->next)
+	    topo->contexts.add_val (ctxval);
+	}
     }
 }
 
@@ -2389,8 +2589,8 @@  value_topo_info<valtype>::propagate_effe
 }
 
 
-/* Propagate constants, binfos and their effects from the summaries
-   interprocedurally.  */
+/* Propagate constants, polymorphic contexts and their effects from the
+   summaries interprocedurally.  */
 
 static void
 ipcp_propagate_stage (struct ipa_topo_info *topo)
@@ -2435,6 +2635,7 @@  ipcp_propagate_stage (struct ipa_topo_in
   ipcp_verify_propagated_values ();
 #endif
   topo->constants.propagate_effects ();
+  topo->contexts.propagate_effects ();
 
   if (dump_file)
     {
@@ -2444,11 +2645,13 @@  ipcp_propagate_stage (struct ipa_topo_in
 }
 
 /* Discover newly direct outgoing edges from NODE which is a new clone with
-   known KNOWN_VALS and make them direct.  */
+   known KNOWN_CSTS and make them direct.  */
 
 static void
 ipcp_discover_new_direct_edges (struct cgraph_node *node,
-				vec<tree> known_vals,
+				vec<tree> known_csts,
+				vec<ipa_polymorphic_call_context>
+				known_contexts,
 				struct ipa_agg_replacement_value *aggvals)
 {
   struct cgraph_edge *ie, *next_ie;
@@ -2459,8 +2662,8 @@  ipcp_discover_new_direct_edges (struct c
       tree target;
 
       next_ie = ie->next_callee;
-      target = ipa_get_indirect_edge_target_1 (ie, known_vals, vNULL, vNULL,
-					       aggvals);
+      target = ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
+					       vNULL, aggvals);
       if (target)
 	{
 	  bool agg_contents = ie->indirect_info->agg_contents;
@@ -2589,7 +2792,7 @@  cgraph_edge_brings_value_p (struct cgrap
     {
       tree t;
       if (src->offset == -1)
-	t = caller_info->known_vals[src->index];
+	t = caller_info->known_csts[src->index];
       else
 	t = get_clone_agg_value (cs->caller, src->offset, src->index);
       return (t != NULL_TREE
@@ -2618,6 +2821,35 @@  cgraph_edge_brings_value_p (struct cgrap
     }
 }
 
+/* Return true if edge CS does bring about the value described by SRC.  */
+
+static bool
+cgraph_edge_brings_value_p (struct cgraph_edge *cs,
+			    ipcp_value_source<ipa_polymorphic_call_context>
+			    *src)
+{
+  struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
+  cgraph_node *real_dest = cs->callee->function_symbol ();
+  struct ipa_node_params *dst_info = IPA_NODE_REF (real_dest);
+
+  if ((dst_info->ipcp_orig_node && !dst_info->is_all_contexts_clone)
+      || caller_info->node_dead)
+    return false;
+  if (!src->val)
+    return true;
+
+  if (caller_info->ipcp_orig_node)
+    return (caller_info->known_contexts.length () > (unsigned) src->index)
+      && values_equal_for_ipcp_p (src->val->value,
+				  caller_info->known_contexts[src->index]);
+
+  struct ipcp_param_lattices *plats = ipa_get_parm_lattices (caller_info,
+							     src->index);
+  return plats->ctxlat.is_single_const ()
+    && values_equal_for_ipcp_p (src->val->value,
+				plats->ctxlat.values->value);
+}
+
 /* Get the next clone in the linked list of clones of an edge.  */
 
 static inline struct cgraph_edge *
@@ -2847,12 +3079,14 @@  update_specialized_profile (struct cgrap
     dump_profile_updates (orig_node, new_node);
 }
 
-/* Create a specialized version of NODE with known constants and types of
-   parameters in KNOWN_VALS and redirect all edges in CALLERS to it.  */
+/* Create a specialized version of NODE with known constants in KNOWN_CSTS,
+   known contexts in KNOWN_CONTEXTS and known aggregate values in AGGVALS and
+   redirect all edges in CALLERS to it.  */
 
 static struct cgraph_node *
 create_specialized_node (struct cgraph_node *node,
-			 vec<tree> known_vals,
+			 vec<tree> known_csts,
+			 vec<ipa_polymorphic_call_context> known_contexts,
 			 struct ipa_agg_replacement_value *aggvals,
 			 vec<cgraph_edge *> callers)
 {
@@ -2870,10 +3104,9 @@  create_specialized_node (struct cgraph_n
       args_to_skip = BITMAP_GGC_ALLOC ();
       for (i = 0; i < count; i++)
 	{
-	  tree t = known_vals[i];
+	  tree t = known_csts[i];
 
-	  if ((t && TREE_CODE (t) != TREE_BINFO)
-	      || !ipa_is_param_used (info, i))
+	  if (t || !ipa_is_param_used (info, i))
 	    bitmap_set_bit (args_to_skip, i);
 	}
     }
@@ -2886,11 +3119,12 @@  create_specialized_node (struct cgraph_n
 
   for (i = 0; i < count ; i++)
     {
-      tree t = known_vals[i];
-      if (t && TREE_CODE (t) != TREE_BINFO)
+      tree t = known_csts[i];
+      if (t)
 	{
 	  struct ipa_replace_map *replace_map;
 
+	  gcc_checking_assert (TREE_CODE (t) != TREE_BINFO);
 	  replace_map = get_replacement_map (info, t, i);
 	  if (replace_map)
 	    vec_safe_push (replace_trees, replace_map);
@@ -2907,6 +3141,15 @@  create_specialized_node (struct cgraph_n
     {
       fprintf (dump_file, "     the new node is %s/%i.\n",
 	       new_node->name (), new_node->order);
+      if (known_contexts.exists ())
+	{
+	  for (i = 0; i < count ; i++)
+	    if (!known_contexts[i].useless_p ())
+	      {
+		fprintf (dump_file, "     known ctx %i is ", i);
+		known_contexts[i].dump (dump_file);
+	      }
+	}
       if (aggvals)
 	ipa_dump_agg_replacement_values (dump_file, aggvals);
     }
@@ -2914,21 +3157,21 @@  create_specialized_node (struct cgraph_n
   update_profiling_info (node, new_node);
   new_info = IPA_NODE_REF (new_node);
   new_info->ipcp_orig_node = node;
-  new_info->known_vals = known_vals;
+  new_info->known_csts = known_csts;
+  new_info->known_contexts = known_contexts;
 
-  ipcp_discover_new_direct_edges (new_node, known_vals, aggvals);
+  ipcp_discover_new_direct_edges (new_node, known_csts, known_contexts, aggvals);
 
   callers.release ();
   return new_node;
 }
 
 /* Given a NODE, and a subset of its CALLERS, try to populate blanks slots in
-   KNOWN_VALS with constants and types that are also known for all of the
-   CALLERS.  */
+   KNOWN_CSTS with constants that are also known for all of the CALLERS.  */
 
 static void
 find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
-					    vec<tree> known_vals,
+					    vec<tree> known_csts,
 					    vec<cgraph_edge *> callers)
 {
   struct ipa_node_params *info = IPA_NODE_REF (node);
@@ -2940,7 +3183,7 @@  find_more_scalar_values_for_callers_subs
       tree newval = NULL_TREE;
       int j;
 
-      if (ipa_get_scalar_lat (info, i)->bottom || known_vals[i])
+      if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i])
 	continue;
 
       FOR_EACH_VEC_ELT (callers, j, cs)
@@ -2977,8 +3220,77 @@  find_more_scalar_values_for_callers_subs
 	      fprintf (dump_file, "\n");
 	    }
 
-	  known_vals[i] = newval;
+	  known_csts[i] = newval;
+	}
+    }
+}
+
+/* Given a NODE and a subset of its CALLERS, try to populate plank slots in
+   KNOWN_CONTEXTS with polymorphic contexts that are also known for all of the
+   CALLERS.  */
+
+static void
+find_more_contexts_for_caller_subset (cgraph_node *node,
+				      vec<ipa_polymorphic_call_context>
+				      *known_contexts,
+				      vec<cgraph_edge *> callers)
+{
+  ipa_node_params *info = IPA_NODE_REF (node);
+  int i, count = ipa_get_param_count (info);
+
+  for (i = 0; i < count ; i++)
+    {
+      cgraph_edge *cs;
+
+      if (ipa_get_poly_ctx_lat (info, i)->bottom
+	  || (known_contexts->exists ()
+	      && !(*known_contexts)[i].useless_p ()))
+	continue;
+
+      ipa_polymorphic_call_context newval;
+      bool found = false;
+      int j;
+
+      FOR_EACH_VEC_ELT (callers, j, cs)
+	{
+	  if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
+	    return;
+	  ipa_jump_func *jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs),
+							    i);
+	  ipa_polymorphic_call_context ctx;
+	  ctx = ipa_context_from_jfunc (IPA_NODE_REF (cs->caller), cs, i,
+					jfunc);
+	  ctx.clear_speculation ();
+	  if (ctx.useless_p ()
+	      || (found && !values_equal_for_ipcp_p (newval, ctx)))
+	    {
+	      found = false;
+	      break;
+	    }
+	  else if (!found)
+	    {
+	      found = true;
+	      newval = ctx;
+	    }
+	}
+
+      if (found)
+	{
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "    adding an extra known polymorphic "
+		       "context ");
+	      print_ipcp_constant_value (dump_file, newval);
+	      fprintf (dump_file, " for ");
+	      ipa_dump_param (dump_file, info, i);
+	      fprintf (dump_file, "\n");
+	    }
+
+	  if (!known_contexts->exists ())
+	    known_contexts->safe_grow_cleared (ipa_get_param_count (info));
+	  (*known_contexts)[i] = newval;
 	}
+
     }
 }
 
@@ -3355,7 +3667,7 @@  cgraph_edge_brings_all_scalars_for_node
       struct ipa_jump_func *jump_func;
       tree val, t;
 
-      val = dest_info->known_vals[i];
+      val = dest_info->known_csts[i];
       if (!val)
 	continue;
 
@@ -3489,28 +3801,70 @@  perhaps_add_new_callers (cgraph_node *no
     update_specialized_profile (val->spec_node, node, redirected_sum);
 }
 
+/* Return true if KNOWN_CONTEXTS contain at least one useful context.  */
+
+static bool
+known_contexts_useful_p (vec<ipa_polymorphic_call_context> known_contexts)
+{
+  ipa_polymorphic_call_context *ctx;
+  int i;
+
+  FOR_EACH_VEC_ELT (known_contexts, i, ctx)
+    if (!ctx->useless_p ())
+      return true;
+  return false;
+}
+
+/* Return a copy of KNOWN_CSTS if it is not empty, otherwise return vNULL.  */
+
+static vec<ipa_polymorphic_call_context>
+copy_useful_known_contexts (vec<ipa_polymorphic_call_context> known_contexts)
+{
+  if (known_contexts_useful_p (known_contexts))
+    return known_contexts.copy ();
+  else
+    return vNULL;
+}
 
-/* Copy KNOWN_BINFOS to KNOWN_VALS.  */
+/* Copy KNOWN_CSTS and modify the copy according to VAL and INDEX.  If
+   non-empty, replace KNOWN_CONTEXTS with its copy too.  */
 
 static void
-move_binfos_to_values (vec<tree> known_vals,
-		       vec<tree> known_binfos)
+modify_known_vectors_with_val (vec<tree> *known_csts,
+			       vec<ipa_polymorphic_call_context> *known_contexts,
+			       ipcp_value<tree> *val,
+			       int index)
 {
-  tree t;
-  int i;
+  *known_csts = known_csts->copy ();
+  *known_contexts = copy_useful_known_contexts (*known_contexts);
+  (*known_csts)[index] = val->value;
+}
 
-  for (i = 0; known_binfos.iterate (i, &t); i++)
-    if (t)
-      known_vals[i] = t;
+/* Replace KNOWN_CSTS with its copy.  Also copy KNOWN_CONTEXTS and modify the
+   copy according to VAL and INDEX.  */
+
+static void
+modify_known_vectors_with_val (vec<tree> *known_csts,
+			       vec<ipa_polymorphic_call_context> *known_contexts,
+			       ipcp_value<ipa_polymorphic_call_context> *val,
+			       int index)
+{
+  *known_csts = known_csts->copy ();
+  *known_contexts = known_contexts->copy ();
+  (*known_contexts)[index] = val->value;
 }
 
-/* Return true if there is a replacement equivalent to VALUE, INDEX and OFFSET
-   among those in the AGGVALS list.  */
+/* Return true if OFFSET indicates this was not an aggregate value or there is
+   a replacement equivalent to VALUE, INDEX and OFFSET among those in the
+   AGGVALS list.  */
 
 DEBUG_FUNCTION bool
-ipcp_val_in_agg_replacements_p (struct ipa_agg_replacement_value *aggvals,
-				int index, HOST_WIDE_INT offset, tree value)
+ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *aggvals,
+			       int index, HOST_WIDE_INT offset, tree value)
 {
+  if (offset == -1)
+    return true;
+
   while (aggvals)
     {
       if (aggvals->index == index
@@ -3522,22 +3876,32 @@  ipcp_val_in_agg_replacements_p (struct i
   return false;
 }
 
+/* Return true if offset is minus one because source of a polymorphic contect
+   cannot be an aggregate value.  */
+
+DEBUG_FUNCTION bool
+ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *,
+			       int , HOST_WIDE_INT offset,
+			       ipa_polymorphic_call_context)
+{
+  return offset == -1;
+}
+
 /* Decide wheter to create a special version of NODE for value VAL of parameter
    at the given INDEX.  If OFFSET is -1, the value is for the parameter itself,
    otherwise it is stored at the given OFFSET of the parameter.  KNOWN_CSTS,
-   KNOWN_BINFOS and KNOWN_AGGS describe the other already known values.  */
+   KNOWN_CONTEXTS and KNOWN_AGGS describe the other already known values.  */
 
 template <typename valtype>
 static bool
 decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset,
 		    ipcp_value<valtype> *val, vec<tree> known_csts,
-		    vec<tree> known_binfos)
+		    vec<ipa_polymorphic_call_context> known_contexts)
 {
   struct ipa_agg_replacement_value *aggvals;
   int freq_sum, caller_count;
   gcov_type count_sum;
   vec<cgraph_edge *> callers;
-  vec<tree> kv;
 
   if (val->spec_node)
     {
@@ -3583,16 +3947,20 @@  decide_about_value (struct cgraph_node *
 	     node->name (), node->order);
 
   callers = gather_edges_for_value (val, caller_count);
-  kv = known_csts.copy ();
-  move_binfos_to_values (kv, known_binfos);
   if (offset == -1)
-    kv[index] = val->value;
-  find_more_scalar_values_for_callers_subset (node, kv, callers);
+    modify_known_vectors_with_val (&known_csts, &known_contexts, val, index);
+  else
+    {
+      known_csts = known_csts.copy ();
+      known_contexts = copy_useful_known_contexts (known_contexts);
+    }
+  find_more_scalar_values_for_callers_subset (node, known_csts, callers);
+  find_more_contexts_for_caller_subset (node, &known_contexts, callers);
   aggvals = find_aggregate_values_for_callers_subset (node, callers);
-  gcc_checking_assert (offset == -1
-		       || ipcp_val_in_agg_replacements_p (aggvals, index,
-							  offset, val->value));
-  val->spec_node = create_specialized_node (node, kv, aggvals, callers);
+  gcc_checking_assert (ipcp_val_agg_replacement_ok_p (aggvals, index,
+						      offset, val->value));
+  val->spec_node = create_specialized_node (node, known_csts, known_contexts,
+					    aggvals, callers);
   overall_size += val->local_size_cost;
 
   /* TODO: If for some lattice there is only one other known value
@@ -3608,7 +3976,8 @@  decide_whether_version_node (struct cgra
 {
   struct ipa_node_params *info = IPA_NODE_REF (node);
   int i, count = ipa_get_param_count (info);
-  vec<tree> known_csts, known_binfos;
+  vec<tree> known_csts;
+  vec<ipa_polymorphic_call_context> known_contexts;
   vec<ipa_agg_jump_function> known_aggs = vNULL;
   bool ret = false;
 
@@ -3619,7 +3988,7 @@  decide_whether_version_node (struct cgra
     fprintf (dump_file, "\nEvaluating opportunities for %s/%i.\n",
 	     node->name (), node->order);
 
-  gather_context_independent_values (info, &known_csts, &known_binfos,
+  gather_context_independent_values (info, &known_csts, &known_contexts,
 				  info->do_clone_for_all_contexts ? &known_aggs
 				  : NULL, NULL);
 
@@ -3627,14 +3996,16 @@  decide_whether_version_node (struct cgra
     {
       struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
       ipcp_lattice<tree> *lat = &plats->itself;
-      ipcp_value<tree> *val;
+      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
 
       if (!lat->bottom
-	  && !known_csts[i]
-	  && !known_binfos[i])
-	for (val = lat->values; val; val = val->next)
-	  ret |= decide_about_value (node, i, -1, val, known_csts,
-				     known_binfos);
+	  && !known_csts[i])
+	{
+	  ipcp_value<tree> *val;
+	  for (val = lat->values; val; val = val->next)
+	    ret |= decide_about_value (node, i, -1, val, known_csts,
+				       known_contexts);
+	}
 
       if (!plats->aggs_bottom)
 	{
@@ -3648,8 +4019,18 @@  decide_whether_version_node (struct cgra
 		    || !aglat->is_single_const ()))
 	      for (val = aglat->values; val; val = val->next)
 		ret |= decide_about_value (node, i, aglat->offset, val,
-					   known_csts, known_binfos);
+					   known_csts, known_contexts);
 	}
+
+      if (!ctxlat->bottom
+	  && known_contexts[i].useless_p ())
+	{
+	  ipcp_value<ipa_polymorphic_call_context> *val;
+	  for (val = ctxlat->values; val; val = val->next)
+	    ret |= decide_about_value (node, i, -1, val, known_csts,
+				       known_contexts);
+	}
+
         info = IPA_NODE_REF (node);
     }
 
@@ -3664,8 +4045,13 @@  decide_whether_version_node (struct cgra
 		 node->order);
 
       callers = node->collect_callers ();
-      move_binfos_to_values (known_csts, known_binfos);
-      clone = create_specialized_node (node, known_csts,
+
+      if (!known_contexts_useful_p (known_contexts))
+	{
+	  known_contexts.release ();
+	  known_contexts = vNULL;
+	}
+      clone = create_specialized_node (node, known_csts, known_contexts,
 			       known_aggs_to_agg_replacement_list (known_aggs),
 			       callers);
       info = IPA_NODE_REF (node);
@@ -3677,9 +4063,11 @@  decide_whether_version_node (struct cgra
       ret = true;
     }
   else
-    known_csts.release ();
+    {
+      known_csts.release ();
+      known_contexts.release ();
+    }
 
-  known_binfos.release ();
   return ret;
 }
 
@@ -3803,8 +4191,11 @@  ipcp_driver (void)
   edge_removal_hook_holder =
     symtab->add_edge_removal_hook (&ipcp_edge_removal_hook, NULL);
 
-  ipcp_values_pool = create_alloc_pool ("IPA-CP values",
-					sizeof (ipcp_value<tree>), 32);
+  ipcp_cst_values_pool = create_alloc_pool ("IPA-CP constant values",
+					    sizeof (ipcp_value<tree>), 32);
+  ipcp_poly_ctx_values_pool = create_alloc_pool
+    ("IPA-CP polymorphic contexts",
+     sizeof (ipcp_value<ipa_polymorphic_call_context>), 32);
   ipcp_sources_pool = create_alloc_pool ("IPA-CP value sources",
 					 sizeof (ipcp_value_source<tree>), 64);
   ipcp_agg_lattice_pool = create_alloc_pool ("IPA_CP aggregate lattices",
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-prop.c	2014-11-14 01:57:35.223696418 +0100
@@ -380,8 +380,14 @@  ipa_print_node_jump_functions_for_edge (
 	      fprintf (f, "\n");
 	    }
 	}
-      if (IPA_EDGE_REF (cs)->polymorphic_call_contexts)
-	ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i)->dump (f);
+
+      struct ipa_polymorphic_call_context *ctx
+	= ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i);
+      if (ctx && !ctx->useless_p ())
+	{
+	  fprintf (f, "         Context: ");
+	  ctx->dump (dump_file);
+	}
     }
 }
 
@@ -559,7 +565,8 @@  ipa_set_ancestor_jf (struct ipa_jump_fun
     type = NULL_TREE;
   if (type)
     type = TYPE_MAIN_VARIANT (type);
-  gcc_assert (!type_preserved || contains_polymorphic_type_p (type));
+  if (!type || !contains_polymorphic_type_p (type))
+    type_preserved = false;
   jfunc->type = IPA_JF_ANCESTOR;
   jfunc->value.ancestor.formal_id = formal_id;
   jfunc->value.ancestor.offset = offset;
@@ -2622,9 +2629,12 @@  combine_known_type_and_ancestor_jfs (str
     + ipa_get_jf_ancestor_offset (dst);
   combined_type = ipa_get_jf_ancestor_type (dst);
 
-  ipa_set_jf_known_type (dst, combined_offset,
-			 ipa_get_jf_known_type_base_type (src),
-			 combined_type);
+  if (combined_type)
+    ipa_set_jf_known_type (dst, combined_offset,
+			   ipa_get_jf_known_type_base_type (src),
+			   combined_type);
+  else
+    dst->type = IPA_JF_UNKNOWN;
 }
 
 /* Update the jump functions associated with call graph edge E when the call
@@ -2669,7 +2679,7 @@  update_jump_functions_after_inlining (st
 	      struct ipa_polymorphic_call_context ctx = *src_ctx;
 
 	      /* TODO: Make type preserved safe WRT contexts.  */
-	      if (!dst->value.ancestor.agg_preserved)
+	      if (!ipa_get_jf_ancestor_type_preserved (dst))
 		ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor);
 	      ctx.offset_by (dst->value.ancestor.offset);
 	      if (!ctx.useless_p ())
@@ -2678,6 +2688,7 @@  update_jump_functions_after_inlining (st
 					 count);
 		  dst_ctx = ipa_get_ith_polymorhic_call_context (args, i);
 		}
+	      dst_ctx->combine_with (ctx);
 	    }
 
 	  if (src->agg.items
@@ -2739,7 +2750,7 @@  update_jump_functions_after_inlining (st
 		  struct ipa_polymorphic_call_context ctx = *src_ctx;
 
 		  /* TODO: Make type preserved safe WRT contexts.  */
-		  if (!dst->value.ancestor.agg_preserved)
+		  if (!ipa_get_jf_pass_through_type_preserved (dst))
 		    ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor);
 		  if (!ctx.useless_p ())
 		    {
@@ -3152,41 +3163,24 @@  ipa_impossible_devirt_target (struct cgr
 /* Try to find a destination for indirect edge IE that corresponds to a virtual
    call based on a formal parameter which is described by jump function JFUNC
    and if it can be determined, make it direct and return the direct edge.
-   Otherwise, return NULL.  NEW_ROOT_INFO is the node info that JFUNC lattices
-   are relative to.  */
+   Otherwise, return NULL.  CTX describes the polymorphic context that the
+   parameter the call is based on brings along with it.  */
 
 static struct cgraph_edge *
 try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
 				   struct ipa_jump_func *jfunc,
-				   struct ipa_node_params *new_root_info,
-				   struct ipa_polymorphic_call_context *ctx_ptr)
+				   struct ipa_polymorphic_call_context ctx)
 {
-  tree binfo, target = NULL;
+  tree target = NULL;
   bool speculative = false;
-  bool updated = false;
 
   if (!flag_devirtualize)
     return NULL;
 
-  /* If this is call of a function parameter, restrict its type
-     based on knowlede of the context.  */
-  if (ctx_ptr && !ie->indirect_info->by_ref)
-    {
-      struct ipa_polymorphic_call_context ctx = *ctx_ptr;
-
-      ctx.offset_by (ie->indirect_info->offset);
-
-      if (ie->indirect_info->vptr_changed)
-	ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
-					  ie->indirect_info->otr_type);
-
-      updated = ie->indirect_info->context.combine_with
-		  (ctx, ie->indirect_info->otr_type);
-    }
+  gcc_assert (!ie->indirect_info->by_ref);
 
   /* Try to do lookup via known virtual table pointer value.  */
-  if (!ie->indirect_info->by_ref
-      && (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively))
+  if (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively)
     {
       tree vtable;
       unsigned HOST_WIDE_INT offset;
@@ -3217,67 +3211,44 @@  try_make_edge_direct_virtual_call (struc
 	}
     }
 
-  binfo = ipa_value_from_jfunc (new_root_info, jfunc);
-
-  if (binfo && TREE_CODE (binfo) != TREE_BINFO)
+  ipa_polymorphic_call_context ie_context (ie);
+  vec <cgraph_node *>targets;
+  bool final;
+
+  ctx.offset_by (ie->indirect_info->offset);
+  if (ie->indirect_info->vptr_changed)
+    ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
+				      ie->indirect_info->otr_type);
+  ctx.combine_with (ie_context, ie->indirect_info->otr_type);
+  targets = possible_polymorphic_call_targets
+    (ie->indirect_info->otr_type,
+     ie->indirect_info->otr_token,
+     ctx, &final);
+  if (final && targets.length () <= 1)
     {
-      struct ipa_polymorphic_call_context ctx (binfo,
-					       ie->indirect_info->otr_type,
-					       ie->indirect_info->offset);
-      updated |= ie->indirect_info->context.combine_with
-		  (ctx, ie->indirect_info->otr_type);
+      if (targets.length () == 1)
+	target = targets[0]->decl;
+      else
+	target = ipa_impossible_devirt_target (ie, NULL_TREE);
     }
-
-  if (updated)
+  else if (!target && flag_devirtualize_speculatively
+	   && !ie->speculative && ie->maybe_hot_p ())
     {
-      ipa_polymorphic_call_context context (ie);
-      vec <cgraph_node *>targets;
-      bool final;
-
-      targets = possible_polymorphic_call_targets
-		 (ie->indirect_info->otr_type,
-		  ie->indirect_info->otr_token,
-		  context, &final);
-      if (final && targets.length () <= 1)
+      cgraph_node *n;
+      n = try_speculative_devirtualization (ie->indirect_info->otr_type,
+					    ie->indirect_info->otr_token,
+					    ie->indirect_info->context);
+      if (n)
 	{
-	  if (targets.length () == 1)
-	    target = targets[0]->decl;
-	  else
-	    target = ipa_impossible_devirt_target (ie, NULL_TREE);
-	}
-      else if (!target && flag_devirtualize_speculatively
-	       && !ie->speculative && ie->maybe_hot_p ())
-	{
-	  cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type,
-							     ie->indirect_info->otr_token,
-							     ie->indirect_info->context);
-	  if (n)
-	    {
-	      target = n->decl;
-	      speculative = true;
-	    }
-	}
-     }
-
-  if (binfo && TREE_CODE (binfo) == TREE_BINFO)
-    {
-      binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset,
-				   ie->indirect_info->otr_type);
-      if (binfo)
-	{
-	  tree t = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
-						     binfo);
-	  if (t)
-	    {
-	      target = t;
-	      speculative = false;
-	    }
+	  target = n->decl;
+	  speculative = true;
 	}
     }
 
   if (target)
     {
-      if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
+      if (!possible_polymorphic_call_target_p
+	  (ie, cgraph_node::get_create (target)))
 	{
 	  if (speculative)
 	    return NULL;
@@ -3336,11 +3307,9 @@  update_indirect_edges_after_inlining (st
 	new_direct_edge = NULL;
       else if (ici->polymorphic)
 	{
-          ipa_polymorphic_call_context *ctx;
-          ctx = ipa_get_ith_polymorhic_call_context (top, param_index);
-	  new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc,
-							       new_root_info,
-							       ctx);
+          ipa_polymorphic_call_context ctx;
+	  ctx = ipa_context_from_jfunc (new_root_info, cs, param_index, jfunc);
+	  new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx);
 	}
       else
 	new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
@@ -3474,7 +3443,7 @@  propagate_controlled_uses (struct cgraph
 	    {
 	      struct cgraph_node *n;
 	      struct ipa_ref *ref;
-	      tree t = new_root_info->known_vals[src_idx];
+	      tree t = new_root_info->known_csts[src_idx];
 
 	      if (t && TREE_CODE (t) == ADDR_EXPR
 		  && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
@@ -3617,7 +3586,8 @@  ipa_free_node_params_substructures (stru
   free (info->lattices);
   /* Lattice values and their sources are deallocated with their alocation
      pool.  */
-  info->known_vals.release ();
+  info->known_csts.release ();
+  info->known_contexts.release ();
   memset (info, 0, sizeof (*info));
 }
 
@@ -3892,7 +3862,8 @@  ipa_free_all_structures_after_ipa_cp (vo
       ipa_free_all_edge_args ();
       ipa_free_all_node_params ();
       free_alloc_pool (ipcp_sources_pool);
-      free_alloc_pool (ipcp_values_pool);
+      free_alloc_pool (ipcp_cst_values_pool);
+      free_alloc_pool (ipcp_poly_ctx_values_pool);
       free_alloc_pool (ipcp_agg_lattice_pool);
       ipa_unregister_cgraph_hooks ();
       if (ipa_refdesc_pool)
@@ -3911,8 +3882,10 @@  ipa_free_all_structures_after_iinln (voi
   ipa_unregister_cgraph_hooks ();
   if (ipcp_sources_pool)
     free_alloc_pool (ipcp_sources_pool);
-  if (ipcp_values_pool)
-    free_alloc_pool (ipcp_values_pool);
+  if (ipcp_cst_values_pool)
+    free_alloc_pool (ipcp_cst_values_pool);
+  if (ipcp_poly_ctx_values_pool)
+    free_alloc_pool (ipcp_poly_ctx_values_pool);
   if (ipcp_agg_lattice_pool)
     free_alloc_pool (ipcp_agg_lattice_pool);
   if (ipa_refdesc_pool)
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-prop.h	2014-11-14 01:56:16.000000000 +0100
@@ -278,13 +278,14 @@  ipa_get_jf_pass_through_agg_preserved (s
   return jfunc->value.pass_through.agg_preserved;
 }
 
-/* Return the type_preserved flag of a pass through jump function JFUNC.  */
+/* Return true if pass through jump function JFUNC preserves type
+   information.  */
 
 static inline bool
 ipa_get_jf_pass_through_type_preserved (struct ipa_jump_func *jfunc)
 {
   gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
-  return jfunc->value.pass_through.type_preserved;
+  return jfunc->value.pass_through.agg_preserved;
 }
 
 /* Return the offset of an ancestor jump function JFUNC.  */
@@ -324,13 +325,13 @@  ipa_get_jf_ancestor_agg_preserved (struc
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return the type_preserved flag of an ancestor jump function JFUNC.  */
+/* Return true if ancestor jump function JFUNC presrves type information.  */
 
 static inline bool
 ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc)
 {
   gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
-  return jfunc->value.ancestor.type_preserved;
+  return jfunc->value.ancestor.agg_preserved;
 }
 
 /* Summary describing a single formal parameter.  */
@@ -363,9 +364,12 @@  struct ipa_node_params
   /* Only for versioned nodes this field would not be NULL,
      it points to the node that IPA cp cloned from.  */
   struct cgraph_node *ipcp_orig_node;
-  /* If this node is an ipa-cp clone, these are the known values that describe
-     what it has been specialized for.  */
-  vec<tree> known_vals;
+  /* If this node is an ipa-cp clone, these are the known constants that
+     describe what it has been specialized for.  */
+  vec<tree> known_csts;
+  /* If this node is an ipa-cp clone, these are the known polymorphic contexts
+     that describe what it has been specialized for.  */
+  vec<ipa_polymorphic_call_context> known_contexts;
   /* Whether the param uses analysis and jump function computation has already
      been performed.  */
   unsigned analysis_done : 1;
@@ -592,7 +596,7 @@  bool ipa_propagate_indirect_call_infos (
 /* Indirect edge and binfo processing.  */
 tree ipa_get_indirect_edge_target (struct cgraph_edge *ie,
 				   vec<tree> ,
-				   vec<tree> ,
+				   vec<ipa_polymorphic_call_context>,
 				   vec<ipa_agg_jump_function_p> );
 struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
 						    bool speculative = false);
@@ -615,7 +619,8 @@  void ipa_print_node_jump_functions (FILE
 void ipa_print_all_jump_functions (FILE * f);
 void ipcp_verify_propagated_values (void);
 
-extern alloc_pool ipcp_values_pool;
+extern alloc_pool ipcp_cst_values_pool;
+extern alloc_pool ipcp_poly_ctx_values_pool;
 extern alloc_pool ipcp_sources_pool;
 extern alloc_pool ipcp_agg_lattice_pool;
 
@@ -716,6 +721,10 @@  int ipa_get_param_decl_index (struct ipa
 tree ipa_value_from_jfunc (struct ipa_node_params *info,
 			   struct ipa_jump_func *jfunc);
 unsigned int ipcp_transform_function (struct cgraph_node *node);
+ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *,
+						     cgraph_edge *,
+						     int,
+						     ipa_jump_func *);
 void ipa_dump_param (FILE *, struct ipa_node_params *info, int i);
 bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec);
 ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *,
Index: src/gcc/ipa-inline-analysis.c
===================================================================
--- src.orig/gcc/ipa-inline-analysis.c	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-inline-analysis.c	2014-11-14 00:56:47.019557387 +0100
@@ -895,7 +895,8 @@  static void
 evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
 			      clause_t *clause_ptr,
 			      vec<tree> *known_vals_ptr,
-			      vec<tree> *known_binfos_ptr,
+			      vec<ipa_polymorphic_call_context>
+			      *known_contexts_ptr,
 			      vec<ipa_agg_jump_function_p> *known_aggs_ptr)
 {
   struct cgraph_node *callee = e->callee->ultimate_alias_target ();
@@ -907,12 +908,12 @@  evaluate_properties_for_edge (struct cgr
     *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition;
   if (known_vals_ptr)
     known_vals_ptr->create (0);
-  if (known_binfos_ptr)
-    known_binfos_ptr->create (0);
+  if (known_contexts_ptr)
+    known_contexts_ptr->create (0);
 
   if (ipa_node_params_vector.exists ()
       && !e->call_stmt_cannot_inline_p
-      && ((clause_ptr && info->conds) || known_vals_ptr || known_binfos_ptr))
+      && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
       struct ipa_node_params *parms_info;
       struct ipa_edge_args *args = IPA_EDGE_REF (e);
@@ -928,8 +929,8 @@  evaluate_properties_for_edge (struct cgr
 	known_vals.safe_grow_cleared (count);
       if (count && (info->conds || known_aggs_ptr))
 	known_aggs.safe_grow_cleared (count);
-      if (count && known_binfos_ptr)
-	known_binfos_ptr->safe_grow_cleared (count);
+      if (count && known_contexts_ptr)
+	known_contexts_ptr->safe_grow_cleared (count);
 
       for (i = 0; i < count; i++)
 	{
@@ -937,14 +938,16 @@  evaluate_properties_for_edge (struct cgr
 	  tree cst = ipa_value_from_jfunc (parms_info, jf);
 	  if (cst)
 	    {
-	      if (known_vals.exists () && TREE_CODE (cst) != TREE_BINFO)
+	      gcc_checking_assert (TREE_CODE (cst) != TREE_BINFO);
+	      if (known_vals.exists ())
 		known_vals[i] = cst;
-	      else if (known_binfos_ptr != NULL
-		       && TREE_CODE (cst) == TREE_BINFO)
-		(*known_binfos_ptr)[i] = cst;
 	    }
 	  else if (inline_p && !es->param[i].change_prob)
 	    known_vals[i] = error_mark_node;
+
+	  if (known_contexts_ptr)
+	    (*known_contexts_ptr)[i] = ipa_context_from_jfunc (parms_info, e,
+							       i, jf);
 	  /* TODO: When IPA-CP starts propagating and merging aggregate jump
 	     functions, use its knowledge of the caller too, just like the
 	     scalar case above.  */
@@ -2969,14 +2972,14 @@  make_pass_inline_parameters (gcc::contex
 }
 
 
-/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and
-   KNOWN_BINFOS.  */
+/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS,
+   KNOWN_CONTEXTS and KNOWN_AGGS.  */
 
 static bool
 estimate_edge_devirt_benefit (struct cgraph_edge *ie,
 			      int *size, int *time,
 			      vec<tree> known_vals,
-			      vec<tree> known_binfos,
+			      vec<ipa_polymorphic_call_context> known_contexts,
 			      vec<ipa_agg_jump_function_p> known_aggs)
 {
   tree target;
@@ -2984,12 +2987,12 @@  estimate_edge_devirt_benefit (struct cgr
   struct inline_summary *isummary;
   enum availability avail;
 
-  if (!known_vals.exists () && !known_binfos.exists ())
+  if (!known_vals.exists () && !known_contexts.exists ())
     return false;
   if (!flag_indirect_inlining)
     return false;
 
-  target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos,
+  target = ipa_get_indirect_edge_target (ie, known_vals, known_contexts,
 					 known_aggs);
   if (!target)
     return false;
@@ -3013,7 +3016,7 @@  estimate_edge_devirt_benefit (struct cgr
 /* Increase SIZE, MIN_SIZE (if non-NULL) and TIME for size and time needed to
    handle edge E with probability PROB.
    Set HINTS if edge may be devirtualized.
-   KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of the call
+   KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS describe context of the call
    site.  */
 
 static inline void
@@ -3021,7 +3024,7 @@  estimate_edge_size_and_time (struct cgra
 			     int *time,
 			     int prob,
 			     vec<tree> known_vals,
-			     vec<tree> known_binfos,
+			     vec<ipa_polymorphic_call_context> known_contexts,
 			     vec<ipa_agg_jump_function_p> known_aggs,
 			     inline_hints *hints)
 {
@@ -3031,7 +3034,7 @@  estimate_edge_size_and_time (struct cgra
   int cur_size;
   if (!e->callee
       && estimate_edge_devirt_benefit (e, &call_size, &call_time,
-				       known_vals, known_binfos, known_aggs)
+				       known_vals, known_contexts, known_aggs)
       && hints && e->maybe_hot_p ())
     *hints |= INLINE_HINT_indirect_call;
   cur_size = call_size * INLINE_SIZE_SCALE;
@@ -3047,9 +3050,8 @@  estimate_edge_size_and_time (struct cgra
 
 
 /* Increase SIZE, MIN_SIZE and TIME for size and time needed to handle all
-   calls in NODE.
-   POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of
-   the call site.  */
+   calls in NODE.  POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
+   describe context of the call site.  */
 
 static void
 estimate_calls_size_and_time (struct cgraph_node *node, int *size,
@@ -3057,7 +3059,7 @@  estimate_calls_size_and_time (struct cgr
 			      inline_hints *hints,
 			      clause_t possible_truths,
 			      vec<tree> known_vals,
-			      vec<tree> known_binfos,
+			      vec<ipa_polymorphic_call_context> known_contexts,
 			      vec<ipa_agg_jump_function_p> known_aggs)
 {
   struct cgraph_edge *e;
@@ -3074,14 +3076,14 @@  estimate_calls_size_and_time (struct cgr
 	      estimate_edge_size_and_time (e, size,
 					   es->predicate ? NULL : min_size,
 					   time, REG_BR_PROB_BASE,
-					   known_vals, known_binfos,
+					   known_vals, known_contexts,
 					   known_aggs, hints);
 	    }
 	  else
 	    estimate_calls_size_and_time (e->callee, size, min_size, time,
 					  hints,
 					  possible_truths,
-					  known_vals, known_binfos,
+					  known_vals, known_contexts,
 					  known_aggs);
 	}
     }
@@ -3093,14 +3095,14 @@  estimate_calls_size_and_time (struct cgr
 	estimate_edge_size_and_time (e, size,
 				     es->predicate ? NULL : min_size,
 				     time, REG_BR_PROB_BASE,
-				     known_vals, known_binfos, known_aggs,
+				     known_vals, known_contexts, known_aggs,
 				     hints);
     }
 }
 
 
 /* Estimate size and time needed to execute NODE assuming
-   POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS
+   POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
    information about NODE's arguments.  If non-NULL use also probability
    information present in INLINE_PARAM_SUMMARY vector.
    Additionally detemine hints determined by the context.  Finally compute
@@ -3112,7 +3114,7 @@  static void
 estimate_node_size_and_time (struct cgraph_node *node,
 			     clause_t possible_truths,
 			     vec<tree> known_vals,
-			     vec<tree> known_binfos,
+			     vec<ipa_polymorphic_call_context> known_contexts,
 			     vec<ipa_agg_jump_function_p> known_aggs,
 			     int *ret_size, int *ret_min_size, int *ret_time,
 			     inline_hints *ret_hints,
@@ -3189,7 +3191,7 @@  estimate_node_size_and_time (struct cgra
     hints |= INLINE_HINT_declared_inline;
 
   estimate_calls_size_and_time (node, &size, &min_size, &time, &hints, possible_truths,
-				known_vals, known_binfos, known_aggs);
+				known_vals, known_contexts, known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
   time = RDIV (time, INLINE_TIME_SCALE);
@@ -3212,13 +3214,14 @@  estimate_node_size_and_time (struct cgra
 
 /* Estimate size and time needed to execute callee of EDGE assuming that
    parameters known to be constant at caller of EDGE are propagated.
-   KNOWN_VALS and KNOWN_BINFOS are vectors of assumed known constant values
+   KNOWN_VALS and KNOWN_CONTEXTS are vectors of assumed known constant values
    and types for parameters.  */
 
 void
 estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
 				   vec<tree> known_vals,
-				   vec<tree> known_binfos,
+				   vec<ipa_polymorphic_call_context>
+				   known_contexts,
 				   vec<ipa_agg_jump_function_p> known_aggs,
 				   int *ret_size, int *ret_time,
 				   inline_hints *hints)
@@ -3227,7 +3230,7 @@  estimate_ipcp_clone_size_and_time (struc
 
   clause = evaluate_conditions_for_known_args (node, false, known_vals,
 					       known_aggs);
-  estimate_node_size_and_time (node, clause, known_vals, known_binfos,
+  estimate_node_size_and_time (node, clause, known_vals, known_contexts,
 			       known_aggs, ret_size, NULL, ret_time, hints, vNULL);
 }
 
@@ -3672,7 +3675,7 @@  do_estimate_edge_time (struct cgraph_edg
   struct cgraph_node *callee;
   clause_t clause;
   vec<tree> known_vals;
-  vec<tree> known_binfos;
+  vec<ipa_polymorphic_call_context> known_contexts;
   vec<ipa_agg_jump_function_p> known_aggs;
   struct inline_edge_summary *es = inline_edge_summary (edge);
   int min_size;
@@ -3681,9 +3684,9 @@  do_estimate_edge_time (struct cgraph_edg
 
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-				&clause, &known_vals, &known_binfos,
+				&clause, &known_vals, &known_contexts,
 				&known_aggs);
-  estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
+  estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
 			       known_aggs, &size, &min_size, &time, &hints, es->param);
 
   /* When we have profile feedback, we can quite safely identify hot
@@ -3697,7 +3700,7 @@  do_estimate_edge_time (struct cgraph_edg
     hints |= INLINE_HINT_known_hot;
 
   known_vals.release ();
-  known_binfos.release ();
+  known_contexts.release ();
   known_aggs.release ();
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
@@ -3728,7 +3731,7 @@  do_estimate_edge_size (struct cgraph_edg
   struct cgraph_node *callee;
   clause_t clause;
   vec<tree> known_vals;
-  vec<tree> known_binfos;
+  vec<ipa_polymorphic_call_context> known_contexts;
   vec<ipa_agg_jump_function_p> known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
@@ -3746,12 +3749,12 @@  do_estimate_edge_size (struct cgraph_edg
   /* Early inliner runs without caching, go ahead and do the dirty work.  */
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-				&clause, &known_vals, &known_binfos,
+				&clause, &known_vals, &known_contexts,
 				&known_aggs);
-  estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
+  estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
 			       known_aggs, &size, NULL, NULL, NULL, vNULL);
   known_vals.release ();
-  known_binfos.release ();
+  known_contexts.release ();
   known_aggs.release ();
   return size;
 }
@@ -3767,7 +3770,7 @@  do_estimate_edge_hints (struct cgraph_ed
   struct cgraph_node *callee;
   clause_t clause;
   vec<tree> known_vals;
-  vec<tree> known_binfos;
+  vec<ipa_polymorphic_call_context> known_contexts;
   vec<ipa_agg_jump_function_p> known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
@@ -3785,12 +3788,12 @@  do_estimate_edge_hints (struct cgraph_ed
   /* Early inliner runs without caching, go ahead and do the dirty work.  */
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-				&clause, &known_vals, &known_binfos,
+				&clause, &known_vals, &known_contexts,
 				&known_aggs);
-  estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
+  estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
 			       known_aggs, NULL, NULL, NULL, &hints, vNULL);
   known_vals.release ();
-  known_binfos.release ();
+  known_contexts.release ();
   known_aggs.release ();
   hints |= simple_edge_hints (edge);
   return hints;
Index: src/gcc/ipa-inline.h
===================================================================
--- src.orig/gcc/ipa-inline.h	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-inline.h	2014-11-14 00:56:47.023557387 +0100
@@ -223,7 +223,8 @@  void initialize_inline_failed (struct cg
 int estimate_time_after_inlining (struct cgraph_node *, struct cgraph_edge *);
 int estimate_size_after_inlining (struct cgraph_node *, struct cgraph_edge *);
 void estimate_ipcp_clone_size_and_time (struct cgraph_node *,
-					vec<tree>,  vec<tree>,
+					vec<tree>,
+					vec<ipa_polymorphic_call_context>,
 					vec<ipa_agg_jump_function_p>,
 					int *, int *, inline_hints *);
 int do_estimate_growth (struct cgraph_node *);
Index: src/gcc/testsuite/g++.dg/ipa/devirt-11.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-11.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-11.C	2014-11-14 00:56:47.023557387 +0100
@@ -1,5 +1,5 @@ 
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline -fno-devirtualize-speculatively" } */
+/* { dg-options "-O2 -fno-ipa-cp -fdump-ipa-inline -fno-devirtualize-speculatively" } */
 int baz ();
 struct A
 {
@@ -42,7 +42,5 @@  bar ()
   baz ();
   c + d;
 }
-/* While inlining function called once we should devirtualize a new call to fn3.
-   Because fn2 is already removed, we should not devirtualize.  */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target" "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: src/gcc/testsuite/g++.dg/ipa/devirt-21.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-21.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-21.C	2014-11-14 00:56:47.023557387 +0100
@@ -37,5 +37,5 @@  main()
 {
   class C c;
 }
-/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" { xfail *-*-* } } } */
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" } } */
 /* { dg-final { cleanup-ipa-dump "cp" } } */
Index: src/gcc/testsuite/g++.dg/ipa/devirt-10.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-10.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-10.C	2014-11-14 01:14:35.211598095 +0100
@@ -27,8 +27,6 @@  struct wxBufferedPaintDC  : public wxBuf
 void  OnPaint(wxPaintEvent & event) {
   wxBufferedPaintDC dc;
 }
-/* IPA-CP should really discover both cases, but for time being the second is handled by inliner.  */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline"  } } */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "cp"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "cp"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
 /* { dg-final { cleanup-ipa-dump "cp" } } */
Index: src/gcc/testsuite/g++.dg/ipa/devirt-41.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-41.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-41.C	2014-11-14 00:56:47.023557387 +0100
@@ -26,6 +26,6 @@  main()
 
    Because the type is in static storage, we know it won't change type in dostuff
    and from callstack we can tell that is is not in construction/destruction.  */
-/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump "Second type is base of first" "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: src/gcc/testsuite/g++.dg/ipa/devirt-43.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-43.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-43.C	2014-11-14 00:56:47.023557387 +0100
@@ -23,5 +23,5 @@  t(struct B *b)
    of type B.  This makes A fully specified and we know C::foo is unlikely.
    FIXME: We could most probably can devirtualize unconditonally because dereference of b in
    &b->a makes the type known.  GIMPLE does not represent this.  */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" { xfail *-*-* } } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: src/gcc/testsuite/g++.dg/ipa/devirt-44.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-44.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-44.C	2014-11-14 00:56:47.023557387 +0100
@@ -26,7 +26,7 @@  main()
 /* Here one invocation of foo is while type is in construction, while other is not.
    Check that we handle that.  */
 
-/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump "Second type is base of first" "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: src/gcc/cgraph.h
===================================================================
--- src.orig/gcc/cgraph.h	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/cgraph.h	2014-11-14 01:33:49.927642101 +0100
@@ -1376,9 +1376,12 @@  public:
 
   /* Return TRUE if context is fully useless.  */
   bool useless_p () const;
+  /* Return TRUE if this context conveys the same information as X.  */
+  bool equal_to (const ipa_polymorphic_call_context &x) const;
 
-  /* Dump human readable context to F.  */
-  void dump (FILE *f) const;
+  /* Dump human readable context to F.  If NEWLINE is true, it will be
+     terminated by a newline.  */
+  void dump (FILE *f, bool newline = true) const;
   void DEBUG_FUNCTION debug () const;
 
   /* LTO streaming.  */
Index: src/gcc/ipa-polymorphic-call.c
===================================================================
--- src.orig/gcc/ipa-polymorphic-call.c	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/ipa-polymorphic-call.c	2014-11-14 00:56:47.023557387 +0100
@@ -599,10 +599,11 @@  decl_maybe_in_construction_p (tree base,
   return false;
 }
 
-/* Dump human readable context to F.  */
+/* Dump human readable context to F.  If NEWLINE is true, it will be terminated
+   by a newline.  */
 
 void
-ipa_polymorphic_call_context::dump (FILE *f) const
+ipa_polymorphic_call_context::dump (FILE *f, bool newline) const
 {
   fprintf (f, "    ");
   if (invalid)
@@ -634,7 +635,8 @@  ipa_polymorphic_call_context::dump (FILE
 		   speculative_offset);
 	}
     }
-  fprintf(f, "\n");
+  if (newline)
+    fprintf(f, "\n");
 }
 
 /* Print context to stderr.  */
@@ -2130,3 +2132,47 @@  ipa_polymorphic_call_context::possible_d
   else if (in_poly_cdtor)
     maybe_in_construction = true;
 }
+
+/* Return TRUE if this context conveys the same information as OTHER.  */
+
+bool
+ipa_polymorphic_call_context::equal_to
+    (const ipa_polymorphic_call_context &x) const
+{
+  if (useless_p ())
+    return x.useless_p ();
+  if (invalid)
+    return x.invalid;
+  if (x.useless_p () || x.invalid)
+    return false;
+
+  if (outer_type)
+    {
+      if (!x.outer_type
+	  || !types_odr_comparable (outer_type, x.outer_type)
+	  || !types_same_for_odr (outer_type, x.outer_type)
+	  || offset != x.offset
+	  || maybe_in_construction != x.maybe_in_construction
+	  || maybe_derived_type != x.maybe_derived_type
+	  || dynamic != x.dynamic)
+	return false;
+    }
+  else if (x.outer_type)
+    return false;
+
+  if (speculative_outer_type)
+    {
+      if (!x.speculative_outer_type
+	  || !types_odr_comparable (speculative_outer_type,
+				    x.speculative_outer_type)
+	  || !types_same_for_odr  (speculative_outer_type,
+				    x.speculative_outer_type)
+	  || speculative_offset != x.speculative_offset
+	  || speculative_maybe_derived_type != x.speculative_maybe_derived_type)
+	return false;
+    }
+  else if (x.speculative_outer_type)
+    return false;
+
+  return true;
+}
Index: src/gcc/testsuite/g++.dg/ipa/devirt-24.C
===================================================================
--- src.orig/gcc/testsuite/g++.dg/ipa/devirt-24.C	2014-11-14 00:56:47.027557387 +0100
+++ src/gcc/testsuite/g++.dg/ipa/devirt-24.C	2014-11-14 00:56:47.023557387 +0100
@@ -36,7 +36,7 @@  C *b = new (C);
     sort(f, *b);
   }
 }
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" { xfail *-*-* } } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
 /* { dg-final { scan-ipa-dump-times "Aggregate passed by reference" 1 "cp"  } } */
 /* { dg-final { cleanup-ipa-dump "cp" } } */