Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -355,6 +355,12 @@ ipa_print_all_jump_functions (FILE *f)
 
 struct type_change_info
 {
+  /* The declaration or SSA_NAME pointer of the base that we are checking for
+     type change.  */
+  tree object;
+  /* If we actually can tell the type that the object has changed to, it is
+     stored in this field.  Otherwise it remains NULL_TREE.  */
+  tree known_current_type;
   /* Set to true if dynamic type change has been detected.  */
   bool type_maybe_changed;
 };
@@ -387,6 +393,49 @@ stmt_may_be_vtbl_ptr_store (gimple stmt)
   return true;
 }
 
+/* If STMT can be proved to be an assignment to the virtual method table
+   pointer of ANALYZED_OBJ and the type associated witht the new table
+   identified, return the type.  Otherwise return NULL_TREE.  */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
+{
+  tree lhs, t, obj;
+
+  if (!is_gimple_assign (stmt))
+    return NULL_TREE;
+
+  lhs = gimple_assign_lhs (stmt);
+
+  if (TREE_CODE (lhs) != COMPONENT_REF)
+    return NULL_TREE;
+   obj = lhs;
+
+   if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+     return NULL_TREE;
+
+   do
+     {
+       obj = TREE_OPERAND (obj, 0);
+     } while (TREE_CODE (obj) == COMPONENT_REF);
+   if (TREE_CODE (obj) == MEM_REF)
+     obj = TREE_OPERAND (obj, 0);
+   if (TREE_CODE (obj) == ADDR_EXPR)
+     obj = TREE_OPERAND (obj, 0);
+   if (obj != analyzed_obj)
+     return NULL_TREE;
+
+
+   t = gimple_assign_rhs1 (stmt);
+   if (TREE_CODE (t) != ADDR_EXPR)
+     return NULL_TREE;
+   t = get_base_address (TREE_OPERAND (t, 0));
+   if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
+     return NULL_TREE;
+
+   return DECL_CONTEXT (t);
+}
+
 /* Callbeck of walk_aliased_vdefs and a helper function for
    detect_type_change_anc to check whether a particular statement may modify
    the virtual table pointer, and if possible also determine the new type of
@@ -402,6 +451,8 @@ check_stmt_for_type_change (ao_ref *ao A
   if (stmt_may_be_vtbl_ptr_store (stmt))
     {
       tci->type_maybe_changed = true;
+      tci->known_current_type = extr_type_from_vtbl_ptr_store (stmt,
+							       tci->object);
       return true;
     }
   else
@@ -417,9 +468,10 @@ check_stmt_for_type_change (ao_ref *ao A
 
 static bool
 detect_type_change_anc (tree arg, gimple call, struct ipa_jump_func *jfunc,
-			HOST_WIDE_INT anc_offset ATTRIBUTE_UNUSED)
+			HOST_WIDE_INT anc_offset)
 {
   struct type_change_info tci;
+  tree obj;
   ao_ref ar;
 
   /* Const calls cannot call virtual methods through VMT and so type changes do
@@ -427,6 +479,15 @@ detect_type_change_anc (tree arg, gimple
   if (!gimple_vuse (call))
     return false;
 
+  obj = arg;
+  while (handled_component_p (obj))
+    obj = TREE_OPERAND (obj, 0);
+  if (TREE_CODE (obj) == MEM_REF)
+    obj = TREE_OPERAND (obj, 0);
+  if (TREE_CODE (obj) == ADDR_EXPR)
+    obj = TREE_OPERAND (obj, 0);
+  tci.object = obj;
+  tci.known_current_type = NULL_TREE;
   tci.type_maybe_changed = false;
 
   ao_ref_init (&ar, arg);
@@ -435,7 +496,31 @@ detect_type_change_anc (tree arg, gimple
   if (!tci.type_maybe_changed)
     return false;
 
-  jfunc->type = IPA_JF_UNKNOWN;
+  if (!tci.known_current_type)
+    jfunc->type = IPA_JF_UNKNOWN;
+  else if (anc_offset != 0)
+    {
+      tree new_binfo = get_binfo_at_offset (TYPE_BINFO (tci.known_current_type),
+					    anc_offset, TREE_TYPE (arg));
+
+      if (new_binfo)
+	{
+	  jfunc->type = IPA_JF_KNOWN_TYPE;
+	  jfunc->value.base_binfo = new_binfo;
+	}
+      else
+	jfunc->type = IPA_JF_UNKNOWN;
+    }
+  else
+    {
+      /* If an offset of a searched-for sub-object actually happens to be zero,
+	 we do not have to worry about non-artificialness.  Either there are no
+	 virtual methods anyway or there is a VMT pointer at offset zero and so
+	 artificial sub-objects start at higher offsets.  */
+      jfunc->type = IPA_JF_KNOWN_TYPE;
+      jfunc->value.base_binfo = TYPE_BINFO (tci.known_current_type);
+    }
+
   return true;
 }
 
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -69,3 +69,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-early-inlining -fno-inline"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -77,3 +77,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-inline"  } */
+/* { dg-options "-O3 -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -78,3 +78,8 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
===================================================================
--- icln.orig/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
@@ -1,7 +1,7 @@
 /* Verify that ipa-cp correctly detects the dynamic type of an object
    under construction when doing devirtualization.  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fno-inline"  } */
+/* { dg-options "-O3 -fno-inline -fdump-tree-optimized"  } */
 
 extern "C" void abort (void);
 
@@ -108,3 +108,6 @@ int main (int argc, char *argv[])
     bah ();
   return 0;
 }
+
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
