https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98078
--- Comment #3 from Martin Jambor <jamborm at gcc dot gnu.org> --- Here is what happens. An IPA-CP clone for a particular devirtualziation context is created but all devirtualziations based on it are speculative. Then the clone is inlined at one of its call sites and the devirtualization turns out to be exactly right. The following goes on in cgraph_edge::set_call_stmt which gets a direct call as the statement. We end up in the update_speculative condition, which calls itself on all the corresponding speculative edges. First at the direct, which then is removed and inserted into the call hash. Then at the indirect, which is not removed from the has because it is not the one in there. At this point, we figure out we have a direct call statement at our hands and so we call make_direct at the edge, which in turn simply resolves the speculation and returns the direct branch, which we store to e. And then we try to add e to call_site_hash, which is already there and the assert condition does not take that possibility into account. (gdb) p/r *slot $42 = (cgraph_edge *) 0x7ffff74d6478 (gdb) p/r e $43 = (cgraph_edge *) 0x7ffff74d6478 So the simplest fix is probably just adding if (*slot == e) return; before the assert.