From 857c5a0bb7c2290d37a1b10405b99012925c2413 Mon Sep 17 00:00:00 2001 From: hubicka Date: Tue, 11 Feb 2014 22:54:21 +0000 Subject: [PATCH] PR lto/59468 * ipa-utils.h (possible_polymorphic_call_targets): Update prototype and wrapper. * ipa-devirt.c: Include demangle.h (odr_violation_reported): New static variable. (add_type_duplicate): Update odr_violations. (maybe_record_node): Add completep parameter; update it. (record_target_from_binfo): Add COMPLETEP parameter; update it as needed. (possible_polymorphic_call_targets_1): Likewise. (struct polymorphic_call_target_d): Add nonconstruction_targets; rename FINAL to COMPLETE. (record_targets_from_bases): Sanity check we found the binfo; fix COMPLETEP updating. (possible_polymorphic_call_targets): Add NONCONSTRUTION_TARGETSP parameter, fix computing of COMPLETEP. (dump_possible_polymorphic_call_targets): Imrove readability of dump; at LTO time do demangling. (ipa_devirt): Use nonconstruction_targets; Improve dumps. * gimple-fold.c (gimple_get_virt_method_for_vtable): Add can_refer parameter. (gimple_get_virt_method_for_binfo): Likewise. * gimple-fold.h (gimple_get_virt_method_for_binfo, gimple_get_virt_method_for_vtable): Update prototypes. PR lto/59468 * g++.dg/ipa/devirt-27.C: New testcase. * g++.dg/ipa/devirt-26.C: New testcase. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@207702 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 27 +++++ gcc/cp/decl2.c | 1 - gcc/gimple-fold.c | 48 ++++++-- gcc/gimple-fold.h | 6 +- gcc/ipa-devirt.c | 218 ++++++++++++++++++++++++----------- gcc/ipa-utils.h | 9 +- gcc/testsuite/ChangeLog | 6 + gcc/testsuite/g++.dg/ipa/devirt-26.C | 29 +++++ gcc/testsuite/g++.dg/ipa/devirt-27.C | 27 +++++ 9 files changed, 286 insertions(+), 85 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-26.C create mode 100644 gcc/testsuite/g++.dg/ipa/devirt-27.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 045ed3f..9b50158 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,30 @@ +2014-02-11 Jan Hubicka + + PR lto/59468 + * ipa-utils.h (possible_polymorphic_call_targets): Update prototype + and wrapper. + * ipa-devirt.c: Include demangle.h + (odr_violation_reported): New static variable. + (add_type_duplicate): Update odr_violations. + (maybe_record_node): Add completep parameter; update it. + (record_target_from_binfo): Add COMPLETEP parameter; + update it as needed. + (possible_polymorphic_call_targets_1): Likewise. + (struct polymorphic_call_target_d): Add nonconstruction_targets; + rename FINAL to COMPLETE. + (record_targets_from_bases): Sanity check we found the binfo; + fix COMPLETEP updating. + (possible_polymorphic_call_targets): Add NONCONSTRUTION_TARGETSP + parameter, fix computing of COMPLETEP. + (dump_possible_polymorphic_call_targets): Imrove readability of dump; at + LTO time do demangling. + (ipa_devirt): Use nonconstruction_targets; Improve dumps. + * gimple-fold.c (gimple_get_virt_method_for_vtable): Add can_refer + parameter. + (gimple_get_virt_method_for_binfo): Likewise. + * gimple-fold.h (gimple_get_virt_method_for_binfo, + gimple_get_virt_method_for_vtable): Update prototypes. + 2014-02-11 Vladimir Makarov PR target/49008 diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 35707a0..01a16d3 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -2660,7 +2660,6 @@ import_export_decl (tree decl) comdat_p = (targetm.cxx.class_data_always_comdat () || (CLASSTYPE_KEY_METHOD (type) && DECL_DECLARED_INLINE_P (CLASSTYPE_KEY_METHOD (type)))); - mark_needed (decl); if (!flag_weak) { comdat_p = false; diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index fd25939..5de4445 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -3170,22 +3170,35 @@ fold_const_aggregate_ref (tree t) } /* Lookup virtual method with index TOKEN in a virtual table V - at OFFSET. */ + at OFFSET. + Set CAN_REFER if non-NULL to false if method + is not referable or if the virtual table is ill-formed (such as rewriten + by non-C++ produced symbol). Otherwise just return NULL in that calse. */ tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, tree v, - unsigned HOST_WIDE_INT offset) + unsigned HOST_WIDE_INT offset, + bool *can_refer) { tree vtable = v, init, fn; unsigned HOST_WIDE_INT size; unsigned HOST_WIDE_INT elt_size, access_index; tree domain_type; + if (can_refer) + *can_refer = true; + /* First of all double check we have virtual table. */ if (TREE_CODE (v) != VAR_DECL || !DECL_VIRTUAL_P (v)) - return NULL_TREE; + { + gcc_assert (in_lto_p); + /* Pass down that we lost track of the target. */ + if (can_refer) + *can_refer = false; + return NULL_TREE; + } init = ctor_for_folding (v); @@ -3197,6 +3210,9 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, if (init == error_mark_node) { gcc_assert (in_lto_p); + /* Pass down that we lost track of the target. */ + if (can_refer) + *can_refer = false; return NULL_TREE; } gcc_checking_assert (TREE_CODE (TREE_TYPE (v)) == ARRAY_TYPE); @@ -3247,7 +3263,14 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, ends up in other partition, because we found devirtualization possibility too late. */ if (!can_refer_decl_in_current_unit_p (fn, vtable)) - return NULL_TREE; + { + if (can_refer) + { + *can_refer = false; + return fn; + } + return NULL_TREE; + } } /* Make sure we create a cgraph node for functions we'll reference. @@ -3261,10 +3284,14 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token, /* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN is integer form of OBJ_TYPE_REF_TOKEN of the reference expression. KNOWN_BINFO carries the binfo describing the true type of - OBJ_TYPE_REF_OBJECT(REF). */ + OBJ_TYPE_REF_OBJECT(REF). + Set CAN_REFER if non-NULL to false if method + is not referable or if the virtual table is ill-formed (such as rewriten + by non-C++ produced symbol). Otherwise just return NULL in that calse. */ tree -gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) +gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo, + bool *can_refer) { unsigned HOST_WIDE_INT offset; tree v; @@ -3275,9 +3302,12 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo) return NULL_TREE; if (!vtable_pointer_value_to_vtable (v, &v, &offset)) - return NULL_TREE; - - return gimple_get_virt_method_for_vtable (token, v, offset); + { + if (can_refer) + *can_refer = false; + return NULL_TREE; + } + return gimple_get_virt_method_for_vtable (token, v, offset, can_refer); } /* Return true iff VAL is a gimple expression that is known to be diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 6d8746c..6b43066 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -36,9 +36,11 @@ extern tree gimple_fold_stmt_to_constant_1 (gimple, tree (*) (tree)); extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree)); extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree)); extern tree fold_const_aggregate_ref (tree); -extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); +extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree, + bool *can_refer = NULL); extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree, - unsigned HOST_WIDE_INT); + unsigned HOST_WIDE_INT, + bool *can_refer = NULL); extern bool gimple_val_nonnegative_real_p (tree); extern tree gimple_fold_indirect_ref (tree); extern bool arith_code_with_undefined_signed_overflow (tree_code); diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c index f1e447f..21649cb 100644 --- a/gcc/ipa-devirt.c +++ b/gcc/ipa-devirt.c @@ -128,6 +128,9 @@ along with GCC; see the file COPYING3. If not see #include "ipa-inline.h" #include "diagnostic.h" #include "tree-dfa.h" +#include "demangle.h" + +static bool odr_violation_reported = false; /* Dummy polymorphic call context. */ @@ -297,6 +300,7 @@ add_type_duplicate (odr_type val, tree type) if (!types_compatible_p (val->type, type)) { merge = false; + odr_violation_reported = true; if (BINFO_VTABLE (TYPE_BINFO (val->type)) && warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0, "type %qD violates one definition rule ", @@ -332,6 +336,7 @@ add_type_duplicate (odr_type val, tree type) if (base_mismatch) { merge = false; + odr_violation_reported = true; if (warning_at (DECL_SOURCE_LOCATION (TYPE_NAME (type)), 0, "type %qD violates one definition rule ", @@ -594,17 +599,31 @@ build_type_inheritance_graph (void) } /* If TARGET has associated node, record it in the NODES array. - if TARGET can not be inserted (for example because its body was - already removed and there is no way to refer to it), clear COMPLETEP. */ + CAN_REFER specify if program can refer to the target directly. + if TARGET is unknown (NULL) or it can not be inserted (for example because + its body was already removed and there is no way to refer to it), clear + COMPLETEP. */ static void maybe_record_node (vec &nodes, tree target, pointer_set_t *inserted, + bool can_refer, bool *completep) { struct cgraph_node *target_node; enum built_in_function fcode; + if (!can_refer) + { + /* The only case when method of anonymous namespace becomes unreferable + is when we completely optimized it out. */ + if (flag_ltrans + || !target + || !type_in_anonymous_namespace_p (DECL_CONTEXT (target))) + *completep = false; + return; + } + if (!target /* Those are used to mark impossible scenarios. */ || (fcode = DECL_FUNCTION_CODE (target)) @@ -649,6 +668,8 @@ maybe_record_node (vec &nodes, inserted. ANONYMOUS is true if BINFO is part of anonymous namespace. + + Clear COMPLETEP when we hit unreferable target. */ static void @@ -661,7 +682,8 @@ record_target_from_binfo (vec &nodes, HOST_WIDE_INT offset, pointer_set_t *inserted, pointer_set_t *matched_vtables, - bool anonymous) + bool anonymous, + bool *completep) { tree type = BINFO_TYPE (binfo); int i; @@ -692,6 +714,11 @@ record_target_from_binfo (vec &nodes, return; tree inner_binfo = get_binfo_at_offset (type_binfo, offset, otr_type); + if (!inner_binfo) + { + gcc_assert (odr_violation_reported); + return; + } /* For types in anonymous namespace first check if the respective vtable is alive. If not, we know the type can't be called. */ if (!flag_ltrans && anonymous) @@ -708,9 +735,11 @@ record_target_from_binfo (vec &nodes, gcc_assert (inner_binfo); if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (inner_binfo))) { - tree target = gimple_get_virt_method_for_binfo (otr_token, inner_binfo); - if (target) - maybe_record_node (nodes, target, inserted, NULL); + bool can_refer; + tree target = gimple_get_virt_method_for_binfo (otr_token, + inner_binfo, + &can_refer); + maybe_record_node (nodes, target, inserted, can_refer, completep); } return; } @@ -722,7 +751,7 @@ record_target_from_binfo (vec &nodes, record_target_from_binfo (nodes, base_binfo, otr_type, type_binfos, otr_token, outer_type, offset, inserted, - matched_vtables, anonymous); + matched_vtables, anonymous, completep); if (BINFO_VTABLE (binfo)) type_binfos.pop (); } @@ -730,7 +759,8 @@ record_target_from_binfo (vec &nodes, /* Lookup virtual methods matching OTR_TYPE (with OFFSET and OTR_TOKEN) of TYPE, insert them to NODES, recurse into derived nodes. INSERTED is used to avoid duplicate insertions of methods into NODES. - MATCHED_VTABLES are used to avoid duplicate walking vtables. */ + MATCHED_VTABLES are used to avoid duplicate walking vtables. + Clear COMPLETEP if unreferable target is found. */ static void possible_polymorphic_call_targets_1 (vec &nodes, @@ -740,7 +770,8 @@ possible_polymorphic_call_targets_1 (vec &nodes, odr_type type, HOST_WIDE_INT otr_token, tree outer_type, - HOST_WIDE_INT offset) + HOST_WIDE_INT offset, + bool *completep) { tree binfo = TYPE_BINFO (type->type); unsigned int i; @@ -749,14 +780,14 @@ possible_polymorphic_call_targets_1 (vec &nodes, record_target_from_binfo (nodes, binfo, otr_type, type_binfos, otr_token, outer_type, offset, inserted, matched_vtables, - type->anonymous_namespace); + type->anonymous_namespace, completep); type_binfos.release (); for (i = 0; i < type->derived_types.length (); i++) possible_polymorphic_call_targets_1 (nodes, inserted, matched_vtables, otr_type, type->derived_types[i], - otr_token, outer_type, offset); + otr_token, outer_type, offset, completep); } /* Cache of queries for polymorphic call targets. @@ -771,7 +802,8 @@ struct polymorphic_call_target_d ipa_polymorphic_call_context context; odr_type type; vec targets; - bool final; + int nonconstruction_targets; + bool complete; }; /* Polymorphic call target cache helpers. */ @@ -1282,7 +1314,7 @@ record_targets_from_bases (tree otr_type, HOST_WIDE_INT otr_token, tree outer_type, HOST_WIDE_INT offset, - vec nodes, + vec &nodes, pointer_set_t *inserted, pointer_set_t *matched_vtables, bool *completep) @@ -1303,7 +1335,9 @@ record_targets_from_bases (tree otr_type, pos = int_bit_position (fld); size = tree_to_shwi (DECL_SIZE (fld)); - if (pos <= offset && (pos + size) > offset) + if (pos <= offset && (pos + size) > offset + /* Do not get confused by zero sized bases. */ + && polymorphic_type_binfo_p (TYPE_BINFO (TREE_TYPE (fld)))) break; } /* Within a class type we should always find correcponding fields. */ @@ -1317,16 +1351,19 @@ record_targets_from_bases (tree otr_type, base_binfo = get_binfo_at_offset (TYPE_BINFO (outer_type), offset, otr_type); + if (!base_binfo) + { + gcc_assert (odr_violation_reported); + return; + } gcc_assert (base_binfo); if (!pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo))) { - tree target = gimple_get_virt_method_for_binfo (otr_token, base_binfo); - if (target) - maybe_record_node (nodes, target, inserted, completep); - /* The only way method in anonymous namespace can become unreferable - is that it has been fully optimized out. */ - else if (flag_ltrans || !type_in_anonymous_namespace_p (outer_type)) - *completep = false; + bool can_refer; + tree target = gimple_get_virt_method_for_binfo (otr_token, + base_binfo, + &can_refer); + maybe_record_node (nodes, target, inserted, can_refer, completep); pointer_set_insert (matched_vtables, BINFO_VTABLE (base_binfo)); } } @@ -1357,6 +1394,10 @@ devirt_variable_node_removal_hook (varpool_node *n, in the target cache. If user needs to visit every target list just once, it can memoize them. + NONCONSTRUCTION_TARGETS specify number of targets with asumption that + the type is not in the construction. Those targets appear first in the + vector returned. + Returned vector is placed into cache. It is NOT caller's responsibility to free it. The vector can be freed on cgraph_remove_node call if the particular node is a virtual function present in the cache. */ @@ -1366,7 +1407,8 @@ possible_polymorphic_call_targets (tree otr_type, HOST_WIDE_INT otr_token, ipa_polymorphic_call_context context, bool *completep, - void **cache_token) + void **cache_token, + int *nonconstruction_targetsp) { static struct cgraph_node_hook_list *node_removal_hook_holder; pointer_set_t *inserted; @@ -1377,12 +1419,15 @@ possible_polymorphic_call_targets (tree otr_type, polymorphic_call_target_d **slot; unsigned int i; tree binfo, target; - bool final; + bool complete; + bool can_refer; if (!odr_hash.is_created ()) { if (completep) *completep = false; + if (nonconstruction_targetsp) + *nonconstruction_targetsp = 0; return nodes; } @@ -1406,7 +1451,7 @@ possible_polymorphic_call_targets (tree otr_type, /* If outer and inner type match, there are no bases to see. */ if (type == outer_type) context.maybe_in_construction = false; - /* If the type is final, there are no derivations. */ + /* If the type is complete, there are no derivations. */ if (TYPE_FINAL_P (outer_type->type)) context.maybe_derived_type = false; @@ -1434,11 +1479,13 @@ possible_polymorphic_call_targets (tree otr_type, if (*slot) { if (completep) - *completep = (*slot)->final; + *completep = (*slot)->complete; + if (nonconstruction_targetsp) + *nonconstruction_targetsp = (*slot)->nonconstruction_targets; return (*slot)->targets; } - final = true; + complete = true; /* Do actual search. */ timevar_push (TV_IPA_VIRTUAL_CALL); @@ -1455,49 +1502,58 @@ possible_polymorphic_call_targets (tree otr_type, /* First see virtual method of type itself. */ binfo = get_binfo_at_offset (TYPE_BINFO (outer_type->type), context.offset, otr_type); - target = gimple_get_virt_method_for_binfo (otr_token, binfo); - if (target) + if (binfo) + target = gimple_get_virt_method_for_binfo (otr_token, binfo, + &can_refer); + else { - maybe_record_node (nodes, target, inserted, &final); + gcc_assert (odr_violation_reported); + target = NULL; + } - /* In the case we get final method, we don't need + maybe_record_node (nodes, target, inserted, can_refer, &complete); + + if (target) + { + /* In the case we get complete method, we don't need to walk derivations. */ if (DECL_FINAL_P (target)) context.maybe_derived_type = false; } - /* The only way method in anonymous namespace can become unreferable - is that it has been fully optimized out. */ - else if (flag_ltrans || !type->anonymous_namespace) - final = false; + else + gcc_assert (!complete); pointer_set_insert (matched_vtables, BINFO_VTABLE (binfo)); - /* Next walk bases, if asked to. */ - if (context.maybe_in_construction) - record_targets_from_bases (otr_type, otr_token, outer_type->type, - context.offset, nodes, inserted, - matched_vtables, &final); - - /* Finally walk recursively all derived types. */ + /* Next walk recursively all derived types. */ if (context.maybe_derived_type) { /* For anonymous namespace types we can attempt to build full type. All derivations must be in this unit (unless we see partial unit). */ if (!type->anonymous_namespace || flag_ltrans) - final = false; + complete = false; for (i = 0; i < outer_type->derived_types.length(); i++) possible_polymorphic_call_targets_1 (nodes, inserted, matched_vtables, otr_type, outer_type->derived_types[i], otr_token, outer_type->type, - context.offset); + context.offset, &complete); } + /* Finally walk bases, if asked to. */ + (*slot)->nonconstruction_targets = nodes.length(); + if (context.maybe_in_construction) + record_targets_from_bases (otr_type, otr_token, outer_type->type, + context.offset, nodes, inserted, + matched_vtables, &complete); + (*slot)->targets = nodes; - (*slot)->final = final; + (*slot)->complete = complete; if (completep) - *completep = final; + *completep = complete; + if (nonconstruction_targetsp) + *nonconstruction_targetsp = (*slot)->nonconstruction_targets; pointer_set_destroy (inserted); pointer_set_destroy (matched_vtables); @@ -1517,28 +1573,46 @@ dump_possible_polymorphic_call_targets (FILE *f, bool final; odr_type type = get_odr_type (otr_type, false); unsigned int i; + int nonconstruction; if (!type) return; targets = possible_polymorphic_call_targets (otr_type, otr_token, ctx, - &final); + &final, NULL, &nonconstruction); fprintf (f, " Targets of polymorphic call of type %i:", type->id); print_generic_expr (f, type->type, TDF_SLIM); - fprintf (f, " token %i\n" - " Contained in type:", - (int)otr_token); - print_generic_expr (f, ctx.outer_type, TDF_SLIM); - fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n" - " %s%s%s\n ", - ctx.offset, - final ? "This is full list." : + fprintf (f, " token %i\n", (int)otr_token); + if (ctx.outer_type || ctx.offset) + { + fprintf (f, " Contained in type:"); + print_generic_expr (f, ctx.outer_type, TDF_SLIM); + fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n", + ctx.offset); + } + + fprintf (f, " %s%s%s\n ", + final ? "This is a complete list." : "This is partial list; extra targets may be defined in other units.", ctx.maybe_in_construction ? " (base types included)" : "", ctx.maybe_derived_type ? " (derived types included)" : ""); for (i = 0; i < targets.length (); i++) - fprintf (f, " %s/%i", targets[i]->name (), - targets[i]->order); + { + char *name = NULL; + if (i == (unsigned)nonconstruction) + fprintf (f, "\n If the type is in construction," + " then additional tarets are:\n" + " "); + if (in_lto_p) + name = cplus_demangle_v3 (targets[i]->asm_name (), 0); + fprintf (f, " %s/%i", name ? name : targets[i]->name (), targets[i]->order); + if (in_lto_p) + free (name); + if (!targets[i]->definition) + fprintf (f, " (no definition%s)", + DECL_DECLARED_INLINE_P (targets[i]->decl) + ? " inline" : ""); + } fprintf (f, "\n\n"); } @@ -1650,9 +1724,10 @@ ipa_devirt (void) struct cgraph_node *likely_target = NULL; void *cache_token; bool final; + int nonconstruction_targets; vec targets = possible_polymorphic_call_targets - (e, &final, &cache_token); + (e, &final, &cache_token, &nonconstruction_targets); unsigned int i; if (dump_file) @@ -1664,14 +1739,14 @@ ipa_devirt (void) if (!cgraph_maybe_hot_edge_p (e)) { if (dump_file) - fprintf (dump_file, "Call is cold\n"); + fprintf (dump_file, "Call is cold\n\n"); ncold++; continue; } if (e->speculative) { if (dump_file) - fprintf (dump_file, "Call is aready speculated\n"); + fprintf (dump_file, "Call is aready speculated\n\n"); nspeculated++; /* When dumping see if we agree with speculation. */ @@ -1682,7 +1757,7 @@ ipa_devirt (void) cache_token)) { if (dump_file) - fprintf (dump_file, "Target list is known to be useless\n"); + fprintf (dump_file, "Target list is known to be useless\n\n"); nmultiple++; continue; } @@ -1691,10 +1766,13 @@ ipa_devirt (void) { if (likely_target) { - likely_target = NULL; - if (dump_file) - fprintf (dump_file, "More than one likely target\n"); - nmultiple++; + if (i < (unsigned) nonconstruction_targets) + { + likely_target = NULL; + if (dump_file) + fprintf (dump_file, "More than one likely target\n\n"); + nmultiple++; + } break; } likely_target = targets[i]; @@ -1714,12 +1792,12 @@ ipa_devirt (void) if (cgraph_function_or_thunk_node (e2->callee, NULL) == cgraph_function_or_thunk_node (likely_target, NULL)) { - fprintf (dump_file, "We agree with speculation\n"); + fprintf (dump_file, "We agree with speculation\n\n"); nok++; } else { - fprintf (dump_file, "We disagree with speculation\n"); + fprintf (dump_file, "We disagree with speculation\n\n"); nwrong++; } continue; @@ -1727,7 +1805,7 @@ ipa_devirt (void) if (!likely_target->definition) { if (dump_file) - fprintf (dump_file, "Target is not an definition\n"); + fprintf (dump_file, "Target is not an definition\n\n"); nnotdefined++; continue; } @@ -1738,7 +1816,7 @@ ipa_devirt (void) if (DECL_EXTERNAL (likely_target->decl)) { if (dump_file) - fprintf (dump_file, "Target is external\n"); + fprintf (dump_file, "Target is external\n\n"); nexternal++; continue; } @@ -1747,7 +1825,7 @@ ipa_devirt (void) && symtab_can_be_discarded (likely_target)) { if (dump_file) - fprintf (dump_file, "Target is overwritable\n"); + fprintf (dump_file, "Target is overwritable\n\n"); noverwritable++; continue; } @@ -1755,7 +1833,7 @@ ipa_devirt (void) { if (dump_file) fprintf (dump_file, - "Speculatively devirtualizing call in %s/%i to %s/%i\n", + "Speculatively devirtualizing call in %s/%i to %s/%i\n\n", n->name (), n->order, likely_target->name (), likely_target->order); diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index d595e8a..a2c985a 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -76,7 +76,8 @@ vec possible_polymorphic_call_targets (tree, HOST_WIDE_INT, ipa_polymorphic_call_context, bool *final = NULL, - void **cache_token = NULL); + void **cache_token = NULL, + int *nonconstruction_targets = NULL); odr_type get_odr_type (tree, bool insert = false); void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT, const ipa_polymorphic_call_context &); @@ -105,7 +106,8 @@ bool vtable_pointer_value_to_vtable (tree, tree *, unsigned HOST_WIDE_INT *); inline vec possible_polymorphic_call_targets (struct cgraph_edge *e, bool *final = NULL, - void **cache_token = NULL) + void **cache_token = NULL, + int *nonconstruction_targets = NULL) { gcc_checking_assert (e->indirect_info->polymorphic); ipa_polymorphic_call_context context = {e->indirect_info->offset, @@ -115,7 +117,8 @@ possible_polymorphic_call_targets (struct cgraph_edge *e, return possible_polymorphic_call_targets (e->indirect_info->otr_type, e->indirect_info->otr_token, context, - final, cache_token); + final, cache_token, + nonconstruction_targets); } /* Same as above but taking OBJ_TYPE_REF as an parameter. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 34c1f04..5705049 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2014-02-11 Jan Hubicka + + PR lto/59468 + * g++.dg/ipa/devirt-27.C: New testcase. + * g++.dg/ipa/devirt-26.C: New testcase. + 2014-02-11 Michael Meissner PR target/60137 diff --git a/gcc/testsuite/g++.dg/ipa/devirt-26.C b/gcc/testsuite/g++.dg/ipa/devirt-26.C new file mode 100644 index 0000000..2df429c --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-26.C @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-devirt" } */ +struct A + { + int a; + virtual int bar(void) {return a;} + }; +struct B + { + virtual int foo(void) {return b;} + int b; + }; +struct C: A,B + { + virtual int foo(void) {return a;} + }; + +struct C c; +int test(void) +{ + struct C *d=&c; + struct B *b=d; + 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" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/devirt-27.C b/gcc/testsuite/g++.dg/ipa/devirt-27.C new file mode 100644 index 0000000..1dcf76c --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/devirt-27.C @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-devirt -fdump-tree-optimized" } */ +struct A + { + int a; + }; +struct B + { + __attribute__ ((visibility("default"))) + virtual int foo(void) {return 42;} + int b; + }; +struct C: A,B + { + __attribute__ ((visibility("hidden"))) + virtual int foo(void); + }; + +struct C c; +int test(void) +{ + struct C *d=&c; + struct B *b=d; + return d->foo()+b->foo(); +} +/* { dg-final { scan-tree-dump "OBJ_TYPE_REF" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ -- 2.7.4