[PATCH] dwarf2cfi: Defer queued register saves some more [PR99334]

Jason Merrill jason@redhat.com
Thu Mar 25 21:43:57 GMT 2021


On 3/24/21 5:31 AM, Jakub Jelinek wrote:
> Hi!
> 
> On the testcase in the PR with
> -fno-tree-sink -O3 -fPIC -fomit-frame-pointer -fno-strict-aliasing -mstackrealign
> we have prologue:
> 0000000000000000 <_func_with_dwarf_issue_>:
>     0:	4c 8d 54 24 08       	lea    0x8(%rsp),%r10
>     5:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
>     9:	41 ff 72 f8          	pushq  -0x8(%r10)
>     d:	55                   	push   %rbp
>     e:	48 89 e5             	mov    %rsp,%rbp
>    11:	41 57                	push   %r15
>    13:	41 56                	push   %r14
>    15:	41 55                	push   %r13
>    17:	41 54                	push   %r12
>    19:	41 52                	push   %r10
>    1b:	53                   	push   %rbx
>    1c:	48 83 ec 20          	sub    $0x20,%rsp
> and emit
> 00000000 0000000000000014 00000000 CIE
>    Version:               1
>    Augmentation:          "zR"
>    Code alignment factor: 1
>    Data alignment factor: -8
>    Return address column: 16
>    Augmentation data:     1b
>    DW_CFA_def_cfa: r7 (rsp) ofs 8
>    DW_CFA_offset: r16 (rip) at cfa-8
>    DW_CFA_nop
>    DW_CFA_nop
> 
> 00000018 0000000000000044 0000001c FDE cie=00000000 pc=0000000000000000..00000000000001d5
>    DW_CFA_advance_loc: 5 to 0000000000000005
>    DW_CFA_def_cfa: r10 (r10) ofs 0
>    DW_CFA_advance_loc: 9 to 000000000000000e
>    DW_CFA_expression: r6 (rbp) (DW_OP_breg6 (rbp): 0)
>    DW_CFA_advance_loc: 13 to 000000000000001b
>    DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -40; DW_OP_deref)
>    DW_CFA_expression: r15 (r15) (DW_OP_breg6 (rbp): -8)
>    DW_CFA_expression: r14 (r14) (DW_OP_breg6 (rbp): -16)
>    DW_CFA_expression: r13 (r13) (DW_OP_breg6 (rbp): -24)
>    DW_CFA_expression: r12 (r12) (DW_OP_breg6 (rbp): -32)
> ...
> unwind info for that.  The problem is when async signal
> (or stepping through in the debugger) stops after the pushq %rbp
> instruction and before movq %rsp, %rbp, the unwind info says that
> caller's %rbp is saved there at *%rbp, but that is not true, caller's
> %rbp is either still available in the %rbp register, or in *%rsp,
> only after executing the next instruction - movq %rsp, %rbp - the
> location for %rbp is correct.  So, either we'd need to temporarily
> say:
>    DW_CFA_advance_loc: 9 to 000000000000000e
>    DW_CFA_expression: r6 (rbp) (DW_OP_breg7 (rsp): 0)
>    DW_CFA_advance_loc: 3 to 0000000000000011
>    DW_CFA_expression: r6 (rbp) (DW_OP_breg6 (rbp): 0)
>    DW_CFA_advance_loc: 10 to 000000000000001b
> or to me it seems more compact to just say:
>    DW_CFA_advance_loc: 12 to 0000000000000011
>    DW_CFA_expression: r6 (rbp) (DW_OP_breg6 (rbp): 0)
>    DW_CFA_advance_loc: 10 to 000000000000001b
> 
> The following patch implements the latter.  It keeps the call/jump and
> flush queue handling as is and only moves the clobbers_queued_reg_save
> forced flushes from before the instruction to immediately after the first
> instruction.  My reading of libgcc unwinder is that while for calls it
> executes cfi instruction until < pc (but pc is the end of call anyway),
> for async signal stops it executes cfi instruction until <= pc, so it
> will only omit cfi instructions starting with advance to a higher loc.
> On the testcase from the PR, the difference with the patch is:
> @@ -16,9 +16,9 @@ Contents of the .eh_frame section:
>   00000018 0000000000000044 0000001c FDE cie=00000000 pc=0000000000000000..00000000000001d5
>     DW_CFA_advance_loc: 5 to 0000000000000005
>     DW_CFA_def_cfa: r10 (r10) ofs 0
> -  DW_CFA_advance_loc: 9 to 000000000000000e
> +  DW_CFA_advance_loc: 12 to 0000000000000011
>     DW_CFA_expression: r6 (rbp) (DW_OP_breg6 (rbp): 0)
> -  DW_CFA_advance_loc: 13 to 000000000000001b
> +  DW_CFA_advance_loc: 10 to 000000000000001b
>     DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -40; DW_OP_deref)
>     DW_CFA_expression: r15 (r15) (DW_OP_breg6 (rbp): -8)
>     DW_CFA_expression: r14 (r14) (DW_OP_breg6 (rbp): -16)
> When looking at other changes, e.g. on cc1plus itself when it has
> identical objdump -dr, the without patch to with patch difference
> is e.g.:
>   0005f388 000000000000002c 0005f38c FDE cie=00000000 pc=0000000000e2fc80..0000000000e2fe8d
>     DW_CFA_advance_loc: 1 to 0000000000e2fc81
>     DW_CFA_def_cfa_offset: 16
>     DW_CFA_offset: r6 (rbp) at cfa-16
>     DW_CFA_advance_loc: 3 to 0000000000e2fc84
>     DW_CFA_def_cfa_register: r6 (rbp)
> -  DW_CFA_advance_loc: 6 to 0000000000e2fc8a
> +  DW_CFA_advance_loc: 9 to 0000000000e2fc8d
>     DW_CFA_offset: r15 (r15) at cfa-24
>     DW_CFA_offset: r14 (r14) at cfa-32
>     DW_CFA_offset: r13 (r13) at cfa-40
> -  DW_CFA_advance_loc: 6 to 0000000000e2fc90
> +  DW_CFA_advance_loc: 5 to 0000000000e2fc92
>     DW_CFA_offset: r12 (r12) at cfa-48
>     DW_CFA_offset: r3 (rbx) at cfa-56
> -  DW_CFA_advance_loc2: 399 to 0000000000e2fe1f
> +  DW_CFA_advance_loc2: 397 to 0000000000e2fe1f
>     DW_CFA_remember_state
>     DW_CFA_def_cfa: r7 (rsp) ofs 8
>     DW_CFA_advance_loc: 1 to 0000000000e2fe20
>     DW_CFA_restore_state
>     DW_CFA_nop
>     DW_CFA_nop
> for
> 0000000000e2fc80 <_ZL13result_vectoriP7rtx_def>:
>    e2fc80:       55                      push   %rbp
>    e2fc81:       48 89 e5                mov    %rsp,%rbp
>    e2fc84:       41 57                   push   %r15
>    e2fc86:       41 56                   push   %r14
>    e2fc88:       41 55                   push   %r13
>    e2fc8a:       45 31 ed                xor    %r13d,%r13d
>    e2fc8d:       41 54                   push   %r12
>    e2fc8f:       53                      push   %rbx
>    e2fc90:       31 db                   xor    %ebx,%ebx
>    e2fc92:       48 81 ec 98 02 00 00    sub    $0x298,%rsp
>    e2fc99:       89 7d c4                mov    %edi,-0x3c(%rbp)
> ...
> prologue, i.e. the change is whether DW_CFA_offset: r13 (r13) at cfa-40
> starts applying before or after the xorl %r13d,%r13d instruction
> and whether DW_CFA_offset: r3 (rbx) at cfa-56 starts applying
> before or after the xorl %ebx,%ebx instruction.
> Or:
> -00061590 0000000000000020 00061594 FDE cie=00000000 pc=0000000000e3d160..0000000000e3d1af
> +00061590 000000000000001c 00061594 FDE cie=00000000 pc=0000000000e3d160..0000000000e3d1af
>     DW_CFA_advance_loc: 1 to 0000000000e3d161
>     DW_CFA_def_cfa_offset: 16
>     DW_CFA_offset: r6 (rbp) at cfa-16
>     DW_CFA_advance_loc: 9 to 0000000000e3d16a
>     DW_CFA_def_cfa_register: r6 (rbp)
> -  DW_CFA_advance_loc: 2 to 0000000000e3d16c
> +  DW_CFA_advance_loc: 5 to 0000000000e3d16f
>     DW_CFA_offset: r12 (r12) at cfa-24
> -  DW_CFA_advance_loc1: 66 to 0000000000e3d1ae
> +  DW_CFA_advance_loc: 63 to 0000000000e3d1ae
>     DW_CFA_def_cfa: r7 (rsp) ofs 8
> -  DW_CFA_nop
> -  DW_CFA_nop
> -  DW_CFA_nop
> for:
> 0000000000e3d160 <_Z23builtin_memset_read_strPvl15scalar_int_mode>:
>    e3d160:       55                      push   %rbp
>    e3d161:       48 63 c2                movslq %edx,%rax
>    e3d164:       49 89 f8                mov    %rdi,%r8
>    e3d167:       48 89 e5                mov    %rsp,%rbp
>    e3d16a:       41 54                   push   %r12
>    e3d16c:       49 89 c4                mov    %rax,%r12
>    e3d16f:       48 83 ec 08             sub    $0x8,%rsp
> again, whether DW_CFA_offset: r12 (r12) at cfa-24 starts applying
> before movq %rax,%r12 instruction or after it.
> I think if for some instructions it is possible that debugger or signal
> would stop in the middle of instruction, with some side-effects of the
> instruction already done and others not yet, then we can't do that,
> but at the start of instructions that modify the register the side-effects
> of the instruction should not have taken effect yet.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux.

LGTM.  This seems consistent with when we flush due to clobbers in 
dwarf2out_frame_debug.

> Will e.g. GDB be happy about the changes?

I would expect so, but it would be good to have someone from GDB verify.

> Thinking some more about this, what can be problematic on the GCC side
> is inline asm, that can and often will contain multiple instructions.
> For that is an easy fix, just test asm_noperands and handle
> clobbers_queued_reg_save before the insn rather than after in that case.

Sure, but does inline asm go through dwarf2out_frame_debug?

> But there is another problem, instruction patterns that emit multiple
> hw instructions, code can stop in between them.  So, do we want some
> instruction attribute that would conservatively tell us which instruction
> patterns are guaranteed to be single machine instructions?

It seems to me that in that situation you'd want to add the save to 
*%rsp, and still not update to *%rbp until after the combined instruction.

Incidentally, it seems a bit odd that clobbers_queued_reg_save returns 
true both for clobbering the saved register and for clobbering the save 
location; if I already know that %rbp is saved at *%rsp, why do I care 
that the insn we're looking at clobbers %rbp?  Maybe we should only 
check modified_in_p (q->reg, insn) if q->reg doesn't appear in 
regs_saved_in_regs.  That wouldn't help with this testcase, though.

> 2021-03-24  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR debug/99334
> 	* dwarf2cfi.c (scan_trace): For clobbers_queued_reg_save (insn)
> 	forced flushes of queued register saves for non-call/jump insns
> 	that can't throw, emit them first after the insn rather than
> 	before it.
> 
> --- gcc/dwarf2cfi.c.jj	2021-03-02 11:25:47.217727061 +0100
> +++ gcc/dwarf2cfi.c	2021-03-23 17:34:58.240281522 +0100
> @@ -2705,12 +2705,15 @@ scan_trace (dw_trace_info *trace, bool e
>   	      dwarf2out_flush_queued_reg_saves ();
>   	    }
>   	  else if (!NONJUMP_INSN_P (insn)
> -		   || clobbers_queued_reg_save (insn)
>   		   || find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL))
>   	    dwarf2out_flush_queued_reg_saves ();
>   	  any_cfis_emitted = false;
>   
>   	  add_cfi_insn = insn;
> +
> +	  if (queued_reg_saves.length () && clobbers_queued_reg_save (insn))
> +	    dwarf2out_flush_queued_reg_saves ();
> +
>   	  scan_insn_after (insn);
>   	  control = insn;
>   	}
> 
> 
> 	Jakub
> 



More information about the Gdb mailing list