This tries to emit proper debug information for early-inlined functions from LTO LTRANS phase (thus, emit DW_TAG_inlined_subroutine and allow gdb to set breakpoints). We need to avoid confusing LTO and dwarf2out with the full abstract block tree, so this patch "flattens" the abstract block tree by always using the ultimate origin for BLOCK_ABSTRACT_ORIGIN on blocks which are inlined_function_outer_scope_p. Thus, it tries to output the minimal info dwarf2out.c needs to emit the desired debug information.
As with LTO all abstract inline instances get generated late for early inlined functions I had to amend the "hack" for extern inline functions to always output dies for decls that come their way through dwarf2out_abstract_function. And further down not crash on a NULL DECL_INITIAL (when LTO decided to output the function body in another LTRANS unit or if it does not get output at all). Currently LTO-bootstrapping and testing on x86_64-unknown-linux-gnu. Jason, are the dwarf2out bits ok with you? I've sofar toyed with examples like int x, y; static inline int foo (int i) { y = i; return y; } static inline int bar (int i) { x = i; return foo (x); } int main () { int k = 0; int res = bar (k); return res; } and debug information with/without LTO is now reasonably the same and I can set breakpoints on the inlined instances. Thanks, Richard. 2012-10-01 Richard Guenther <rguent...@suse.de> PR lto/47788 * tree-streamer-out.c (write_ts_block_tree_pointers): For inlined functions outer scopes write the ultimate origin as BLOCK_ABSTRACT_ORIGIN and BLOCK_SOURCE_LOCATION. Do not stream the fragment chains. (lto_input_ts_block_tree_pointers): Likewise. * dwarf2out.c (gen_subprogram_die): Handle NULL DECL_INITIAL. (dwarf2out_decl): Always output DECL_ABSTRACT function decls. Index: gcc/tree-streamer-in.c =================================================================== *** gcc/tree-streamer-in.c (revision 191824) --- gcc/tree-streamer-in.c (working copy) *************** static void *** 789,810 **** lto_input_ts_block_tree_pointers (struct lto_input_block *ib, struct data_in *data_in, tree expr) { - /* Do not stream BLOCK_SOURCE_LOCATION. We cannot handle debug information - for early inlining so drop it on the floor instead of ICEing in - dwarf2out.c. */ BLOCK_VARS (expr) = streamer_read_chain (ib, data_in); - /* Do not stream BLOCK_NONLOCALIZED_VARS. We cannot handle debug information - for early inlining so drop it on the floor instead of ICEing in - dwarf2out.c. */ - BLOCK_SUPERCONTEXT (expr) = stream_read_tree (ib, data_in); ! /* Do not stream BLOCK_ABSTRACT_ORIGIN. We cannot handle debug information ! for early inlining so drop it on the floor instead of ICEing in dwarf2out.c. */ ! BLOCK_FRAGMENT_ORIGIN (expr) = stream_read_tree (ib, data_in); ! BLOCK_FRAGMENT_CHAIN (expr) = stream_read_tree (ib, data_in); /* We re-compute BLOCK_SUBBLOCKS of our parent here instead of streaming it. For non-BLOCK BLOCK_SUPERCONTEXTs we still --- 789,810 ---- lto_input_ts_block_tree_pointers (struct lto_input_block *ib, struct data_in *data_in, tree expr) { BLOCK_VARS (expr) = streamer_read_chain (ib, data_in); BLOCK_SUPERCONTEXT (expr) = stream_read_tree (ib, data_in); ! /* Stream BLOCK_ABSTRACT_ORIGIN and BLOCK_SOURCE_LOCATION for ! the limited cases we can handle - those that represent inlined ! function scopes. For the rest them on the floor instead of ICEing in dwarf2out.c. */ ! BLOCK_ABSTRACT_ORIGIN (expr) = stream_read_tree (ib, data_in); ! BLOCK_SOURCE_LOCATION (expr) = lto_input_location (ib, data_in); ! /* Do not stream BLOCK_NONLOCALIZED_VARS. We cannot handle debug information ! for early inlined BLOCKs so drop it on the floor instead of ICEing in ! dwarf2out.c. */ ! ! /* BLOCK_FRAGMENT_ORIGIN and BLOCK_FRAGMENT_CHAIN is not live at LTO ! streaming time. */ /* We re-compute BLOCK_SUBBLOCKS of our parent here instead of streaming it. For non-BLOCK BLOCK_SUPERCONTEXTs we still Index: gcc/tree-streamer-out.c =================================================================== *** gcc/tree-streamer-out.c (revision 191824) --- gcc/tree-streamer-out.c (working copy) *************** write_ts_exp_tree_pointers (struct outpu *** 682,702 **** static void write_ts_block_tree_pointers (struct output_block *ob, tree expr, bool ref_p) { - /* Do not stream BLOCK_SOURCE_LOCATION. We cannot handle debug information - for early inlining so drop it on the floor instead of ICEing in - dwarf2out.c. */ streamer_write_chain (ob, BLOCK_VARS (expr), ref_p); /* Do not stream BLOCK_NONLOCALIZED_VARS. We cannot handle debug information ! for early inlining so drop it on the floor instead of ICEing in dwarf2out.c. */ ! stream_write_tree (ob, BLOCK_SUPERCONTEXT (expr), ref_p); ! /* Do not stream BLOCK_ABSTRACT_ORIGIN. We cannot handle debug information ! for early inlining so drop it on the floor instead of ICEing in ! dwarf2out.c. */ ! stream_write_tree (ob, BLOCK_FRAGMENT_ORIGIN (expr), ref_p); ! stream_write_tree (ob, BLOCK_FRAGMENT_CHAIN (expr), ref_p); /* Do not output BLOCK_SUBBLOCKS. Instead on streaming-in this list is re-constructed from BLOCK_SUPERCONTEXT. */ } --- 682,713 ---- static void write_ts_block_tree_pointers (struct output_block *ob, tree expr, bool ref_p) { streamer_write_chain (ob, BLOCK_VARS (expr), ref_p); + stream_write_tree (ob, BLOCK_SUPERCONTEXT (expr), ref_p); + + /* Stream BLOCK_ABSTRACT_ORIGIN and BLOCK_SOURCE_LOCATION for + the limited cases we can handle - those that represent inlined + function scopes. For the rest them on the floor instead of ICEing in + dwarf2out.c. */ + if (inlined_function_outer_scope_p (expr)) + { + tree ultimate_origin = block_ultimate_origin (expr); + stream_write_tree (ob, ultimate_origin, ref_p); + lto_output_location (ob, BLOCK_SOURCE_LOCATION (expr)); + } + else + { + stream_write_tree (ob, NULL_TREE, ref_p); + lto_output_location (ob, UNKNOWN_LOCATION); + } /* Do not stream BLOCK_NONLOCALIZED_VARS. We cannot handle debug information ! for early inlined BLOCKs so drop it on the floor instead of ICEing in dwarf2out.c. */ ! /* BLOCK_FRAGMENT_ORIGIN and BLOCK_FRAGMENT_CHAIN is not live at LTO ! streaming time. */ ! /* Do not output BLOCK_SUBBLOCKS. Instead on streaming-in this list is re-constructed from BLOCK_SUPERCONTEXT. */ } Index: gcc/dwarf2out.c =================================================================== *** gcc/dwarf2out.c (revision 191824) --- gcc/dwarf2out.c (working copy) *************** gen_subprogram_die (tree decl, dw_die_re *** 17327,17333 **** a BLOCK node representing the function's outermost pair of curly braces, and any blocks used for the base and member initializers of a C++ constructor function. */ ! if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) { int call_site_note_count = 0; int tail_call_site_note_count = 0; --- 17327,17333 ---- a BLOCK node representing the function's outermost pair of curly braces, and any blocks used for the base and member initializers of a C++ constructor function. */ ! if (! declaration && outer_scope && TREE_CODE (outer_scope) != ERROR_MARK) { int call_site_note_count = 0; int tail_call_site_note_count = 0; *************** dwarf2out_decl (tree decl) *** 19620,19627 **** inline" functions as DECL_EXTERNAL, but we need to generate DWARF for them anyway. Note that the C++ front-end also plays some similar games for inline function definitions appearing within include files which ! also contain `#pragma interface' pragmas. */ ! if (DECL_INITIAL (decl) == NULL_TREE) return; /* If we're a nested function, initially use a parent of NULL; if we're --- 19620,19633 ---- inline" functions as DECL_EXTERNAL, but we need to generate DWARF for them anyway. Note that the C++ front-end also plays some similar games for inline function definitions appearing within include files which ! also contain `#pragma interface' pragmas. ! ! If we are called from dwarf2out_abstract_function output a DIE ! anyway. We can end up here this way with early inlining and LTO ! where the inlined function is output in a different LTRANS unit ! or not at all. */ ! if (DECL_INITIAL (decl) == NULL_TREE ! && ! DECL_ABSTRACT (decl)) return; /* If we're a nested function, initially use a parent of NULL; if we're