From 568cda29c5a4ffb086d8308745ae8b86427dab61 Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Thu, 5 Sep 2013 14:41:16 +0200 Subject: [PATCH] ipa-prop.c (remove_described_reference): Accept missing references, return false if that hppens, otherwise return true. 2013-09-05 Martin Jambor * ipa-prop.c (remove_described_reference): Accept missing references, return false if that hppens, otherwise return true. (cgraph_node_for_jfunc): New function. (try_decrement_rdesc_refcount): Likewise. (try_make_edge_direct_simple_call): Use them. (ipa_edge_removal_hook): Remove references from rdescs. (ipa_edge_duplication_hook): Clone rdescs and their references when the new edge has the same caller as the old one. * cgraph.c (cgraph_resolve_speculation): Remove speculative reference before removing any edges. testsuite/ * g++.dg/ipa/remref-1.C: New test. * g++.dg/ipa/remref-2.C: Likewise. From-SVN: r202281 --- gcc/ChangeLog | 13 +++++ gcc/cgraph.c | 2 +- gcc/ipa-prop.c | 101 ++++++++++++++++++++++++++++++++---- gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/g++.dg/ipa/remref-1.C | 36 +++++++++++++ gcc/testsuite/g++.dg/ipa/remref-2.C | 37 +++++++++++++ 6 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/remref-1.C create mode 100644 gcc/testsuite/g++.dg/ipa/remref-2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 46153b7edf0..d7b6bfa6728 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2013-09-05 Martin Jambor + + * ipa-prop.c (remove_described_reference): Accept missing references, + return false if that hppens, otherwise return true. + (cgraph_node_for_jfunc): New function. + (try_decrement_rdesc_refcount): Likewise. + (try_make_edge_direct_simple_call): Use them. + (ipa_edge_removal_hook): Remove references from rdescs. + (ipa_edge_duplication_hook): Clone rdescs and their references + when the new edge has the same caller as the old one. + * cgraph.c (cgraph_resolve_speculation): Remove speculative + reference before removing any edges. + 2013-09-05 Richard Earnshaw * arm.c (thumb2_emit_strd_push): Rewrite to use pre-decrement on diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 5fc87ae339d..f12bf1ba4be 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -1225,13 +1225,13 @@ cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl) edge->frequency = CGRAPH_FREQ_MAX; edge->speculative = false; e2->speculative = false; + ipa_remove_reference (ref); if (e2->indirect_unknown_callee || e2->inline_failed) cgraph_remove_edge (e2); else cgraph_remove_node_and_inline_clones (e2->callee, NULL); if (edge->caller->call_site_hash) cgraph_update_edge_in_call_site_hash (edge); - ipa_remove_reference (ref); return edge; } diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 177283c7267..f9f8e2d773e 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -2496,9 +2496,10 @@ ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, } /* Remove a reference to SYMBOL from the list of references of a node given by - reference description RDESC. */ + reference description RDESC. Return true if the reference has been + successfully found and removed. */ -static void +static bool remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc) { struct ipa_ref *to_del; @@ -2507,12 +2508,15 @@ remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc) origin = rdesc->cs; to_del = ipa_find_reference ((symtab_node) origin->caller, symbol, origin->call_stmt, origin->lto_stmt_uid); - gcc_assert (to_del); + if (!to_del) + return false; + ipa_remove_reference (to_del); if (dump_file) fprintf (dump_file, "ipa-prop: Removed a reference from %s/%i to %s.\n", xstrdup (cgraph_node_name (origin->caller)), origin->caller->symbol.order, xstrdup (symtab_node_name (symbol))); + return true; } /* If JFUNC has a reference description with refcount different from @@ -2529,6 +2533,45 @@ jfunc_rdesc_usable (struct ipa_jump_func *jfunc) return NULL; } +/* If the value of constant jump function JFUNC is an address of a function + declaration, return the associated call graph node. Otherwise return + NULL. */ + +static cgraph_node * +cgraph_node_for_jfunc (struct ipa_jump_func *jfunc) +{ + gcc_checking_assert (jfunc->type == IPA_JF_CONST); + tree cst = ipa_get_jf_constant (jfunc); + if (TREE_CODE (cst) != ADDR_EXPR + || TREE_CODE (TREE_OPERAND (cst, 0)) != FUNCTION_DECL) + return NULL; + + return cgraph_get_node (TREE_OPERAND (cst, 0)); +} + + +/* If JFUNC is a constant jump function with a usable rdesc, decrement its + refcount and if it hits zero, remove reference to SYMBOL from the caller of + the edge specified in the rdesc. Return false if either the symbol or the + reference could not be found, otherwise return true. */ + +static bool +try_decrement_rdesc_refcount (struct ipa_jump_func *jfunc) +{ + struct ipa_cst_ref_desc *rdesc; + if (jfunc->type == IPA_JF_CONST + && (rdesc = jfunc_rdesc_usable (jfunc)) + && --rdesc->refcount == 0) + { + symtab_node symbol = (symtab_node) cgraph_node_for_jfunc (jfunc); + if (!symbol) + return false; + + return remove_described_reference (symbol, rdesc); + } + return true; +} + /* Try to find a destination for indirect edge IE that corresponds to a simple call or a call of a member function pointer and where the destination is a pointer formal parameter described by jump function JFUNC. If it can be @@ -2544,7 +2587,6 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, tree target; bool agg_contents = ie->indirect_info->agg_contents; bool speculative = ie->speculative; - struct ipa_cst_ref_desc *rdesc; if (ie->indirect_info->agg_contents) target = ipa_find_agg_cst_for_param (&jfunc->agg, @@ -2557,11 +2599,16 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, cs = ipa_make_edge_direct_to_target (ie, target); /* FIXME: speculative edges can be handled. */ - if (cs && !agg_contents && !speculative - && jfunc->type == IPA_JF_CONST - && (rdesc = jfunc_rdesc_usable (jfunc)) - && --rdesc->refcount == 0) - remove_described_reference ((symtab_node) cs->callee, rdesc); + if (cs && !agg_contents && !speculative) + { + bool ok; + gcc_checking_assert (cs->callee + && (jfunc->type != IPA_JF_CONST + || !cgraph_node_for_jfunc (jfunc) + || cs->callee == cgraph_node_for_jfunc (jfunc))); + ok = try_decrement_rdesc_refcount (jfunc); + gcc_checking_assert (ok); + } return cs; } @@ -2817,7 +2864,9 @@ propagate_controlled_uses (struct cgraph_edge *cs) if (n) { struct cgraph_node *clone; - remove_described_reference ((symtab_node) n, rdesc); + bool ok; + ok = remove_described_reference ((symtab_node) n, rdesc); + gcc_checking_assert (ok); clone = cs->caller; while (clone->global.inlined_to @@ -2960,9 +3009,21 @@ ipa_set_node_agg_value_chain (struct cgraph_node *node, static void ipa_edge_removal_hook (struct cgraph_edge *cs, void *data ATTRIBUTE_UNUSED) { - /* During IPA-CP updating we can be called on not-yet analyze clones. */ + struct ipa_edge_args *args; + + /* During IPA-CP updating we can be called on not-yet analyzed clones. */ if (vec_safe_length (ipa_edge_args_vector) <= (unsigned)cs->uid) return; + + args = IPA_EDGE_REF (cs); + if (args->jump_functions) + { + struct ipa_jump_func *jf; + int i; + FOR_EACH_VEC_ELT (*args->jump_functions, i, jf) + try_decrement_rdesc_refcount (jf); + } + ipa_free_edge_args_substructures (IPA_EDGE_REF (cs)); } @@ -3007,6 +3068,24 @@ ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst, if (!src_rdesc) dst_jf->value.constant.rdesc = NULL; + else if (src->caller == dst->caller) + { + struct ipa_ref *ref; + symtab_node n = (symtab_node) cgraph_node_for_jfunc (src_jf); + gcc_checking_assert (n); + ref = ipa_find_reference ((symtab_node) src->caller, n, + src->call_stmt, src->lto_stmt_uid); + gcc_checking_assert (ref); + ipa_clone_ref (ref, (symtab_node) dst->caller, ref->stmt); + + gcc_checking_assert (ipa_refdesc_pool); + struct ipa_cst_ref_desc *dst_rdesc + = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool); + dst_rdesc->cs = dst; + dst_rdesc->refcount = src_rdesc->refcount; + dst_rdesc->next_duplicate = NULL; + dst_jf->value.constant.rdesc = dst_rdesc; + } else if (src_rdesc->cs == src) { struct ipa_cst_ref_desc *dst_rdesc; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 176d9973f6c..5431d212acc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2013-09-05 Martin Jambor + + * g++.dg/ipa/remref-1.C: New test. + * g++.dg/ipa/remref-2.C: Likewise. + 2013-09-04 Paolo Carlini PR c++/24926 diff --git a/gcc/testsuite/g++.dg/ipa/remref-1.C b/gcc/testsuite/g++.dg/ipa/remref-1.C new file mode 100644 index 00000000000..c25c425e9b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/remref-1.C @@ -0,0 +1,36 @@ +/* Verify that indirect-inlining induced removal of referenes will not remove + too many references in presence of speculative devirtualization. */ +/* { dg-do link } */ +/* { dg-options "-O3 -fno-early-inlining" } */ + +class A +{ + public: + virtual void foo(void (*)(void)); +}; + +static +void b(void) +{ +} + +void +A::foo(void (*back)(void)) +{ + back(); +} + +class A *a; + +void __attribute__ ((noinline, noclone)) +allocate_a () +{ + a = new A(); +} + +main() +{ + allocate_a(); + for (int i=0; i<10000;i++) + a->foo(b); +} diff --git a/gcc/testsuite/g++.dg/ipa/remref-2.C b/gcc/testsuite/g++.dg/ipa/remref-2.C new file mode 100644 index 00000000000..06bc71a5b00 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/remref-2.C @@ -0,0 +1,37 @@ +/* Verify that we survive creation and deletion of references to facilitate + reference removal while also doing (unsuccessful) speculative + devirtualization. */ +/* { dg-do link } */ +/* { dg-options "-O3 -fno-early-inlining" } */ + +class A +{ + public: + virtual void __attribute__ ((noinline)) foo(void (*)(void)); +}; + +static +void b(void) +{ +} + +void __attribute__ ((noinline)) +A::foo(void (*back)(void)) +{ + back(); +} + +class A *a; + +void __attribute__ ((noinline, noclone)) +allocate_a () +{ + a = new A(); +} + +main() +{ + allocate_a(); + for (int i=0; i<10000;i++) + a->foo(b); +} -- 2.11.4.GIT