diff mbox

Strenghten assumption about dynamic type changes (placement new)

Message ID 20140704213952.GA28252@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka July 4, 2014, 9:39 p.m. UTC
> On 07/02/2014 01:18 PM, Jan Hubicka wrote:
> >We propagate types from places we know instances are created across pointers
> >passed to functions.  Once non-POD type is created at a given memory location,
> >one can not change its type by placement_new into something else.
> 
> Hmm.  If the memory location is untyped (i.e. from malloc) or a
> character array, or a union, you can indeed destroy an object of one
> type and create an object of a different type in that location.
> 
> >Jason, this assumes that one can not destroy the type and re-construct same
> >type at the same spot.
> 
> That is an invalid assumption; you can destroy one object and
> construct a new one in the same location.  Doing it within a method
> would be unusual, but I don't think there's a rule against it.
Hi,
this is updated patch that does the legwork needed to prove we are not
re-constructing an object in the same location as we created previous one.
Currenlty we track only objects places in declarations, but I would really
like to understand how precisely the rules of the game differs when the
object lives in dynamically allocated memory - i.e. I work out the dynamic
type by spotting either virtual table store or constructor call.

Bootstrapped/regtested x86_64-linux, will commit it after bit more
testing.

	* cgraph.c (cgraph_create_indirect_edge): Update call of
	get_polymorphic_call_info.
	* ipa-utils.h (get_polymorphic_call_info): Add parameter CALL.
	(possible_polymorphic_call_targets): Add parameter call.
	(decl_maybe_in_construction_p): New predicate.
	(get_polymorphic_call_info): Add parameter call;
	use decl_maybe_in_construction_p.
	* gimple-fold.c (fold_gimple_assign): Update use of
	possible_polymorphic_call_targets.
	(gimple_fold_call): Likewise.
	* ipa-prop.c: Inlcude calls.h
	(ipa_binfo_from_known_type_jfunc): Check that known type is record.
	(param_type_may_change_p): New predicate.
	(detect_type_change_from_memory_writes): Break out from ...
	(detect_type_change): ... this one; use 
	param_type_may_change_p.
	(detect_type_change_ssa): Use param_type_may_change_p.
	(compute_known_type_jump_func): Use decl_maybe_in_construction_p.

	* g++.dg/ipa/devirt-26.C: Update testcase.
	* g++.dg/ipa/imm-devirt-1.C: Update testcase.
	* g++.dg/ipa/imm-devirt-2.C: Update testcase.

Comments

Andreas Schwab July 7, 2014, 8 a.m. UTC | #1
Jan Hubicka <hubicka@ucw.cz> writes:

> 	* cgraph.c (cgraph_create_indirect_edge): Update call of
> 	get_polymorphic_call_info.
> 	* ipa-utils.h (get_polymorphic_call_info): Add parameter CALL.
> 	(possible_polymorphic_call_targets): Add parameter call.
> 	(decl_maybe_in_construction_p): New predicate.
> 	(get_polymorphic_call_info): Add parameter call;
> 	use decl_maybe_in_construction_p.
> 	* gimple-fold.c (fold_gimple_assign): Update use of
> 	possible_polymorphic_call_targets.
> 	(gimple_fold_call): Likewise.
> 	* ipa-prop.c: Inlcude calls.h
> 	(ipa_binfo_from_known_type_jfunc): Check that known type is record.
> 	(param_type_may_change_p): New predicate.
> 	(detect_type_change_from_memory_writes): Break out from ...
> 	(detect_type_change): ... this one; use 
> 	param_type_may_change_p.
> 	(detect_type_change_ssa): Use param_type_may_change_p.
> 	(compute_known_type_jump_func): Use decl_maybe_in_construction_p.

This breaks g++.dg/ipa/pr61085.C on ia64.

(gdb) bt
#0  0xa000000000040721 in __kernel_syscall_via_break ()
#1  0x20000000004331d0 in *__GI_raise (sig=<optimized out>)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
#2  0x2000000000435ab0 in *__GI_abort () at abort.c:92
#3  0x40000000000009e0 in C::m_virt (this=0x4000000000000760 <main()+96>)
    at /usr/local/gcc/gcc-20140707/gcc/testsuite/g++.dg/ipa/pr61085.C:26
#4  0x4000000000000760 in m_foo (this=0x600ffffffffeee40)
    at /usr/local/gcc/gcc-20140707/gcc/testsuite/g++.dg/ipa/pr61085.C:20
#5  ~B (__vtt_parm=<optimized out>, this=0x600ffffffffeee40, 
    __in_chrg=<optimized out>)
    at /usr/local/gcc/gcc-20140707/gcc/testsuite/g++.dg/ipa/pr61085.C:14
#6  ~C (this=0x600ffffffffeee40, __in_chrg=<optimized out>, 
    __vtt_parm=<optimized out>)
    at /usr/local/gcc/gcc-20140707/gcc/testsuite/g++.dg/ipa/pr61085.C:24
#7  main ()
    at /usr/local/gcc/gcc-20140707/gcc/testsuite/g++.dg/ipa/pr61085.C:32

Andreas.
Andreas Schwab July 7, 2014, 8:04 a.m. UTC | #2
Jan Hubicka <hubicka@ucw.cz> writes:

> Index: testsuite/g++.dg/ipa/imm-devirt-2.C
> ===================================================================
> --- testsuite/g++.dg/ipa/imm-devirt-2.C	(revision 212278)
> +++ testsuite/g++.dg/ipa/imm-devirt-2.C	(working copy)
> @@ -1,7 +1,7 @@
>  /* Verify that virtual calls are folded even early inlining puts them into one
>     function with the definition.  */
>  /* { dg-do run } */
> -/* { dg-options "-O2 -fdump-tree-fre1-details"  } */
> +/* { dg-options "-O2 -fdump-tree-einline"  } */
>  
>  extern "C" void abort (void);
>  
> @@ -91,5 +91,6 @@ int main (int argc, char *argv[])
>    return 0;
>  }
>  
> -/* { dg-final { scan-tree-dump "converting indirect call to function" "fre1"  } } */
> -/* { dg-final { cleanup-tree-dump "fre1" } } */
> +/* We fold into thunk of C. Eventually we should inline the thunk.  */
> +/* { dg-final { scan-tree-dump "C::_ZThn24_N1C3fooEi (" "einline"  } } */

FAIL: g++.dg/ipa/imm-devirt-2.C  -std=gnu++11  scan-tree-dump einline "C::_ZThn24_N1C3fooEi \\("

Andreas.
diff mbox

Patch

Index: cgraph.c
===================================================================
--- cgraph.c	(revision 212278)
+++ cgraph.c	(working copy)
@@ -967,7 +967,7 @@  cgraph_create_indirect_edge (struct cgra
       get_polymorphic_call_info (caller->decl,
 				 target,
 				 &otr_type, &otr_token,
-				 &context);
+				 &context, call_stmt);
 
       /* Only record types can have virtual calls.  */
       gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);
Index: testsuite/g++.dg/ipa/devirt-26.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-26.C	(revision 212278)
+++ testsuite/g++.dg/ipa/devirt-26.C	(working copy)
@@ -1,5 +1,5 @@ 
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-ipa-devirt-details"  } */
+/* { dg-options "-O3 -fdump-tree-ccp1"  } */
 struct A
  {
    int a;
@@ -23,7 +23,6 @@  int test(void)
   return d->foo()+b->foo();
 }
 /* The call to b->foo() is perfectly devirtualizable because C can not be in construction
-   when &c was used, but we can not analyze that so far.  Test that we at least speculate
-   that type is in the construction.  */
-/* { dg-final { scan-ipa-dump "speculatively devirtualizing" "devirt"  } } */
-/* { dg-final { cleanup-ipa-dump "devirt" } } */
+   when &c was used.  */
+/* { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "ccp1"  } } */
+/* { dg-final { cleanup-tree-dump "ccp1" } } */
Index: testsuite/g++.dg/ipa/imm-devirt-1.C
===================================================================
--- testsuite/g++.dg/ipa/imm-devirt-1.C	(revision 212278)
+++ testsuite/g++.dg/ipa/imm-devirt-1.C	(working copy)
@@ -1,7 +1,7 @@ 
 /* Verify that virtual calls are folded even early inlining puts them into one
    function with the definition.  */
 /* { dg-do run } */
-/* { dg-options "-O2 -fdump-tree-fre1-details"  } */
+/* { dg-options "-O2 -fdump-tree-einline"  } */
 
 extern "C" void abort (void);
 
@@ -58,5 +58,10 @@  int main (int argc, char *argv[])
   return 0;
 }
 
-/* { dg-final { scan-tree-dump "converting indirect call to function virtual int B::foo" "fre1"  } } */
-/* { dg-final { cleanup-tree-dump "fre1" } } */
+/* middleman_2 gets early inlined and the virtual call should get turned to
+   a direct call.  */
+/* { dg-final { scan-tree-dump "Inlining int middleman_1" "einline"  } } */
+/* { dg-final { scan-tree-dump "Inlining int middleman_2" "einline"  } } */
+/* { dg-final { scan-tree-dump "B::foo (" "einline"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 2 "einline"  } } */
+/* { dg-final { cleanup-tree-dump "einline" } } */
Index: testsuite/g++.dg/ipa/imm-devirt-2.C
===================================================================
--- testsuite/g++.dg/ipa/imm-devirt-2.C	(revision 212278)
+++ testsuite/g++.dg/ipa/imm-devirt-2.C	(working copy)
@@ -1,7 +1,7 @@ 
 /* Verify that virtual calls are folded even early inlining puts them into one
    function with the definition.  */
 /* { dg-do run } */
-/* { dg-options "-O2 -fdump-tree-fre1-details"  } */
+/* { dg-options "-O2 -fdump-tree-einline"  } */
 
 extern "C" void abort (void);
 
@@ -91,5 +91,6 @@  int main (int argc, char *argv[])
   return 0;
 }
 
-/* { dg-final { scan-tree-dump "converting indirect call to function" "fre1"  } } */
-/* { dg-final { cleanup-tree-dump "fre1" } } */
+/* We fold into thunk of C. Eventually we should inline the thunk.  */
+/* { dg-final { scan-tree-dump "C::_ZThn24_N1C3fooEi (" "einline"  } } */
+/* { dg-final { cleanup-tree-dump "einline" } } */
Index: ipa-utils.h
===================================================================
--- ipa-utils.h	(revision 212278)
+++ ipa-utils.h	(working copy)
@@ -87,9 +87,11 @@  bool possible_polymorphic_call_target_p
 tree method_class_type (const_tree);
 tree get_polymorphic_call_info (tree, tree, tree *,
 				HOST_WIDE_INT *,
-				ipa_polymorphic_call_context *);
+				ipa_polymorphic_call_context *,
+				gimple call = NULL);
 bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *,
 					       tree, tree, HOST_WIDE_INT);
+bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
 tree vtable_pointer_value_to_binfo (const_tree);
 bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
 bool contains_polymorphic_type_p (const_tree);
@@ -125,7 +127,8 @@  possible_polymorphic_call_targets (struc
 /* Same as above but taking OBJ_TYPE_REF as an parameter.  */
 
 inline vec <cgraph_node *>
-possible_polymorphic_call_targets (tree call,
+possible_polymorphic_call_targets (tree ref,
+				   gimple call,
 				   bool *final = NULL,
 				   void **cache_token = NULL)
 {
@@ -134,11 +137,11 @@  possible_polymorphic_call_targets (tree
   ipa_polymorphic_call_context context;
 
   get_polymorphic_call_info (current_function_decl,
-			     call,
-			     &otr_type, &otr_token, &context);
-  return possible_polymorphic_call_targets (obj_type_ref_class (call),
+			     ref,
+			     &otr_type, &otr_token, &context, call);
+  return possible_polymorphic_call_targets (obj_type_ref_class (ref),
 					    tree_to_uhwi
-					      (OBJ_TYPE_REF_TOKEN (call)),
+					      (OBJ_TYPE_REF_TOKEN (ref)),
 					    context,
 					    final, cache_token);
 }
Index: ipa-devirt.c
===================================================================
--- ipa-devirt.c	(revision 212278)
+++ ipa-devirt.c	(working copy)
@@ -1438,6 +1438,99 @@  vtable_pointer_value_to_binfo (const_tre
 					 offset, vtable);
 }
 
+/* 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.
+
+   For the variable to be in construction we actually need to
+   be in constructor of corresponding global variable or
+   the inline stack of CALL must contain the constructor.
+   Check this condition.  This check works safely only before
+   IPA passes, because inline stacks may become out of date
+   later.  */
+
+bool
+decl_maybe_in_construction_p (tree base, tree outer_type,
+			      gimple call, tree function)
+{
+  outer_type = TYPE_MAIN_VARIANT (outer_type);
+  gcc_assert (DECL_P (base));
+
+  /* After inlining the code unification optimizations may invalidate
+     inline stacks.  Also we need to give up on global variables after
+     IPA, because addresses of these may have been propagated to their
+     constructors.  */
+  if (DECL_STRUCT_FUNCTION (function)->after_inlining)
+    return true;
+
+  /* Pure functions can not do any changes on the dynamic type;
+     that require writting to memory.  */
+  if (!auto_var_in_fn_p (base, function)
+      && flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
+    return false;
+
+  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 (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
+		|| !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;
+
+	/* FIXME: this can go away once we have ODR types equivalency on
+	   LTO level.  */
+	if (in_lto_p && !polymorphic_type_binfo_p (TYPE_BINFO (outer_type)))
+	  return true;
+	tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (fn)));
+	if (types_same_for_odr (type, outer_type))
+	  return true;
+      }
+
+  if (TREE_CODE (base) == VAR_DECL
+      && is_global_var (base))
+    {
+      if (TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
+	  || (!DECL_CXX_CONSTRUCTOR_P (function)
+	      || !DECL_CXX_DESTRUCTOR_P (function)))
+	{
+	  if (!DECL_ABSTRACT_ORIGIN (function))
+	    return false;
+	  /* Watch for clones where we constant propagated the first
+	     argument (pointer to the instance).  */
+	  function = DECL_ABSTRACT_ORIGIN (function);
+	  if (!function
+	      || TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
+	      || (!DECL_CXX_CONSTRUCTOR_P (function)
+		  || !DECL_CXX_DESTRUCTOR_P (function)))
+	    return false;
+	}
+      /* FIXME: this can go away once we have ODR types equivalency on
+	 LTO level.  */
+      if (in_lto_p && !polymorphic_type_binfo_p (TYPE_BINFO (outer_type)))
+	return true;
+      tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (function)));
+      if (types_same_for_odr (type, outer_type))
+	return true;
+    }
+  return false;
+}
+
 /* Proudce polymorphic call context for call method of instance
    that is located within BASE (that is assumed to be a decl) at OFFSET. */
 
@@ -1490,6 +1583,8 @@  get_polymorphic_call_info_from_invariant
 
 /* Given REF call in FNDECL, determine class of the polymorphic
    call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT.
+   CALL is optional argument giving the actual statement (usually call) where
+   the context is used.
    Return pointer to object described by the context  */
 
 tree
@@ -1497,7 +1592,8 @@  get_polymorphic_call_info (tree fndecl,
 			   tree ref,
 			   tree *otr_type,
 			   HOST_WIDE_INT *otr_token,
-			   ipa_polymorphic_call_context *context)
+			   ipa_polymorphic_call_context *context,
+			   gimple call)
 {
   tree base_pointer;
   *otr_type = obj_type_ref_class (ref);
@@ -1561,6 +1657,12 @@  get_polymorphic_call_info (tree fndecl,
 		    }
 		  get_polymorphic_call_info_for_decl (context, base,
 						      context->offset + offset2);
+		  if (context->maybe_in_construction && call)
+		    context->maybe_in_construction
+		     = decl_maybe_in_construction_p (base,
+						     context->outer_type,
+						     call,
+						     current_function_decl);
 		  return NULL;
 		}
 	      else
Index: gimple-fold.c
===================================================================
--- gimple-fold.c	(revision 212278)
+++ gimple-fold.c	(working copy)
@@ -376,7 +376,7 @@  fold_gimple_assign (gimple_stmt_iterator
 	      {
 		bool final;
 		vec <cgraph_node *>targets
-		  = possible_polymorphic_call_targets (val, &final);
+		  = possible_polymorphic_call_targets (val, stmt, &final);
 		if (final && targets.length () <= 1 && dbg_cnt (devirt))
 		  {
 		    tree fndecl;
@@ -1125,7 +1125,7 @@  gimple_fold_call (gimple_stmt_iterator *
 	{
 	  bool final;
 	  vec <cgraph_node *>targets
-	    = possible_polymorphic_call_targets (callee, &final);
+	    = possible_polymorphic_call_targets (callee, stmt, &final);
 	  if (final && targets.length () <= 1 && dbg_cnt (devirt))
 	    {
 	      tree lhs = gimple_call_lhs (stmt);
Index: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 212278)
+++ ipa-prop.c	(working copy)
@@ -62,6 +62,7 @@  along with GCC; see the file COPYING3.
 #include "dbgcnt.h"
 #include "domwalk.h"
 #include "builtins.h"
+#include "calls.h"
 
 /* Intermediate information that we get from alias analysis about a particular
    parameter in a particular basic_block.  When a parameter or the memory it
@@ -552,7 +553,11 @@  ipa_set_ancestor_jf (struct ipa_jump_fun
 tree
 ipa_binfo_from_known_type_jfunc (struct ipa_jump_func *jfunc)
 {
+  if (!RECORD_OR_UNION_TYPE_P (jfunc->value.known_type.base_type))
+    return NULL_TREE;
+
   tree base_binfo = TYPE_BINFO (jfunc->value.known_type.base_type);
+
   if (!base_binfo)
     return NULL_TREE;
   return get_binfo_at_offset (base_binfo,
@@ -731,18 +736,84 @@  check_stmt_for_type_change (ao_ref *ao A
     return false;
 }
 
+/* See if ARG is PARAM_DECl describing instance passed by pointer
+   or reference in FUNCTION.  Return false if the dynamic type may change
+   in between beggining of the function until CALL is invoked.
+
+   Generally functions are not allowed to change type of such instances,
+   but they call destructors.  We assume that methods can not destroy the THIS
+   pointer.  Also as a special cases, constructor and destructors may change
+   type of the THIS pointer.  */
 
+static bool
+param_type_may_change_p (tree function, tree arg, gimple call)
+{
+  /* Pure functions can not do any changes on the dynamic type;
+     that require writting to memory.  */
+  if (flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
+    return false;
+  /* We need to check if we are within inlined consturctor
+     or destructor (ideally we would have way to check that the
+     inline cdtor is actually working on ARG, but we don't have
+     easy tie on this, so punt on all non-pure cdtors.
+     We may also record the types of cdtors and once we know type
+     of the instance match them.
+
+     Also code unification optimizations may merge calls from
+     different blocks making return values unreliable.  So
+     do nothing during late optimization.  */
+  if (DECL_STRUCT_FUNCTION (function)->after_inlining)
+    return true;
+  if (TREE_CODE (arg) == SSA_NAME
+      && SSA_NAME_IS_DEFAULT_DEF (arg)
+      && TREE_CODE (SSA_NAME_VAR (arg)) == PARM_DECL)
+    {
+      /* Normal (non-THIS) argument.  */
+      if ((SSA_NAME_VAR (arg) != DECL_ARGUMENTS (function)
+	   || TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE)
+	  /* THIS pointer of an method - here we we want to watch constructors
+	     and destructors as those definitely may change the dynamic
+	     type.  */
+	  || (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE
+	      && !DECL_CXX_CONSTRUCTOR_P (function)
+	      && !DECL_CXX_DESTRUCTOR_P (function)
+	      && (SSA_NAME_VAR (arg) == DECL_ARGUMENTS (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;
+	      }
+	  return false;
+	}
+    }
+  return true;
+}
 
 /* Detect whether the dynamic type of ARG of COMP_TYPE has changed (before
    callsite CALL) by looking for assignments to its virtual table pointer.  If
    it is, return true and fill in the jump function JFUNC with relevant type
    information or set it to unknown.  ARG is the object itself (not a pointer
    to it, unless dereferenced).  BASE is the base of the memory access as
-   returned by get_ref_base_and_extent, as is the offset.  */
+   returned by get_ref_base_and_extent, as is the offset. 
+
+   This is helper function for detect_type_change and detect_type_change_ssa
+   that does the heavy work which is usually unnecesary.  */
 
 static bool
-detect_type_change (tree arg, tree base, tree comp_type, gimple call,
-		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
+detect_type_change_from_memory_writes (tree arg, tree base, tree comp_type,
+				       gimple call, struct ipa_jump_func *jfunc,
+				       HOST_WIDE_INT offset)
 {
   struct type_change_info tci;
   ao_ref ao;
@@ -753,25 +824,6 @@  detect_type_change (tree arg, tree base,
 
   comp_type = TYPE_MAIN_VARIANT (comp_type);
 
-  if (!flag_devirtualize)
-    return false;
-
-  /* C++ methods are not allowed to change THIS pointer unless they
-     are constructors or destructors.  */
-  if (TREE_CODE	(base) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
-      && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
-      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (base, 0))) == PARM_DECL
-      && TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
-      && !DECL_CXX_CONSTRUCTOR_P (current_function_decl)
-      && !DECL_CXX_DESTRUCTOR_P (current_function_decl)
-      && (SSA_NAME_VAR (TREE_OPERAND (base, 0))
-	  == DECL_ARGUMENTS (current_function_decl)))
-    {
-      gcc_assert (comp_type);
-      return false;
-    }
-
   /* Const calls cannot call virtual methods through VMT and so type changes do
      not matter.  */
   if (!flag_devirtualize || !gimple_vuse (call)
@@ -809,6 +861,28 @@  detect_type_change (tree arg, tree base,
   return true;
 }
 
+/* Detect whether the dynamic type of ARG of COMP_TYPE may have changed.
+   If it is, return true and fill in the jump function JFUNC with relevant type
+   information or set it to unknown.  ARG is the object itself (not a pointer
+   to it, unless dereferenced).  BASE is the base of the memory access as
+   returned by get_ref_base_and_extent, as is the offset.  */
+
+static bool
+detect_type_change (tree arg, tree base, tree comp_type, gimple call,
+		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
+{
+  if (!flag_devirtualize)
+    return false;
+
+  if (TREE_CODE	(base) == MEM_REF
+      && !param_type_may_change_p (current_function_decl,
+				   TREE_OPERAND (base, 0),
+				   call))
+    return false;
+  return detect_type_change_from_memory_writes (arg, base, comp_type,
+						call, jfunc, offset);
+}
+
 /* Like detect_type_change but ARG is supposed to be a non-dereferenced pointer
    SSA name (its dereference will become the base and the offset is assumed to
    be zero).  */
@@ -822,10 +896,14 @@  detect_type_change_ssa (tree arg, tree c
       || !POINTER_TYPE_P (TREE_TYPE (arg)))
     return false;
 
+  if (!param_type_may_change_p (current_function_decl, arg, call))
+    return false;
+
   arg = build2 (MEM_REF, ptr_type_node, arg,
 		build_int_cst (ptr_type_node, 0));
 
-  return detect_type_change (arg, arg, comp_type, call, jfunc, 0);
+  return detect_type_change_from_memory_writes (arg, arg, comp_type,
+						call, jfunc, 0);
 }
 
 /* Callback of walk_aliased_vdefs.  Flags that it has been invoked to the
@@ -1433,11 +1511,15 @@  compute_known_type_jump_func (tree op, s
   if (!DECL_P (base)
       || max_size == -1
       || max_size != size
-      || !contains_polymorphic_type_p (TREE_TYPE (base))
-      || is_global_var (base))
+      || !contains_polymorphic_type_p (TREE_TYPE (base)))
     return;
 
-  if (detect_type_change (op, base, expected_type, call, jfunc, offset))
+  if (decl_maybe_in_construction_p (base, TREE_TYPE (base),
+				    call, current_function_decl)
+      /* Even if the var seems to be in construction by inline call stack,
+	 we may work out the actual type by walking memory writes.  */
+      && (!is_global_var (base)
+	  && detect_type_change (op, base, expected_type, call, jfunc, offset)))
     return;
 
   ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base),