I have committed the following patch to make debug refs prevail in LTO tree merging. This will fix mixing -g0 and -g units as well as cases where we fail to emit early debug for some decls in one unit but emit them for the copy in a second like for the testcase. So for the testcase a FE fix would be possible as well to not lose debug info.
Bootstrapped and tested on x86_64-unknown-linux-gnu. Richard. 2018-07-20 Richard Biener <rguent...@suse.de> PR debug/86585 * dwarf2out.c (dwarf2out_die_ref_for_decl): Test in_lto_p to cover -flto-partition=none. lto/ * lto.c (unify_scc): Before we throw away an SCC see if we can amend prevailing single-entry SCC with debug refs. * g++.dg/lto/pr86585_0.C: New testcase. * g++.dg/lto/pr86585_1.C: Likewise. diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index bd45e0b0685..8377cbc5dd1 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -5851,8 +5851,7 @@ dwarf2out_die_ref_for_decl (tree decl, const char **sym, { dw_die_ref die; - if ((flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO) - && !decl_die_table) + if (in_lto_p && !decl_die_table) return false; if (TREE_CODE (decl) == BLOCK) @@ -5865,8 +5864,7 @@ dwarf2out_die_ref_for_decl (tree decl, const char **sym, /* During WPA stage and incremental linking we currently use DIEs to store the decl <-> label + offset map. That's quite inefficient but it works for now. */ - if (flag_wpa - || flag_incremental_link == INCREMENTAL_LINK_LTO) + if (in_lto_p) { dw_die_ref ref = get_AT_ref (die, DW_AT_abstract_origin); if (!ref) diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c index d1add15efeb..8db280ecefc 100644 --- a/gcc/lto/lto.c +++ b/gcc/lto/lto.c @@ -1638,6 +1638,21 @@ unify_scc (struct data_in *data_in, unsigned from, to the tree node mapping computed by compare_tree_sccs. */ if (len == 1) { + /* If we got a debug reference queued, see if the prevailing + tree has a debug reference and if not, register the one + for the tree we are about to throw away. */ + if (dref_queue.length () == 1) + { + dref_entry e = dref_queue.pop (); + gcc_assert (e.decl + == streamer_tree_cache_get_tree (cache, from)); + const char *sym; + unsigned HOST_WIDE_INT off; + if (!debug_hooks->die_ref_for_decl (pscc->entries[0], &sym, + &off)) + debug_hooks->register_external_die (pscc->entries[0], + e.sym, e.off); + } lto_maybe_register_decl (data_in, pscc->entries[0], from); streamer_tree_cache_replace_tree (cache, pscc->entries[0], from); } @@ -1669,7 +1684,9 @@ unify_scc (struct data_in *data_in, unsigned from, free_node (scc->entries[i]); } - /* Drop DIE references. */ + /* Drop DIE references. + ??? Do as in the size-one SCC case which involves sorting + the queue. */ dref_queue.truncate (0); break; diff --git a/gcc/testsuite/g++.dg/lto/pr86585_0.C b/gcc/testsuite/g++.dg/lto/pr86585_0.C new file mode 100644 index 00000000000..2c3ae9da414 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr86585_0.C @@ -0,0 +1,18 @@ +// { dg-lto-do link } +// { dg-require-effective-target fpic } +// { dg-require-effective-target shared } +// { dg-lto-options { { -flto -g -nostdlib -shared -fPIC } } } +namespace Inkscape { + class a; +} +class b { + Inkscape::a *c; + virtual void d(); +}; +class e { + b f; +}; +class g : e { + void h(); +}; +void g::h() {} diff --git a/gcc/testsuite/g++.dg/lto/pr86585_1.C b/gcc/testsuite/g++.dg/lto/pr86585_1.C new file mode 100644 index 00000000000..ebcbe126768 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr86585_1.C @@ -0,0 +1,24 @@ +struct a { + struct b { + b(); + } c; +}; +class d { + a e; +}; +namespace aa { + class h {}; +} // namespace aa +class k { + typedef aa::h f; + f g; +}; +namespace Inkscape { + class l { + k i; +class : d { + } j; + l(); + }; + l::l() {} +} // namespace Inkscape