--- gcc/ipa-utils.h.jj	2015-03-30 12:18:16.578355523 +0200
+++ gcc/ipa-utils.h	2015-03-30 21:00:55.549241356 +0200
@@ -70,6 +70,7 @@ bool possible_polymorphic_call_target_p
 				         const ipa_polymorphic_call_context &,
 					 struct cgraph_node *);
 tree method_class_type (const_tree);
+tree inlined_polymorphic_ctor_dtor_block_p (tree, bool);
 bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
 tree vtable_pointer_value_to_binfo (const_tree);
 bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
--- gcc/ipa-polymorphic-call.c.jj	2015-03-30 12:18:16.494356880 +0200
+++ gcc/ipa-polymorphic-call.c	2015-03-30 21:02:56.895297752 +0200
@@ -513,6 +513,38 @@ contains_type_p (tree outer_type, HOST_W
 }
 
 
+/* Return a FUNCTION_DECL if BLOCK represents a constructor or destructor.
+   If CHECK_CLONES is true, also check for clones of ctor/dtors.  */
+
+tree
+inlined_polymorphic_ctor_dtor_block_p (tree block, bool check_clones)
+{
+  tree fn = BLOCK_ABSTRACT_ORIGIN (block);
+  if (fn == NULL || TREE_CODE (fn) != FUNCTION_DECL)
+    return NULL_TREE;
+
+  if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
+      || (!DECL_CXX_CONSTRUCTOR_P (fn) && !DECL_CXX_DESTRUCTOR_P (fn)))
+    {
+      if (!check_clones)
+	return NULL_TREE;
+
+      /* Watch for clones where we constant propagated the first
+	 argument (pointer to the instance).  */
+      fn = DECL_ABSTRACT_ORIGIN (fn);
+      if (!fn
+	  || TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
+	  || (!DECL_CXX_CONSTRUCTOR_P (fn) && !DECL_CXX_DESTRUCTOR_P (fn)))
+	return NULL_TREE;
+    }
+
+  if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
+    return NULL_TREE;
+
+  return fn;
+}
+
+
 /* We know that the instance is stored in variable or parameter
    (not dynamically allocated) and we want to disprove the fact
    that it may be in construction at invocation of CALL.
@@ -550,30 +582,11 @@ decl_maybe_in_construction_p (tree base,
       && flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
     return false;
 
+  bool check_clones = !base || is_global_var (base);
   for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
        block = BLOCK_SUPERCONTEXT (block))
-    if (BLOCK_ABSTRACT_ORIGIN (block)
-	&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
+    if (tree fn = inlined_polymorphic_ctor_dtor_block_p (block, check_clones))
       {
-	tree fn = BLOCK_ABSTRACT_ORIGIN (block);
-
-	if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
-	    || (!DECL_CXX_CONSTRUCTOR_P (fn)
-		&& !DECL_CXX_DESTRUCTOR_P (fn)))
-	  {
-	    /* Watch for clones where we constant propagated the first
-	       argument (pointer to the instance).  */
-	    fn = DECL_ABSTRACT_ORIGIN (fn);
-	    if (!fn
-		|| (base && !is_global_var (base))
-	        || TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
-		|| (!DECL_CXX_CONSTRUCTOR_P (fn)
-		    && !DECL_CXX_DESTRUCTOR_P (fn)))
-	      continue;
-	  }
-	if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
-	  continue;
-
 	tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (fn)));
 
 	if (!outer_type || !types_odr_comparable (type, outer_type))
@@ -1163,15 +1176,7 @@ noncall_stmt_may_be_vtbl_ptr_store (gimp
        block = BLOCK_SUPERCONTEXT (block))
     if (BLOCK_ABSTRACT_ORIGIN (block)
 	&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
-      {
-	tree fn = BLOCK_ABSTRACT_ORIGIN (block);
-
-	if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
-	  return false;
-	return (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
-		&& (DECL_CXX_CONSTRUCTOR_P (fn)
-		    || DECL_CXX_DESTRUCTOR_P (fn)));
-      }
+      return inlined_polymorphic_ctor_dtor_block_p (block, false);
   return (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
 	  && (DECL_CXX_CONSTRUCTOR_P (current_function_decl)
 	      || DECL_CXX_DESTRUCTOR_P (current_function_decl)));
--- gcc/ipa-prop.c.jj	2015-02-26 13:23:51.000000000 +0100
+++ gcc/ipa-prop.c	2015-03-30 21:03:40.681595669 +0200
@@ -715,18 +715,8 @@ param_type_may_change_p (tree function,
 	  /* Walk the inline stack and watch out for ctors/dtors.  */
 	  for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
 	       block = BLOCK_SUPERCONTEXT (block))
-	    if (BLOCK_ABSTRACT_ORIGIN (block)
-	        && TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
-	      {
-		tree fn = BLOCK_ABSTRACT_ORIGIN (block);
-
-		if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
-		  continue;
-		if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
-		    && (DECL_CXX_CONSTRUCTOR_P (fn)
-		        || DECL_CXX_DESTRUCTOR_P (fn)))
-		  return true;
-	      }
+	    if (inlined_polymorphic_ctor_dtor_block_p (block, false))
+	      return true;
 	  return false;
 	}
     }
--- gcc/tree-ssa-live.c.jj	2015-03-30 12:18:16.545356056 +0200
+++ gcc/tree-ssa-live.c	2015-03-30 21:00:55.551241324 +0200
@@ -76,6 +76,10 @@ along with GCC; see the file COPYING3.
 #include "diagnostic-core.h"
 #include "debug.h"
 #include "tree-ssa.h"
+#include "lto-streamer.h"
+#include "ipa-ref.h"
+#include "cgraph.h"
+#include "ipa-utils.h"
 
 #ifdef ENABLE_CHECKING
 static void  verify_live_on_entry (tree_live_info_p);
@@ -509,12 +513,29 @@ mark_scope_block_unused (tree scope)
    done by the inliner.  */
 
 static bool
-remove_unused_scope_block_p (tree scope)
+remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
 {
   tree *t, *next;
   bool unused = !TREE_USED (scope);
   int nsubblocks = 0;
 
+  /* For ipa-polymorphic-call.c purposes, preserve blocks:
+     1) with BLOCK_ABSTRACT_ORIGIN of a ctor/dtor or their clones  */
+  if (inlined_polymorphic_ctor_dtor_block_p (scope, true))
+    {
+      in_ctor_dtor_block = true;
+      unused = false;
+    }
+  /* 2) inside such blocks, the outermost block with BLOCK_ABSTRACT_ORIGIN
+     being a FUNCTION_DECL.  */
+  else if (in_ctor_dtor_block
+	   && BLOCK_ABSTRACT_ORIGIN (scope)
+	   && TREE_CODE (BLOCK_ABSTRACT_ORIGIN (scope)) == FUNCTION_DECL)
+    {
+      in_ctor_dtor_block = false;
+      unused = false;
+    }
+
   for (t = &BLOCK_VARS (scope); *t; t = next)
     {
       next = &DECL_CHAIN (*t);
@@ -594,7 +615,7 @@ remove_unused_scope_block_p (tree scope)
     }
 
   for (t = &BLOCK_SUBBLOCKS (scope); *t ;)
-    if (remove_unused_scope_block_p (*t))
+    if (remove_unused_scope_block_p (*t, in_ctor_dtor_block))
       {
 	if (BLOCK_SUBBLOCKS (*t))
 	  {
@@ -959,7 +980,7 @@ remove_unused_locals (void)
       cfun->local_decls->truncate (dstidx);
     }
 
-  remove_unused_scope_block_p (DECL_INITIAL (current_function_decl));
+  remove_unused_scope_block_p (DECL_INITIAL (current_function_decl), false);
   clear_unused_block_pointer ();
 
   BITMAP_FREE (usedvars);
--- gcc/testsuite/g++.dg/ubsan/pr65610.C.jj	2015-03-30 21:00:55.552241308 +0200
+++ gcc/testsuite/g++.dg/ubsan/pr65610.C	2015-03-30 21:00:55.552241308 +0200
@@ -0,0 +1,57 @@
+// PR ipa/65610
+// { dg-do compile }
+// { dg-options "-std=c++11 -fsanitize=undefined -fno-sanitize=vptr -fcompare-debug" }
+
+class A;
+class B {};
+enum C { D };
+class E;
+class F;
+class G;
+class H
+{
+  F m1 (const A &t) const;
+  G m2 () const;
+};
+class G {};
+template <class S, class T>
+class I;
+template <class S, class T>
+class J
+{
+  friend class I <S,T>;
+  J <S,T> *j;
+};
+template <class S, class T>
+struct I
+{
+  virtual ~I ();
+  virtual void m3 (void *p) {}
+  J <S,T> *i;
+  void m4 (J <S,T>*& t);
+};
+template <class S, class T>
+void I <S,T>::m4 (J <S,T> * &t)
+{
+  m4 (t->j);
+  m3 (t);
+}
+template <class S, class T>
+I <S,T>::~I ()
+{
+  m4 (i);
+}
+struct F
+{
+  explicit inline F (C v);
+  inline ~F ();
+  I <B, E> f;
+};
+inline F::F (C v) {}
+inline F::~F () {}
+F H::m1 (const A &t) const
+{
+  F q (D);
+  G r = m2 ();
+  return q;
+}
