Hi, this patch strengthens detect_type_change and makes it cheaper based on the following observation.
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. This makes it easier to check if types may be altered from function start to a given call site (information needed to produce jump function) by checking whether function in question is a constructor or destructor and the parameter is THIS pointer. In other cases we can not retype anymore. Jason, this assumes that one can not destroy the type and re-construct same type at the same spot. Is this valid for THIS pointer of normal methods and is this valid in general? If so, we will need to walk inline stack of the call statement and watch for inlined constructor, as we may be within the destructing/reconstructing sequence. This is not hard to do and I want to do it anyway to disprove the fact htat local variable may be in construction/destruction - that can be true only for calls that are within the inlined constructor/destructor. Bootstrapped/regtested x86_64-linux, will commit it if the above question is resolved. Honza * ipa-prop.c (param_type_may_change_p): New function. (detect_type_change_from_memory_writes): Break out from .... (detect_type_change): ... here; use param_type_may_change_p. (detect_type_change_ssa): Use param_type_may_change_p. Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 212234) +++ ipa-prop.c (working copy) @@ -731,18 +731,45 @@ 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 not change. + Generally functions are not allowed to change type of such instances, + only excetions are C++ constructors and destructors. */ + +static bool +param_type_may_change_p (tree function, tree arg) +{ + if (TREE_CODE (arg) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (arg) + && TREE_CODE (SSA_NAME_VAR (arg)) == PARM_DECL) + { + if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE + && !DECL_CXX_CONSTRUCTOR_P (function) + && !DECL_CXX_DESTRUCTOR_P (function) + && (SSA_NAME_VAR (arg) == DECL_ARGUMENTS (function))) + return false; + + if (SSA_NAME_VAR (arg) != DECL_ARGUMENTS (function)) + 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 +780,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 +817,27 @@ 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))) + 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 +851,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)) + 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