Hi! create_dispatcher_calls iterates over ipa_ref *s referring the current node and wants to remove them all and create new ones. The problem is that if there are any ipa_ref objects where two or more of them are from the same cgraph node to the current node (in the testcases there are both cases where there are two or more foo -> foo self-references, or two or more foo.avx -> foo references), when we ref->remove_reference () one of them, that method will actually move some other ipa_ref that was last in references vector over to that and thus invalidate what some other ipa_ref pointers point to (the pointers still point to some elements of some references vector, but could either point past the last one, or could point to an element that was overwritten with something else).
The following patch fixes it by remove_reference all references immediately, but push into the vector their content first; besides that ref->remove_reference we are just accessing a couple of fields from that, not calling any other methods. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-03-13 Jakub Jelinek <ja...@redhat.com> PR ipa/89684 * multiple_target.c (create_dispatcher_calls): Change references_to_redirect from vector of ipa_ref * to vector of ipa_ref. In the node->iterate_referring loop, push *ref rather than ref, call ref->remove_reference () and always pass 0 to iterate_referring. --- gcc/multiple_target.c.jj 2019-03-13 09:23:35.345567298 +0100 +++ gcc/multiple_target.c 2019-03-13 10:12:49.071371927 +0100 @@ -103,10 +103,16 @@ create_dispatcher_calls (struct cgraph_n inode->resolve_alias (cgraph_node::get (resolver_decl)); auto_vec<cgraph_edge *> edges_to_redirect; - auto_vec<ipa_ref *> references_to_redirect; + /* We need to capture the references by value rather than just pointers to them + and remove them right away, as removing them later would invalidate what + some other reference pointers point to. */ + auto_vec<ipa_ref> references_to_redirect; - for (unsigned i = 0; node->iterate_referring (i, ref); i++) - references_to_redirect.safe_push (ref); + while (node->iterate_referring (0, ref)) + { + references_to_redirect.safe_push (*ref); + ref->remove_reference (); + } /* We need to remember NEXT_CALLER as it could be modified in the loop. */ for (cgraph_edge *e = node->callers; e ; e = e->next_caller) @@ -146,13 +152,11 @@ create_dispatcher_calls (struct cgraph_n } symtab_node *source = ref->referring; - ref->remove_reference (); source->create_reference (inode, IPA_REF_ADDR); } else if (ref->use == IPA_REF_ALIAS) { symtab_node *source = ref->referring; - ref->remove_reference (); source->create_reference (inode, IPA_REF_ALIAS); source->add_to_same_comdat_group (inode); } --- gcc/testsuite/gcc.target/i386/pr89684.c.jj 2019-03-13 10:12:05.677080120 +0100 +++ gcc/testsuite/gcc.target/i386/pr89684.c 2019-03-13 10:11:56.390231682 +0100 @@ -0,0 +1,23 @@ +/* PR ipa/89684 */ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ + +void bar (int, void (*) (void)); + +__attribute__((target_clones ("default", "avx"))) +void foo (void) +{ + bar (0, foo); + bar (0, foo); +} + +__attribute__((target_clones ("default", "avx", "avx2"))) +void baz (void) +{ + bar (0, foo); + bar (0, foo); + bar (0, foo); + bar (0, foo); + bar (0, foo); + bar (0, foo); +} Jakub