diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8eba08d457f..beee78f21af 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2014-08-07 Jan Hubicka + + * ipa-devirt.c: Include gimple-pretty-print.h + (referenced_from_vtable_p): Exclude DECL_EXTERNAL from + further tests. + (decl_maybe_in_construction_p): Fix conditional on cdtor check + (get_polymorphic_call_info): Fix return value + (type_change_info): New sturcture based on ipa-prop + variant. + (noncall_stmt_may_be_vtbl_ptr_store): New predicate + based on ipa-prop variant. + (extr_type_from_vtbl_ptr_store): New function + based on ipa-prop variant. + (record_known_type): New function. + (check_stmt_for_type_change): New function. + (get_dynamic_type): New function. + * ipa-prop.c (ipa_analyze_call_uses): Use get_dynamic_type. + * tree-ssa-pre.c: ipa-utils.h + (eliminate_dom_walker::before_dom_children): Use ipa-devirt + machinery; sanity check with ipa-prop devirtualization. + * trans-mem.c (ipa_tm_insert_gettmclone_call): Clear + polymorphic flag. + 2014-08-07 Trevor Saunders * Makefile.in: Remove references to pointer-set.c and pointer-set.h. diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c index 56eeaf5c9c2..8827d0ecb52 100644 --- a/gcc/ipa-devirt.c +++ b/gcc/ipa-devirt.c @@ -131,6 +131,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-dfa.h" #include "demangle.h" #include "dbgcnt.h" +#include "gimple-pretty-print.h" #include "stor-layout.h" #include "intl.h" #include "hash-map.h" @@ -1323,6 +1324,7 @@ referenced_from_vtable_p (struct cgraph_node *node) bool found = false; if (node->externally_visible + || DECL_EXTERNAL (node->decl) || node->used_from_other_partition) return true; @@ -2113,7 +2115,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type, if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE || (!DECL_CXX_CONSTRUCTOR_P (fn) - || !DECL_CXX_DESTRUCTOR_P (fn))) + && !DECL_CXX_DESTRUCTOR_P (fn))) { /* Watch for clones where we constant propagated the first argument (pointer to the instance). */ @@ -2122,7 +2124,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type, || !is_global_var (base) || TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE || (!DECL_CXX_CONSTRUCTOR_P (fn) - || !DECL_CXX_DESTRUCTOR_P (fn))) + && !DECL_CXX_DESTRUCTOR_P (fn))) continue; } if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST)) @@ -2142,7 +2144,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type, { if (TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE || (!DECL_CXX_CONSTRUCTOR_P (function) - || !DECL_CXX_DESTRUCTOR_P (function))) + && !DECL_CXX_DESTRUCTOR_P (function))) { if (!DECL_ABSTRACT_ORIGIN (function)) return false; @@ -2152,7 +2154,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type, if (!function || TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE || (!DECL_CXX_CONSTRUCTOR_P (function) - || !DECL_CXX_DESTRUCTOR_P (function))) + && !DECL_CXX_DESTRUCTOR_P (function))) return false; } /* FIXME: this can go away once we have ODR types equivalency on @@ -2243,7 +2245,8 @@ walk_ssa_copies (tree op) 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 */ + Return pointer to object described by the context or an declaration if + we found the instance to be stored in the static storage. */ tree get_polymorphic_call_info (tree fndecl, @@ -2317,7 +2320,7 @@ get_polymorphic_call_info (tree fndecl, context->outer_type, call, current_function_decl); - return base_pointer; + return base; } else break; @@ -2436,6 +2439,515 @@ get_polymorphic_call_info (tree fndecl, return base_pointer; } +/* Structure to be passed in between detect_type_change and + check_stmt_for_type_change. */ + +struct type_change_info +{ + /* Offset into the object where there is the virtual method pointer we are + looking for. */ + HOST_WIDE_INT offset; + /* The declaration or SSA_NAME pointer of the base that we are checking for + type change. */ + tree instance; + /* The reference to virtual table pointer used. */ + tree vtbl_ptr_ref; + tree otr_type; + /* 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; + HOST_WIDE_INT known_current_offset; + + /* Set to true if dynamic type change has been detected. */ + bool type_maybe_changed; + /* Set to true if multiple types have been encountered. known_current_type + must be disregarded in that case. */ + bool multiple_types_encountered; + /* Set to true if we possibly missed some dynamic type changes and we should + consider the set to be speculative. */ + bool speculative; + bool seen_unanalyzed_store; +}; + +/* Return true if STMT is not call and can modify a virtual method table pointer. + We take advantage of fact that vtable stores must appear within constructor + and destructor functions. */ + +bool +noncall_stmt_may_be_vtbl_ptr_store (gimple stmt) +{ + if (is_gimple_assign (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + + if (gimple_clobber_p (stmt)) + return false; + if (!AGGREGATE_TYPE_P (TREE_TYPE (lhs))) + { + if (flag_strict_aliasing + && !POINTER_TYPE_P (TREE_TYPE (lhs))) + return false; + + if (TREE_CODE (lhs) == COMPONENT_REF + && !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) + return false; + /* In the future we might want to use get_base_ref_and_offset to find + if there is a field corresponding to the offset and if so, proceed + almost like if it was a component ref. */ + } + } + + /* Code unification may mess with inline stacks. */ + if (cfun->after_inlining) + return true; + + /* Walk the inline stack and watch out for ctors/dtors. + TODO: Maybe we can require the store to appear in toplevel + block of CTOR/DTOR. */ + for (tree block = gimple_block (stmt); 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)) + return false; + return (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE + && (DECL_CXX_CONSTRUCTOR_P (fn) + || DECL_CXX_DESTRUCTOR_P (fn))); + } + return (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE + && (DECL_CXX_CONSTRUCTOR_P (current_function_decl) + || DECL_CXX_DESTRUCTOR_P (current_function_decl))); +} + +/* If STMT can be proved to be an assignment to the virtual method table + pointer of ANALYZED_OBJ and the type associated with the new table + identified, return the type. Otherwise return NULL_TREE. */ + +static tree +extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci, + HOST_WIDE_INT *type_offset) +{ + HOST_WIDE_INT offset, size, max_size; + tree lhs, rhs, base, binfo; + + if (!gimple_assign_single_p (stmt)) + return NULL_TREE; + + lhs = gimple_assign_lhs (stmt); + rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (lhs) != COMPONENT_REF + || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) + return NULL_TREE; + + if (tci->vtbl_ptr_ref && operand_equal_p (lhs, tci->vtbl_ptr_ref, 0)) + ; + else + { + base = get_ref_base_and_extent (lhs, &offset, &size, &max_size); + if (offset != tci->offset + || size != POINTER_SIZE + || max_size != POINTER_SIZE) + return NULL_TREE; + if (DECL_P (tci->instance)) + { + if (base != tci->instance) + return NULL_TREE; + } + else if (TREE_CODE (base) == MEM_REF) + { + if (!operand_equal_p (tci->instance, TREE_OPERAND (base, 0), 0) + || !integer_zerop (TREE_OPERAND (base, 1))) + return NULL_TREE; + } + else if (!operand_equal_p (tci->instance, base, 0) + || tci->offset) + return NULL_TREE; + } + + binfo = vtable_pointer_value_to_binfo (rhs); + + if (!binfo) + return NULL; + *type_offset = tree_to_shwi (BINFO_OFFSET (binfo)) * BITS_PER_UNIT; + if (TYPE_BINFO (BINFO_TYPE (binfo)) == binfo) + return BINFO_TYPE (binfo); + + /* TODO: Figure out the type containing BINFO. */ + return NULL; +} + +/* Record dynamic type change of TCI to TYPE. */ + +void +record_known_type (struct type_change_info *tci, tree type, HOST_WIDE_INT offset) +{ + if (dump_file) + { + if (type) + { + fprintf (dump_file, " Recording type: "); + print_generic_expr (dump_file, type, TDF_SLIM); + fprintf (dump_file, " at offset %i\n", (int)offset); + } + else + fprintf (dump_file, " Recording unknown type\n"); + } + if (tci->type_maybe_changed + && (type != tci->known_current_type + || offset != tci->known_current_offset)) + tci->multiple_types_encountered = true; + tci->known_current_type = type; + tci->known_current_offset = offset; + tci->type_maybe_changed = true; +} + +/* Callback of walk_aliased_vdefs and a helper function for + detect_type_change to check whether a particular statement may modify + the virtual table pointer, and if possible also determine the new type of + the (sub-)object. It stores its result into DATA, which points to a + type_change_info structure. */ + +static bool +check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data) +{ + gimple stmt = SSA_NAME_DEF_STMT (vdef); + struct type_change_info *tci = (struct type_change_info *) data; + tree fn; + + /* If we already gave up, just terminate the rest of walk. */ + if (tci->multiple_types_encountered) + return true; + + if (is_gimple_call (stmt)) + { + if (gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE)) + return false; + + /* Check for a constructor call. */ + if ((fn = gimple_call_fndecl (stmt)) != NULL_TREE + && DECL_CXX_CONSTRUCTOR_P (fn) + && TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE + && gimple_call_num_args (stmt)) + { + tree op = walk_ssa_copies (gimple_call_arg (stmt, 0)); + tree type = method_class_type (TREE_TYPE (fn)); + HOST_WIDE_INT offset = 0, size, max_size; + + if (dump_file) + { + fprintf (dump_file, " Checking constructor call: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + /* See if THIS parameter seems like instance pointer. */ + if (TREE_CODE (op) == ADDR_EXPR) + { + op = get_ref_base_and_extent (TREE_OPERAND (op, 0), + &offset, &size, &max_size); + if (size != max_size || max_size == -1) + { + tci->speculative = true; + return false; + } + if (op && TREE_CODE (op) == MEM_REF) + { + if (!tree_fits_shwi_p (TREE_OPERAND (op, 1))) + { + tci->speculative = true; + return false; + } + offset += tree_to_shwi (TREE_OPERAND (op, 1)) + * BITS_PER_UNIT; + op = TREE_OPERAND (op, 0); + } + else + { + tci->speculative = true; + return false; + } + op = walk_ssa_copies (op); + } + if (operand_equal_p (op, tci->instance, 0) + && TYPE_SIZE (type) + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && tree_fits_shwi_p (TYPE_SIZE (type)) + && tree_to_shwi (TYPE_SIZE (type)) + offset > tci->offset) + { + record_known_type (tci, type, tci->offset - offset); + return true; + } + } + /* Calls may possibly change dynamic type by placement new. Assume + it will not happen, but make result speculative only. */ + if (dump_file) + { + fprintf (dump_file, " Function call may change dynamic type:"); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + tci->speculative = true; + return false; + } + /* Check for inlined virtual table store. */ + else if (noncall_stmt_may_be_vtbl_ptr_store (stmt)) + { + tree type; + HOST_WIDE_INT offset = 0; + if (dump_file) + { + fprintf (dump_file, " Checking vtbl store: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + type = extr_type_from_vtbl_ptr_store (stmt, tci, &offset); + gcc_assert (!type || TYPE_MAIN_VARIANT (type) == type); + if (!type) + { + if (dump_file) + fprintf (dump_file, " Unanalyzed store may change type.\n"); + tci->seen_unanalyzed_store = true; + tci->speculative = true; + } + else + record_known_type (tci, type, offset); + return true; + } + else + return false; +} + +/* CONTEXT is polymorphic call context obtained from get_polymorphic_context. + OTR_OBJECT is pointer to the instance returned by OBJ_TYPE_REF_OBJECT. + INSTANCE is pointer to the outer instance as returned by + get_polymorphic_context. To avoid creation of temporary expressions, + INSTANCE may also be an declaration of get_polymorphic_context found the + value to be in static storage. + + If the type of instance is not fully determined + (either OUTER_TYPE is unknown or MAYBE_IN_CONSTRUCTION/INCLUDE_DERIVED_TYPES + is set), try to walk memory writes and find the actual construction of the + instance. + + We do not include this analysis in the context analysis itself, because + it needs memory SSA to be fully built and the walk may be expensive. + So it is not suitable for use withing fold_stmt and similar uses. */ + +bool +get_dynamic_type (tree instance, + ipa_polymorphic_call_context *context, + tree otr_object, + tree otr_type, + gimple call) +{ + struct type_change_info tci; + ao_ref ao; + bool function_entry_reached = false; + tree instance_ref = NULL; + gimple stmt = call; + + if (!context->maybe_in_construction && !context->maybe_derived_type) + return false; + + /* We need to obtain refernce to virtual table pointer. It is better + to look it up in the code rather than build our own. This require bit + of pattern matching, but we end up verifying that what we found is + correct. + + What we pattern match is: + + tmp = instance->_vptr.A; // vtbl ptr load + tmp2 = tmp[otr_token]; // vtable lookup + OBJ_TYPE_REF(tmp2;instance->0) (instance); + + We want to start alias oracle walk from vtbl pointer load, + but we may not be able to identify it, for example, when PRE moved the + load around. */ + + if (gimple_code (call) == GIMPLE_CALL) + { + tree ref = gimple_call_fn (call); + HOST_WIDE_INT offset2, size, max_size; + + if (TREE_CODE (ref) == OBJ_TYPE_REF) + { + ref = OBJ_TYPE_REF_EXPR (ref); + ref = walk_ssa_copies (ref); + + /* Check if definition looks like vtable lookup. */ + if (TREE_CODE (ref) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (ref) + && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref)) + && TREE_CODE (gimple_assign_rhs1 + (SSA_NAME_DEF_STMT (ref))) == MEM_REF) + { + ref = get_base_address + (TREE_OPERAND (gimple_assign_rhs1 + (SSA_NAME_DEF_STMT (ref)), 0)); + ref = walk_ssa_copies (ref); + /* Find base address of the lookup and see if it looks like + vptr load. */ + if (TREE_CODE (ref) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (ref) + && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref))) + { + tree ref_exp = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (ref)); + tree base_ref = get_ref_base_and_extent + (ref_exp, &offset2, &size, &max_size); + + /* Finally verify that what we found looks like read from OTR_OBJECT + or from INSTANCE with offset OFFSET. */ + if (base_ref + && TREE_CODE (base_ref) == MEM_REF + && ((offset2 == context->offset + && TREE_OPERAND (base_ref, 0) == instance) + || (!offset2 && TREE_OPERAND (base_ref, 0) == otr_object))) + { + stmt = SSA_NAME_DEF_STMT (ref); + instance_ref = ref_exp; + } + } + } + } + } + + /* If we failed to look up the refernece in code, build our own. */ + if (!instance_ref) + { + /* If the statement in question does not use memory, we can't tell + anything. */ + if (!gimple_vuse (stmt)) + return false; + ao_ref_init_from_ptr_and_size (&ao, otr_object, NULL); + } + else + /* Otherwise use the real reference. */ + ao_ref_init (&ao, instance_ref); + + /* We look for vtbl pointer read. */ + ao.size = POINTER_SIZE; + ao.max_size = ao.size; + ao.ref_alias_set + = get_deref_alias_set (TREE_TYPE (BINFO_VTABLE (TYPE_BINFO (otr_type)))); + + if (dump_file) + { + fprintf (dump_file, "Determining dynamic type for call: "); + print_gimple_stmt (dump_file, call, 0, 0); + fprintf (dump_file, " Starting walk at: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + fprintf (dump_file, " instance pointer: "); + print_generic_expr (dump_file, otr_object, TDF_SLIM); + fprintf (dump_file, " Outer instance pointer: "); + print_generic_expr (dump_file, instance, TDF_SLIM); + fprintf (dump_file, " offset: %i (bits)", (int)context->offset); + fprintf (dump_file, " vtbl reference: "); + print_generic_expr (dump_file, instance_ref, TDF_SLIM); + fprintf (dump_file, "\n"); + } + + tci.offset = context->offset; + tci.instance = instance; + tci.vtbl_ptr_ref = instance_ref; + gcc_assert (TREE_CODE (instance) != MEM_REF); + tci.known_current_type = NULL_TREE; + tci.known_current_offset = 0; + tci.otr_type = otr_type; + tci.type_maybe_changed = false; + tci.multiple_types_encountered = false; + tci.speculative = false; + tci.seen_unanalyzed_store = false; + + walk_aliased_vdefs (&ao, gimple_vuse (stmt), check_stmt_for_type_change, + &tci, NULL, &function_entry_reached); + + /* If we did not find any type changing statements, we may still drop + maybe_in_construction flag if the context already have outer type. + + Here we make special assumptions about both constructors and + destructors which are all the functions that are allowed to alter the + VMT pointers. It assumes that destructors begin with assignment into + all VMT pointers and that constructors essentially look in the + following way: + + 1) The very first thing they do is that they call constructors of + ancestor sub-objects that have them. + + 2) Then VMT pointers of this and all its ancestors is set to new + values corresponding to the type corresponding to the constructor. + + 3) Only afterwards, other stuff such as constructor of member + sub-objects and the code written by the user is run. Only this may + include calling virtual functions, directly or indirectly. + + 4) placement new can not be used to change type of non-POD statically + allocated variables. + + There is no way to call a constructor of an ancestor sub-object in any + other way. + + This means that we do not have to care whether constructors get the + correct type information because they will always change it (in fact, + if we define the type to be given by the VMT pointer, it is undefined). + + The most important fact to derive from the above is that if, for some + statement in the section 3, we try to detect whether the dynamic type + has changed, we can safely ignore all calls as we examine the function + body backwards until we reach statements in section 2 because these + calls cannot be ancestor constructors or destructors (if the input is + not bogus) and so do not change the dynamic type (this holds true only + for automatically allocated objects but at the moment we devirtualize + only these). We then must detect that statements in section 2 change + the dynamic type and can try to derive the new type. That is enough + and we can stop, we will never see the calls into constructors of + sub-objects in this code. + + Therefore if the static outer type was found (context->outer_type) + we can safely ignore tci.speculative that is set on calls and give up + only if there was dyanmic type store that may affect given variable + (seen_unanalyzed_store) */ + + if (!tci.type_maybe_changed) + { + if (!context->outer_type || tci.seen_unanalyzed_store) + return false; + if (context->maybe_in_construction) + context->maybe_in_construction = false; + if (dump_file) + fprintf (dump_file, " No dynamic type change found.\n"); + return true; + } + + if (tci.known_current_type + && !function_entry_reached + && !tci.multiple_types_encountered) + { + if (!tci.speculative) + { + context->outer_type = tci.known_current_type; + context->offset = tci.known_current_offset; + context->maybe_in_construction = false; + context->maybe_derived_type = false; + if (dump_file) + fprintf (dump_file, " Determined dynamic type.\n"); + } + else if (!context->speculative_outer_type + || context->speculative_maybe_derived_type) + { + context->speculative_outer_type = tci.known_current_type; + context->speculative_offset = tci.known_current_offset; + context->speculative_maybe_derived_type = false; + if (dump_file) + fprintf (dump_file, " Determined speculative dynamic type.\n"); + } + } + else if (dump_file) + fprintf (dump_file, " Found multiple types.\n"); + + return true; +} + /* Walk bases of OUTER_TYPE that contain OTR_TYPE at OFFSET. Lookup their respecitve virtual methods for OTR_TOKEN and OTR_TYPE and insert them to NODES. @@ -2516,6 +3028,7 @@ devirt_variable_node_removal_hook (varpool_node *n, } /* Record about how many calls would benefit from given type to be final. */ + struct odr_type_warn_count { tree type; @@ -2524,6 +3037,7 @@ struct odr_type_warn_count }; /* Record about how many calls would benefit from given method to be final. */ + struct decl_warn_count { tree decl; @@ -2532,6 +3046,7 @@ struct decl_warn_count }; /* Information about type and decl warnings. */ + struct final_warning_record { gcov_type dyn_count; diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 4b309b97978..3e975d6d497 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -2337,11 +2337,46 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call) && !virtual_method_call_p (target))) return; + struct cgraph_edge *cs = fbi->node->get_edge (call); /* If we previously turned the call into a direct call, there is no need to analyze. */ - struct cgraph_edge *cs = fbi->node->get_edge (call); if (cs && !cs->indirect_unknown_callee) return; + + if (cs->indirect_info->polymorphic) + { + tree otr_type; + HOST_WIDE_INT otr_token; + ipa_polymorphic_call_context context; + tree instance; + tree target = gimple_call_fn (call); + + instance = get_polymorphic_call_info (current_function_decl, + target, + &otr_type, &otr_token, + &context, call); + + if (get_dynamic_type (instance, &context, + OBJ_TYPE_REF_OBJECT (target), + otr_type, call)) + { + gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE); + cs->indirect_info->polymorphic = true; + cs->indirect_info->param_index = -1; + cs->indirect_info->otr_token = otr_token; + cs->indirect_info->otr_type = otr_type; + cs->indirect_info->outer_type = context.outer_type; + cs->indirect_info->speculative_outer_type = context.speculative_outer_type; + cs->indirect_info->offset = context.offset; + cs->indirect_info->speculative_offset = context.speculative_offset; + cs->indirect_info->maybe_in_construction + = context.maybe_in_construction; + cs->indirect_info->maybe_derived_type = context.maybe_derived_type; + cs->indirect_info->speculative_maybe_derived_type + = context.speculative_maybe_derived_type; + } + } + if (TREE_CODE (target) == SSA_NAME) ipa_analyze_indirect_call_uses (fbi, call, target); else if (virtual_method_call_p (target)) diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index 12543048289..3801525ae7d 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -95,7 +95,7 @@ tree get_polymorphic_call_info (tree, tree, tree *, HOST_WIDE_INT *, ipa_polymorphic_call_context *, gimple call = NULL); -bool get_dynamic_type (tree, ipa_polymorphic_call_context *, tree, gimple); +bool get_dynamic_type (tree, ipa_polymorphic_call_context *, tree, tree, gimple); 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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 16956b781ab..c6264802235 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2014-08-07 Jan Hubicka + + * g++.dg/ipa/devirt-35.C: New testcase. + * g++.dg/ipa/devirt-36.C: New testcase. + 2014-08-07 Paolo Carlini PR c++/51312 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-35.C b/gcc/testsuite/g++.dg/ipa/devirt-35.C new file mode 100644 index 00000000000..6c304304f29 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-35.C @@ -0,0 +1,23 @@ +/* { dg-options "-O2 -fdump-ipa-devirt-details -fdump-tree-fre1-details" } */ +struct A {virtual int t(void) {return 1;}}; +struct B:A {B(); virtual int t(void) {return 2;}}; +void test2(struct A *); +int +m(struct B *b) +{ + struct A *a = new (B); + a->t(); // This call should be devirtualized by + // FRE because we know type from ctor call + ((struct B *)a)->B::t(); // Make devirt possible + // C++ FE won't produce inline body without this + test2(a); + return a->t(); // This call should be devirtualized speculatively because + // test2 may change the type of A by placement new. + // C++ standard is bit imprecise about this. +} +/* { dg-final { scan-ipa-dump "converting indirect call to function virtual int B::t" "fre1" } } */ +/* { dg-final { scan-ipa-dump "to virtual int B::t" "devirt" } } */ +/* { dg-final { scan-ipa-dump "1 speculatively devirtualized" "devirt" } } */ +/* { dg-final { cleanup-ipa-dump "devirt" } } */ +/* { dg-final { cleanup-tree-dump "fre" } } */ + diff --git a/gcc/testsuite/g++.dg/ipa/devirt-36.C b/gcc/testsuite/g++.dg/ipa/devirt-36.C new file mode 100644 index 00000000000..c32531ded58 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-36.C @@ -0,0 +1,25 @@ +/* { dg-options "-O2 -fdump-ipa-devirt-details -fdump-tree-fre1-details" } */ +struct A {virtual int t(void) {return 1;}}; +struct B:A {B(); virtual int t(void) {return 2;}}; +struct C {int a; struct B b;}; +void test2(struct A *); +int +m(struct B *b) +{ + struct C *c = new (C); + struct A *a = &c->b; + a->t(); // This call should be devirtualized by + // FRE because we know type from ctor call + ((struct B *)a)->B::t(); // Make devirt possible + // C++ FE won't produce inline body without this + test2(a); + return a->t(); // This call should be devirtualized speculatively because + // test2 may change the type of A by placement new. + // C++ standard is bit imprecise about this. +} +/* { dg-final { scan-ipa-dump "converting indirect call to function virtual int B::t" "fre1" } } */ +/* { dg-final { scan-ipa-dump "to virtual int B::t" "devirt" } } */ +/* { dg-final { scan-ipa-dump "1 speculatively devirtualized" "devirt" } } */ +/* { dg-final { cleanup-ipa-dump "devirt" } } */ +/* { dg-final { cleanup-tree-dump "fre" } } */ + diff --git a/gcc/trans-mem.c b/gcc/trans-mem.c index 5633398e96b..36412319aa9 100644 --- a/gcc/trans-mem.c +++ b/gcc/trans-mem.c @@ -5042,6 +5042,9 @@ ipa_tm_insert_gettmclone_call (struct cgraph_node *node, } update_stmt (stmt); + cgraph_edge *e = cgraph_node::get (current_function_decl)->get_edge (stmt); + if (e && e->indirect_info) + e->indirect_info->polymorphic = false; return true; } diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 8b4d2badb6f..0e968d5e22c 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see #include "domwalk.h" #include "ipa-prop.h" #include "tree-ssa-propagate.h" +#include "ipa-utils.h" /* TODO: @@ -4360,12 +4361,41 @@ eliminate_dom_walker::before_dom_children (basic_block b) { tree fn = gimple_call_fn (stmt); if (fn - && TREE_CODE (fn) == OBJ_TYPE_REF - && TREE_CODE (OBJ_TYPE_REF_EXPR (fn)) == SSA_NAME) + && flag_devirtualize + && virtual_method_call_p (fn)) { - fn = ipa_intraprocedural_devirtualization (stmt); - if (fn && dbg_cnt (devirt)) + tree otr_type; + HOST_WIDE_INT otr_token; + ipa_polymorphic_call_context context; + tree instance; + bool final; + + instance = get_polymorphic_call_info (current_function_decl, + fn, + &otr_type, &otr_token, &context, stmt); + + get_dynamic_type (instance, &context, + OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt); + + vec targets + = possible_polymorphic_call_targets (obj_type_ref_class (fn), + tree_to_uhwi + (OBJ_TYPE_REF_TOKEN (fn)), + context, + &final); + if (dump_enabled_p ()) + dump_possible_polymorphic_call_targets (dump_file, + obj_type_ref_class (fn), + tree_to_uhwi + (OBJ_TYPE_REF_TOKEN (fn)), + context); + if (final && targets.length () <= 1 && dbg_cnt (devirt)) { + tree fn; + if (targets.length () == 1) + fn = targets[0]->decl; + else + fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE); if (dump_enabled_p ()) { location_t loc = gimple_location_safe (stmt); @@ -4377,6 +4407,8 @@ eliminate_dom_walker::before_dom_children (basic_block b) gimple_call_set_fndecl (stmt, fn); gimple_set_modified (stmt, true); } + else + gcc_assert (!ipa_intraprocedural_devirtualization (stmt)); } }