Hi Guys, Sometimes gcc can generate instructions out of order with respect to lines of source code, and this can lead to problems for debuggers. For example, consider this source code snippet:
v = 0; /* Line 31 */ goto b; /* Line 32 */ a: v++; /* Line 33 */ b: if (v < 3) goto a; /* Line 34 */ Compiled without optimization, but with -g, gcc will produce code like this: 400692: c7 05 94 09 20 00 00 movl $0x0,0x200994(%rip) 400699: 00 00 00 40069c: eb 10 jmp 4006ae <loop_test+0xc6> 40069e: 90 nop 40069f: 8b 05 8b 09 20 00 mov 0x20098b(%rip),%eax 4006a5: 83 c0 01 add $0x1,%eax 4006a8: 89 05 82 09 20 00 mov %eax,0x200982(%rip) 4006ae: 8b 05 7c 09 20 00 mov 0x20097c(%rip),%eax 4006b4: 83 f8 02 cmp $0x2,%eax 4006b7: 7e e5 jle 40069e <loop_test+0xb6> 4006b9: 90 nop This example uses x86_64, but the same problem occurs for most architectures. Note the two NOP instructions. The DWARF line table information produced for this code looks like this: advance Address by 5 to 0x400692 and Line by 7 to 31 advance Address by 10 to 0x40069c and Line by 1 to 32 advance Address by 2 to 0x40069e and Line by 2 to 34 advance Address by 1 to 0x40069f and Line by -1 to 33 advance Address by 15 to 0x4006ae and Line by 1 to 34 advance Address by 11 to 0x4006b9 and Line by 1 to 35 (Note the backwards movement of the line number to the start of line 33). The problem is line 34. According to the line table it starts at address 0x40069e whereas in reality it starts at 0x4006ae. What has happened is that gcc has generated a NOP instruction to be the destination of the goto on line 34 - ie the label a: - but rather than associate it with line 33, it has associated it with line 34. This means that line 34 now appears twice in the line number table. I am offering the patch below as a fix for this problem, although I am open to suggestions for better solutions. The patch creates a hash table for instructions which gcc knows should not be considered to be the start of a source code line, then it checks this table when noticing source line changes. The patch only adds the NOP instructions generated for the test code above, although I expect that in the future there may be other places that need to record this information. Tested with no regressions on an x86_64-pc-linux toolchain. Comments / questions / approval ? Cheers Nick gcc/ChangeLog 2015-10-22 Nick Clifton <ni...@redhat.com> * cfgrtl.c (not_a_stmt_hasher): New struct. (not_a_stmt_htab): New hash table. (is_a_stmt): New function. Returns true iff the given insn is on the list of insns known not to start a line of source code. (fixup_reorder_chain): Add generated NOP instructions to the list of instructions that are known not to start a source line. * cfgrtl.h (is_a_stmt): Add prototype. * final.c (notice_source_line): Use the new function to set the is_stmt return value. Index: gcc/cfgrtl.c =================================================================== --- gcc/cfgrtl.c (revision 229162) +++ gcc/cfgrtl.c (working copy) @@ -3665,7 +3665,21 @@ compact_blocks (); } +struct not_a_stmt_hasher : ggc_cache_ptr_hash<rtx_def> +{ + static hashval_t hash (rtx x) { return htab_hash_pointer (x); } + static bool equal (rtx a, rtx b) { return a == b; } +}; +static GTY((cache)) hash_table<not_a_stmt_hasher> *not_a_stmt_htab; + +/* Return TRUE if INSN is on the list of not-an-insn. */ +bool +is_a_stmt (rtx_insn *insn) +{ + return not_a_stmt_htab == NULL || ! not_a_stmt_htab->find (insn); +} + /* Given a reorder chain, rearrange the code to match. */ static void @@ -3944,8 +3958,18 @@ } nb = split_edge (e); if (!INSN_P (BB_END (nb))) - BB_END (nb) = emit_insn_after_noloc (gen_nop (), BB_END (nb), - nb); + { + BB_END (nb) = emit_insn_after_noloc (gen_nop (), BB_END (nb), + nb); + + /* Note that this insn is a fake and should not be considered + to be the starting insn of a line of source code. */ + if (not_a_stmt_htab == NULL) + not_a_stmt_htab = hash_table<not_a_stmt_hasher>::create_ggc (17); + rtx_def ** slot; + slot = not_a_stmt_htab->find_slot (BB_END (nb), INSERT); + * slot = BB_END (nb); + } INSN_LOCATION (BB_END (nb)) = e->goto_locus; /* If there are other incoming edges to the destination block Index: gcc/cfgrtl.h =================================================================== --- gcc/cfgrtl.h (revision 229162) +++ gcc/cfgrtl.h (working copy) @@ -54,5 +54,6 @@ extern void cfg_layout_finalize (void); extern void break_superblocks (void); extern void init_rtl_bb_info (basic_block); +extern bool is_a_stmt (rtx_insn *); #endif /* GCC_CFGRTL_H */ Index: gcc/final.c =================================================================== --- gcc/final.c (revision 229162) +++ gcc/final.c (working copy) @@ -3081,7 +3081,7 @@ last_filename = filename; last_linenum = linenum; last_discriminator = discriminator; - *is_stmt = true; + *is_stmt = is_a_stmt (insn); high_block_linenum = MAX (last_linenum, high_block_linenum); high_function_linenum = MAX (last_linenum, high_function_linenum); return true;