public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] dwarf: Multi-register CFI address support.
@ 2021-06-13 13:27 Hafiz Abid Qadeer
  2021-07-22 10:58 ` Hafiz Abid Qadeer
  2021-11-09 15:59 ` Jakub Jelinek
  0 siblings, 2 replies; 15+ messages in thread
From: Hafiz Abid Qadeer @ 2021-06-13 13:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub, ams, abidh

Add support for architectures such as AMD GCN, in which the pointer size is
larger than the register size.  This allows the CFI information to include
multi-register locations for the stack pointer, frame pointer, and return
address.

This patch was originally posted by Andrew Stubbs in
https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html

It has now been re-worked according to the review comments. It does not use
DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses
DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple
consecutive registers. Here is how .debug_frame looks before and after this
patch:

$ cat factorial.c
int factorial(int n) {
  if (n == 0) return 1;
  return n * factorial (n - 1);
}

$ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o
$ llvm-dwarfdump -debug-frame fac.o

*** without this patch (edited for brevity)***

00000000 00000014 ffffffff CIE

  DW_CFA_def_cfa: reg48 +0
  DW_CFA_register: reg16 reg50

00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac
  DW_CFA_advance_loc4: 96
  DW_CFA_offset: reg46 0
  DW_CFA_offset: reg47 4
  DW_CFA_offset: reg50 8
  DW_CFA_offset: reg51 12
  DW_CFA_offset: reg16 8
  DW_CFA_advance_loc4: 4
  DW_CFA_def_cfa_sf: reg46 -16

*** with this patch (edited for brevity)***

00000000 00000024 ffffffff CIE

  DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
  DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus

00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
  DW_CFA_advance_loc4: 96
  DW_CFA_offset: reg46 0
  DW_CFA_offset: reg47 4
  DW_CFA_offset: reg50 8
  DW_CFA_offset: reg51 12
  DW_CFA_offset: reg16 8
  DW_CFA_advance_loc4: 4
  DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus

gcc/ChangeLog:

	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
	(dw_frame_pointer_regnum): Likewise.
	(new_cfi_row): Use set_by_dwreg.
	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,
	DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus.
	(lookup_cfa_1): Use set_by_dwreg.
	(def_cfa_0): Update for cfa_reg and support register spans.
	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
	spans.
	(dwf_cfa_reg): New function.
	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
	dwf_regno.
	(dwarf2out_frame_debug_def_cfa): Likewise.
	(dwarf2out_frame_debug_adjust_cfa): Likewise.
	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
	(dwarf2out_frame_debug_cfa_register): Likewise.
	(dwarf2out_frame_debug_expr): Likewise.
	(create_pseudo_cfg): Use set_by_dwreg.
	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
	(create_cie_data): Use dwf_cfa_reg.
	(execute_dwarf2_frame): Use dwf_cfa_reg.
	(dump_cfi_row): Use set_by_dwreg.
	* dwarf2out.c (build_span_loc, build_breg_loc): New function.
	(build_cfa_loc): Support register spans.
	(build_cfa_aligned_loc): Update cfa_reg usage.
	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
	* dwarf2out.h (struct cfa_reg): New type.
	(struct dw_cfa_location): Use struct cfa_reg.
	(build_span_loc): New prototype.
	* gengtype.c (main): Accept poly_uint16_pod type.
---
 gcc/dwarf2cfi.c | 260 ++++++++++++++++++++++++++++++++++++------------
 gcc/dwarf2out.c |  55 +++++++++-
 gcc/dwarf2out.h |  37 ++++++-
 gcc/gengtype.c  |   1 +
 4 files changed, 283 insertions(+), 70 deletions(-)

diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index c27ac1960b0..5aacdcd094a 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
 static bool any_cfis_emitted;
 
 /* Short-hand for commonly used register numbers.  */
-static unsigned dw_stack_pointer_regnum;
-static unsigned dw_frame_pointer_regnum;
+static struct cfa_reg dw_stack_pointer_regnum;
+static struct cfa_reg dw_frame_pointer_regnum;
 \f
 /* Hook used by __throw.  */
 
@@ -430,7 +430,7 @@ new_cfi_row (void)
 {
   dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
 
-  row->cfa.reg = INVALID_REGNUM;
+  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
 
   return row;
 }
@@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
   cfa->offset = 0;
   cfa->base_offset = 0;
   cfa->indirect = 0;
-  cfa->reg = -1;
+  cfa->reg.set_by_dwreg (INVALID_REGNUM);
 
   for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
     {
@@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_reg29:
 	case DW_OP_reg30:
 	case DW_OP_reg31:
-	  cfa->reg = op - DW_OP_reg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
 	  break;
 	case DW_OP_regx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  break;
 	case DW_OP_breg0:
 	case DW_OP_breg1:
@@ -615,16 +615,92 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_breg29:
 	case DW_OP_breg30:
 	case DW_OP_breg31:
-	  cfa->reg = op - DW_OP_breg0;
-	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
-	  break;
 	case DW_OP_bregx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
-	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
+	  if (cfa->reg.reg == INVALID_REGNUM)
+	    {
+	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
+		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));
+	      cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
+	    }
+	  else
+	    {
+	      /* Handle case when span can cover multiple registers.  We
+		 only support the simple case of consecutive registers
+		 all with the same size.  DWARF that we are dealing with
+		 will look something like:
+		 <DW_OP_bregx: (r49) 0; DW_OP_const1u: 32; DW_OP_shl;
+		  DW_OP_bregx: (r48) 0; DW_OP_plus> */
+
+	      unsigned int regno = (op == DW_OP_bregx)
+		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);
+	      gcc_assert (regno == (cfa->reg.reg - 1));
+	      cfa->reg.span++;
+	      /* From all the consecutive registers used, we want to set
+	         cfa->reg.reg to lower number register.  */
+	      cfa->reg.reg = regno;
+	      /* The offset was the shift value.  Use it to get the
+		 span_width and then set it to 0.  */
+	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);
+	      cfa->offset = 0;
+	    }
 	  break;
 	case DW_OP_deref:
 	  cfa->indirect = 1;
 	  break;
+	case DW_OP_shl:
+	  break;
+	case DW_OP_lit0:
+	case DW_OP_lit1:
+	case DW_OP_lit2:
+	case DW_OP_lit3:
+	case DW_OP_lit4:
+	case DW_OP_lit5:
+	case DW_OP_lit6:
+	case DW_OP_lit7:
+	case DW_OP_lit8:
+	case DW_OP_lit9:
+	case DW_OP_lit10:
+	case DW_OP_lit11:
+	case DW_OP_lit12:
+	case DW_OP_lit13:
+	case DW_OP_lit14:
+	case DW_OP_lit15:
+	case DW_OP_lit16:
+	case DW_OP_lit17:
+	case DW_OP_lit18:
+	case DW_OP_lit19:
+	case DW_OP_lit20:
+	case DW_OP_lit21:
+	case DW_OP_lit22:
+	case DW_OP_lit23:
+	case DW_OP_lit24:
+	case DW_OP_lit25:
+	case DW_OP_lit26:
+	case DW_OP_lit27:
+	case DW_OP_lit28:
+	case DW_OP_lit29:
+	case DW_OP_lit30:
+	case DW_OP_lit31:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = op - DW_OP_lit0;
+	  break;
+	case DW_OP_const1u:
+	case DW_OP_const1s:
+	case DW_OP_const2u:
+	case DW_OP_const2s:
+	case DW_OP_const4s:
+	case DW_OP_const8s:
+	case DW_OP_constu:
+	case DW_OP_consts:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_minus:
+	  cfa->offset = -cfa->offset;
+	  break;
+	case DW_OP_plus:
+	  /* The offset is already in place.  */
+	  break;
 	case DW_OP_plus_uconst:
 	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
 	  break;
@@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
       loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_register:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       break;
     case DW_CFA_def_cfa:
     case DW_CFA_def_cfa_sf:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_expression:
@@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 
   HOST_WIDE_INT const_offset;
   if (new_cfa->reg == old_cfa->reg
+      && new_cfa->reg.span == 1
       && !new_cfa->indirect
       && !old_cfa->indirect
       && new_cfa->offset.is_constant (&const_offset))
@@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
     }
   else if (new_cfa->offset.is_constant ()
 	   && known_eq (new_cfa->offset, old_cfa->offset)
-	   && old_cfa->reg != INVALID_REGNUM
+	   && old_cfa->reg.reg != INVALID_REGNUM
+	   && new_cfa->reg.span == 1
 	   && !new_cfa->indirect
 	   && !old_cfa->indirect)
     {
@@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	 been set as a register plus offset rather than a general
 	 DW_CFA_def_cfa_expression.  */
       cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
     }
   else if (new_cfa->indirect == 0
-	   && new_cfa->offset.is_constant (&const_offset))
+	   && new_cfa->offset.is_constant (&const_offset)
+	   && new_cfa->reg.span == 1)
     {
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
 	 indicating the CFA register has changed to <register> with
@@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
       else
 	cfi->dw_cfi_opc = DW_CFA_def_cfa;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
       cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
     }
   else
@@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
 }
 
 /* Add the CFI for saving a register.  REG is the CFA column number.
-   If SREG is -1, the register is saved at OFFSET from the CFA;
+   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
+reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
 {
   dw_fde_ref fde = cfun ? cfun->fde : NULL;
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
 
-  if (sreg == INVALID_REGNUM)
+  if (sreg.reg == INVALID_REGNUM)
     {
       HOST_WIDE_INT const_offset;
       /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
@@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	    = build_cfa_loc (&cur_row->cfa, offset);
 	}
     }
-  else if (sreg == reg)
+  else if (sreg.reg == reg)
     {
       /* While we could emit something like DW_CFA_same_value or
 	 DW_CFA_restore, we never expect to see something like that
@@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	 can always bypass this by using REG_CFA_RESTORE directly.  */
       gcc_unreachable ();
     }
+  else if (sreg.span > 1)
+    {
+      cfi->dw_cfi_opc = DW_CFA_expression;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
+    }
   else
     {
       cfi->dw_cfi_opc = DW_CFA_register;
-      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
     }
 
   add_cfi (cfi);
@@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
   return DWARF_FRAME_REGNUM (REGNO (reg));
 }
 
+/* Like dwf_regno, but when the value can span multiple registers.  */
+
+static struct cfa_reg
+dwf_cfa_reg (rtx reg)
+{
+  struct cfa_reg result;
+
+  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
+
+  result.reg = dwf_regno (reg);
+  result.span = 1;
+  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
+
+  rtx span = targetm.dwarf_register_span (reg);
+  if (span)
+    {
+      /* We only support the simple case of consecutive registers all with the
+	 same size.  */
+      result.span = XVECLEN (span, 0);
+      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
+
+#if CHECKING_P
+      /* Ensure that the above assumption is accurate.  */
+      for (unsigned int i = 0; i < result.span; i++)
+	{
+	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
+								  0, i))),
+				result.span_width));
+	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
+	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
+	}
+#endif
+    }
+
+  return result;
+}
+
 /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
 
 static bool
@@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
 
   FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     {
-      unsigned int reg, sreg;
+      unsigned int reg;
+      struct cfa_reg sreg;
 
       record_reg_saved_in_reg (q->saved_reg, q->reg);
 
@@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
       else
         reg = dwf_regno (q->reg);
       if (q->saved_reg)
-	sreg = dwf_regno (q->saved_reg);
+	sreg = dwf_cfa_reg (q->saved_reg);
       else
-	sreg = INVALID_REGNUM;
+	sreg.set_by_dwreg (INVALID_REGNUM);
       reg_save (reg, sreg, q->cfa_offset);
     }
 
@@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
   /* ??? If this fails, we could be calling into the _loc functions to
      define a full expression.  So far no port does that.  */
   gcc_assert (REG_P (pat));
-  cur_cfa->reg = dwf_regno (pat);
+  cur_cfa->reg = dwf_cfa_reg (pat);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
@@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
   switch (GET_CODE (src))
     {
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
       cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
@@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
       gcc_unreachable ();
     }
 
-  cur_cfa->reg = dwf_regno (dest);
+  cur_cfa->reg = dwf_cfa_reg (dest);
   gcc_assert (cur_cfa->indirect == 0);
 }
 
@@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   switch (GET_CODE (addr))
     {
     case REG:
-      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
       offset = -cur_cfa->offset;
       break;
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
       offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
@@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
+  struct cfa_reg invalid;
+  invalid.set_by_dwreg (INVALID_REGNUM);
   if (!span)
-    reg_save (sregno, INVALID_REGNUM, offset);
+    reg_save (sregno, invalid, offset);
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
@@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 	{
 	  rtx elem = XVECEXP (span, 0, par_index);
 	  sregno = dwf_regno (src);
-	  reg_save (sregno, INVALID_REGNUM, span_offset);
+	  reg_save (sregno, invalid, span_offset);
 	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	}
     }
@@ -1270,7 +1395,8 @@ static void
 dwarf2out_frame_debug_cfa_register (rtx set)
 {
   rtx src, dest;
-  unsigned sregno, dregno;
+  unsigned sregno;
+  struct cfa_reg dregno;
 
   src = XEXP (set, 1);
   dest = XEXP (set, 0);
@@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
   else
     sregno = dwf_regno (src);
 
-  dregno = dwf_regno (dest);
+  dregno = dwf_cfa_reg (dest);
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
@@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	{
 	  /* Setting FP from SP.  */
 	case REG:
-	  if (cur_cfa->reg == dwf_regno (src))
+	  if (cur_cfa->reg == dwf_cfa_reg (src))
 	    {
 	      /* Rule 1 */
 	      /* Update the CFA rule wrt SP or FP.  Make sure src is
@@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		 ARM copies SP to a temporary register, and from there to
 		 FP.  So we just rely on the backends to only set
 		 RTX_FRAME_RELATED_P on appropriate insns.  */
-	      cur_cfa->reg = dwf_regno (dest);
+	      cur_cfa->reg = dwf_cfa_reg (dest);
 	      cur_trace->cfa_temp.reg = cur_cfa->reg;
 	      cur_trace->cfa_temp.offset = cur_cfa->offset;
 	    }
@@ -1698,7 +1824,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		{
 		  gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
 			      && fde->drap_reg != INVALID_REGNUM
-			      && cur_cfa->reg != dwf_regno (src)
+			      && cur_cfa->reg != dwf_cfa_reg (src)
 			      && fde->rule18);
 		  fde->rule18 = 0;
 		  /* The save of hard frame pointer has been deferred
@@ -1722,7 +1848,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      /* Adjusting SP.  */
 	      if (REG_P (XEXP (src, 1)))
 		{
-		  gcc_assert (dwf_regno (XEXP (src, 1))
+		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
 			      == cur_trace->cfa_temp.reg);
 		  offset = cur_trace->cfa_temp.offset;
 		}
@@ -1756,7 +1882,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      gcc_assert (frame_pointer_needed);
 
 	      gcc_assert (REG_P (XEXP (src, 0))
-			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
 	      offset = rtx_to_poly_int64 (XEXP (src, 1));
 	      if (GET_CODE (src) != MINUS)
 		offset = -offset;
@@ -1769,14 +1895,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 4 */
 	      if (REG_P (XEXP (src, 0))
-		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
+		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
 		  && poly_int_rtx_p (XEXP (src, 1), &offset))
 		{
 		  /* Setting a temporary CFA register that will be copied
 		     into the FP later on.  */
 		  offset = -offset;
 		  cur_cfa->offset += offset;
-		  cur_cfa->reg = dwf_regno (dest);
+		  cur_cfa->reg = dwf_cfa_reg (dest);
 		  /* Or used to save regs to the stack.  */
 		  cur_trace->cfa_temp.reg = cur_cfa->reg;
 		  cur_trace->cfa_temp.offset = cur_cfa->offset;
@@ -1784,13 +1910,13 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 5 */
 	      else if (REG_P (XEXP (src, 0))
-		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		       && XEXP (src, 1) == stack_pointer_rtx)
 		{
 		  /* Setting a scratch register that we will use instead
 		     of SP for saving registers to the stack.  */
 		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
-		  cur_trace->cfa_store.reg = dwf_regno (dest);
+		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
 		  cur_trace->cfa_store.offset
 		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
 		}
@@ -1799,7 +1925,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      else if (GET_CODE (src) == LO_SUM
 		       && poly_int_rtx_p (XEXP (src, 1),
 					  &cur_trace->cfa_temp.offset))
-		cur_trace->cfa_temp.reg = dwf_regno (dest);
+		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	      else
 		gcc_unreachable ();
 	    }
@@ -1808,17 +1934,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 6 */
 	case CONST_INT:
 	case CONST_POLY_INT:
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
 	  break;
 
 	  /* Rule 7 */
 	case IOR:
 	  gcc_assert (REG_P (XEXP (src, 0))
-		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		      && CONST_INT_P (XEXP (src, 1)));
 
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
 			  &cur_trace->cfa_temp.offset))
 	    /* The target shouldn't generate this kind of CFI note if we
@@ -1851,14 +1977,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      dwarf2out_flush_queued_reg_saves ();
 
               gcc_assert (cur_trace->cfa_store.reg
-			  == dwf_regno (XEXP (src, 0)));
+			  == dwf_cfa_reg (XEXP (src, 0)));
               fde->stack_realign = 1;
               fde->stack_realignment = INTVAL (XEXP (src, 1));
               cur_trace->cfa_store.offset = 0;
 
 	      if (cur_cfa->reg != dw_stack_pointer_regnum
 		  && cur_cfa->reg != dw_frame_pointer_regnum)
-		fde->drap_reg = cur_cfa->reg;
+		{
+		  gcc_assert (cur_cfa->reg.span == 1);
+		  fde->drap_reg = cur_cfa->reg.reg;
+		}
             }
           return;
 
@@ -1935,14 +2064,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 	case MINUS:
 	case LO_SUM:
 	  {
-	    unsigned int regno;
+	    struct cfa_reg regno;
 
 	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
 	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
 	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
 	      offset = -offset;
 
-	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
+	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset -= cur_cfa->offset;
@@ -1960,7 +2089,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Without an offset.  */
 	case REG:
 	  {
-	    unsigned int regno = dwf_regno (XEXP (dest, 0));
+	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset = -cur_cfa->offset;
@@ -1977,7 +2106,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 14 */
 	case POST_INC:
 	  gcc_assert (cur_trace->cfa_temp.reg
-		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
+		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
 	  offset = -cur_trace->cfa_temp.offset;
 	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
 	  break;
@@ -1995,7 +2124,7 @@ dwarf2out_frame_debug_expr (rtx expr)
       if (REG_P (src)
 	  && REGNO (src) != STACK_POINTER_REGNUM
 	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
-	  && dwf_regno (src) == cur_cfa->reg)
+	  && dwf_cfa_reg (src) == cur_cfa->reg)
 	{
 	  /* We're storing the current CFA reg into the stack.  */
 
@@ -2012,7 +2141,7 @@ dwarf2out_frame_debug_expr (rtx expr)
                   && cur_cfa->indirect == 0
                   && cur_cfa->reg != dw_frame_pointer_regnum)
                 {
-		  gcc_assert (fde->drap_reg == cur_cfa->reg);
+		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
 
 		  cur_cfa->indirect = 1;
 		  cur_cfa->reg = dw_frame_pointer_regnum;
@@ -2039,7 +2168,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		x = XEXP (x, 0);
 	      gcc_assert (REG_P (x));
 
-	      cur_cfa->reg = dwf_regno (x);
+	      cur_cfa->reg = dwf_cfa_reg (x);
 	      cur_cfa->base_offset = offset;
 	      cur_cfa->indirect = 1;
 	      break;
@@ -2951,7 +3080,7 @@ create_pseudo_cfg (void)
   ti.head = get_insns ();
   ti.beg_row = cie_cfi_row;
   ti.cfa_store = cie_cfi_row->cfa;
-  ti.cfa_temp.reg = INVALID_REGNUM;
+  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
   trace_info.quick_push (ti);
 
   if (cie_return_save)
@@ -3014,14 +3143,15 @@ create_pseudo_cfg (void)
 static void
 initial_return_save (rtx rtl)
 {
-  unsigned int reg = INVALID_REGNUM;
+  struct cfa_reg reg;
+  reg.set_by_dwreg (INVALID_REGNUM);
   poly_int64 offset = 0;
 
   switch (GET_CODE (rtl))
     {
     case REG:
       /* RA is in a register.  */
-      reg = dwf_regno (rtl);
+      reg = dwf_cfa_reg (rtl);
       break;
 
     case MEM:
@@ -3062,9 +3192,9 @@ initial_return_save (rtx rtl)
       gcc_unreachable ();
     }
 
-  if (reg != DWARF_FRAME_RETURN_COLUMN)
+  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
     {
-      if (reg != INVALID_REGNUM)
+      if (reg.reg != INVALID_REGNUM)
         record_reg_saved_in_reg (rtl, pc_rtx);
       reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
     }
@@ -3076,7 +3206,8 @@ create_cie_data (void)
   dw_cfa_location loc;
   dw_trace_info cie_trace;
 
-  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
+  dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode,
+						      STACK_POINTER_REGNUM));
 
   memset (&cie_trace, 0, sizeof (cie_trace));
   cur_trace = &cie_trace;
@@ -3135,7 +3266,8 @@ static unsigned int
 execute_dwarf2_frame (void)
 {
   /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
-  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
+					(Pmode, HARD_FRAME_POINTER_REGNUM));
 
   /* The first time we're called, compute the incoming frame state.  */
   if (cie_cfi_vec == NULL)
@@ -3515,7 +3647,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
     {
       dw_cfa_location dummy;
       memset (&dummy, 0, sizeof (dummy));
-      dummy.reg = INVALID_REGNUM;
+      dummy.reg.set_by_dwreg (INVALID_REGNUM);
       cfi = def_cfa_0 (&dummy, &row->cfa);
     }
   output_cfi_directive (f, cfi);
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 88eb3f9c455..a0b41df6da0 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -2785,6 +2785,44 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
     }
 }
 
+static void
+build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
+{
+  if (regno <= 31)
+    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
+		  (DW_OP_breg0 + regno),  0, 0));
+  else
+    add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0));
+}
+
+/* Build a dwarf location for a cfa_reg spanning multiple
+   consecutive registers.  */
+
+struct dw_loc_descr_node *
+build_span_loc (struct cfa_reg reg)
+{
+  struct dw_loc_descr_node *head = NULL;
+
+  gcc_assert (known_gt (reg.span_width, 0));
+  gcc_assert (reg.span > 1);
+
+  /* Start from the highest number register as it goes in the upper bits.  */
+  unsigned int regno = reg.reg + reg.span - 1;
+  build_breg_loc (&head, regno);
+
+  /* deal with the remaining registers in the span.  */
+  for (int i = (reg.span - 2); i >= 0; i--)
+    {
+      add_loc_descr (&head, int_loc_descriptor
+		    (reg.span_width.to_constant () * 8));
+      add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0));
+      regno--;
+      build_breg_loc (&head, regno);
+      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
+    }
+  return head;
+}
+
 /* This function builds a dwarf location descriptor sequence from a
    dw_cfa_location, adding the given OFFSET to the result of the
    expression.  */
@@ -2796,9 +2834,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
 
   offset += cfa->offset;
 
-  if (cfa->indirect)
+  if (cfa->reg.span > 1)
+    {
+      head = build_span_loc (cfa->reg);
+
+      if (maybe_ne (offset, 0))
+	  loc_descr_plus_const (&head, offset);
+    }
+  else if (cfa->indirect)
     {
-      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
+      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
       head->dw_loc_oprnd1.val_class = dw_val_class_const;
       head->dw_loc_oprnd1.val_entry = NULL;
       tmp = new_loc_descr (DW_OP_deref, 0, 0);
@@ -2806,7 +2851,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
       loc_descr_plus_const (&head, offset);
     }
   else
-    head = new_reg_loc_descr (cfa->reg, offset);
+    head = new_reg_loc_descr (cfa->reg.reg, offset);
 
   return head;
 }
@@ -2824,7 +2869,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
     = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
 
   /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
-  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
+  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
     {
       head = new_reg_loc_descr (dwarf_fp, 0);
       add_loc_descr (&head, int_loc_descriptor (alignment));
@@ -20865,7 +20910,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
   list = NULL;
 
   memset (&next_cfa, 0, sizeof (next_cfa));
-  next_cfa.reg = INVALID_REGNUM;
+  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
   remember = next_cfa;
 
   start_label = fde->dw_fde_begin;
diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
index 54b6343704c..38b52c8e1c2 100644
--- a/gcc/dwarf2out.h
+++ b/gcc/dwarf2out.h
@@ -119,6 +119,40 @@ struct GTY(()) dw_fde_node {
 };
 
 
+/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
+   definitions and expressions.
+   Most architectures only need a single register number, but some (amdgcn)
+   have pointers that span multiple registers.  DWARF permits arbitrary
+   register sets but existing use-cases only require contiguous register
+   sets, as represented here.  */
+struct GTY(()) cfa_reg {
+  unsigned int reg;
+  unsigned int span;
+  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
+
+  cfa_reg& set_by_dwreg (unsigned int r)
+    {
+      reg = r;
+      span = 1;
+      span_width = 0;  /* Unknown size (permitted when span == 1).  */
+      return *this;
+    }
+
+  bool operator== (const cfa_reg other) const
+    {
+      return (reg == other.reg
+	      && span == other.span
+	      && (known_eq (span_width, other.span_width)
+		  || (span == 1
+		      && (known_eq (span_width, 0)
+			  || known_eq (other.span_width, 0)))));
+    }
+  bool operator!= (const cfa_reg other) const
+    {
+      return !(*this == other);
+    }
+};
+
 /* This is how we define the location of the CFA. We use to handle it
    as REG + OFFSET all the time,  but now it can be more complex.
    It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
@@ -128,7 +162,7 @@ struct GTY(()) dw_cfa_location {
   poly_int64_pod offset;
   poly_int64_pod base_offset;
   /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
-  unsigned int reg;
+  struct cfa_reg reg;
   BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
   BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
 };
@@ -285,6 +319,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
   (dw_cfa_location *, poly_int64);
 extern struct dw_loc_descr_node *build_cfa_aligned_loc
   (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
+extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
 extern struct dw_loc_descr_node *mem_loc_descriptor
   (rtx, machine_mode mode, machine_mode mem_mode,
    enum var_init_status);
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index b94e2f126ec..45e9f856470 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -5195,6 +5195,7 @@ main (int argc, char **argv)
       POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("double_int", &pos));
+      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
       POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
       POS_HERE (do_scalar_typedef ("offset_int", &pos));
       POS_HERE (do_scalar_typedef ("widest_int", &pos));
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-06-13 13:27 [PATCH] dwarf: Multi-register CFI address support Hafiz Abid Qadeer
@ 2021-07-22 10:58 ` Hafiz Abid Qadeer
  2021-08-24 15:55   ` Hafiz Abid Qadeer
  2021-11-09 15:59 ` Jakub Jelinek
  1 sibling, 1 reply; 15+ messages in thread
From: Hafiz Abid Qadeer @ 2021-07-22 10:58 UTC (permalink / raw)
  To: Hafiz Abid Qadeer, gcc-patches; +Cc: jakub, ams

Ping.

On 13/06/2021 14:27, Hafiz Abid Qadeer wrote:
> Add support for architectures such as AMD GCN, in which the pointer size is
> larger than the register size.  This allows the CFI information to include
> multi-register locations for the stack pointer, frame pointer, and return
> address.
> 
> This patch was originally posted by Andrew Stubbs in
> https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html
> 
> It has now been re-worked according to the review comments. It does not use
> DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses
> DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple
> consecutive registers. Here is how .debug_frame looks before and after this
> patch:
> 
> $ cat factorial.c
> int factorial(int n) {
>   if (n == 0) return 1;
>   return n * factorial (n - 1);
> }
> 
> $ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o
> $ llvm-dwarfdump -debug-frame fac.o
> 
> *** without this patch (edited for brevity)***
> 
> 00000000 00000014 ffffffff CIE
> 
>   DW_CFA_def_cfa: reg48 +0
>   DW_CFA_register: reg16 reg50
> 
> 00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac
>   DW_CFA_advance_loc4: 96
>   DW_CFA_offset: reg46 0
>   DW_CFA_offset: reg47 4
>   DW_CFA_offset: reg50 8
>   DW_CFA_offset: reg51 12
>   DW_CFA_offset: reg16 8
>   DW_CFA_advance_loc4: 4
>   DW_CFA_def_cfa_sf: reg46 -16
> 
> *** with this patch (edited for brevity)***
> 
> 00000000 00000024 ffffffff CIE
> 
>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
>   DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus
> 
> 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
>   DW_CFA_advance_loc4: 96
>   DW_CFA_offset: reg46 0
>   DW_CFA_offset: reg47 4
>   DW_CFA_offset: reg50 8
>   DW_CFA_offset: reg51 12
>   DW_CFA_offset: reg16 8
>   DW_CFA_advance_loc4: 4
>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus
> 
> gcc/ChangeLog:
> 
> 	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
> 	(dw_frame_pointer_regnum): Likewise.
> 	(new_cfi_row): Use set_by_dwreg.
> 	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
> 	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,
> 	DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus.
> 	(lookup_cfa_1): Use set_by_dwreg.
> 	(def_cfa_0): Update for cfa_reg and support register spans.
> 	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
> 	spans.
> 	(dwf_cfa_reg): New function.
> 	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
> 	dwf_regno.
> 	(dwarf2out_frame_debug_def_cfa): Likewise.
> 	(dwarf2out_frame_debug_adjust_cfa): Likewise.
> 	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
> 	(dwarf2out_frame_debug_cfa_register): Likewise.
> 	(dwarf2out_frame_debug_expr): Likewise.
> 	(create_pseudo_cfg): Use set_by_dwreg.
> 	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
> 	(create_cie_data): Use dwf_cfa_reg.
> 	(execute_dwarf2_frame): Use dwf_cfa_reg.
> 	(dump_cfi_row): Use set_by_dwreg.
> 	* dwarf2out.c (build_span_loc, build_breg_loc): New function.
> 	(build_cfa_loc): Support register spans.
> 	(build_cfa_aligned_loc): Update cfa_reg usage.
> 	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
> 	* dwarf2out.h (struct cfa_reg): New type.
> 	(struct dw_cfa_location): Use struct cfa_reg.
> 	(build_span_loc): New prototype.
> 	* gengtype.c (main): Accept poly_uint16_pod type.
> ---
>  gcc/dwarf2cfi.c | 260 ++++++++++++++++++++++++++++++++++++------------
>  gcc/dwarf2out.c |  55 +++++++++-
>  gcc/dwarf2out.h |  37 ++++++-
>  gcc/gengtype.c  |   1 +
>  4 files changed, 283 insertions(+), 70 deletions(-)
> 
> diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
> index c27ac1960b0..5aacdcd094a 100644
> --- a/gcc/dwarf2cfi.c
> +++ b/gcc/dwarf2cfi.c
> @@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
>  static bool any_cfis_emitted;
>  
>  /* Short-hand for commonly used register numbers.  */
> -static unsigned dw_stack_pointer_regnum;
> -static unsigned dw_frame_pointer_regnum;
> +static struct cfa_reg dw_stack_pointer_regnum;
> +static struct cfa_reg dw_frame_pointer_regnum;
>  \f
>  /* Hook used by __throw.  */
>  
> @@ -430,7 +430,7 @@ new_cfi_row (void)
>  {
>    dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
>  
> -  row->cfa.reg = INVALID_REGNUM;
> +  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
>  
>    return row;
>  }
> @@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>    cfa->offset = 0;
>    cfa->base_offset = 0;
>    cfa->indirect = 0;
> -  cfa->reg = -1;
> +  cfa->reg.set_by_dwreg (INVALID_REGNUM);
>  
>    for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
>      {
> @@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>  	case DW_OP_reg29:
>  	case DW_OP_reg30:
>  	case DW_OP_reg31:
> -	  cfa->reg = op - DW_OP_reg0;
> +	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
>  	  break;
>  	case DW_OP_regx:
> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
> +	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
>  	  break;
>  	case DW_OP_breg0:
>  	case DW_OP_breg1:
> @@ -615,16 +615,92 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>  	case DW_OP_breg29:
>  	case DW_OP_breg30:
>  	case DW_OP_breg31:
> -	  cfa->reg = op - DW_OP_breg0;
> -	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
> -	  break;
>  	case DW_OP_bregx:
> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
> -	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
> +	  if (cfa->reg.reg == INVALID_REGNUM)
> +	    {
> +	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));
> +	      cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
> +	    }
> +	  else
> +	    {
> +	      /* Handle case when span can cover multiple registers.  We
> +		 only support the simple case of consecutive registers
> +		 all with the same size.  DWARF that we are dealing with
> +		 will look something like:
> +		 <DW_OP_bregx: (r49) 0; DW_OP_const1u: 32; DW_OP_shl;
> +		  DW_OP_bregx: (r48) 0; DW_OP_plus> */
> +
> +	      unsigned int regno = (op == DW_OP_bregx)
> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);
> +	      gcc_assert (regno == (cfa->reg.reg - 1));
> +	      cfa->reg.span++;
> +	      /* From all the consecutive registers used, we want to set
> +	         cfa->reg.reg to lower number register.  */
> +	      cfa->reg.reg = regno;
> +	      /* The offset was the shift value.  Use it to get the
> +		 span_width and then set it to 0.  */
> +	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);
> +	      cfa->offset = 0;
> +	    }
>  	  break;
>  	case DW_OP_deref:
>  	  cfa->indirect = 1;
>  	  break;
> +	case DW_OP_shl:
> +	  break;
> +	case DW_OP_lit0:
> +	case DW_OP_lit1:
> +	case DW_OP_lit2:
> +	case DW_OP_lit3:
> +	case DW_OP_lit4:
> +	case DW_OP_lit5:
> +	case DW_OP_lit6:
> +	case DW_OP_lit7:
> +	case DW_OP_lit8:
> +	case DW_OP_lit9:
> +	case DW_OP_lit10:
> +	case DW_OP_lit11:
> +	case DW_OP_lit12:
> +	case DW_OP_lit13:
> +	case DW_OP_lit14:
> +	case DW_OP_lit15:
> +	case DW_OP_lit16:
> +	case DW_OP_lit17:
> +	case DW_OP_lit18:
> +	case DW_OP_lit19:
> +	case DW_OP_lit20:
> +	case DW_OP_lit21:
> +	case DW_OP_lit22:
> +	case DW_OP_lit23:
> +	case DW_OP_lit24:
> +	case DW_OP_lit25:
> +	case DW_OP_lit26:
> +	case DW_OP_lit27:
> +	case DW_OP_lit28:
> +	case DW_OP_lit29:
> +	case DW_OP_lit30:
> +	case DW_OP_lit31:
> +	  gcc_assert (known_eq (cfa->offset, 0));
> +	  cfa->offset = op - DW_OP_lit0;
> +	  break;
> +	case DW_OP_const1u:
> +	case DW_OP_const1s:
> +	case DW_OP_const2u:
> +	case DW_OP_const2s:
> +	case DW_OP_const4s:
> +	case DW_OP_const8s:
> +	case DW_OP_constu:
> +	case DW_OP_consts:
> +	  gcc_assert (known_eq (cfa->offset, 0));
> +	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
> +	  break;
> +	case DW_OP_minus:
> +	  cfa->offset = -cfa->offset;
> +	  break;
> +	case DW_OP_plus:
> +	  /* The offset is already in place.  */
> +	  break;
>  	case DW_OP_plus_uconst:
>  	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
>  	  break;
> @@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
>        loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
>        break;
>      case DW_CFA_def_cfa_register:
> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>        break;
>      case DW_CFA_def_cfa:
>      case DW_CFA_def_cfa_sf:
> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>        loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
>        break;
>      case DW_CFA_def_cfa_expression:
> @@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>  
>    HOST_WIDE_INT const_offset;
>    if (new_cfa->reg == old_cfa->reg
> +      && new_cfa->reg.span == 1
>        && !new_cfa->indirect
>        && !old_cfa->indirect
>        && new_cfa->offset.is_constant (&const_offset))
> @@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>      }
>    else if (new_cfa->offset.is_constant ()
>  	   && known_eq (new_cfa->offset, old_cfa->offset)
> -	   && old_cfa->reg != INVALID_REGNUM
> +	   && old_cfa->reg.reg != INVALID_REGNUM
> +	   && new_cfa->reg.span == 1
>  	   && !new_cfa->indirect
>  	   && !old_cfa->indirect)
>      {
> @@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>  	 been set as a register plus offset rather than a general
>  	 DW_CFA_def_cfa_expression.  */
>        cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>      }
>    else if (new_cfa->indirect == 0
> -	   && new_cfa->offset.is_constant (&const_offset))
> +	   && new_cfa->offset.is_constant (&const_offset)
> +	   && new_cfa->reg.span == 1)
>      {
>        /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
>  	 indicating the CFA register has changed to <register> with
> @@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>  	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
>        else
>  	cfi->dw_cfi_opc = DW_CFA_def_cfa;
> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>        cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
>      }
>    else
> @@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
>  }
>  
>  /* Add the CFI for saving a register.  REG is the CFA column number.
> -   If SREG is -1, the register is saved at OFFSET from the CFA;
> +   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
>     otherwise it is saved in SREG.  */
>  
>  static void
> -reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
> +reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
>  {
>    dw_fde_ref fde = cfun ? cfun->fde : NULL;
>    dw_cfi_ref cfi = new_cfi ();
>  
>    cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
>  
> -  if (sreg == INVALID_REGNUM)
> +  if (sreg.reg == INVALID_REGNUM)
>      {
>        HOST_WIDE_INT const_offset;
>        /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
> @@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>  	    = build_cfa_loc (&cur_row->cfa, offset);
>  	}
>      }
> -  else if (sreg == reg)
> +  else if (sreg.reg == reg)
>      {
>        /* While we could emit something like DW_CFA_same_value or
>  	 DW_CFA_restore, we never expect to see something like that
> @@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>  	 can always bypass this by using REG_CFA_RESTORE directly.  */
>        gcc_unreachable ();
>      }
> +  else if (sreg.span > 1)
> +    {
> +      cfi->dw_cfi_opc = DW_CFA_expression;
> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
> +      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
> +    }
>    else
>      {
>        cfi->dw_cfi_opc = DW_CFA_register;
> -      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
> +      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
>      }
>  
>    add_cfi (cfi);
> @@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
>    return DWARF_FRAME_REGNUM (REGNO (reg));
>  }
>  
> +/* Like dwf_regno, but when the value can span multiple registers.  */
> +
> +static struct cfa_reg
> +dwf_cfa_reg (rtx reg)
> +{
> +  struct cfa_reg result;
> +
> +  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
> +
> +  result.reg = dwf_regno (reg);
> +  result.span = 1;
> +  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
> +
> +  rtx span = targetm.dwarf_register_span (reg);
> +  if (span)
> +    {
> +      /* We only support the simple case of consecutive registers all with the
> +	 same size.  */
> +      result.span = XVECLEN (span, 0);
> +      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
> +
> +#if CHECKING_P
> +      /* Ensure that the above assumption is accurate.  */
> +      for (unsigned int i = 0; i < result.span; i++)
> +	{
> +	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
> +								  0, i))),
> +				result.span_width));
> +	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
> +	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
> +	}
> +#endif
> +    }
> +
> +  return result;
> +}
> +
>  /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
>  
>  static bool
> @@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
>  
>    FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
>      {
> -      unsigned int reg, sreg;
> +      unsigned int reg;
> +      struct cfa_reg sreg;
>  
>        record_reg_saved_in_reg (q->saved_reg, q->reg);
>  
> @@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
>        else
>          reg = dwf_regno (q->reg);
>        if (q->saved_reg)
> -	sreg = dwf_regno (q->saved_reg);
> +	sreg = dwf_cfa_reg (q->saved_reg);
>        else
> -	sreg = INVALID_REGNUM;
> +	sreg.set_by_dwreg (INVALID_REGNUM);
>        reg_save (reg, sreg, q->cfa_offset);
>      }
>  
> @@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
>    /* ??? If this fails, we could be calling into the _loc functions to
>       define a full expression.  So far no port does that.  */
>    gcc_assert (REG_P (pat));
> -  cur_cfa->reg = dwf_regno (pat);
> +  cur_cfa->reg = dwf_cfa_reg (pat);
>  }
>  
>  /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
> @@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>    switch (GET_CODE (src))
>      {
>      case PLUS:
> -      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
> +      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>        cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
>        break;
>  
> @@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>        gcc_unreachable ();
>      }
>  
> -  cur_cfa->reg = dwf_regno (dest);
> +  cur_cfa->reg = dwf_cfa_reg (dest);
>    gcc_assert (cur_cfa->indirect == 0);
>  }
>  
> @@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>    switch (GET_CODE (addr))
>      {
>      case REG:
> -      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
> +      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
>        offset = -cur_cfa->offset;
>        break;
>      case PLUS:
> -      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
> +      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
>        offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
>        break;
>      default:
> @@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>  
>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>       a different flushing heuristic for epilogues.  */
> +  struct cfa_reg invalid;
> +  invalid.set_by_dwreg (INVALID_REGNUM);
>    if (!span)
> -    reg_save (sregno, INVALID_REGNUM, offset);
> +    reg_save (sregno, invalid, offset);
>    else
>      {
>        /* We have a PARALLEL describing where the contents of SRC live.
> @@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>  	{
>  	  rtx elem = XVECEXP (span, 0, par_index);
>  	  sregno = dwf_regno (src);
> -	  reg_save (sregno, INVALID_REGNUM, span_offset);
> +	  reg_save (sregno, invalid, span_offset);
>  	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
>  	}
>      }
> @@ -1270,7 +1395,8 @@ static void
>  dwarf2out_frame_debug_cfa_register (rtx set)
>  {
>    rtx src, dest;
> -  unsigned sregno, dregno;
> +  unsigned sregno;
> +  struct cfa_reg dregno;
>  
>    src = XEXP (set, 1);
>    dest = XEXP (set, 0);
> @@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
>    else
>      sregno = dwf_regno (src);
>  
> -  dregno = dwf_regno (dest);
> +  dregno = dwf_cfa_reg (dest);
>  
>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>       a different flushing heuristic for epilogues.  */
> @@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	{
>  	  /* Setting FP from SP.  */
>  	case REG:
> -	  if (cur_cfa->reg == dwf_regno (src))
> +	  if (cur_cfa->reg == dwf_cfa_reg (src))
>  	    {
>  	      /* Rule 1 */
>  	      /* Update the CFA rule wrt SP or FP.  Make sure src is
> @@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  		 ARM copies SP to a temporary register, and from there to
>  		 FP.  So we just rely on the backends to only set
>  		 RTX_FRAME_RELATED_P on appropriate insns.  */
> -	      cur_cfa->reg = dwf_regno (dest);
> +	      cur_cfa->reg = dwf_cfa_reg (dest);
>  	      cur_trace->cfa_temp.reg = cur_cfa->reg;
>  	      cur_trace->cfa_temp.offset = cur_cfa->offset;
>  	    }
> @@ -1698,7 +1824,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  		{
>  		  gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
>  			      && fde->drap_reg != INVALID_REGNUM
> -			      && cur_cfa->reg != dwf_regno (src)
> +			      && cur_cfa->reg != dwf_cfa_reg (src)
>  			      && fde->rule18);
>  		  fde->rule18 = 0;
>  		  /* The save of hard frame pointer has been deferred
> @@ -1722,7 +1848,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	      /* Adjusting SP.  */
>  	      if (REG_P (XEXP (src, 1)))
>  		{
> -		  gcc_assert (dwf_regno (XEXP (src, 1))
> +		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
>  			      == cur_trace->cfa_temp.reg);
>  		  offset = cur_trace->cfa_temp.offset;
>  		}
> @@ -1756,7 +1882,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	      gcc_assert (frame_pointer_needed);
>  
>  	      gcc_assert (REG_P (XEXP (src, 0))
> -			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
> +			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>  	      offset = rtx_to_poly_int64 (XEXP (src, 1));
>  	      if (GET_CODE (src) != MINUS)
>  		offset = -offset;
> @@ -1769,14 +1895,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>  
>  	      /* Rule 4 */
>  	      if (REG_P (XEXP (src, 0))
> -		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
> +		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
>  		  && poly_int_rtx_p (XEXP (src, 1), &offset))
>  		{
>  		  /* Setting a temporary CFA register that will be copied
>  		     into the FP later on.  */
>  		  offset = -offset;
>  		  cur_cfa->offset += offset;
> -		  cur_cfa->reg = dwf_regno (dest);
> +		  cur_cfa->reg = dwf_cfa_reg (dest);
>  		  /* Or used to save regs to the stack.  */
>  		  cur_trace->cfa_temp.reg = cur_cfa->reg;
>  		  cur_trace->cfa_temp.offset = cur_cfa->offset;
> @@ -1784,13 +1910,13 @@ dwarf2out_frame_debug_expr (rtx expr)
>  
>  	      /* Rule 5 */
>  	      else if (REG_P (XEXP (src, 0))
> -		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
> +		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>  		       && XEXP (src, 1) == stack_pointer_rtx)
>  		{
>  		  /* Setting a scratch register that we will use instead
>  		     of SP for saving registers to the stack.  */
>  		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
> -		  cur_trace->cfa_store.reg = dwf_regno (dest);
> +		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
>  		  cur_trace->cfa_store.offset
>  		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
>  		}
> @@ -1799,7 +1925,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	      else if (GET_CODE (src) == LO_SUM
>  		       && poly_int_rtx_p (XEXP (src, 1),
>  					  &cur_trace->cfa_temp.offset))
> -		cur_trace->cfa_temp.reg = dwf_regno (dest);
> +		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>  	      else
>  		gcc_unreachable ();
>  	    }
> @@ -1808,17 +1934,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	  /* Rule 6 */
>  	case CONST_INT:
>  	case CONST_POLY_INT:
> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>  	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
>  	  break;
>  
>  	  /* Rule 7 */
>  	case IOR:
>  	  gcc_assert (REG_P (XEXP (src, 0))
> -		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
> +		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>  		      && CONST_INT_P (XEXP (src, 1)));
>  
> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>  	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
>  			  &cur_trace->cfa_temp.offset))
>  	    /* The target shouldn't generate this kind of CFI note if we
> @@ -1851,14 +1977,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	      dwarf2out_flush_queued_reg_saves ();
>  
>                gcc_assert (cur_trace->cfa_store.reg
> -			  == dwf_regno (XEXP (src, 0)));
> +			  == dwf_cfa_reg (XEXP (src, 0)));
>                fde->stack_realign = 1;
>                fde->stack_realignment = INTVAL (XEXP (src, 1));
>                cur_trace->cfa_store.offset = 0;
>  
>  	      if (cur_cfa->reg != dw_stack_pointer_regnum
>  		  && cur_cfa->reg != dw_frame_pointer_regnum)
> -		fde->drap_reg = cur_cfa->reg;
> +		{
> +		  gcc_assert (cur_cfa->reg.span == 1);
> +		  fde->drap_reg = cur_cfa->reg.reg;
> +		}
>              }
>            return;
>  
> @@ -1935,14 +2064,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	case MINUS:
>  	case LO_SUM:
>  	  {
> -	    unsigned int regno;
> +	    struct cfa_reg regno;
>  
>  	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
>  	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
>  	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
>  	      offset = -offset;
>  
> -	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
> +	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
>  
>  	    if (cur_cfa->reg == regno)
>  	      offset -= cur_cfa->offset;
> @@ -1960,7 +2089,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	  /* Without an offset.  */
>  	case REG:
>  	  {
> -	    unsigned int regno = dwf_regno (XEXP (dest, 0));
> +	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
>  
>  	    if (cur_cfa->reg == regno)
>  	      offset = -cur_cfa->offset;
> @@ -1977,7 +2106,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  	  /* Rule 14 */
>  	case POST_INC:
>  	  gcc_assert (cur_trace->cfa_temp.reg
> -		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
> +		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
>  	  offset = -cur_trace->cfa_temp.offset;
>  	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
>  	  break;
> @@ -1995,7 +2124,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>        if (REG_P (src)
>  	  && REGNO (src) != STACK_POINTER_REGNUM
>  	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
> -	  && dwf_regno (src) == cur_cfa->reg)
> +	  && dwf_cfa_reg (src) == cur_cfa->reg)
>  	{
>  	  /* We're storing the current CFA reg into the stack.  */
>  
> @@ -2012,7 +2141,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>                    && cur_cfa->indirect == 0
>                    && cur_cfa->reg != dw_frame_pointer_regnum)
>                  {
> -		  gcc_assert (fde->drap_reg == cur_cfa->reg);
> +		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
>  
>  		  cur_cfa->indirect = 1;
>  		  cur_cfa->reg = dw_frame_pointer_regnum;
> @@ -2039,7 +2168,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>  		x = XEXP (x, 0);
>  	      gcc_assert (REG_P (x));
>  
> -	      cur_cfa->reg = dwf_regno (x);
> +	      cur_cfa->reg = dwf_cfa_reg (x);
>  	      cur_cfa->base_offset = offset;
>  	      cur_cfa->indirect = 1;
>  	      break;
> @@ -2951,7 +3080,7 @@ create_pseudo_cfg (void)
>    ti.head = get_insns ();
>    ti.beg_row = cie_cfi_row;
>    ti.cfa_store = cie_cfi_row->cfa;
> -  ti.cfa_temp.reg = INVALID_REGNUM;
> +  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
>    trace_info.quick_push (ti);
>  
>    if (cie_return_save)
> @@ -3014,14 +3143,15 @@ create_pseudo_cfg (void)
>  static void
>  initial_return_save (rtx rtl)
>  {
> -  unsigned int reg = INVALID_REGNUM;
> +  struct cfa_reg reg;
> +  reg.set_by_dwreg (INVALID_REGNUM);
>    poly_int64 offset = 0;
>  
>    switch (GET_CODE (rtl))
>      {
>      case REG:
>        /* RA is in a register.  */
> -      reg = dwf_regno (rtl);
> +      reg = dwf_cfa_reg (rtl);
>        break;
>  
>      case MEM:
> @@ -3062,9 +3192,9 @@ initial_return_save (rtx rtl)
>        gcc_unreachable ();
>      }
>  
> -  if (reg != DWARF_FRAME_RETURN_COLUMN)
> +  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
>      {
> -      if (reg != INVALID_REGNUM)
> +      if (reg.reg != INVALID_REGNUM)
>          record_reg_saved_in_reg (rtl, pc_rtx);
>        reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
>      }
> @@ -3076,7 +3206,8 @@ create_cie_data (void)
>    dw_cfa_location loc;
>    dw_trace_info cie_trace;
>  
> -  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
> +  dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode,
> +						      STACK_POINTER_REGNUM));
>  
>    memset (&cie_trace, 0, sizeof (cie_trace));
>    cur_trace = &cie_trace;
> @@ -3135,7 +3266,8 @@ static unsigned int
>  execute_dwarf2_frame (void)
>  {
>    /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
> -  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
> +  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
> +					(Pmode, HARD_FRAME_POINTER_REGNUM));
>  
>    /* The first time we're called, compute the incoming frame state.  */
>    if (cie_cfi_vec == NULL)
> @@ -3515,7 +3647,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
>      {
>        dw_cfa_location dummy;
>        memset (&dummy, 0, sizeof (dummy));
> -      dummy.reg = INVALID_REGNUM;
> +      dummy.reg.set_by_dwreg (INVALID_REGNUM);
>        cfi = def_cfa_0 (&dummy, &row->cfa);
>      }
>    output_cfi_directive (f, cfi);
> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
> index 88eb3f9c455..a0b41df6da0 100644
> --- a/gcc/dwarf2out.c
> +++ b/gcc/dwarf2out.c
> @@ -2785,6 +2785,44 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
>      }
>  }
>  
> +static void
> +build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
> +{
> +  if (regno <= 31)
> +    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
> +		  (DW_OP_breg0 + regno),  0, 0));
> +  else
> +    add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0));
> +}
> +
> +/* Build a dwarf location for a cfa_reg spanning multiple
> +   consecutive registers.  */
> +
> +struct dw_loc_descr_node *
> +build_span_loc (struct cfa_reg reg)
> +{
> +  struct dw_loc_descr_node *head = NULL;
> +
> +  gcc_assert (known_gt (reg.span_width, 0));
> +  gcc_assert (reg.span > 1);
> +
> +  /* Start from the highest number register as it goes in the upper bits.  */
> +  unsigned int regno = reg.reg + reg.span - 1;
> +  build_breg_loc (&head, regno);
> +
> +  /* deal with the remaining registers in the span.  */
> +  for (int i = (reg.span - 2); i >= 0; i--)
> +    {
> +      add_loc_descr (&head, int_loc_descriptor
> +		    (reg.span_width.to_constant () * 8));
> +      add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0));
> +      regno--;
> +      build_breg_loc (&head, regno);
> +      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
> +    }
> +  return head;
> +}
> +
>  /* This function builds a dwarf location descriptor sequence from a
>     dw_cfa_location, adding the given OFFSET to the result of the
>     expression.  */
> @@ -2796,9 +2834,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>  
>    offset += cfa->offset;
>  
> -  if (cfa->indirect)
> +  if (cfa->reg.span > 1)
> +    {
> +      head = build_span_loc (cfa->reg);
> +
> +      if (maybe_ne (offset, 0))
> +	  loc_descr_plus_const (&head, offset);
> +    }
> +  else if (cfa->indirect)
>      {
> -      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
> +      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
>        head->dw_loc_oprnd1.val_class = dw_val_class_const;
>        head->dw_loc_oprnd1.val_entry = NULL;
>        tmp = new_loc_descr (DW_OP_deref, 0, 0);
> @@ -2806,7 +2851,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>        loc_descr_plus_const (&head, offset);
>      }
>    else
> -    head = new_reg_loc_descr (cfa->reg, offset);
> +    head = new_reg_loc_descr (cfa->reg.reg, offset);
>  
>    return head;
>  }
> @@ -2824,7 +2869,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
>      = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
>  
>    /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
> -  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
> +  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
>      {
>        head = new_reg_loc_descr (dwarf_fp, 0);
>        add_loc_descr (&head, int_loc_descriptor (alignment));
> @@ -20865,7 +20910,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
>    list = NULL;
>  
>    memset (&next_cfa, 0, sizeof (next_cfa));
> -  next_cfa.reg = INVALID_REGNUM;
> +  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
>    remember = next_cfa;
>  
>    start_label = fde->dw_fde_begin;
> diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
> index 54b6343704c..38b52c8e1c2 100644
> --- a/gcc/dwarf2out.h
> +++ b/gcc/dwarf2out.h
> @@ -119,6 +119,40 @@ struct GTY(()) dw_fde_node {
>  };
>  
>  
> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
> +   definitions and expressions.
> +   Most architectures only need a single register number, but some (amdgcn)
> +   have pointers that span multiple registers.  DWARF permits arbitrary
> +   register sets but existing use-cases only require contiguous register
> +   sets, as represented here.  */
> +struct GTY(()) cfa_reg {
> +  unsigned int reg;
> +  unsigned int span;
> +  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
> +
> +  cfa_reg& set_by_dwreg (unsigned int r)
> +    {
> +      reg = r;
> +      span = 1;
> +      span_width = 0;  /* Unknown size (permitted when span == 1).  */
> +      return *this;
> +    }
> +
> +  bool operator== (const cfa_reg other) const
> +    {
> +      return (reg == other.reg
> +	      && span == other.span
> +	      && (known_eq (span_width, other.span_width)
> +		  || (span == 1
> +		      && (known_eq (span_width, 0)
> +			  || known_eq (other.span_width, 0)))));
> +    }
> +  bool operator!= (const cfa_reg other) const
> +    {
> +      return !(*this == other);
> +    }
> +};
> +
>  /* This is how we define the location of the CFA. We use to handle it
>     as REG + OFFSET all the time,  but now it can be more complex.
>     It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
> @@ -128,7 +162,7 @@ struct GTY(()) dw_cfa_location {
>    poly_int64_pod offset;
>    poly_int64_pod base_offset;
>    /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
> -  unsigned int reg;
> +  struct cfa_reg reg;
>    BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
>    BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
>  };
> @@ -285,6 +319,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
>    (dw_cfa_location *, poly_int64);
>  extern struct dw_loc_descr_node *build_cfa_aligned_loc
>    (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
> +extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
>  extern struct dw_loc_descr_node *mem_loc_descriptor
>    (rtx, machine_mode mode, machine_mode mem_mode,
>     enum var_init_status);
> diff --git a/gcc/gengtype.c b/gcc/gengtype.c
> index b94e2f126ec..45e9f856470 100644
> --- a/gcc/gengtype.c
> +++ b/gcc/gengtype.c
> @@ -5195,6 +5195,7 @@ main (int argc, char **argv)
>        POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
>        POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
>        POS_HERE (do_scalar_typedef ("double_int", &pos));
> +      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
>        POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
>        POS_HERE (do_scalar_typedef ("offset_int", &pos));
>        POS_HERE (do_scalar_typedef ("widest_int", &pos));
> 


-- 
Hafiz Abid Qadeer
Mentor, a Siemens Business

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-07-22 10:58 ` Hafiz Abid Qadeer
@ 2021-08-24 15:55   ` Hafiz Abid Qadeer
  2021-11-02 15:02     ` Hafiz Abid Qadeer
  0 siblings, 1 reply; 15+ messages in thread
From: Hafiz Abid Qadeer @ 2021-08-24 15:55 UTC (permalink / raw)
  To: Hafiz Abid Qadeer, gcc-patches; +Cc: jakub, ams

Ping.

On 22/07/2021 11:58, Hafiz Abid Qadeer wrote:
> Ping.
> 
> On 13/06/2021 14:27, Hafiz Abid Qadeer wrote:
>> Add support for architectures such as AMD GCN, in which the pointer size is
>> larger than the register size.  This allows the CFI information to include
>> multi-register locations for the stack pointer, frame pointer, and return
>> address.
>>
>> This patch was originally posted by Andrew Stubbs in
>> https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html
>>
>> It has now been re-worked according to the review comments. It does not use
>> DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses
>> DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple
>> consecutive registers. Here is how .debug_frame looks before and after this
>> patch:
>>
>> $ cat factorial.c
>> int factorial(int n) {
>>   if (n == 0) return 1;
>>   return n * factorial (n - 1);
>> }
>>
>> $ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o
>> $ llvm-dwarfdump -debug-frame fac.o
>>
>> *** without this patch (edited for brevity)***
>>
>> 00000000 00000014 ffffffff CIE
>>
>>   DW_CFA_def_cfa: reg48 +0
>>   DW_CFA_register: reg16 reg50
>>
>> 00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac
>>   DW_CFA_advance_loc4: 96
>>   DW_CFA_offset: reg46 0
>>   DW_CFA_offset: reg47 4
>>   DW_CFA_offset: reg50 8
>>   DW_CFA_offset: reg51 12
>>   DW_CFA_offset: reg16 8
>>   DW_CFA_advance_loc4: 4
>>   DW_CFA_def_cfa_sf: reg46 -16
>>
>> *** with this patch (edited for brevity)***
>>
>> 00000000 00000024 ffffffff CIE
>>
>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
>>   DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus
>>
>> 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
>>   DW_CFA_advance_loc4: 96
>>   DW_CFA_offset: reg46 0
>>   DW_CFA_offset: reg47 4
>>   DW_CFA_offset: reg50 8
>>   DW_CFA_offset: reg51 12
>>   DW_CFA_offset: reg16 8
>>   DW_CFA_advance_loc4: 4
>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus
>>
>> gcc/ChangeLog:
>>
>> 	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
>> 	(dw_frame_pointer_regnum): Likewise.
>> 	(new_cfi_row): Use set_by_dwreg.
>> 	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
>> 	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,
>> 	DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus.
>> 	(lookup_cfa_1): Use set_by_dwreg.
>> 	(def_cfa_0): Update for cfa_reg and support register spans.
>> 	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
>> 	spans.
>> 	(dwf_cfa_reg): New function.
>> 	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
>> 	dwf_regno.
>> 	(dwarf2out_frame_debug_def_cfa): Likewise.
>> 	(dwarf2out_frame_debug_adjust_cfa): Likewise.
>> 	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
>> 	(dwarf2out_frame_debug_cfa_register): Likewise.
>> 	(dwarf2out_frame_debug_expr): Likewise.
>> 	(create_pseudo_cfg): Use set_by_dwreg.
>> 	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
>> 	(create_cie_data): Use dwf_cfa_reg.
>> 	(execute_dwarf2_frame): Use dwf_cfa_reg.
>> 	(dump_cfi_row): Use set_by_dwreg.
>> 	* dwarf2out.c (build_span_loc, build_breg_loc): New function.
>> 	(build_cfa_loc): Support register spans.
>> 	(build_cfa_aligned_loc): Update cfa_reg usage.
>> 	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
>> 	* dwarf2out.h (struct cfa_reg): New type.
>> 	(struct dw_cfa_location): Use struct cfa_reg.
>> 	(build_span_loc): New prototype.
>> 	* gengtype.c (main): Accept poly_uint16_pod type.
>> ---
>>  gcc/dwarf2cfi.c | 260 ++++++++++++++++++++++++++++++++++++------------
>>  gcc/dwarf2out.c |  55 +++++++++-
>>  gcc/dwarf2out.h |  37 ++++++-
>>  gcc/gengtype.c  |   1 +
>>  4 files changed, 283 insertions(+), 70 deletions(-)
>>
>> diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
>> index c27ac1960b0..5aacdcd094a 100644
>> --- a/gcc/dwarf2cfi.c
>> +++ b/gcc/dwarf2cfi.c
>> @@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
>>  static bool any_cfis_emitted;
>>  
>>  /* Short-hand for commonly used register numbers.  */
>> -static unsigned dw_stack_pointer_regnum;
>> -static unsigned dw_frame_pointer_regnum;
>> +static struct cfa_reg dw_stack_pointer_regnum;
>> +static struct cfa_reg dw_frame_pointer_regnum;
>>  \f
>>  /* Hook used by __throw.  */
>>  
>> @@ -430,7 +430,7 @@ new_cfi_row (void)
>>  {
>>    dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
>>  
>> -  row->cfa.reg = INVALID_REGNUM;
>> +  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
>>  
>>    return row;
>>  }
>> @@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>    cfa->offset = 0;
>>    cfa->base_offset = 0;
>>    cfa->indirect = 0;
>> -  cfa->reg = -1;
>> +  cfa->reg.set_by_dwreg (INVALID_REGNUM);
>>  
>>    for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
>>      {
>> @@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>  	case DW_OP_reg29:
>>  	case DW_OP_reg30:
>>  	case DW_OP_reg31:
>> -	  cfa->reg = op - DW_OP_reg0;
>> +	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
>>  	  break;
>>  	case DW_OP_regx:
>> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
>> +	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
>>  	  break;
>>  	case DW_OP_breg0:
>>  	case DW_OP_breg1:
>> @@ -615,16 +615,92 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>  	case DW_OP_breg29:
>>  	case DW_OP_breg30:
>>  	case DW_OP_breg31:
>> -	  cfa->reg = op - DW_OP_breg0;
>> -	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
>> -	  break;
>>  	case DW_OP_bregx:
>> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
>> -	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
>> +	  if (cfa->reg.reg == INVALID_REGNUM)
>> +	    {
>> +	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
>> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));
>> +	      cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
>> +	    }
>> +	  else
>> +	    {
>> +	      /* Handle case when span can cover multiple registers.  We
>> +		 only support the simple case of consecutive registers
>> +		 all with the same size.  DWARF that we are dealing with
>> +		 will look something like:
>> +		 <DW_OP_bregx: (r49) 0; DW_OP_const1u: 32; DW_OP_shl;
>> +		  DW_OP_bregx: (r48) 0; DW_OP_plus> */
>> +
>> +	      unsigned int regno = (op == DW_OP_bregx)
>> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);
>> +	      gcc_assert (regno == (cfa->reg.reg - 1));
>> +	      cfa->reg.span++;
>> +	      /* From all the consecutive registers used, we want to set
>> +	         cfa->reg.reg to lower number register.  */
>> +	      cfa->reg.reg = regno;
>> +	      /* The offset was the shift value.  Use it to get the
>> +		 span_width and then set it to 0.  */
>> +	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);
>> +	      cfa->offset = 0;
>> +	    }
>>  	  break;
>>  	case DW_OP_deref:
>>  	  cfa->indirect = 1;
>>  	  break;
>> +	case DW_OP_shl:
>> +	  break;
>> +	case DW_OP_lit0:
>> +	case DW_OP_lit1:
>> +	case DW_OP_lit2:
>> +	case DW_OP_lit3:
>> +	case DW_OP_lit4:
>> +	case DW_OP_lit5:
>> +	case DW_OP_lit6:
>> +	case DW_OP_lit7:
>> +	case DW_OP_lit8:
>> +	case DW_OP_lit9:
>> +	case DW_OP_lit10:
>> +	case DW_OP_lit11:
>> +	case DW_OP_lit12:
>> +	case DW_OP_lit13:
>> +	case DW_OP_lit14:
>> +	case DW_OP_lit15:
>> +	case DW_OP_lit16:
>> +	case DW_OP_lit17:
>> +	case DW_OP_lit18:
>> +	case DW_OP_lit19:
>> +	case DW_OP_lit20:
>> +	case DW_OP_lit21:
>> +	case DW_OP_lit22:
>> +	case DW_OP_lit23:
>> +	case DW_OP_lit24:
>> +	case DW_OP_lit25:
>> +	case DW_OP_lit26:
>> +	case DW_OP_lit27:
>> +	case DW_OP_lit28:
>> +	case DW_OP_lit29:
>> +	case DW_OP_lit30:
>> +	case DW_OP_lit31:
>> +	  gcc_assert (known_eq (cfa->offset, 0));
>> +	  cfa->offset = op - DW_OP_lit0;
>> +	  break;
>> +	case DW_OP_const1u:
>> +	case DW_OP_const1s:
>> +	case DW_OP_const2u:
>> +	case DW_OP_const2s:
>> +	case DW_OP_const4s:
>> +	case DW_OP_const8s:
>> +	case DW_OP_constu:
>> +	case DW_OP_consts:
>> +	  gcc_assert (known_eq (cfa->offset, 0));
>> +	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
>> +	  break;
>> +	case DW_OP_minus:
>> +	  cfa->offset = -cfa->offset;
>> +	  break;
>> +	case DW_OP_plus:
>> +	  /* The offset is already in place.  */
>> +	  break;
>>  	case DW_OP_plus_uconst:
>>  	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
>>  	  break;
>> @@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
>>        loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
>>        break;
>>      case DW_CFA_def_cfa_register:
>> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
>> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>>        break;
>>      case DW_CFA_def_cfa:
>>      case DW_CFA_def_cfa_sf:
>> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
>> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>>        loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
>>        break;
>>      case DW_CFA_def_cfa_expression:
>> @@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>  
>>    HOST_WIDE_INT const_offset;
>>    if (new_cfa->reg == old_cfa->reg
>> +      && new_cfa->reg.span == 1
>>        && !new_cfa->indirect
>>        && !old_cfa->indirect
>>        && new_cfa->offset.is_constant (&const_offset))
>> @@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>      }
>>    else if (new_cfa->offset.is_constant ()
>>  	   && known_eq (new_cfa->offset, old_cfa->offset)
>> -	   && old_cfa->reg != INVALID_REGNUM
>> +	   && old_cfa->reg.reg != INVALID_REGNUM
>> +	   && new_cfa->reg.span == 1
>>  	   && !new_cfa->indirect
>>  	   && !old_cfa->indirect)
>>      {
>> @@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>  	 been set as a register plus offset rather than a general
>>  	 DW_CFA_def_cfa_expression.  */
>>        cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
>> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>>      }
>>    else if (new_cfa->indirect == 0
>> -	   && new_cfa->offset.is_constant (&const_offset))
>> +	   && new_cfa->offset.is_constant (&const_offset)
>> +	   && new_cfa->reg.span == 1)
>>      {
>>        /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
>>  	 indicating the CFA register has changed to <register> with
>> @@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>  	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
>>        else
>>  	cfi->dw_cfi_opc = DW_CFA_def_cfa;
>> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>>        cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
>>      }
>>    else
>> @@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
>>  }
>>  
>>  /* Add the CFI for saving a register.  REG is the CFA column number.
>> -   If SREG is -1, the register is saved at OFFSET from the CFA;
>> +   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
>>     otherwise it is saved in SREG.  */
>>  
>>  static void
>> -reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>> +reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
>>  {
>>    dw_fde_ref fde = cfun ? cfun->fde : NULL;
>>    dw_cfi_ref cfi = new_cfi ();
>>  
>>    cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
>>  
>> -  if (sreg == INVALID_REGNUM)
>> +  if (sreg.reg == INVALID_REGNUM)
>>      {
>>        HOST_WIDE_INT const_offset;
>>        /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
>> @@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>>  	    = build_cfa_loc (&cur_row->cfa, offset);
>>  	}
>>      }
>> -  else if (sreg == reg)
>> +  else if (sreg.reg == reg)
>>      {
>>        /* While we could emit something like DW_CFA_same_value or
>>  	 DW_CFA_restore, we never expect to see something like that
>> @@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>>  	 can always bypass this by using REG_CFA_RESTORE directly.  */
>>        gcc_unreachable ();
>>      }
>> +  else if (sreg.span > 1)
>> +    {
>> +      cfi->dw_cfi_opc = DW_CFA_expression;
>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
>> +      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
>> +    }
>>    else
>>      {
>>        cfi->dw_cfi_opc = DW_CFA_register;
>> -      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
>> +      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
>>      }
>>  
>>    add_cfi (cfi);
>> @@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
>>    return DWARF_FRAME_REGNUM (REGNO (reg));
>>  }
>>  
>> +/* Like dwf_regno, but when the value can span multiple registers.  */
>> +
>> +static struct cfa_reg
>> +dwf_cfa_reg (rtx reg)
>> +{
>> +  struct cfa_reg result;
>> +
>> +  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
>> +
>> +  result.reg = dwf_regno (reg);
>> +  result.span = 1;
>> +  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
>> +
>> +  rtx span = targetm.dwarf_register_span (reg);
>> +  if (span)
>> +    {
>> +      /* We only support the simple case of consecutive registers all with the
>> +	 same size.  */
>> +      result.span = XVECLEN (span, 0);
>> +      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
>> +
>> +#if CHECKING_P
>> +      /* Ensure that the above assumption is accurate.  */
>> +      for (unsigned int i = 0; i < result.span; i++)
>> +	{
>> +	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
>> +								  0, i))),
>> +				result.span_width));
>> +	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
>> +	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
>> +	}
>> +#endif
>> +    }
>> +
>> +  return result;
>> +}
>> +
>>  /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
>>  
>>  static bool
>> @@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
>>  
>>    FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
>>      {
>> -      unsigned int reg, sreg;
>> +      unsigned int reg;
>> +      struct cfa_reg sreg;
>>  
>>        record_reg_saved_in_reg (q->saved_reg, q->reg);
>>  
>> @@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
>>        else
>>          reg = dwf_regno (q->reg);
>>        if (q->saved_reg)
>> -	sreg = dwf_regno (q->saved_reg);
>> +	sreg = dwf_cfa_reg (q->saved_reg);
>>        else
>> -	sreg = INVALID_REGNUM;
>> +	sreg.set_by_dwreg (INVALID_REGNUM);
>>        reg_save (reg, sreg, q->cfa_offset);
>>      }
>>  
>> @@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
>>    /* ??? If this fails, we could be calling into the _loc functions to
>>       define a full expression.  So far no port does that.  */
>>    gcc_assert (REG_P (pat));
>> -  cur_cfa->reg = dwf_regno (pat);
>> +  cur_cfa->reg = dwf_cfa_reg (pat);
>>  }
>>  
>>  /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
>> @@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>>    switch (GET_CODE (src))
>>      {
>>      case PLUS:
>> -      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
>> +      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>>        cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
>>        break;
>>  
>> @@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>>        gcc_unreachable ();
>>      }
>>  
>> -  cur_cfa->reg = dwf_regno (dest);
>> +  cur_cfa->reg = dwf_cfa_reg (dest);
>>    gcc_assert (cur_cfa->indirect == 0);
>>  }
>>  
>> @@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>    switch (GET_CODE (addr))
>>      {
>>      case REG:
>> -      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
>> +      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
>>        offset = -cur_cfa->offset;
>>        break;
>>      case PLUS:
>> -      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
>> +      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
>>        offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
>>        break;
>>      default:
>> @@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>  
>>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>>       a different flushing heuristic for epilogues.  */
>> +  struct cfa_reg invalid;
>> +  invalid.set_by_dwreg (INVALID_REGNUM);
>>    if (!span)
>> -    reg_save (sregno, INVALID_REGNUM, offset);
>> +    reg_save (sregno, invalid, offset);
>>    else
>>      {
>>        /* We have a PARALLEL describing where the contents of SRC live.
>> @@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>  	{
>>  	  rtx elem = XVECEXP (span, 0, par_index);
>>  	  sregno = dwf_regno (src);
>> -	  reg_save (sregno, INVALID_REGNUM, span_offset);
>> +	  reg_save (sregno, invalid, span_offset);
>>  	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
>>  	}
>>      }
>> @@ -1270,7 +1395,8 @@ static void
>>  dwarf2out_frame_debug_cfa_register (rtx set)
>>  {
>>    rtx src, dest;
>> -  unsigned sregno, dregno;
>> +  unsigned sregno;
>> +  struct cfa_reg dregno;
>>  
>>    src = XEXP (set, 1);
>>    dest = XEXP (set, 0);
>> @@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
>>    else
>>      sregno = dwf_regno (src);
>>  
>> -  dregno = dwf_regno (dest);
>> +  dregno = dwf_cfa_reg (dest);
>>  
>>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>>       a different flushing heuristic for epilogues.  */
>> @@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	{
>>  	  /* Setting FP from SP.  */
>>  	case REG:
>> -	  if (cur_cfa->reg == dwf_regno (src))
>> +	  if (cur_cfa->reg == dwf_cfa_reg (src))
>>  	    {
>>  	      /* Rule 1 */
>>  	      /* Update the CFA rule wrt SP or FP.  Make sure src is
>> @@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  		 ARM copies SP to a temporary register, and from there to
>>  		 FP.  So we just rely on the backends to only set
>>  		 RTX_FRAME_RELATED_P on appropriate insns.  */
>> -	      cur_cfa->reg = dwf_regno (dest);
>> +	      cur_cfa->reg = dwf_cfa_reg (dest);
>>  	      cur_trace->cfa_temp.reg = cur_cfa->reg;
>>  	      cur_trace->cfa_temp.offset = cur_cfa->offset;
>>  	    }
>> @@ -1698,7 +1824,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  		{
>>  		  gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
>>  			      && fde->drap_reg != INVALID_REGNUM
>> -			      && cur_cfa->reg != dwf_regno (src)
>> +			      && cur_cfa->reg != dwf_cfa_reg (src)
>>  			      && fde->rule18);
>>  		  fde->rule18 = 0;
>>  		  /* The save of hard frame pointer has been deferred
>> @@ -1722,7 +1848,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	      /* Adjusting SP.  */
>>  	      if (REG_P (XEXP (src, 1)))
>>  		{
>> -		  gcc_assert (dwf_regno (XEXP (src, 1))
>> +		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
>>  			      == cur_trace->cfa_temp.reg);
>>  		  offset = cur_trace->cfa_temp.offset;
>>  		}
>> @@ -1756,7 +1882,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	      gcc_assert (frame_pointer_needed);
>>  
>>  	      gcc_assert (REG_P (XEXP (src, 0))
>> -			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
>> +			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>>  	      offset = rtx_to_poly_int64 (XEXP (src, 1));
>>  	      if (GET_CODE (src) != MINUS)
>>  		offset = -offset;
>> @@ -1769,14 +1895,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  
>>  	      /* Rule 4 */
>>  	      if (REG_P (XEXP (src, 0))
>> -		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
>> +		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
>>  		  && poly_int_rtx_p (XEXP (src, 1), &offset))
>>  		{
>>  		  /* Setting a temporary CFA register that will be copied
>>  		     into the FP later on.  */
>>  		  offset = -offset;
>>  		  cur_cfa->offset += offset;
>> -		  cur_cfa->reg = dwf_regno (dest);
>> +		  cur_cfa->reg = dwf_cfa_reg (dest);
>>  		  /* Or used to save regs to the stack.  */
>>  		  cur_trace->cfa_temp.reg = cur_cfa->reg;
>>  		  cur_trace->cfa_temp.offset = cur_cfa->offset;
>> @@ -1784,13 +1910,13 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  
>>  	      /* Rule 5 */
>>  	      else if (REG_P (XEXP (src, 0))
>> -		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>> +		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>  		       && XEXP (src, 1) == stack_pointer_rtx)
>>  		{
>>  		  /* Setting a scratch register that we will use instead
>>  		     of SP for saving registers to the stack.  */
>>  		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
>> -		  cur_trace->cfa_store.reg = dwf_regno (dest);
>> +		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
>>  		  cur_trace->cfa_store.offset
>>  		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
>>  		}
>> @@ -1799,7 +1925,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	      else if (GET_CODE (src) == LO_SUM
>>  		       && poly_int_rtx_p (XEXP (src, 1),
>>  					  &cur_trace->cfa_temp.offset))
>> -		cur_trace->cfa_temp.reg = dwf_regno (dest);
>> +		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>  	      else
>>  		gcc_unreachable ();
>>  	    }
>> @@ -1808,17 +1934,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	  /* Rule 6 */
>>  	case CONST_INT:
>>  	case CONST_POLY_INT:
>> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
>> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>  	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
>>  	  break;
>>  
>>  	  /* Rule 7 */
>>  	case IOR:
>>  	  gcc_assert (REG_P (XEXP (src, 0))
>> -		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>> +		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>  		      && CONST_INT_P (XEXP (src, 1)));
>>  
>> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
>> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>  	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
>>  			  &cur_trace->cfa_temp.offset))
>>  	    /* The target shouldn't generate this kind of CFI note if we
>> @@ -1851,14 +1977,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	      dwarf2out_flush_queued_reg_saves ();
>>  
>>                gcc_assert (cur_trace->cfa_store.reg
>> -			  == dwf_regno (XEXP (src, 0)));
>> +			  == dwf_cfa_reg (XEXP (src, 0)));
>>                fde->stack_realign = 1;
>>                fde->stack_realignment = INTVAL (XEXP (src, 1));
>>                cur_trace->cfa_store.offset = 0;
>>  
>>  	      if (cur_cfa->reg != dw_stack_pointer_regnum
>>  		  && cur_cfa->reg != dw_frame_pointer_regnum)
>> -		fde->drap_reg = cur_cfa->reg;
>> +		{
>> +		  gcc_assert (cur_cfa->reg.span == 1);
>> +		  fde->drap_reg = cur_cfa->reg.reg;
>> +		}
>>              }
>>            return;
>>  
>> @@ -1935,14 +2064,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	case MINUS:
>>  	case LO_SUM:
>>  	  {
>> -	    unsigned int regno;
>> +	    struct cfa_reg regno;
>>  
>>  	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
>>  	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
>>  	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
>>  	      offset = -offset;
>>  
>> -	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
>> +	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
>>  
>>  	    if (cur_cfa->reg == regno)
>>  	      offset -= cur_cfa->offset;
>> @@ -1960,7 +2089,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	  /* Without an offset.  */
>>  	case REG:
>>  	  {
>> -	    unsigned int regno = dwf_regno (XEXP (dest, 0));
>> +	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
>>  
>>  	    if (cur_cfa->reg == regno)
>>  	      offset = -cur_cfa->offset;
>> @@ -1977,7 +2106,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  	  /* Rule 14 */
>>  	case POST_INC:
>>  	  gcc_assert (cur_trace->cfa_temp.reg
>> -		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
>> +		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
>>  	  offset = -cur_trace->cfa_temp.offset;
>>  	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
>>  	  break;
>> @@ -1995,7 +2124,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>        if (REG_P (src)
>>  	  && REGNO (src) != STACK_POINTER_REGNUM
>>  	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
>> -	  && dwf_regno (src) == cur_cfa->reg)
>> +	  && dwf_cfa_reg (src) == cur_cfa->reg)
>>  	{
>>  	  /* We're storing the current CFA reg into the stack.  */
>>  
>> @@ -2012,7 +2141,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>                    && cur_cfa->indirect == 0
>>                    && cur_cfa->reg != dw_frame_pointer_regnum)
>>                  {
>> -		  gcc_assert (fde->drap_reg == cur_cfa->reg);
>> +		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
>>  
>>  		  cur_cfa->indirect = 1;
>>  		  cur_cfa->reg = dw_frame_pointer_regnum;
>> @@ -2039,7 +2168,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>  		x = XEXP (x, 0);
>>  	      gcc_assert (REG_P (x));
>>  
>> -	      cur_cfa->reg = dwf_regno (x);
>> +	      cur_cfa->reg = dwf_cfa_reg (x);
>>  	      cur_cfa->base_offset = offset;
>>  	      cur_cfa->indirect = 1;
>>  	      break;
>> @@ -2951,7 +3080,7 @@ create_pseudo_cfg (void)
>>    ti.head = get_insns ();
>>    ti.beg_row = cie_cfi_row;
>>    ti.cfa_store = cie_cfi_row->cfa;
>> -  ti.cfa_temp.reg = INVALID_REGNUM;
>> +  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
>>    trace_info.quick_push (ti);
>>  
>>    if (cie_return_save)
>> @@ -3014,14 +3143,15 @@ create_pseudo_cfg (void)
>>  static void
>>  initial_return_save (rtx rtl)
>>  {
>> -  unsigned int reg = INVALID_REGNUM;
>> +  struct cfa_reg reg;
>> +  reg.set_by_dwreg (INVALID_REGNUM);
>>    poly_int64 offset = 0;
>>  
>>    switch (GET_CODE (rtl))
>>      {
>>      case REG:
>>        /* RA is in a register.  */
>> -      reg = dwf_regno (rtl);
>> +      reg = dwf_cfa_reg (rtl);
>>        break;
>>  
>>      case MEM:
>> @@ -3062,9 +3192,9 @@ initial_return_save (rtx rtl)
>>        gcc_unreachable ();
>>      }
>>  
>> -  if (reg != DWARF_FRAME_RETURN_COLUMN)
>> +  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
>>      {
>> -      if (reg != INVALID_REGNUM)
>> +      if (reg.reg != INVALID_REGNUM)
>>          record_reg_saved_in_reg (rtl, pc_rtx);
>>        reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
>>      }
>> @@ -3076,7 +3206,8 @@ create_cie_data (void)
>>    dw_cfa_location loc;
>>    dw_trace_info cie_trace;
>>  
>> -  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
>> +  dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode,
>> +						      STACK_POINTER_REGNUM));
>>  
>>    memset (&cie_trace, 0, sizeof (cie_trace));
>>    cur_trace = &cie_trace;
>> @@ -3135,7 +3266,8 @@ static unsigned int
>>  execute_dwarf2_frame (void)
>>  {
>>    /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
>> -  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
>> +  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
>> +					(Pmode, HARD_FRAME_POINTER_REGNUM));
>>  
>>    /* The first time we're called, compute the incoming frame state.  */
>>    if (cie_cfi_vec == NULL)
>> @@ -3515,7 +3647,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
>>      {
>>        dw_cfa_location dummy;
>>        memset (&dummy, 0, sizeof (dummy));
>> -      dummy.reg = INVALID_REGNUM;
>> +      dummy.reg.set_by_dwreg (INVALID_REGNUM);
>>        cfi = def_cfa_0 (&dummy, &row->cfa);
>>      }
>>    output_cfi_directive (f, cfi);
>> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
>> index 88eb3f9c455..a0b41df6da0 100644
>> --- a/gcc/dwarf2out.c
>> +++ b/gcc/dwarf2out.c
>> @@ -2785,6 +2785,44 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
>>      }
>>  }
>>  
>> +static void
>> +build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
>> +{
>> +  if (regno <= 31)
>> +    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
>> +		  (DW_OP_breg0 + regno),  0, 0));
>> +  else
>> +    add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0));
>> +}
>> +
>> +/* Build a dwarf location for a cfa_reg spanning multiple
>> +   consecutive registers.  */
>> +
>> +struct dw_loc_descr_node *
>> +build_span_loc (struct cfa_reg reg)
>> +{
>> +  struct dw_loc_descr_node *head = NULL;
>> +
>> +  gcc_assert (known_gt (reg.span_width, 0));
>> +  gcc_assert (reg.span > 1);
>> +
>> +  /* Start from the highest number register as it goes in the upper bits.  */
>> +  unsigned int regno = reg.reg + reg.span - 1;
>> +  build_breg_loc (&head, regno);
>> +
>> +  /* deal with the remaining registers in the span.  */
>> +  for (int i = (reg.span - 2); i >= 0; i--)
>> +    {
>> +      add_loc_descr (&head, int_loc_descriptor
>> +		    (reg.span_width.to_constant () * 8));
>> +      add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0));
>> +      regno--;
>> +      build_breg_loc (&head, regno);
>> +      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
>> +    }
>> +  return head;
>> +}
>> +
>>  /* This function builds a dwarf location descriptor sequence from a
>>     dw_cfa_location, adding the given OFFSET to the result of the
>>     expression.  */
>> @@ -2796,9 +2834,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>>  
>>    offset += cfa->offset;
>>  
>> -  if (cfa->indirect)
>> +  if (cfa->reg.span > 1)
>> +    {
>> +      head = build_span_loc (cfa->reg);
>> +
>> +      if (maybe_ne (offset, 0))
>> +	  loc_descr_plus_const (&head, offset);
>> +    }
>> +  else if (cfa->indirect)
>>      {
>> -      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
>> +      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
>>        head->dw_loc_oprnd1.val_class = dw_val_class_const;
>>        head->dw_loc_oprnd1.val_entry = NULL;
>>        tmp = new_loc_descr (DW_OP_deref, 0, 0);
>> @@ -2806,7 +2851,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>>        loc_descr_plus_const (&head, offset);
>>      }
>>    else
>> -    head = new_reg_loc_descr (cfa->reg, offset);
>> +    head = new_reg_loc_descr (cfa->reg.reg, offset);
>>  
>>    return head;
>>  }
>> @@ -2824,7 +2869,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
>>      = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
>>  
>>    /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
>> -  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
>> +  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
>>      {
>>        head = new_reg_loc_descr (dwarf_fp, 0);
>>        add_loc_descr (&head, int_loc_descriptor (alignment));
>> @@ -20865,7 +20910,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
>>    list = NULL;
>>  
>>    memset (&next_cfa, 0, sizeof (next_cfa));
>> -  next_cfa.reg = INVALID_REGNUM;
>> +  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
>>    remember = next_cfa;
>>  
>>    start_label = fde->dw_fde_begin;
>> diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
>> index 54b6343704c..38b52c8e1c2 100644
>> --- a/gcc/dwarf2out.h
>> +++ b/gcc/dwarf2out.h
>> @@ -119,6 +119,40 @@ struct GTY(()) dw_fde_node {
>>  };
>>  
>>  
>> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
>> +   definitions and expressions.
>> +   Most architectures only need a single register number, but some (amdgcn)
>> +   have pointers that span multiple registers.  DWARF permits arbitrary
>> +   register sets but existing use-cases only require contiguous register
>> +   sets, as represented here.  */
>> +struct GTY(()) cfa_reg {
>> +  unsigned int reg;
>> +  unsigned int span;
>> +  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
>> +
>> +  cfa_reg& set_by_dwreg (unsigned int r)
>> +    {
>> +      reg = r;
>> +      span = 1;
>> +      span_width = 0;  /* Unknown size (permitted when span == 1).  */
>> +      return *this;
>> +    }
>> +
>> +  bool operator== (const cfa_reg other) const
>> +    {
>> +      return (reg == other.reg
>> +	      && span == other.span
>> +	      && (known_eq (span_width, other.span_width)
>> +		  || (span == 1
>> +		      && (known_eq (span_width, 0)
>> +			  || known_eq (other.span_width, 0)))));
>> +    }
>> +  bool operator!= (const cfa_reg other) const
>> +    {
>> +      return !(*this == other);
>> +    }
>> +};
>> +
>>  /* This is how we define the location of the CFA. We use to handle it
>>     as REG + OFFSET all the time,  but now it can be more complex.
>>     It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
>> @@ -128,7 +162,7 @@ struct GTY(()) dw_cfa_location {
>>    poly_int64_pod offset;
>>    poly_int64_pod base_offset;
>>    /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
>> -  unsigned int reg;
>> +  struct cfa_reg reg;
>>    BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
>>    BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
>>  };
>> @@ -285,6 +319,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
>>    (dw_cfa_location *, poly_int64);
>>  extern struct dw_loc_descr_node *build_cfa_aligned_loc
>>    (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
>> +extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
>>  extern struct dw_loc_descr_node *mem_loc_descriptor
>>    (rtx, machine_mode mode, machine_mode mem_mode,
>>     enum var_init_status);
>> diff --git a/gcc/gengtype.c b/gcc/gengtype.c
>> index b94e2f126ec..45e9f856470 100644
>> --- a/gcc/gengtype.c
>> +++ b/gcc/gengtype.c
>> @@ -5195,6 +5195,7 @@ main (int argc, char **argv)
>>        POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
>>        POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
>>        POS_HERE (do_scalar_typedef ("double_int", &pos));
>> +      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
>>        POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
>>        POS_HERE (do_scalar_typedef ("offset_int", &pos));
>>        POS_HERE (do_scalar_typedef ("widest_int", &pos));
>>
> 
> 


-- 
Hafiz Abid Qadeer
Mentor, a Siemens Business

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-08-24 15:55   ` Hafiz Abid Qadeer
@ 2021-11-02 15:02     ` Hafiz Abid Qadeer
  0 siblings, 0 replies; 15+ messages in thread
From: Hafiz Abid Qadeer @ 2021-11-02 15:02 UTC (permalink / raw)
  To: Hafiz Abid Qadeer, gcc-patches; +Cc: jakub, ams

Ping.

On 24/08/2021 16:55, Hafiz Abid Qadeer wrote:
> Ping.
> 
> On 22/07/2021 11:58, Hafiz Abid Qadeer wrote:
>> Ping.
>>
>> On 13/06/2021 14:27, Hafiz Abid Qadeer wrote:
>>> Add support for architectures such as AMD GCN, in which the pointer size is
>>> larger than the register size.  This allows the CFI information to include
>>> multi-register locations for the stack pointer, frame pointer, and return
>>> address.
>>>
>>> This patch was originally posted by Andrew Stubbs in
>>> https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html
>>>
>>> It has now been re-worked according to the review comments. It does not use
>>> DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses
>>> DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple
>>> consecutive registers. Here is how .debug_frame looks before and after this
>>> patch:
>>>
>>> $ cat factorial.c
>>> int factorial(int n) {
>>>   if (n == 0) return 1;
>>>   return n * factorial (n - 1);
>>> }
>>>
>>> $ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o
>>> $ llvm-dwarfdump -debug-frame fac.o
>>>
>>> *** without this patch (edited for brevity)***
>>>
>>> 00000000 00000014 ffffffff CIE
>>>
>>>   DW_CFA_def_cfa: reg48 +0
>>>   DW_CFA_register: reg16 reg50
>>>
>>> 00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac
>>>   DW_CFA_advance_loc4: 96
>>>   DW_CFA_offset: reg46 0
>>>   DW_CFA_offset: reg47 4
>>>   DW_CFA_offset: reg50 8
>>>   DW_CFA_offset: reg51 12
>>>   DW_CFA_offset: reg16 8
>>>   DW_CFA_advance_loc4: 4
>>>   DW_CFA_def_cfa_sf: reg46 -16
>>>
>>> *** with this patch (edited for brevity)***
>>>
>>> 00000000 00000024 ffffffff CIE
>>>
>>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
>>>   DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus
>>>
>>> 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
>>>   DW_CFA_advance_loc4: 96
>>>   DW_CFA_offset: reg46 0
>>>   DW_CFA_offset: reg47 4
>>>   DW_CFA_offset: reg50 8
>>>   DW_CFA_offset: reg51 12
>>>   DW_CFA_offset: reg16 8
>>>   DW_CFA_advance_loc4: 4
>>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus
>>>
>>> gcc/ChangeLog:
>>>
>>> 	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
>>> 	(dw_frame_pointer_regnum): Likewise.
>>> 	(new_cfi_row): Use set_by_dwreg.
>>> 	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
>>> 	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,
>>> 	DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus.
>>> 	(lookup_cfa_1): Use set_by_dwreg.
>>> 	(def_cfa_0): Update for cfa_reg and support register spans.
>>> 	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
>>> 	spans.
>>> 	(dwf_cfa_reg): New function.
>>> 	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
>>> 	dwf_regno.
>>> 	(dwarf2out_frame_debug_def_cfa): Likewise.
>>> 	(dwarf2out_frame_debug_adjust_cfa): Likewise.
>>> 	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
>>> 	(dwarf2out_frame_debug_cfa_register): Likewise.
>>> 	(dwarf2out_frame_debug_expr): Likewise.
>>> 	(create_pseudo_cfg): Use set_by_dwreg.
>>> 	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
>>> 	(create_cie_data): Use dwf_cfa_reg.
>>> 	(execute_dwarf2_frame): Use dwf_cfa_reg.
>>> 	(dump_cfi_row): Use set_by_dwreg.
>>> 	* dwarf2out.c (build_span_loc, build_breg_loc): New function.
>>> 	(build_cfa_loc): Support register spans.
>>> 	(build_cfa_aligned_loc): Update cfa_reg usage.
>>> 	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
>>> 	* dwarf2out.h (struct cfa_reg): New type.
>>> 	(struct dw_cfa_location): Use struct cfa_reg.
>>> 	(build_span_loc): New prototype.
>>> 	* gengtype.c (main): Accept poly_uint16_pod type.
>>> ---
>>>  gcc/dwarf2cfi.c | 260 ++++++++++++++++++++++++++++++++++++------------
>>>  gcc/dwarf2out.c |  55 +++++++++-
>>>  gcc/dwarf2out.h |  37 ++++++-
>>>  gcc/gengtype.c  |   1 +
>>>  4 files changed, 283 insertions(+), 70 deletions(-)
>>>
>>> diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
>>> index c27ac1960b0..5aacdcd094a 100644
>>> --- a/gcc/dwarf2cfi.c
>>> +++ b/gcc/dwarf2cfi.c
>>> @@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
>>>  static bool any_cfis_emitted;
>>>  
>>>  /* Short-hand for commonly used register numbers.  */
>>> -static unsigned dw_stack_pointer_regnum;
>>> -static unsigned dw_frame_pointer_regnum;
>>> +static struct cfa_reg dw_stack_pointer_regnum;
>>> +static struct cfa_reg dw_frame_pointer_regnum;
>>>  \f
>>>  /* Hook used by __throw.  */
>>>  
>>> @@ -430,7 +430,7 @@ new_cfi_row (void)
>>>  {
>>>    dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
>>>  
>>> -  row->cfa.reg = INVALID_REGNUM;
>>> +  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
>>>  
>>>    return row;
>>>  }
>>> @@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>>    cfa->offset = 0;
>>>    cfa->base_offset = 0;
>>>    cfa->indirect = 0;
>>> -  cfa->reg = -1;
>>> +  cfa->reg.set_by_dwreg (INVALID_REGNUM);
>>>  
>>>    for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
>>>      {
>>> @@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>>  	case DW_OP_reg29:
>>>  	case DW_OP_reg30:
>>>  	case DW_OP_reg31:
>>> -	  cfa->reg = op - DW_OP_reg0;
>>> +	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
>>>  	  break;
>>>  	case DW_OP_regx:
>>> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
>>> +	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
>>>  	  break;
>>>  	case DW_OP_breg0:
>>>  	case DW_OP_breg1:
>>> @@ -615,16 +615,92 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
>>>  	case DW_OP_breg29:
>>>  	case DW_OP_breg30:
>>>  	case DW_OP_breg31:
>>> -	  cfa->reg = op - DW_OP_breg0;
>>> -	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
>>> -	  break;
>>>  	case DW_OP_bregx:
>>> -	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
>>> -	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
>>> +	  if (cfa->reg.reg == INVALID_REGNUM)
>>> +	    {
>>> +	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
>>> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));
>>> +	      cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
>>> +	    }
>>> +	  else
>>> +	    {
>>> +	      /* Handle case when span can cover multiple registers.  We
>>> +		 only support the simple case of consecutive registers
>>> +		 all with the same size.  DWARF that we are dealing with
>>> +		 will look something like:
>>> +		 <DW_OP_bregx: (r49) 0; DW_OP_const1u: 32; DW_OP_shl;
>>> +		  DW_OP_bregx: (r48) 0; DW_OP_plus> */
>>> +
>>> +	      unsigned int regno = (op == DW_OP_bregx)
>>> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);
>>> +	      gcc_assert (regno == (cfa->reg.reg - 1));
>>> +	      cfa->reg.span++;
>>> +	      /* From all the consecutive registers used, we want to set
>>> +	         cfa->reg.reg to lower number register.  */
>>> +	      cfa->reg.reg = regno;
>>> +	      /* The offset was the shift value.  Use it to get the
>>> +		 span_width and then set it to 0.  */
>>> +	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);
>>> +	      cfa->offset = 0;
>>> +	    }
>>>  	  break;
>>>  	case DW_OP_deref:
>>>  	  cfa->indirect = 1;
>>>  	  break;
>>> +	case DW_OP_shl:
>>> +	  break;
>>> +	case DW_OP_lit0:
>>> +	case DW_OP_lit1:
>>> +	case DW_OP_lit2:
>>> +	case DW_OP_lit3:
>>> +	case DW_OP_lit4:
>>> +	case DW_OP_lit5:
>>> +	case DW_OP_lit6:
>>> +	case DW_OP_lit7:
>>> +	case DW_OP_lit8:
>>> +	case DW_OP_lit9:
>>> +	case DW_OP_lit10:
>>> +	case DW_OP_lit11:
>>> +	case DW_OP_lit12:
>>> +	case DW_OP_lit13:
>>> +	case DW_OP_lit14:
>>> +	case DW_OP_lit15:
>>> +	case DW_OP_lit16:
>>> +	case DW_OP_lit17:
>>> +	case DW_OP_lit18:
>>> +	case DW_OP_lit19:
>>> +	case DW_OP_lit20:
>>> +	case DW_OP_lit21:
>>> +	case DW_OP_lit22:
>>> +	case DW_OP_lit23:
>>> +	case DW_OP_lit24:
>>> +	case DW_OP_lit25:
>>> +	case DW_OP_lit26:
>>> +	case DW_OP_lit27:
>>> +	case DW_OP_lit28:
>>> +	case DW_OP_lit29:
>>> +	case DW_OP_lit30:
>>> +	case DW_OP_lit31:
>>> +	  gcc_assert (known_eq (cfa->offset, 0));
>>> +	  cfa->offset = op - DW_OP_lit0;
>>> +	  break;
>>> +	case DW_OP_const1u:
>>> +	case DW_OP_const1s:
>>> +	case DW_OP_const2u:
>>> +	case DW_OP_const2s:
>>> +	case DW_OP_const4s:
>>> +	case DW_OP_const8s:
>>> +	case DW_OP_constu:
>>> +	case DW_OP_consts:
>>> +	  gcc_assert (known_eq (cfa->offset, 0));
>>> +	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
>>> +	  break;
>>> +	case DW_OP_minus:
>>> +	  cfa->offset = -cfa->offset;
>>> +	  break;
>>> +	case DW_OP_plus:
>>> +	  /* The offset is already in place.  */
>>> +	  break;
>>>  	case DW_OP_plus_uconst:
>>>  	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
>>>  	  break;
>>> @@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
>>>        loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
>>>        break;
>>>      case DW_CFA_def_cfa_register:
>>> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
>>> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>>>        break;
>>>      case DW_CFA_def_cfa:
>>>      case DW_CFA_def_cfa_sf:
>>> -      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
>>> +      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
>>>        loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
>>>        break;
>>>      case DW_CFA_def_cfa_expression:
>>> @@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>>  
>>>    HOST_WIDE_INT const_offset;
>>>    if (new_cfa->reg == old_cfa->reg
>>> +      && new_cfa->reg.span == 1
>>>        && !new_cfa->indirect
>>>        && !old_cfa->indirect
>>>        && new_cfa->offset.is_constant (&const_offset))
>>> @@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>>      }
>>>    else if (new_cfa->offset.is_constant ()
>>>  	   && known_eq (new_cfa->offset, old_cfa->offset)
>>> -	   && old_cfa->reg != INVALID_REGNUM
>>> +	   && old_cfa->reg.reg != INVALID_REGNUM
>>> +	   && new_cfa->reg.span == 1
>>>  	   && !new_cfa->indirect
>>>  	   && !old_cfa->indirect)
>>>      {
>>> @@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>>  	 been set as a register plus offset rather than a general
>>>  	 DW_CFA_def_cfa_expression.  */
>>>        cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
>>> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
>>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>>>      }
>>>    else if (new_cfa->indirect == 0
>>> -	   && new_cfa->offset.is_constant (&const_offset))
>>> +	   && new_cfa->offset.is_constant (&const_offset)
>>> +	   && new_cfa->reg.span == 1)
>>>      {
>>>        /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
>>>  	 indicating the CFA register has changed to <register> with
>>> @@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
>>>  	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
>>>        else
>>>  	cfi->dw_cfi_opc = DW_CFA_def_cfa;
>>> -      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
>>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
>>>        cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
>>>      }
>>>    else
>>> @@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
>>>  }
>>>  
>>>  /* Add the CFI for saving a register.  REG is the CFA column number.
>>> -   If SREG is -1, the register is saved at OFFSET from the CFA;
>>> +   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
>>>     otherwise it is saved in SREG.  */
>>>  
>>>  static void
>>> -reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>>> +reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
>>>  {
>>>    dw_fde_ref fde = cfun ? cfun->fde : NULL;
>>>    dw_cfi_ref cfi = new_cfi ();
>>>  
>>>    cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
>>>  
>>> -  if (sreg == INVALID_REGNUM)
>>> +  if (sreg.reg == INVALID_REGNUM)
>>>      {
>>>        HOST_WIDE_INT const_offset;
>>>        /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
>>> @@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>>>  	    = build_cfa_loc (&cur_row->cfa, offset);
>>>  	}
>>>      }
>>> -  else if (sreg == reg)
>>> +  else if (sreg.reg == reg)
>>>      {
>>>        /* While we could emit something like DW_CFA_same_value or
>>>  	 DW_CFA_restore, we never expect to see something like that
>>> @@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
>>>  	 can always bypass this by using REG_CFA_RESTORE directly.  */
>>>        gcc_unreachable ();
>>>      }
>>> +  else if (sreg.span > 1)
>>> +    {
>>> +      cfi->dw_cfi_opc = DW_CFA_expression;
>>> +      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
>>> +      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
>>> +    }
>>>    else
>>>      {
>>>        cfi->dw_cfi_opc = DW_CFA_register;
>>> -      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
>>> +      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
>>>      }
>>>  
>>>    add_cfi (cfi);
>>> @@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
>>>    return DWARF_FRAME_REGNUM (REGNO (reg));
>>>  }
>>>  
>>> +/* Like dwf_regno, but when the value can span multiple registers.  */
>>> +
>>> +static struct cfa_reg
>>> +dwf_cfa_reg (rtx reg)
>>> +{
>>> +  struct cfa_reg result;
>>> +
>>> +  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
>>> +
>>> +  result.reg = dwf_regno (reg);
>>> +  result.span = 1;
>>> +  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
>>> +
>>> +  rtx span = targetm.dwarf_register_span (reg);
>>> +  if (span)
>>> +    {
>>> +      /* We only support the simple case of consecutive registers all with the
>>> +	 same size.  */
>>> +      result.span = XVECLEN (span, 0);
>>> +      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
>>> +
>>> +#if CHECKING_P
>>> +      /* Ensure that the above assumption is accurate.  */
>>> +      for (unsigned int i = 0; i < result.span; i++)
>>> +	{
>>> +	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
>>> +								  0, i))),
>>> +				result.span_width));
>>> +	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
>>> +	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
>>> +	}
>>> +#endif
>>> +    }
>>> +
>>> +  return result;
>>> +}
>>> +
>>>  /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
>>>  
>>>  static bool
>>> @@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
>>>  
>>>    FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
>>>      {
>>> -      unsigned int reg, sreg;
>>> +      unsigned int reg;
>>> +      struct cfa_reg sreg;
>>>  
>>>        record_reg_saved_in_reg (q->saved_reg, q->reg);
>>>  
>>> @@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
>>>        else
>>>          reg = dwf_regno (q->reg);
>>>        if (q->saved_reg)
>>> -	sreg = dwf_regno (q->saved_reg);
>>> +	sreg = dwf_cfa_reg (q->saved_reg);
>>>        else
>>> -	sreg = INVALID_REGNUM;
>>> +	sreg.set_by_dwreg (INVALID_REGNUM);
>>>        reg_save (reg, sreg, q->cfa_offset);
>>>      }
>>>  
>>> @@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
>>>    /* ??? If this fails, we could be calling into the _loc functions to
>>>       define a full expression.  So far no port does that.  */
>>>    gcc_assert (REG_P (pat));
>>> -  cur_cfa->reg = dwf_regno (pat);
>>> +  cur_cfa->reg = dwf_cfa_reg (pat);
>>>  }
>>>  
>>>  /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
>>> @@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>>>    switch (GET_CODE (src))
>>>      {
>>>      case PLUS:
>>> -      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
>>> +      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>>>        cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
>>>        break;
>>>  
>>> @@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
>>>        gcc_unreachable ();
>>>      }
>>>  
>>> -  cur_cfa->reg = dwf_regno (dest);
>>> +  cur_cfa->reg = dwf_cfa_reg (dest);
>>>    gcc_assert (cur_cfa->indirect == 0);
>>>  }
>>>  
>>> @@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>>    switch (GET_CODE (addr))
>>>      {
>>>      case REG:
>>> -      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
>>> +      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
>>>        offset = -cur_cfa->offset;
>>>        break;
>>>      case PLUS:
>>> -      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
>>> +      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
>>>        offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
>>>        break;
>>>      default:
>>> @@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>>  
>>>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>>>       a different flushing heuristic for epilogues.  */
>>> +  struct cfa_reg invalid;
>>> +  invalid.set_by_dwreg (INVALID_REGNUM);
>>>    if (!span)
>>> -    reg_save (sregno, INVALID_REGNUM, offset);
>>> +    reg_save (sregno, invalid, offset);
>>>    else
>>>      {
>>>        /* We have a PARALLEL describing where the contents of SRC live.
>>> @@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
>>>  	{
>>>  	  rtx elem = XVECEXP (span, 0, par_index);
>>>  	  sregno = dwf_regno (src);
>>> -	  reg_save (sregno, INVALID_REGNUM, span_offset);
>>> +	  reg_save (sregno, invalid, span_offset);
>>>  	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
>>>  	}
>>>      }
>>> @@ -1270,7 +1395,8 @@ static void
>>>  dwarf2out_frame_debug_cfa_register (rtx set)
>>>  {
>>>    rtx src, dest;
>>> -  unsigned sregno, dregno;
>>> +  unsigned sregno;
>>> +  struct cfa_reg dregno;
>>>  
>>>    src = XEXP (set, 1);
>>>    dest = XEXP (set, 0);
>>> @@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
>>>    else
>>>      sregno = dwf_regno (src);
>>>  
>>> -  dregno = dwf_regno (dest);
>>> +  dregno = dwf_cfa_reg (dest);
>>>  
>>>    /* ??? We'd like to use queue_reg_save, but we need to come up with
>>>       a different flushing heuristic for epilogues.  */
>>> @@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	{
>>>  	  /* Setting FP from SP.  */
>>>  	case REG:
>>> -	  if (cur_cfa->reg == dwf_regno (src))
>>> +	  if (cur_cfa->reg == dwf_cfa_reg (src))
>>>  	    {
>>>  	      /* Rule 1 */
>>>  	      /* Update the CFA rule wrt SP or FP.  Make sure src is
>>> @@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  		 ARM copies SP to a temporary register, and from there to
>>>  		 FP.  So we just rely on the backends to only set
>>>  		 RTX_FRAME_RELATED_P on appropriate insns.  */
>>> -	      cur_cfa->reg = dwf_regno (dest);
>>> +	      cur_cfa->reg = dwf_cfa_reg (dest);
>>>  	      cur_trace->cfa_temp.reg = cur_cfa->reg;
>>>  	      cur_trace->cfa_temp.offset = cur_cfa->offset;
>>>  	    }
>>> @@ -1698,7 +1824,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  		{
>>>  		  gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
>>>  			      && fde->drap_reg != INVALID_REGNUM
>>> -			      && cur_cfa->reg != dwf_regno (src)
>>> +			      && cur_cfa->reg != dwf_cfa_reg (src)
>>>  			      && fde->rule18);
>>>  		  fde->rule18 = 0;
>>>  		  /* The save of hard frame pointer has been deferred
>>> @@ -1722,7 +1848,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	      /* Adjusting SP.  */
>>>  	      if (REG_P (XEXP (src, 1)))
>>>  		{
>>> -		  gcc_assert (dwf_regno (XEXP (src, 1))
>>> +		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
>>>  			      == cur_trace->cfa_temp.reg);
>>>  		  offset = cur_trace->cfa_temp.offset;
>>>  		}
>>> @@ -1756,7 +1882,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	      gcc_assert (frame_pointer_needed);
>>>  
>>>  	      gcc_assert (REG_P (XEXP (src, 0))
>>> -			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
>>> +			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
>>>  	      offset = rtx_to_poly_int64 (XEXP (src, 1));
>>>  	      if (GET_CODE (src) != MINUS)
>>>  		offset = -offset;
>>> @@ -1769,14 +1895,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  
>>>  	      /* Rule 4 */
>>>  	      if (REG_P (XEXP (src, 0))
>>> -		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
>>> +		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
>>>  		  && poly_int_rtx_p (XEXP (src, 1), &offset))
>>>  		{
>>>  		  /* Setting a temporary CFA register that will be copied
>>>  		     into the FP later on.  */
>>>  		  offset = -offset;
>>>  		  cur_cfa->offset += offset;
>>> -		  cur_cfa->reg = dwf_regno (dest);
>>> +		  cur_cfa->reg = dwf_cfa_reg (dest);
>>>  		  /* Or used to save regs to the stack.  */
>>>  		  cur_trace->cfa_temp.reg = cur_cfa->reg;
>>>  		  cur_trace->cfa_temp.offset = cur_cfa->offset;
>>> @@ -1784,13 +1910,13 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  
>>>  	      /* Rule 5 */
>>>  	      else if (REG_P (XEXP (src, 0))
>>> -		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>> +		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>>  		       && XEXP (src, 1) == stack_pointer_rtx)
>>>  		{
>>>  		  /* Setting a scratch register that we will use instead
>>>  		     of SP for saving registers to the stack.  */
>>>  		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
>>> -		  cur_trace->cfa_store.reg = dwf_regno (dest);
>>> +		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
>>>  		  cur_trace->cfa_store.offset
>>>  		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
>>>  		}
>>> @@ -1799,7 +1925,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	      else if (GET_CODE (src) == LO_SUM
>>>  		       && poly_int_rtx_p (XEXP (src, 1),
>>>  					  &cur_trace->cfa_temp.offset))
>>> -		cur_trace->cfa_temp.reg = dwf_regno (dest);
>>> +		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>>  	      else
>>>  		gcc_unreachable ();
>>>  	    }
>>> @@ -1808,17 +1934,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	  /* Rule 6 */
>>>  	case CONST_INT:
>>>  	case CONST_POLY_INT:
>>> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
>>> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>>  	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
>>>  	  break;
>>>  
>>>  	  /* Rule 7 */
>>>  	case IOR:
>>>  	  gcc_assert (REG_P (XEXP (src, 0))
>>> -		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>> +		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
>>>  		      && CONST_INT_P (XEXP (src, 1)));
>>>  
>>> -	  cur_trace->cfa_temp.reg = dwf_regno (dest);
>>> +	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
>>>  	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
>>>  			  &cur_trace->cfa_temp.offset))
>>>  	    /* The target shouldn't generate this kind of CFI note if we
>>> @@ -1851,14 +1977,17 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	      dwarf2out_flush_queued_reg_saves ();
>>>  
>>>                gcc_assert (cur_trace->cfa_store.reg
>>> -			  == dwf_regno (XEXP (src, 0)));
>>> +			  == dwf_cfa_reg (XEXP (src, 0)));
>>>                fde->stack_realign = 1;
>>>                fde->stack_realignment = INTVAL (XEXP (src, 1));
>>>                cur_trace->cfa_store.offset = 0;
>>>  
>>>  	      if (cur_cfa->reg != dw_stack_pointer_regnum
>>>  		  && cur_cfa->reg != dw_frame_pointer_regnum)
>>> -		fde->drap_reg = cur_cfa->reg;
>>> +		{
>>> +		  gcc_assert (cur_cfa->reg.span == 1);
>>> +		  fde->drap_reg = cur_cfa->reg.reg;
>>> +		}
>>>              }
>>>            return;
>>>  
>>> @@ -1935,14 +2064,14 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	case MINUS:
>>>  	case LO_SUM:
>>>  	  {
>>> -	    unsigned int regno;
>>> +	    struct cfa_reg regno;
>>>  
>>>  	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
>>>  	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
>>>  	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
>>>  	      offset = -offset;
>>>  
>>> -	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
>>> +	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
>>>  
>>>  	    if (cur_cfa->reg == regno)
>>>  	      offset -= cur_cfa->offset;
>>> @@ -1960,7 +2089,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	  /* Without an offset.  */
>>>  	case REG:
>>>  	  {
>>> -	    unsigned int regno = dwf_regno (XEXP (dest, 0));
>>> +	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
>>>  
>>>  	    if (cur_cfa->reg == regno)
>>>  	      offset = -cur_cfa->offset;
>>> @@ -1977,7 +2106,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  	  /* Rule 14 */
>>>  	case POST_INC:
>>>  	  gcc_assert (cur_trace->cfa_temp.reg
>>> -		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
>>> +		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
>>>  	  offset = -cur_trace->cfa_temp.offset;
>>>  	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
>>>  	  break;
>>> @@ -1995,7 +2124,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>        if (REG_P (src)
>>>  	  && REGNO (src) != STACK_POINTER_REGNUM
>>>  	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
>>> -	  && dwf_regno (src) == cur_cfa->reg)
>>> +	  && dwf_cfa_reg (src) == cur_cfa->reg)
>>>  	{
>>>  	  /* We're storing the current CFA reg into the stack.  */
>>>  
>>> @@ -2012,7 +2141,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>                    && cur_cfa->indirect == 0
>>>                    && cur_cfa->reg != dw_frame_pointer_regnum)
>>>                  {
>>> -		  gcc_assert (fde->drap_reg == cur_cfa->reg);
>>> +		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
>>>  
>>>  		  cur_cfa->indirect = 1;
>>>  		  cur_cfa->reg = dw_frame_pointer_regnum;
>>> @@ -2039,7 +2168,7 @@ dwarf2out_frame_debug_expr (rtx expr)
>>>  		x = XEXP (x, 0);
>>>  	      gcc_assert (REG_P (x));
>>>  
>>> -	      cur_cfa->reg = dwf_regno (x);
>>> +	      cur_cfa->reg = dwf_cfa_reg (x);
>>>  	      cur_cfa->base_offset = offset;
>>>  	      cur_cfa->indirect = 1;
>>>  	      break;
>>> @@ -2951,7 +3080,7 @@ create_pseudo_cfg (void)
>>>    ti.head = get_insns ();
>>>    ti.beg_row = cie_cfi_row;
>>>    ti.cfa_store = cie_cfi_row->cfa;
>>> -  ti.cfa_temp.reg = INVALID_REGNUM;
>>> +  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
>>>    trace_info.quick_push (ti);
>>>  
>>>    if (cie_return_save)
>>> @@ -3014,14 +3143,15 @@ create_pseudo_cfg (void)
>>>  static void
>>>  initial_return_save (rtx rtl)
>>>  {
>>> -  unsigned int reg = INVALID_REGNUM;
>>> +  struct cfa_reg reg;
>>> +  reg.set_by_dwreg (INVALID_REGNUM);
>>>    poly_int64 offset = 0;
>>>  
>>>    switch (GET_CODE (rtl))
>>>      {
>>>      case REG:
>>>        /* RA is in a register.  */
>>> -      reg = dwf_regno (rtl);
>>> +      reg = dwf_cfa_reg (rtl);
>>>        break;
>>>  
>>>      case MEM:
>>> @@ -3062,9 +3192,9 @@ initial_return_save (rtx rtl)
>>>        gcc_unreachable ();
>>>      }
>>>  
>>> -  if (reg != DWARF_FRAME_RETURN_COLUMN)
>>> +  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
>>>      {
>>> -      if (reg != INVALID_REGNUM)
>>> +      if (reg.reg != INVALID_REGNUM)
>>>          record_reg_saved_in_reg (rtl, pc_rtx);
>>>        reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
>>>      }
>>> @@ -3076,7 +3206,8 @@ create_cie_data (void)
>>>    dw_cfa_location loc;
>>>    dw_trace_info cie_trace;
>>>  
>>> -  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
>>> +  dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode,
>>> +						      STACK_POINTER_REGNUM));
>>>  
>>>    memset (&cie_trace, 0, sizeof (cie_trace));
>>>    cur_trace = &cie_trace;
>>> @@ -3135,7 +3266,8 @@ static unsigned int
>>>  execute_dwarf2_frame (void)
>>>  {
>>>    /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
>>> -  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
>>> +  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
>>> +					(Pmode, HARD_FRAME_POINTER_REGNUM));
>>>  
>>>    /* The first time we're called, compute the incoming frame state.  */
>>>    if (cie_cfi_vec == NULL)
>>> @@ -3515,7 +3647,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
>>>      {
>>>        dw_cfa_location dummy;
>>>        memset (&dummy, 0, sizeof (dummy));
>>> -      dummy.reg = INVALID_REGNUM;
>>> +      dummy.reg.set_by_dwreg (INVALID_REGNUM);
>>>        cfi = def_cfa_0 (&dummy, &row->cfa);
>>>      }
>>>    output_cfi_directive (f, cfi);
>>> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
>>> index 88eb3f9c455..a0b41df6da0 100644
>>> --- a/gcc/dwarf2out.c
>>> +++ b/gcc/dwarf2out.c
>>> @@ -2785,6 +2785,44 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
>>>      }
>>>  }
>>>  
>>> +static void
>>> +build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
>>> +{
>>> +  if (regno <= 31)
>>> +    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
>>> +		  (DW_OP_breg0 + regno),  0, 0));
>>> +  else
>>> +    add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0));
>>> +}
>>> +
>>> +/* Build a dwarf location for a cfa_reg spanning multiple
>>> +   consecutive registers.  */
>>> +
>>> +struct dw_loc_descr_node *
>>> +build_span_loc (struct cfa_reg reg)
>>> +{
>>> +  struct dw_loc_descr_node *head = NULL;
>>> +
>>> +  gcc_assert (known_gt (reg.span_width, 0));
>>> +  gcc_assert (reg.span > 1);
>>> +
>>> +  /* Start from the highest number register as it goes in the upper bits.  */
>>> +  unsigned int regno = reg.reg + reg.span - 1;
>>> +  build_breg_loc (&head, regno);
>>> +
>>> +  /* deal with the remaining registers in the span.  */
>>> +  for (int i = (reg.span - 2); i >= 0; i--)
>>> +    {
>>> +      add_loc_descr (&head, int_loc_descriptor
>>> +		    (reg.span_width.to_constant () * 8));
>>> +      add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0));
>>> +      regno--;
>>> +      build_breg_loc (&head, regno);
>>> +      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
>>> +    }
>>> +  return head;
>>> +}
>>> +
>>>  /* This function builds a dwarf location descriptor sequence from a
>>>     dw_cfa_location, adding the given OFFSET to the result of the
>>>     expression.  */
>>> @@ -2796,9 +2834,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>>>  
>>>    offset += cfa->offset;
>>>  
>>> -  if (cfa->indirect)
>>> +  if (cfa->reg.span > 1)
>>> +    {
>>> +      head = build_span_loc (cfa->reg);
>>> +
>>> +      if (maybe_ne (offset, 0))
>>> +	  loc_descr_plus_const (&head, offset);
>>> +    }
>>> +  else if (cfa->indirect)
>>>      {
>>> -      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
>>> +      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
>>>        head->dw_loc_oprnd1.val_class = dw_val_class_const;
>>>        head->dw_loc_oprnd1.val_entry = NULL;
>>>        tmp = new_loc_descr (DW_OP_deref, 0, 0);
>>> @@ -2806,7 +2851,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
>>>        loc_descr_plus_const (&head, offset);
>>>      }
>>>    else
>>> -    head = new_reg_loc_descr (cfa->reg, offset);
>>> +    head = new_reg_loc_descr (cfa->reg.reg, offset);
>>>  
>>>    return head;
>>>  }
>>> @@ -2824,7 +2869,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
>>>      = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
>>>  
>>>    /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
>>> -  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
>>> +  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
>>>      {
>>>        head = new_reg_loc_descr (dwarf_fp, 0);
>>>        add_loc_descr (&head, int_loc_descriptor (alignment));
>>> @@ -20865,7 +20910,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
>>>    list = NULL;
>>>  
>>>    memset (&next_cfa, 0, sizeof (next_cfa));
>>> -  next_cfa.reg = INVALID_REGNUM;
>>> +  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
>>>    remember = next_cfa;
>>>  
>>>    start_label = fde->dw_fde_begin;
>>> diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
>>> index 54b6343704c..38b52c8e1c2 100644
>>> --- a/gcc/dwarf2out.h
>>> +++ b/gcc/dwarf2out.h
>>> @@ -119,6 +119,40 @@ struct GTY(()) dw_fde_node {
>>>  };
>>>  
>>>  
>>> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
>>> +   definitions and expressions.
>>> +   Most architectures only need a single register number, but some (amdgcn)
>>> +   have pointers that span multiple registers.  DWARF permits arbitrary
>>> +   register sets but existing use-cases only require contiguous register
>>> +   sets, as represented here.  */
>>> +struct GTY(()) cfa_reg {
>>> +  unsigned int reg;
>>> +  unsigned int span;
>>> +  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
>>> +
>>> +  cfa_reg& set_by_dwreg (unsigned int r)
>>> +    {
>>> +      reg = r;
>>> +      span = 1;
>>> +      span_width = 0;  /* Unknown size (permitted when span == 1).  */
>>> +      return *this;
>>> +    }
>>> +
>>> +  bool operator== (const cfa_reg other) const
>>> +    {
>>> +      return (reg == other.reg
>>> +	      && span == other.span
>>> +	      && (known_eq (span_width, other.span_width)
>>> +		  || (span == 1
>>> +		      && (known_eq (span_width, 0)
>>> +			  || known_eq (other.span_width, 0)))));
>>> +    }
>>> +  bool operator!= (const cfa_reg other) const
>>> +    {
>>> +      return !(*this == other);
>>> +    }
>>> +};
>>> +
>>>  /* This is how we define the location of the CFA. We use to handle it
>>>     as REG + OFFSET all the time,  but now it can be more complex.
>>>     It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
>>> @@ -128,7 +162,7 @@ struct GTY(()) dw_cfa_location {
>>>    poly_int64_pod offset;
>>>    poly_int64_pod base_offset;
>>>    /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
>>> -  unsigned int reg;
>>> +  struct cfa_reg reg;
>>>    BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
>>>    BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
>>>  };
>>> @@ -285,6 +319,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
>>>    (dw_cfa_location *, poly_int64);
>>>  extern struct dw_loc_descr_node *build_cfa_aligned_loc
>>>    (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
>>> +extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
>>>  extern struct dw_loc_descr_node *mem_loc_descriptor
>>>    (rtx, machine_mode mode, machine_mode mem_mode,
>>>     enum var_init_status);
>>> diff --git a/gcc/gengtype.c b/gcc/gengtype.c
>>> index b94e2f126ec..45e9f856470 100644
>>> --- a/gcc/gengtype.c
>>> +++ b/gcc/gengtype.c
>>> @@ -5195,6 +5195,7 @@ main (int argc, char **argv)
>>>        POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
>>>        POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
>>>        POS_HERE (do_scalar_typedef ("double_int", &pos));
>>> +      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
>>>        POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
>>>        POS_HERE (do_scalar_typedef ("offset_int", &pos));
>>>        POS_HERE (do_scalar_typedef ("widest_int", &pos));
>>>
>>
>>
> 
> 


-- 
Hafiz Abid Qadeer
Mentor, a Siemens Business

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-06-13 13:27 [PATCH] dwarf: Multi-register CFI address support Hafiz Abid Qadeer
  2021-07-22 10:58 ` Hafiz Abid Qadeer
@ 2021-11-09 15:59 ` Jakub Jelinek
  2021-11-11 18:12   ` Hafiz Abid Qadeer
  1 sibling, 1 reply; 15+ messages in thread
From: Jakub Jelinek @ 2021-11-09 15:59 UTC (permalink / raw)
  To: Hafiz Abid Qadeer, Jason Merrill; +Cc: gcc-patches, ams

On Sun, Jun 13, 2021 at 02:27:38PM +0100, Hafiz Abid Qadeer wrote:
> *** with this patch (edited for brevity)***
> 
> 00000000 00000024 ffffffff CIE
> 
>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
>   DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus
> 
> 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
>   DW_CFA_advance_loc4: 96
>   DW_CFA_offset: reg46 0
>   DW_CFA_offset: reg47 4
>   DW_CFA_offset: reg50 8
>   DW_CFA_offset: reg51 12
>   DW_CFA_offset: reg16 8
>   DW_CFA_advance_loc4: 4
>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus

I guess as a temporary solution until DWARF6 comes with something more
compact for cases like that it can be fine, but is there a DWARF issue
filed for it?  Is AMDGCN a DWARF2_ADDR_SIZE == 8 target?

> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
> +   definitions and expressions.
> +   Most architectures only need a single register number, but some (amdgcn)
> +   have pointers that span multiple registers.  DWARF permits arbitrary
> +   register sets but existing use-cases only require contiguous register
> +   sets, as represented here.  */
> +struct GTY(()) cfa_reg {
> +  unsigned int reg;
> +  unsigned int span;
> +  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */

If this is only used for span > 1, wouldn't it be better to
make it
  unsigned int reg;
  unsigned short span;
  unsigned short span_width;
and keep span_width 0 for the span == 1 cases and only set span_width
to ....to_constant () if span > 1 is needed?  If at least for now
the only target that needs this is AMDGCN and the only target that has
NUM_POLY_INT_COEFFS != 1 is aarch64 (maybe eventually riscv?), then I don't
see why we should represent it in poly_uint16...
Of course we can change it later if a target which needs both
NUM_POLY_INT_COEFFS > 1 and span > 1 registers with non-constant span_width,
we can change it, but doing it just in case seems unnecessary
complication...

	Jakub


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-11-09 15:59 ` Jakub Jelinek
@ 2021-11-11 18:12   ` Hafiz Abid Qadeer
  2021-12-01 15:49     ` Jakub Jelinek
  0 siblings, 1 reply; 15+ messages in thread
From: Hafiz Abid Qadeer @ 2021-11-11 18:12 UTC (permalink / raw)
  To: Jakub Jelinek, Hafiz Abid Qadeer, Jason Merrill; +Cc: gcc-patches, ams

[-- Attachment #1: Type: text/plain, Size: 2598 bytes --]

On 09/11/2021 15:59, Jakub Jelinek wrote:
> On Sun, Jun 13, 2021 at 02:27:38PM +0100, Hafiz Abid Qadeer wrote:
>> *** with this patch (edited for brevity)***
>>
>> 00000000 00000024 ffffffff CIE
>>
>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
>>   DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus
>>
>> 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
>>   DW_CFA_advance_loc4: 96
>>   DW_CFA_offset: reg46 0
>>   DW_CFA_offset: reg47 4
>>   DW_CFA_offset: reg50 8
>>   DW_CFA_offset: reg51 12
>>   DW_CFA_offset: reg16 8
>>   DW_CFA_advance_loc4: 4
>>   DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus
> 
> I guess as a temporary solution until DWARF6 comes with something more
> compact for cases like that it can be fine, but is there a DWARF issue
> filed for it?  

I have filed an issue today describing the problem. Although I am not sure what is the best way to
handle it as there is not much encoding space left in CFA defining instructions.

> Is AMDGCN a DWARF2_ADDR_SIZE == 8 target?
Yes

> 
>> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
>> +   definitions and expressions.
>> +   Most architectures only need a single register number, but some (amdgcn)
>> +   have pointers that span multiple registers.  DWARF permits arbitrary
>> +   register sets but existing use-cases only require contiguous register
>> +   sets, as represented here.  */
>> +struct GTY(()) cfa_reg {
>> +  unsigned int reg;
>> +  unsigned int span;
>> +  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
> 
> If this is only used for span > 1, wouldn't it be better to
> make it
>   unsigned int reg;
>   unsigned short span;
>   unsigned short span_width;
> and keep span_width 0 for the span == 1 cases and only set span_width
> to ....to_constant () if span > 1 is needed?  If at least for now
> the only target that needs this is AMDGCN and the only target that has
> NUM_POLY_INT_COEFFS != 1 is aarch64 (maybe eventually riscv?), then I don't
> see why we should represent it in poly_uint16...
> Of course we can change it later if a target which needs both
> NUM_POLY_INT_COEFFS > 1 and span > 1 registers with non-constant span_width,
> we can change it, but doing it just in case seems unnecessary
> complication...

Done in the attached patch. Is it ok?

Thanks,
-- 
Hafiz Abid Qadeer
Mentor, a Siemens Business

[-- Attachment #2: 0001-dwarf-Multi-register-CFI-address-support.patch --]
[-- Type: text/x-patch, Size: 30897 bytes --]

From dd25eccd2458c6b6d38a922d9b2c9107c4c0ba2d Mon Sep 17 00:00:00 2001
From: Hafiz Abid Qadeer <abidh@codesourcery.com>
Date: Thu, 11 Nov 2021 13:43:04 +0000
Subject: [PATCH] dwarf: Multi-register CFI address support.

Add support for architectures such as AMD GCN, in which the pointer size is
larger than the register size.  This allows the CFI information to include
multi-register locations for the stack pointer, frame pointer, and return
address.

This patch was originally posted by Andrew Stubbs in
https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html

It has now been re-worked according to the review comments. It does not use
DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses
DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple
consecutive registers. Here is how .debug_frame looks before and after this
patch:

$ cat factorial.c
int factorial(int n) {
  if (n == 0) return 1;
  return n * factorial (n - 1);
}

$ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o
$ llvm-dwarfdump -debug-frame fac.o

*** without this patch (edited for brevity)***

00000000 00000014 ffffffff CIE

  DW_CFA_def_cfa: reg48 +0
  DW_CFA_register: reg16 reg50

00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac
  DW_CFA_advance_loc4: 96
  DW_CFA_offset: reg46 0
  DW_CFA_offset: reg47 4
  DW_CFA_offset: reg50 8
  DW_CFA_offset: reg51 12
  DW_CFA_offset: reg16 8
  DW_CFA_advance_loc4: 4
  DW_CFA_def_cfa_sf: reg46 -16

*** with this patch (edited for brevity)***

00000000 00000024 ffffffff CIE

  DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus
  DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus

00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac
  DW_CFA_advance_loc4: 96
  DW_CFA_offset: reg46 0
  DW_CFA_offset: reg47 4
  DW_CFA_offset: reg50 8
  DW_CFA_offset: reg51 12
  DW_CFA_offset: reg16 8
  DW_CFA_advance_loc4: 4
  DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus

gcc/ChangeLog:

	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
	(dw_frame_pointer_regnum): Likewise.
	(new_cfi_row): Use set_by_dwreg.
	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,
	DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus.
	(lookup_cfa_1): Use set_by_dwreg.
	(def_cfa_0): Update for cfa_reg and support register spans.
	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
	spans.
	(dwf_cfa_reg): New function.
	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
	dwf_regno.
	(dwarf2out_frame_debug_def_cfa): Likewise.
	(dwarf2out_frame_debug_adjust_cfa): Likewise.
	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
	(dwarf2out_frame_debug_cfa_register): Likewise.
	(dwarf2out_frame_debug_expr): Likewise.
	(create_pseudo_cfg): Use set_by_dwreg.
	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
	(create_cie_data): Use dwf_cfa_reg.
	(execute_dwarf2_frame): Use dwf_cfa_reg.
	(dump_cfi_row): Use set_by_dwreg.
	* dwarf2out.c (build_span_loc, build_breg_loc): New function.
	(build_cfa_loc): Support register spans.
	(build_cfa_aligned_loc): Update cfa_reg usage.
	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
	* dwarf2out.h (struct cfa_reg): New type.
	(struct dw_cfa_location): Use struct cfa_reg.
	(build_span_loc): New prototype.
---
 gcc/dwarf2cfi.c | 261 ++++++++++++++++++++++++++++++++++++------------
 gcc/dwarf2out.c |  54 +++++++++-
 gcc/dwarf2out.h |  35 ++++++-
 3 files changed, 280 insertions(+), 70 deletions(-)

diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index df9b625f5bc..eea9959379e 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
 static bool any_cfis_emitted;
 
 /* Short-hand for commonly used register numbers.  */
-static unsigned dw_stack_pointer_regnum;
-static unsigned dw_frame_pointer_regnum;
+static struct cfa_reg dw_stack_pointer_regnum;
+static struct cfa_reg dw_frame_pointer_regnum;
 \f
 /* Hook used by __throw.  */
 
@@ -430,7 +430,7 @@ new_cfi_row (void)
 {
   dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
 
-  row->cfa.reg = INVALID_REGNUM;
+  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
 
   return row;
 }
@@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
   cfa->offset = 0;
   cfa->base_offset = 0;
   cfa->indirect = 0;
-  cfa->reg = -1;
+  cfa->reg.set_by_dwreg (INVALID_REGNUM);
 
   for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
     {
@@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_reg29:
 	case DW_OP_reg30:
 	case DW_OP_reg31:
-	  cfa->reg = op - DW_OP_reg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
 	  break;
 	case DW_OP_regx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  break;
 	case DW_OP_breg0:
 	case DW_OP_breg1:
@@ -615,16 +615,92 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_breg29:
 	case DW_OP_breg30:
 	case DW_OP_breg31:
-	  cfa->reg = op - DW_OP_breg0;
-	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
-	  break;
 	case DW_OP_bregx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
-	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
+	  if (cfa->reg.reg == INVALID_REGNUM)
+	    {
+	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
+		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));
+	      cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
+	    }
+	  else
+	    {
+	      /* Handle case when span can cover multiple registers.  We
+		 only support the simple case of consecutive registers
+		 all with the same size.  DWARF that we are dealing with
+		 will look something like:
+		 <DW_OP_bregx: (r49) 0; DW_OP_const1u: 32; DW_OP_shl;
+		  DW_OP_bregx: (r48) 0; DW_OP_plus> */
+
+	      unsigned int regno = (op == DW_OP_bregx)
+		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);
+	      gcc_assert (regno == (cfa->reg.reg - 1));
+	      cfa->reg.span++;
+	      /* From all the consecutive registers used, we want to set
+		 cfa->reg.reg to lower number register.  */
+	      cfa->reg.reg = regno;
+	      /* The offset was the shift value.  Use it to get the
+		 span_width and then set it to 0.  */
+	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);
+	      cfa->offset = 0;
+	    }
 	  break;
 	case DW_OP_deref:
 	  cfa->indirect = 1;
 	  break;
+	case DW_OP_shl:
+	  break;
+	case DW_OP_lit0:
+	case DW_OP_lit1:
+	case DW_OP_lit2:
+	case DW_OP_lit3:
+	case DW_OP_lit4:
+	case DW_OP_lit5:
+	case DW_OP_lit6:
+	case DW_OP_lit7:
+	case DW_OP_lit8:
+	case DW_OP_lit9:
+	case DW_OP_lit10:
+	case DW_OP_lit11:
+	case DW_OP_lit12:
+	case DW_OP_lit13:
+	case DW_OP_lit14:
+	case DW_OP_lit15:
+	case DW_OP_lit16:
+	case DW_OP_lit17:
+	case DW_OP_lit18:
+	case DW_OP_lit19:
+	case DW_OP_lit20:
+	case DW_OP_lit21:
+	case DW_OP_lit22:
+	case DW_OP_lit23:
+	case DW_OP_lit24:
+	case DW_OP_lit25:
+	case DW_OP_lit26:
+	case DW_OP_lit27:
+	case DW_OP_lit28:
+	case DW_OP_lit29:
+	case DW_OP_lit30:
+	case DW_OP_lit31:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = op - DW_OP_lit0;
+	  break;
+	case DW_OP_const1u:
+	case DW_OP_const1s:
+	case DW_OP_const2u:
+	case DW_OP_const2s:
+	case DW_OP_const4s:
+	case DW_OP_const8s:
+	case DW_OP_constu:
+	case DW_OP_consts:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_minus:
+	  cfa->offset = -cfa->offset;
+	  break;
+	case DW_OP_plus:
+	  /* The offset is already in place.  */
+	  break;
 	case DW_OP_plus_uconst:
 	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
 	  break;
@@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
       loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_register:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       break;
     case DW_CFA_def_cfa:
     case DW_CFA_def_cfa_sf:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_expression:
@@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 
   HOST_WIDE_INT const_offset;
   if (new_cfa->reg == old_cfa->reg
+      && new_cfa->reg.span == 1
       && !new_cfa->indirect
       && !old_cfa->indirect
       && new_cfa->offset.is_constant (&const_offset))
@@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
     }
   else if (new_cfa->offset.is_constant ()
 	   && known_eq (new_cfa->offset, old_cfa->offset)
-	   && old_cfa->reg != INVALID_REGNUM
+	   && old_cfa->reg.reg != INVALID_REGNUM
+	   && new_cfa->reg.span == 1
 	   && !new_cfa->indirect
 	   && !old_cfa->indirect)
     {
@@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	 been set as a register plus offset rather than a general
 	 DW_CFA_def_cfa_expression.  */
       cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
     }
   else if (new_cfa->indirect == 0
-	   && new_cfa->offset.is_constant (&const_offset))
+	   && new_cfa->offset.is_constant (&const_offset)
+	   && new_cfa->reg.span == 1)
     {
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
 	 indicating the CFA register has changed to <register> with
@@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
       else
 	cfi->dw_cfi_opc = DW_CFA_def_cfa;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
       cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
     }
   else
@@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
 }
 
 /* Add the CFI for saving a register.  REG is the CFA column number.
-   If SREG is -1, the register is saved at OFFSET from the CFA;
+   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
+reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
 {
   dw_fde_ref fde = cfun ? cfun->fde : NULL;
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
 
-  if (sreg == INVALID_REGNUM)
+  if (sreg.reg == INVALID_REGNUM)
     {
       HOST_WIDE_INT const_offset;
       /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
@@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	    = build_cfa_loc (&cur_row->cfa, offset);
 	}
     }
-  else if (sreg == reg)
+  else if (sreg.reg == reg)
     {
       /* While we could emit something like DW_CFA_same_value or
 	 DW_CFA_restore, we never expect to see something like that
@@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	 can always bypass this by using REG_CFA_RESTORE directly.  */
       gcc_unreachable ();
     }
+  else if (sreg.span > 1)
+    {
+      cfi->dw_cfi_opc = DW_CFA_expression;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
+    }
   else
     {
       cfi->dw_cfi_opc = DW_CFA_register;
-      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
     }
 
   add_cfi (cfi);
@@ -1018,6 +1103,44 @@ dwf_regno (const_rtx reg)
   return DWARF_FRAME_REGNUM (REGNO (reg));
 }
 
+/* Like dwf_regno, but when the value can span multiple registers.  */
+
+static struct cfa_reg
+dwf_cfa_reg (rtx reg)
+{
+  struct cfa_reg result;
+
+  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
+
+  result.reg = dwf_regno (reg);
+  result.span = 1;
+  result.span_width = 0;
+
+  rtx span = targetm.dwarf_register_span (reg);
+  if (span)
+    {
+      /* We only support the simple case of consecutive registers all with the
+	 same size.  */
+      result.span = XVECLEN (span, 0);
+      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0))).
+			  to_constant ();
+
+#if CHECKING_P
+      /* Ensure that the above assumption is accurate.  */
+      for (unsigned int i = 0; i < result.span; i++)
+	{
+	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
+								  0, i))),
+				result.span_width));
+	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
+	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
+	}
+#endif
+    }
+
+  return result;
+}
+
 /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
 
 static bool
@@ -1086,7 +1209,8 @@ dwarf2out_flush_queued_reg_saves (void)
 
   FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     {
-      unsigned int reg, sreg;
+      unsigned int reg;
+      struct cfa_reg sreg;
 
       record_reg_saved_in_reg (q->saved_reg, q->reg);
 
@@ -1095,9 +1219,9 @@ dwarf2out_flush_queued_reg_saves (void)
       else
         reg = dwf_regno (q->reg);
       if (q->saved_reg)
-	sreg = dwf_regno (q->saved_reg);
+	sreg = dwf_cfa_reg (q->saved_reg);
       else
-	sreg = INVALID_REGNUM;
+	sreg.set_by_dwreg (INVALID_REGNUM);
       reg_save (reg, sreg, q->cfa_offset);
     }
 
@@ -1169,7 +1293,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
   /* ??? If this fails, we could be calling into the _loc functions to
      define a full expression.  So far no port does that.  */
   gcc_assert (REG_P (pat));
-  cur_cfa->reg = dwf_regno (pat);
+  cur_cfa->reg = dwf_cfa_reg (pat);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
@@ -1186,7 +1310,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
   switch (GET_CODE (src))
     {
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
       cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
@@ -1197,7 +1321,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
       gcc_unreachable ();
     }
 
-  cur_cfa->reg = dwf_regno (dest);
+  cur_cfa->reg = dwf_cfa_reg (dest);
   gcc_assert (cur_cfa->indirect == 0);
 }
 
@@ -1219,11 +1343,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   switch (GET_CODE (addr))
     {
     case REG:
-      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
       offset = -cur_cfa->offset;
       break;
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
       offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
@@ -1243,8 +1367,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
+  struct cfa_reg invalid;
+  invalid.set_by_dwreg (INVALID_REGNUM);
   if (!span)
-    reg_save (sregno, INVALID_REGNUM, offset);
+    reg_save (sregno, invalid, offset);
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
@@ -1258,7 +1384,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 	{
 	  rtx elem = XVECEXP (span, 0, par_index);
 	  sregno = dwf_regno (src);
-	  reg_save (sregno, INVALID_REGNUM, span_offset);
+	  reg_save (sregno, invalid, span_offset);
 	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	}
     }
@@ -1270,7 +1396,8 @@ static void
 dwarf2out_frame_debug_cfa_register (rtx set)
 {
   rtx src, dest;
-  unsigned sregno, dregno;
+  unsigned sregno;
+  struct cfa_reg dregno;
 
   src = XEXP (set, 1);
   dest = XEXP (set, 0);
@@ -1281,7 +1408,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
   else
     sregno = dwf_regno (src);
 
-  dregno = dwf_regno (dest);
+  dregno = dwf_cfa_reg (dest);
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
@@ -1667,7 +1794,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	{
 	  /* Setting FP from SP.  */
 	case REG:
-	  if (cur_cfa->reg == dwf_regno (src))
+	  if (cur_cfa->reg == dwf_cfa_reg (src))
 	    {
 	      /* Rule 1 */
 	      /* Update the CFA rule wrt SP or FP.  Make sure src is
@@ -1677,7 +1804,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		 ARM copies SP to a temporary register, and from there to
 		 FP.  So we just rely on the backends to only set
 		 RTX_FRAME_RELATED_P on appropriate insns.  */
-	      cur_cfa->reg = dwf_regno (dest);
+	      cur_cfa->reg = dwf_cfa_reg (dest);
 	      cur_trace->cfa_temp.reg = cur_cfa->reg;
 	      cur_trace->cfa_temp.offset = cur_cfa->offset;
 	    }
@@ -1698,7 +1825,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		{
 		  gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
 			      && fde->drap_reg != INVALID_REGNUM
-			      && cur_cfa->reg != dwf_regno (src)
+			      && cur_cfa->reg != dwf_cfa_reg (src)
 			      && fde->rule18);
 		  fde->rule18 = 0;
 		  /* The save of hard frame pointer has been deferred
@@ -1722,7 +1849,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      /* Adjusting SP.  */
 	      if (REG_P (XEXP (src, 1)))
 		{
-		  gcc_assert (dwf_regno (XEXP (src, 1))
+		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
 			      == cur_trace->cfa_temp.reg);
 		  offset = cur_trace->cfa_temp.offset;
 		}
@@ -1756,7 +1883,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      gcc_assert (frame_pointer_needed);
 
 	      gcc_assert (REG_P (XEXP (src, 0))
-			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
 	      offset = rtx_to_poly_int64 (XEXP (src, 1));
 	      if (GET_CODE (src) != MINUS)
 		offset = -offset;
@@ -1769,14 +1896,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 4 */
 	      if (REG_P (XEXP (src, 0))
-		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
+		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
 		  && poly_int_rtx_p (XEXP (src, 1), &offset))
 		{
 		  /* Setting a temporary CFA register that will be copied
 		     into the FP later on.  */
 		  offset = -offset;
 		  cur_cfa->offset += offset;
-		  cur_cfa->reg = dwf_regno (dest);
+		  cur_cfa->reg = dwf_cfa_reg (dest);
 		  /* Or used to save regs to the stack.  */
 		  cur_trace->cfa_temp.reg = cur_cfa->reg;
 		  cur_trace->cfa_temp.offset = cur_cfa->offset;
@@ -1784,13 +1911,13 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 5 */
 	      else if (REG_P (XEXP (src, 0))
-		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		       && XEXP (src, 1) == stack_pointer_rtx)
 		{
 		  /* Setting a scratch register that we will use instead
 		     of SP for saving registers to the stack.  */
 		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
-		  cur_trace->cfa_store.reg = dwf_regno (dest);
+		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
 		  cur_trace->cfa_store.offset
 		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
 		}
@@ -1799,7 +1926,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      else if (GET_CODE (src) == LO_SUM
 		       && poly_int_rtx_p (XEXP (src, 1),
 					  &cur_trace->cfa_temp.offset))
-		cur_trace->cfa_temp.reg = dwf_regno (dest);
+		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	      else
 		gcc_unreachable ();
 	    }
@@ -1808,17 +1935,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 6 */
 	case CONST_INT:
 	case CONST_POLY_INT:
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
 	  break;
 
 	  /* Rule 7 */
 	case IOR:
 	  gcc_assert (REG_P (XEXP (src, 0))
-		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		      && CONST_INT_P (XEXP (src, 1)));
 
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
 			  &cur_trace->cfa_temp.offset))
 	    /* The target shouldn't generate this kind of CFI note if we
@@ -1851,14 +1978,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      dwarf2out_flush_queued_reg_saves ();
 
               gcc_assert (cur_trace->cfa_store.reg
-			  == dwf_regno (XEXP (src, 0)));
+			  == dwf_cfa_reg (XEXP (src, 0)));
               fde->stack_realign = 1;
               fde->stack_realignment = INTVAL (XEXP (src, 1));
               cur_trace->cfa_store.offset = 0;
 
 	      if (cur_cfa->reg != dw_stack_pointer_regnum
 		  && cur_cfa->reg != dw_frame_pointer_regnum)
-		fde->drap_reg = cur_cfa->reg;
+		{
+		  gcc_assert (cur_cfa->reg.span == 1);
+		  fde->drap_reg = cur_cfa->reg.reg;
+		}
             }
           return;
 
@@ -1935,14 +2065,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 	case MINUS:
 	case LO_SUM:
 	  {
-	    unsigned int regno;
+	    struct cfa_reg regno;
 
 	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
 	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
 	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
 	      offset = -offset;
 
-	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
+	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset -= cur_cfa->offset;
@@ -1960,7 +2090,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Without an offset.  */
 	case REG:
 	  {
-	    unsigned int regno = dwf_regno (XEXP (dest, 0));
+	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset = -cur_cfa->offset;
@@ -1977,7 +2107,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 14 */
 	case POST_INC:
 	  gcc_assert (cur_trace->cfa_temp.reg
-		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
+		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
 	  offset = -cur_trace->cfa_temp.offset;
 	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
 	  break;
@@ -1995,7 +2125,7 @@ dwarf2out_frame_debug_expr (rtx expr)
       if (REG_P (src)
 	  && REGNO (src) != STACK_POINTER_REGNUM
 	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
-	  && dwf_regno (src) == cur_cfa->reg)
+	  && dwf_cfa_reg (src) == cur_cfa->reg)
 	{
 	  /* We're storing the current CFA reg into the stack.  */
 
@@ -2012,7 +2142,7 @@ dwarf2out_frame_debug_expr (rtx expr)
                   && cur_cfa->indirect == 0
                   && cur_cfa->reg != dw_frame_pointer_regnum)
                 {
-		  gcc_assert (fde->drap_reg == cur_cfa->reg);
+		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
 
 		  cur_cfa->indirect = 1;
 		  cur_cfa->reg = dw_frame_pointer_regnum;
@@ -2039,7 +2169,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		x = XEXP (x, 0);
 	      gcc_assert (REG_P (x));
 
-	      cur_cfa->reg = dwf_regno (x);
+	      cur_cfa->reg = dwf_cfa_reg (x);
 	      cur_cfa->base_offset = offset;
 	      cur_cfa->indirect = 1;
 	      break;
@@ -2951,7 +3081,7 @@ create_pseudo_cfg (void)
   ti.head = get_insns ();
   ti.beg_row = cie_cfi_row;
   ti.cfa_store = cie_cfi_row->cfa;
-  ti.cfa_temp.reg = INVALID_REGNUM;
+  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
   trace_info.quick_push (ti);
 
   if (cie_return_save)
@@ -3014,14 +3144,15 @@ create_pseudo_cfg (void)
 static void
 initial_return_save (rtx rtl)
 {
-  unsigned int reg = INVALID_REGNUM;
+  struct cfa_reg reg;
+  reg.set_by_dwreg (INVALID_REGNUM);
   poly_int64 offset = 0;
 
   switch (GET_CODE (rtl))
     {
     case REG:
       /* RA is in a register.  */
-      reg = dwf_regno (rtl);
+      reg = dwf_cfa_reg (rtl);
       break;
 
     case MEM:
@@ -3062,9 +3193,9 @@ initial_return_save (rtx rtl)
       gcc_unreachable ();
     }
 
-  if (reg != DWARF_FRAME_RETURN_COLUMN)
+  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
     {
-      if (reg != INVALID_REGNUM)
+      if (reg.reg != INVALID_REGNUM)
         record_reg_saved_in_reg (rtl, pc_rtx);
       reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
     }
@@ -3076,7 +3207,8 @@ create_cie_data (void)
   dw_cfa_location loc;
   dw_trace_info cie_trace;
 
-  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
+  dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode,
+						      STACK_POINTER_REGNUM));
 
   memset (&cie_trace, 0, sizeof (cie_trace));
   cur_trace = &cie_trace;
@@ -3135,7 +3267,8 @@ static unsigned int
 execute_dwarf2_frame (void)
 {
   /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
-  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
+					(Pmode, HARD_FRAME_POINTER_REGNUM));
 
   /* The first time we're called, compute the incoming frame state.  */
   if (cie_cfi_vec == NULL)
@@ -3515,7 +3648,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
     {
       dw_cfa_location dummy;
       memset (&dummy, 0, sizeof (dummy));
-      dummy.reg = INVALID_REGNUM;
+      dummy.reg.set_by_dwreg (INVALID_REGNUM);
       cfi = def_cfa_0 (&dummy, &row->cfa);
     }
   output_cfi_directive (f, cfi);
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index fb0e3381e5b..44efefdbacd 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -2780,6 +2780,43 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
     }
 }
 
+static void
+build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
+{
+  if (regno <= 31)
+    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
+		  (DW_OP_breg0 + regno),  0, 0));
+  else
+    add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0));
+}
+
+/* Build a dwarf location for a cfa_reg spanning multiple
+   consecutive registers.  */
+
+struct dw_loc_descr_node *
+build_span_loc (struct cfa_reg reg)
+{
+  struct dw_loc_descr_node *head = NULL;
+
+  gcc_assert (known_gt (reg.span_width, 0));
+  gcc_assert (reg.span > 1);
+
+  /* Start from the highest number register as it goes in the upper bits.  */
+  unsigned int regno = reg.reg + reg.span - 1;
+  build_breg_loc (&head, regno);
+
+  /* deal with the remaining registers in the span.  */
+  for (int i = (reg.span - 2); i >= 0; i--)
+    {
+      add_loc_descr (&head, int_loc_descriptor (reg.span_width * 8));
+      add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0));
+      regno--;
+      build_breg_loc (&head, regno);
+      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
+    }
+  return head;
+}
+
 /* This function builds a dwarf location descriptor sequence from a
    dw_cfa_location, adding the given OFFSET to the result of the
    expression.  */
@@ -2791,9 +2828,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
 
   offset += cfa->offset;
 
-  if (cfa->indirect)
+  if (cfa->reg.span > 1)
+    {
+      head = build_span_loc (cfa->reg);
+
+      if (maybe_ne (offset, 0))
+	  loc_descr_plus_const (&head, offset);
+    }
+  else if (cfa->indirect)
     {
-      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
+      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
       head->dw_loc_oprnd1.val_class = dw_val_class_const;
       head->dw_loc_oprnd1.val_entry = NULL;
       tmp = new_loc_descr (DW_OP_deref, 0, 0);
@@ -2801,7 +2845,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
       loc_descr_plus_const (&head, offset);
     }
   else
-    head = new_reg_loc_descr (cfa->reg, offset);
+    head = new_reg_loc_descr (cfa->reg.reg, offset);
 
   return head;
 }
@@ -2819,7 +2863,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
     = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
 
   /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
-  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
+  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
     {
       head = new_reg_loc_descr (dwarf_fp, 0);
       add_loc_descr (&head, int_loc_descriptor (alignment));
@@ -20875,7 +20919,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
   list = NULL;
 
   memset (&next_cfa, 0, sizeof (next_cfa));
-  next_cfa.reg = INVALID_REGNUM;
+  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
   remember = next_cfa;
 
   start_label = fde->dw_fde_begin;
diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
index 312a9909784..00d53a58b54 100644
--- a/gcc/dwarf2out.h
+++ b/gcc/dwarf2out.h
@@ -119,6 +119,38 @@ struct GTY(()) dw_fde_node {
 };
 
 
+/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
+   definitions and expressions.
+   Most architectures only need a single register number, but some (amdgcn)
+   have pointers that span multiple registers.  DWARF permits arbitrary
+   register sets but existing use-cases only require contiguous register
+   sets, as represented here.  */
+struct GTY(()) cfa_reg {
+  unsigned int reg;
+  unsigned short span;
+  unsigned short span_width;  /* A.K.A. register mode size.  */
+
+  cfa_reg& set_by_dwreg (unsigned int r)
+    {
+      reg = r;
+      span = 1;
+      span_width = 0;  /* Unknown size (permitted when span == 1).  */
+      return *this;
+    }
+
+  bool operator== (const cfa_reg other) const
+    {
+      return (reg == other.reg && span == other.span
+	      && (span_width == other.span_width
+		  || (span == 1
+		      && (span_width == 0 || other.span_width == 0))));
+    }
+  bool operator!= (const cfa_reg other) const
+    {
+      return !(*this == other);
+    }
+};
+
 /* This is how we define the location of the CFA. We use to handle it
    as REG + OFFSET all the time,  but now it can be more complex.
    It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
@@ -128,7 +160,7 @@ struct GTY(()) dw_cfa_location {
   poly_int64_pod offset;
   poly_int64_pod base_offset;
   /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
-  unsigned int reg;
+  struct cfa_reg reg;
   BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
   BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
 };
@@ -285,6 +317,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
   (dw_cfa_location *, poly_int64);
 extern struct dw_loc_descr_node *build_cfa_aligned_loc
   (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
+extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
 extern struct dw_loc_descr_node *mem_loc_descriptor
   (rtx, machine_mode mode, machine_mode mem_mode,
    enum var_init_status);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support.
  2021-11-11 18:12   ` Hafiz Abid Qadeer
@ 2021-12-01 15:49     ` Jakub Jelinek
  0 siblings, 0 replies; 15+ messages in thread
From: Jakub Jelinek @ 2021-12-01 15:49 UTC (permalink / raw)
  To: Hafiz Abid Qadeer; +Cc: Hafiz Abid Qadeer, Jason Merrill, gcc-patches, ams

On Thu, Nov 11, 2021 at 06:12:50PM +0000, Hafiz Abid Qadeer wrote:
> 	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
> 	(dw_frame_pointer_regnum): Likewise.
> 	(new_cfi_row): Use set_by_dwreg.
> 	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans.
> 	handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*,

2 spaces instead of 1 before Support above.

> +	      cfa->reg.set_by_dwreg ((op == DW_OP_bregx)
> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0));

Formatting.  All those 3 () inner pairs are unnecessary, and it would be
nicer to use a temporary, like:
	      unsigned regno
		= (op == DW_OP_bregx
		   ? ptr->dw_loc_oprnd1.v.val_int : op - DW_OP_breg0);
	      cfa->reg.set_by_dwreg (regno);

> +	      unsigned int regno = (op == DW_OP_bregx)
> +		? (ptr->dw_loc_oprnd1.v.val_int) : (op - DW_OP_breg0);

With the ()s similarly, and also ? should be below op.

> +	      gcc_assert (regno == (cfa->reg.reg - 1));

Again, the inner () pair is unnecessary.
> +	      cfa->reg.span_width = (cfa->offset.to_constant () / 8);

And here the outer () pair.

> +
> +#if CHECKING_P

Please use if (CHECKING_P) instead of #if.

> +      /* Ensure that the above assumption is accurate.  */
> +      for (unsigned int i = 0; i < result.span; i++)
> +	{
> +	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
> +								  0, i))),
> +				result.span_width));
> +	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
> +	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
> +	}
> +#endif
> +    }
> +
> +  return result;

> @@ -3135,7 +3267,8 @@ static unsigned int
>  execute_dwarf2_frame (void)
>  {
>    /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
> -  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
> +  dw_frame_pointer_regnum = dwf_cfa_reg (gen_rtx_REG
> +					(Pmode, HARD_FRAME_POINTER_REGNUM));

If at all possible, avoid function name on one line and ( with first
argument on another one.  In the above case it can be easily avoided by
  dw_frame_pointer_regnum
    = dwf_cfa_reg (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM));

> +static void
> +build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno)
> +{
> +  if (regno <= 31)
> +    add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom)
> +		  (DW_OP_breg0 + regno),  0, 0));

Bad formatting, (DW_OP_breg0 should be below (enum
> +
> +  /* deal with the remaining registers in the span.  */

Capital letter on Deal

> +  for (int i = (reg.span - 2); i >= 0; i--)

Please remove the redundant inner () pair.
> --- a/gcc/dwarf2out.h
> +++ b/gcc/dwarf2out.h
> @@ -119,6 +119,38 @@ struct GTY(()) dw_fde_node {
>  };
>  
>  
> +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
> +   definitions and expressions.
> +   Most architectures only need a single register number, but some (amdgcn)
> +   have pointers that span multiple registers.  DWARF permits arbitrary
> +   register sets but existing use-cases only require contiguous register
> +   sets, as represented here.  */
> +struct GTY(()) cfa_reg {
> +  unsigned int reg;
> +  unsigned short span;
> +  unsigned short span_width;  /* A.K.A. register mode size.  */
> +
> +  cfa_reg& set_by_dwreg (unsigned int r)
> +    {
> +      reg = r;
> +      span = 1;
> +      span_width = 0;  /* Unknown size (permitted when span == 1).  */
> +      return *this;
> +    }
> +
> +  bool operator== (const cfa_reg other) const

The normal C++ way would be (const cfa_reg &other), wouldn't it?
Or otherwise the const keyword doesn't make much sense.

> +    {
> +      return (reg == other.reg && span == other.span
> +	      && (span_width == other.span_width
> +		  || (span == 1
> +		      && (span_width == 0 || other.span_width == 0))));
> +    }
> +  bool operator!= (const cfa_reg other) const

Likewise.
Please add an empty line above operator!=

> +    {
> +      return !(*this == other);
> +    }
> +};
> +

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-08-28 12:04 Andrew Stubbs
  2020-09-02 17:49 ` Tom Tromey
  2020-09-03 15:29 ` Andrew Stubbs
@ 2020-10-19  9:36 ` Jakub Jelinek
  2 siblings, 0 replies; 15+ messages in thread
From: Jakub Jelinek @ 2020-10-19  9:36 UTC (permalink / raw)
  To: Andrew Stubbs, Jason Merrill; +Cc: gcc-patches

On Fri, Aug 28, 2020 at 01:04:51PM +0100, Andrew Stubbs wrote:
> This patch introduces DWARF CFI support for architectures that require
> multiple registers to hold pointers, such as the stack pointer, frame
> pointer, and return address. The motivating case is the AMD GCN architecture
> which has 64-bit address pointers, but 32-bit registers.
> 
> The current implementation permits program variables to span as many
> registers as they need, but assumes that CFI expressions will only need a
> single register for each frame value.
> 
> To be fair, the DWARF standard makes a similar assumption; the engineers
> working on LLVM and GDB, at AMD, have therefore invented some new DWARF
> operators that they plan to propose for a future standard. Only one is
> relevant here, however: DW_OP_LLVM_piece_end. (Unfortunately this clashes
> with an AArch64 extension, but I think we can cope using an alias -- only
> GCC dumps will be confusing.)

First of all, in GCC it definitely should not be called DW_OP_LLVM_*, either
we adopt it also as a GNU extension and then we should call it DW_OP_GNU_*,
or we don't and then we shouldn't emit it.

For the beginning, it would help if you posted some examples of how
the CFI info would look like on typical functions.

I fear the piece_end is just a sign of misunderstanding of the DWARF
expression vs. DWARF location description differences on the AMD side.
Because all of DW_CFA_def_cfa_expression, DW_CFA_expression and
DW_CFA_val_expression take as one of their operands a DWARF expression
rather than DWARF location description, so e.g. DW_OP_piece can't appear in
those.

And, if GCN DWARF uses 64-bit addresses, it isn't clear why one can't use
existing
DW_CFA_def_cfa_expression <DW_OP_breg4 0 DW_OP_const1u 32 DW_OP_shl DW_OP_breg5 16 DW_OP_plus>
or similar (assuming you want CFA of (reg4 << 32) + reg5 + 16.

	Jakub


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-09-21 13:51   ` Andrew Stubbs
@ 2020-10-05 10:07     ` Andrew Stubbs
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Stubbs @ 2020-10-05 10:07 UTC (permalink / raw)
  To: gcc-patches

Ping.

On 21/09/2020 14:51, Andrew Stubbs wrote:
> Ping.
> 
> On 03/09/2020 16:29, Andrew Stubbs wrote:
>> On 28/08/2020 13:04, Andrew Stubbs wrote:
>>> Hi all,
>>>
>>> This patch introduces DWARF CFI support for architectures that 
>>> require multiple registers to hold pointers, such as the stack 
>>> pointer, frame pointer, and return address. The motivating case is 
>>> the AMD GCN architecture which has 64-bit address pointers, but 
>>> 32-bit registers.
>>>
>>> The current implementation permits program variables to span as many 
>>> registers as they need, but assumes that CFI expressions will only 
>>> need a single register for each frame value.
>>>
>>> To be fair, the DWARF standard makes a similar assumption; the 
>>> engineers working on LLVM and GDB, at AMD, have therefore invented 
>>> some new DWARF operators that they plan to propose for a future 
>>> standard. Only one is relevant here, however: DW_OP_LLVM_piece_end. 
>>> (Unfortunately this clashes with an AArch64 extension, but I think we 
>>> can cope using an alias -- only GCC dumps will be confusing.)
>>>
>>> My approach is to change the type representing a DWARF register 
>>> throughout the CFI code. This permits the register span information 
>>> to propagate to where it is needed.
>>>
>>> I've taken advantage of C++ struct copies and operator== to minimize 
>>> the amount of refactoring required. I'm not sure this meets the GCC 
>>> guidelines exactly, but if not I can change that once the basic form 
>>> is agreed. (I also considered an operator= to make assigning single 
>>> dwreg values transparent, but that hid too many invalid assumptions.)
>>>
>>> OK to commit? (Although, I'll hold off until AMD release the 
>>> compatible GDB.)
>>
>> Minor patch update, following Tom's feedback.
>>
>> Andrew
> 
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-09-03 15:29 ` Andrew Stubbs
@ 2020-09-21 13:51   ` Andrew Stubbs
  2020-10-05 10:07     ` Andrew Stubbs
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Stubbs @ 2020-09-21 13:51 UTC (permalink / raw)
  To: gcc-patches

Ping.

On 03/09/2020 16:29, Andrew Stubbs wrote:
> On 28/08/2020 13:04, Andrew Stubbs wrote:
>> Hi all,
>>
>> This patch introduces DWARF CFI support for architectures that require 
>> multiple registers to hold pointers, such as the stack pointer, frame 
>> pointer, and return address. The motivating case is the AMD GCN 
>> architecture which has 64-bit address pointers, but 32-bit registers.
>>
>> The current implementation permits program variables to span as many 
>> registers as they need, but assumes that CFI expressions will only 
>> need a single register for each frame value.
>>
>> To be fair, the DWARF standard makes a similar assumption; the 
>> engineers working on LLVM and GDB, at AMD, have therefore invented 
>> some new DWARF operators that they plan to propose for a future 
>> standard. Only one is relevant here, however: DW_OP_LLVM_piece_end. 
>> (Unfortunately this clashes with an AArch64 extension, but I think we 
>> can cope using an alias -- only GCC dumps will be confusing.)
>>
>> My approach is to change the type representing a DWARF register 
>> throughout the CFI code. This permits the register span information to 
>> propagate to where it is needed.
>>
>> I've taken advantage of C++ struct copies and operator== to minimize 
>> the amount of refactoring required. I'm not sure this meets the GCC 
>> guidelines exactly, but if not I can change that once the basic form 
>> is agreed. (I also considered an operator= to make assigning single 
>> dwreg values transparent, but that hid too many invalid assumptions.)
>>
>> OK to commit? (Although, I'll hold off until AMD release the 
>> compatible GDB.)
> 
> Minor patch update, following Tom's feedback.
> 
> Andrew


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-08-28 12:04 Andrew Stubbs
  2020-09-02 17:49 ` Tom Tromey
@ 2020-09-03 15:29 ` Andrew Stubbs
  2020-09-21 13:51   ` Andrew Stubbs
  2020-10-19  9:36 ` Jakub Jelinek
  2 siblings, 1 reply; 15+ messages in thread
From: Andrew Stubbs @ 2020-09-03 15:29 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1628 bytes --]

On 28/08/2020 13:04, Andrew Stubbs wrote:
> Hi all,
> 
> This patch introduces DWARF CFI support for architectures that require 
> multiple registers to hold pointers, such as the stack pointer, frame 
> pointer, and return address. The motivating case is the AMD GCN 
> architecture which has 64-bit address pointers, but 32-bit registers.
> 
> The current implementation permits program variables to span as many 
> registers as they need, but assumes that CFI expressions will only need 
> a single register for each frame value.
> 
> To be fair, the DWARF standard makes a similar assumption; the engineers 
> working on LLVM and GDB, at AMD, have therefore invented some new DWARF 
> operators that they plan to propose for a future standard. Only one is 
> relevant here, however: DW_OP_LLVM_piece_end. (Unfortunately this 
> clashes with an AArch64 extension, but I think we can cope using an 
> alias -- only GCC dumps will be confusing.)
> 
> My approach is to change the type representing a DWARF register 
> throughout the CFI code. This permits the register span information to 
> propagate to where it is needed.
> 
> I've taken advantage of C++ struct copies and operator== to minimize the 
> amount of refactoring required. I'm not sure this meets the GCC 
> guidelines exactly, but if not I can change that once the basic form is 
> agreed. (I also considered an operator= to make assigning single dwreg 
> values transparent, but that hid too many invalid assumptions.)
> 
> OK to commit? (Although, I'll hold off until AMD release the compatible 
> GDB.)

Minor patch update, following Tom's feedback.

Andrew

[-- Attachment #2: 200903-dwarf-reg-spans.patch --]
[-- Type: text/x-patch, Size: 29948 bytes --]

dwarf: Multi-register CFI address support

Add support for architectures such as AMD GCN, in which the pointer size is
larger than the register size.  This allows the CFI information to include
multi-register locations for the stack pointer, frame pointer, and return
address.

Note that this uses a newly proposed DWARF operator DW_OP_LLVM_piece_end,
which is currently only recognized by the ROCGDB debugger from AMD.  The exact
name and encoding for this operator is subject to change if and when the DWARF
standard accepts it.

gcc/ChangeLog:

	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
	(dw_frame_pointer_regnum): Likewise.
	(new_cfi_row): Use set_by_dwreg.
	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans
	with DW_OP_piece and DW_OP_LLVM_piece_end.  Support DW_OP_lit*,
	DW_OP_const*, DW_OP_minus, and DW_OP_plus.
	(lookup_cfa_1): Use set_by_dwreg.
	(def_cfa_0): Update for cfa_reg and support register spans.
	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
	spans.
	(dwf_cfa_reg): New function.
	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
	dwf_regno.
	(dwarf2out_frame_debug_def_cfa): Likewise.
	(dwarf2out_frame_debug_adjust_cfa): Likewise.
	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
	(dwarf2out_frame_debug_cfa_register): Likewise.
	(dwarf2out_frame_debug_expr): Likewise.
	(create_pseudo_cfg): Use set_by_dwreg.
	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
	(create_cie_data): Use dwf_cfa_reg.
	(execute_dwarf2_frame): Use dwf_cfa_reg.
	(dump_cfi_row): Use set_by_dwreg.
	* dwarf2out.c (build_span_loc): New function.
	(build_cfa_loc): Support register spans.
	(build_cfa_aligned_loc): Update cfa_reg usage.
	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
	* dwarf2out.h (struct cfa_reg): New type.
	(struct dw_cfa_location): Use struct cfa_reg.
	(build_span_loc): New prototype.
	* gengtype.c (main): Accept poly_uint16_pod type.

include/ChangeLog:

	* dwarf2.def (DW_OP_LLVM_piece_end): New extension operator.

diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index 0d179b388e4..63cb6de5c4f 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
 static bool any_cfis_emitted;
 
 /* Short-hand for commonly used register numbers.  */
-static unsigned dw_stack_pointer_regnum;
-static unsigned dw_frame_pointer_regnum;
+static struct cfa_reg dw_stack_pointer_regnum;
+static struct cfa_reg dw_frame_pointer_regnum;
 \f
 /* Hook used by __throw.  */
 
@@ -430,7 +430,7 @@ new_cfi_row (void)
 {
   dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
 
-  row->cfa.reg = INVALID_REGNUM;
+  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
 
   return row;
 }
@@ -538,7 +538,11 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
   cfa->offset = 0;
   cfa->base_offset = 0;
   cfa->indirect = 0;
-  cfa->reg = -1;
+  cfa->reg.set_by_dwreg (-1);
+
+  /* Record previous register pieces here.  */
+  struct cfa_reg span;
+  span.set_by_dwreg (INVALID_REGNUM);
 
   for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
     {
@@ -578,10 +582,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_reg29:
 	case DW_OP_reg30:
 	case DW_OP_reg31:
-	  cfa->reg = op - DW_OP_reg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
 	  break;
 	case DW_OP_regx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  break;
 	case DW_OP_breg0:
 	case DW_OP_breg1:
@@ -615,17 +619,89 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_breg29:
 	case DW_OP_breg30:
 	case DW_OP_breg31:
-	  cfa->reg = op - DW_OP_breg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_breg0);
 	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
 	  break;
 	case DW_OP_bregx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
 	  break;
+	case DW_OP_piece:
+	  if (span.reg != INVALID_REGNUM)
+	    {
+	      /* We only support contiguous pieces, for now.  */
+	      gcc_assert (cfa->reg.reg == span.reg + span.span);
+	      gcc_assert (known_eq (ptr->dw_loc_oprnd1.v.val_int,
+				    span.span_width));
+	      span.span++;
+	      cfa->reg = span;
+	    }
+	  else
+	    {
+	      cfa->reg.span_width = ptr->dw_loc_oprnd1.v.val_int;
+	      span = cfa->reg;
+	    }
+	  break;
+	case DW_OP_LLVM_piece_end:
+	  break;
 	case DW_OP_deref:
 	  cfa->indirect = 1;
 	  break;
+	case DW_OP_lit0:
+	case DW_OP_lit1:
+	case DW_OP_lit2:
+	case DW_OP_lit3:
+	case DW_OP_lit4:
+	case DW_OP_lit5:
+	case DW_OP_lit6:
+	case DW_OP_lit7:
+	case DW_OP_lit8:
+	case DW_OP_lit9:
+	case DW_OP_lit10:
+	case DW_OP_lit11:
+	case DW_OP_lit12:
+	case DW_OP_lit13:
+	case DW_OP_lit14:
+	case DW_OP_lit15:
+	case DW_OP_lit16:
+	case DW_OP_lit17:
+	case DW_OP_lit18:
+	case DW_OP_lit19:
+	case DW_OP_lit20:
+	case DW_OP_lit21:
+	case DW_OP_lit22:
+	case DW_OP_lit23:
+	case DW_OP_lit24:
+	case DW_OP_lit25:
+	case DW_OP_lit26:
+	case DW_OP_lit27:
+	case DW_OP_lit28:
+	case DW_OP_lit29:
+	case DW_OP_lit30:
+	case DW_OP_lit31:
+	  /* Stacked operands are not yet supported.  */
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = op - DW_OP_lit0;
+	  break;
+	case DW_OP_const1u:
+	case DW_OP_const1s:
+	case DW_OP_const2u:
+	case DW_OP_const2s:
+	case DW_OP_const4s:
+	case DW_OP_const8s:
+	case DW_OP_constu:
+	case DW_OP_consts:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_minus:
+	  cfa->offset = -cfa->offset;
+	  break;
+	case DW_OP_plus:
+	  /* The offset is already in place.  */
+	  break;
 	case DW_OP_plus_uconst:
+	  gcc_assert (known_eq (cfa->offset, 0));
 	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
 	  break;
 	default:
@@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
       loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_register:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       break;
     case DW_CFA_def_cfa:
     case DW_CFA_def_cfa_sf:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_expression:
@@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 
   HOST_WIDE_INT const_offset;
   if (new_cfa->reg == old_cfa->reg
+      && new_cfa->reg.span == 1
       && !new_cfa->indirect
       && !old_cfa->indirect
       && new_cfa->offset.is_constant (&const_offset))
@@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
     }
   else if (new_cfa->offset.is_constant ()
 	   && known_eq (new_cfa->offset, old_cfa->offset)
-	   && old_cfa->reg != INVALID_REGNUM
+	   && old_cfa->reg.reg != INVALID_REGNUM
+	   && new_cfa->reg.span == 1
 	   && !new_cfa->indirect
 	   && !old_cfa->indirect)
     {
@@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	 been set as a register plus offset rather than a general
 	 DW_CFA_def_cfa_expression.  */
       cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
     }
   else if (new_cfa->indirect == 0
-	   && new_cfa->offset.is_constant (&const_offset))
+	   && new_cfa->offset.is_constant (&const_offset)
+	   && new_cfa->reg.span == 1)
     {
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
 	 indicating the CFA register has changed to <register> with
@@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
       else
 	cfi->dw_cfi_opc = DW_CFA_def_cfa;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
       cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
     }
   else
@@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
 }
 
 /* Add the CFI for saving a register.  REG is the CFA column number.
-   If SREG is -1, the register is saved at OFFSET from the CFA;
+   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
+reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
 {
   dw_fde_ref fde = cfun ? cfun->fde : NULL;
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
 
-  if (sreg == INVALID_REGNUM)
+  if (sreg.reg == INVALID_REGNUM)
     {
       HOST_WIDE_INT const_offset;
       /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
@@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	    = build_cfa_loc (&cur_row->cfa, offset);
 	}
     }
-  else if (sreg == reg)
+  else if (sreg.reg == reg)
     {
       /* While we could emit something like DW_CFA_same_value or
 	 DW_CFA_restore, we never expect to see something like that
@@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	 can always bypass this by using REG_CFA_RESTORE directly.  */
       gcc_unreachable ();
     }
+  else if (sreg.span > 1)
+    {
+      cfi->dw_cfi_opc = DW_CFA_expression;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
+    }
   else
     {
       cfi->dw_cfi_opc = DW_CFA_register;
-      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
     }
 
   add_cfi (cfi);
@@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
   return DWARF_FRAME_REGNUM (REGNO (reg));
 }
 
+/* Like dwf_regno, but when the value can span multiple registers.  */
+
+static struct cfa_reg
+dwf_cfa_reg (rtx reg)
+{
+  struct cfa_reg result;
+
+  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
+
+  result.reg = dwf_regno (reg);
+  result.span = 1;
+  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
+
+  rtx span = targetm.dwarf_register_span (reg);
+  if (span)
+    {
+      /* We only support the simple case of consecutive registers all with the
+	 same size.  */
+      result.span = XVECLEN (span, 0);
+      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
+
+#if CHECKING_P
+      /* Ensure that the above assumption is accurate.  */
+      for (unsigned int i = 0; i < result.span; i++)
+	{
+	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
+								  0, i))),
+				result.span_width));
+	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
+	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
+	}
+#endif
+    }
+
+  return result;
+}
+
 /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
 
 static bool
@@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
 
   FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     {
-      unsigned int reg, sreg;
+      unsigned int reg;
+      struct cfa_reg sreg;
 
       record_reg_saved_in_reg (q->saved_reg, q->reg);
 
@@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
       else
         reg = dwf_regno (q->reg);
       if (q->saved_reg)
-	sreg = dwf_regno (q->saved_reg);
+	sreg = dwf_cfa_reg (q->saved_reg);
       else
-	sreg = INVALID_REGNUM;
+	sreg.set_by_dwreg (INVALID_REGNUM);
       reg_save (reg, sreg, q->cfa_offset);
     }
 
@@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
   /* ??? If this fails, we could be calling into the _loc functions to
      define a full expression.  So far no port does that.  */
   gcc_assert (REG_P (pat));
-  cur_cfa->reg = dwf_regno (pat);
+  cur_cfa->reg = dwf_cfa_reg (pat);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
@@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
   switch (GET_CODE (src))
     {
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
       cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
@@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
       gcc_unreachable ();
     }
 
-  cur_cfa->reg = dwf_regno (dest);
+  cur_cfa->reg = dwf_cfa_reg (dest);
   gcc_assert (cur_cfa->indirect == 0);
 }
 
@@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   switch (GET_CODE (addr))
     {
     case REG:
-      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
       offset = -cur_cfa->offset;
       break;
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
       offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
@@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
+  struct cfa_reg invalid;
+  invalid.set_by_dwreg (INVALID_REGNUM);
   if (!span)
-    reg_save (sregno, INVALID_REGNUM, offset);
+    reg_save (sregno, invalid, offset);
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
@@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 	{
 	  rtx elem = XVECEXP (span, 0, par_index);
 	  sregno = dwf_regno (src);
-	  reg_save (sregno, INVALID_REGNUM, span_offset);
+	  reg_save (sregno, invalid, span_offset);
 	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	}
     }
@@ -1270,7 +1395,8 @@ static void
 dwarf2out_frame_debug_cfa_register (rtx set)
 {
   rtx src, dest;
-  unsigned sregno, dregno;
+  unsigned sregno;
+  struct cfa_reg dregno;
 
   src = XEXP (set, 1);
   dest = XEXP (set, 0);
@@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
   else
     sregno = dwf_regno (src);
 
-  dregno = dwf_regno (dest);
+  dregno = dwf_cfa_reg (dest);
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
@@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	{
 	  /* Setting FP from SP.  */
 	case REG:
-	  if (cur_cfa->reg == dwf_regno (src))
+	  if (cur_cfa->reg == dwf_cfa_reg (src))
 	    {
 	      /* Rule 1 */
 	      /* Update the CFA rule wrt SP or FP.  Make sure src is
@@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		 ARM copies SP to a temporary register, and from there to
 		 FP.  So we just rely on the backends to only set
 		 RTX_FRAME_RELATED_P on appropriate insns.  */
-	      cur_cfa->reg = dwf_regno (dest);
+	      cur_cfa->reg = dwf_cfa_reg (dest);
 	      cur_trace->cfa_temp.reg = cur_cfa->reg;
 	      cur_trace->cfa_temp.offset = cur_cfa->offset;
 	    }
@@ -1697,7 +1823,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		  && REGNO (src) == STACK_POINTER_REGNUM)
 		gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
 			    && fde->drap_reg != INVALID_REGNUM
-			    && cur_cfa->reg != dwf_regno (src));
+			    && cur_cfa->reg != dwf_cfa_reg (src));
 	      else
 		queue_reg_save (src, dest, 0);
 	    }
@@ -1712,7 +1838,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      /* Adjusting SP.  */
 	      if (REG_P (XEXP (src, 1)))
 		{
-		  gcc_assert (dwf_regno (XEXP (src, 1))
+		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
 			      == cur_trace->cfa_temp.reg);
 		  offset = cur_trace->cfa_temp.offset;
 		}
@@ -1746,7 +1872,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      gcc_assert (frame_pointer_needed);
 
 	      gcc_assert (REG_P (XEXP (src, 0))
-			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
 	      offset = rtx_to_poly_int64 (XEXP (src, 1));
 	      if (GET_CODE (src) != MINUS)
 		offset = -offset;
@@ -1759,14 +1885,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 4 */
 	      if (REG_P (XEXP (src, 0))
-		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
+		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
 		  && poly_int_rtx_p (XEXP (src, 1), &offset))
 		{
 		  /* Setting a temporary CFA register that will be copied
 		     into the FP later on.  */
 		  offset = -offset;
 		  cur_cfa->offset += offset;
-		  cur_cfa->reg = dwf_regno (dest);
+		  cur_cfa->reg = dwf_cfa_reg (dest);
 		  /* Or used to save regs to the stack.  */
 		  cur_trace->cfa_temp.reg = cur_cfa->reg;
 		  cur_trace->cfa_temp.offset = cur_cfa->offset;
@@ -1774,13 +1900,13 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 5 */
 	      else if (REG_P (XEXP (src, 0))
-		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		       && XEXP (src, 1) == stack_pointer_rtx)
 		{
 		  /* Setting a scratch register that we will use instead
 		     of SP for saving registers to the stack.  */
 		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
-		  cur_trace->cfa_store.reg = dwf_regno (dest);
+		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
 		  cur_trace->cfa_store.offset
 		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
 		}
@@ -1789,7 +1915,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      else if (GET_CODE (src) == LO_SUM
 		       && poly_int_rtx_p (XEXP (src, 1),
 					  &cur_trace->cfa_temp.offset))
-		cur_trace->cfa_temp.reg = dwf_regno (dest);
+		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	      else
 		gcc_unreachable ();
 	    }
@@ -1798,17 +1924,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 6 */
 	case CONST_INT:
 	case CONST_POLY_INT:
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
 	  break;
 
 	  /* Rule 7 */
 	case IOR:
 	  gcc_assert (REG_P (XEXP (src, 0))
-		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		      && CONST_INT_P (XEXP (src, 1)));
 
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
 			  &cur_trace->cfa_temp.offset))
 	    /* The target shouldn't generate this kind of CFI note if we
@@ -1841,14 +1967,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      dwarf2out_flush_queued_reg_saves ();
 
               gcc_assert (cur_trace->cfa_store.reg
-			  == dwf_regno (XEXP (src, 0)));
+			  == dwf_cfa_reg (XEXP (src, 0)));
               fde->stack_realign = 1;
               fde->stack_realignment = INTVAL (XEXP (src, 1));
               cur_trace->cfa_store.offset = 0;
 
 	      if (cur_cfa->reg != dw_stack_pointer_regnum
 		  && cur_cfa->reg != dw_frame_pointer_regnum)
-		fde->drap_reg = cur_cfa->reg;
+		{
+		  gcc_assert (cur_cfa->reg.span == 1);
+		  fde->drap_reg = cur_cfa->reg.reg;
+		}
             }
           return;
 
@@ -1924,14 +2053,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 	case MINUS:
 	case LO_SUM:
 	  {
-	    unsigned int regno;
+	    struct cfa_reg regno;
 
 	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
 	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
 	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
 	      offset = -offset;
 
-	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
+	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset -= cur_cfa->offset;
@@ -1949,7 +2078,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Without an offset.  */
 	case REG:
 	  {
-	    unsigned int regno = dwf_regno (XEXP (dest, 0));
+	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset = -cur_cfa->offset;
@@ -1966,7 +2095,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 14 */
 	case POST_INC:
 	  gcc_assert (cur_trace->cfa_temp.reg
-		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
+		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
 	  offset = -cur_trace->cfa_temp.offset;
 	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
 	  break;
@@ -1984,7 +2113,7 @@ dwarf2out_frame_debug_expr (rtx expr)
       if (REG_P (src)
 	  && REGNO (src) != STACK_POINTER_REGNUM
 	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
-	  && dwf_regno (src) == cur_cfa->reg)
+	  && dwf_cfa_reg (src) == cur_cfa->reg)
 	{
 	  /* We're storing the current CFA reg into the stack.  */
 
@@ -2001,7 +2130,7 @@ dwarf2out_frame_debug_expr (rtx expr)
                   && cur_cfa->indirect == 0
                   && cur_cfa->reg != dw_frame_pointer_regnum)
                 {
-		  gcc_assert (fde->drap_reg == cur_cfa->reg);
+		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
 
 		  cur_cfa->indirect = 1;
 		  cur_cfa->reg = dw_frame_pointer_regnum;
@@ -2028,7 +2157,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		x = XEXP (x, 0);
 	      gcc_assert (REG_P (x));
 
-	      cur_cfa->reg = dwf_regno (x);
+	      cur_cfa->reg = dwf_cfa_reg (x);
 	      cur_cfa->base_offset = offset;
 	      cur_cfa->indirect = 1;
 	      break;
@@ -2924,7 +3053,7 @@ create_pseudo_cfg (void)
   ti.head = get_insns ();
   ti.beg_row = cie_cfi_row;
   ti.cfa_store = cie_cfi_row->cfa;
-  ti.cfa_temp.reg = INVALID_REGNUM;
+  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
   trace_info.quick_push (ti);
 
   if (cie_return_save)
@@ -2987,14 +3116,15 @@ create_pseudo_cfg (void)
 static void
 initial_return_save (rtx rtl)
 {
-  unsigned int reg = INVALID_REGNUM;
+  struct cfa_reg reg;
+  reg.set_by_dwreg (INVALID_REGNUM);
   poly_int64 offset = 0;
 
   switch (GET_CODE (rtl))
     {
     case REG:
       /* RA is in a register.  */
-      reg = dwf_regno (rtl);
+      reg = dwf_cfa_reg (rtl);
       break;
 
     case MEM:
@@ -3035,9 +3165,9 @@ initial_return_save (rtx rtl)
       gcc_unreachable ();
     }
 
-  if (reg != DWARF_FRAME_RETURN_COLUMN)
+  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
     {
-      if (reg != INVALID_REGNUM)
+      if (reg.reg != INVALID_REGNUM)
         record_reg_saved_in_reg (rtl, pc_rtx);
       reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
     }
@@ -3049,7 +3179,8 @@ create_cie_data (void)
   dw_cfa_location loc;
   dw_trace_info cie_trace;
 
-  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
+  dw_stack_pointer_regnum =
+    dwf_cfa_reg (gen_rtx_REG (Pmode, STACK_POINTER_REGNUM));
 
   memset (&cie_trace, 0, sizeof (cie_trace));
   cur_trace = &cie_trace;
@@ -3108,7 +3239,8 @@ static unsigned int
 execute_dwarf2_frame (void)
 {
   /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
-  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+  dw_frame_pointer_regnum =
+    dwf_cfa_reg (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM));
 
   /* The first time we're called, compute the incoming frame state.  */
   if (cie_cfi_vec == NULL)
@@ -3488,7 +3620,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
     {
       dw_cfa_location dummy;
       memset (&dummy, 0, sizeof (dummy));
-      dummy.reg = INVALID_REGNUM;
+      dummy.reg.set_by_dwreg (INVALID_REGNUM);
       cfi = def_cfa_0 (&dummy, &row->cfa);
     }
   output_cfi_directive (f, cfi);
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 9deca031fc2..f779ac5d464 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -2723,6 +2723,34 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
     }
 }
 
+/* Build a dwarf location for a cfa_reg spanning multiple
+   consecutive registers.  */
+
+struct dw_loc_descr_node *
+build_span_loc (struct cfa_reg reg)
+{
+  struct dw_loc_descr_node *head = NULL;
+
+  gcc_assert (known_gt (reg.span_width, 0));
+  for (unsigned int i = 0; i < reg.span; i++)
+    {
+      unsigned int regno = reg.reg + i;
+      if (regno <= 31)
+	add_loc_descr (&head,
+		       new_loc_descr ((enum dwarf_location_atom)
+				      (DW_OP_reg0 + regno),
+				      0, 0));
+      else
+	add_loc_descr (&head, new_loc_descr (DW_OP_regx, regno, 0));
+      add_loc_descr (&head,
+		     new_loc_descr (DW_OP_piece,
+				    reg.span_width.to_constant (),
+				    0));
+    }
+
+  return head;
+}
+
 /* This function builds a dwarf location descriptor sequence from a
    dw_cfa_location, adding the given OFFSET to the result of the
    expression.  */
@@ -2734,9 +2762,20 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
 
   offset += cfa->offset;
 
-  if (cfa->indirect)
+  if (cfa->reg.span > 1)
+    {
+      head = build_span_loc (cfa->reg);
+
+      if (maybe_ne (offset, 0))
+	{
+	  add_loc_descr (&head, new_loc_descr (DW_OP_LLVM_piece_end, 0, 0));
+	  add_loc_descr (&head, new_loc_descr (DW_OP_deref, 0, 0));
+	  loc_descr_plus_const (&head, offset);
+	}
+    }
+  else if (cfa->indirect)
     {
-      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
+      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
       head->dw_loc_oprnd1.val_class = dw_val_class_const;
       head->dw_loc_oprnd1.val_entry = NULL;
       tmp = new_loc_descr (DW_OP_deref, 0, 0);
@@ -2744,7 +2783,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
       loc_descr_plus_const (&head, offset);
     }
   else
-    head = new_reg_loc_descr (cfa->reg, offset);
+    head = new_reg_loc_descr (cfa->reg.reg, offset);
 
   return head;
 }
@@ -2762,7 +2801,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
     = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
 
   /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
-  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
+  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
     {
       head = new_reg_loc_descr (dwarf_fp, 0);
       add_loc_descr (&head, int_loc_descriptor (alignment));
@@ -20374,7 +20413,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
   list = NULL;
 
   memset (&next_cfa, 0, sizeof (next_cfa));
-  next_cfa.reg = INVALID_REGNUM;
+  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
   remember = next_cfa;
 
   start_label = fde->dw_fde_begin;
diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
index 9571f8b4b10..5daf44ff6ba 100644
--- a/gcc/dwarf2out.h
+++ b/gcc/dwarf2out.h
@@ -111,6 +111,40 @@ struct GTY(()) dw_fde_node {
 };
 
 
+/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
+   definitions and expressions.
+   Most architectures only need a single register number, but some (amdgcn)
+   have pointers that span multiple registers.  DWARF permits arbitrary
+   register sets, using DW_OP_piece, but existing use-cases only require
+   contiguous register sets, as represented here.  */
+struct GTY(()) cfa_reg {
+  unsigned int reg;
+  unsigned int span;
+  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
+
+  cfa_reg& set_by_dwreg (unsigned int r)
+    {
+      reg = r;
+      span = 1;
+      span_width = 0;  /* Unknown size (permitted when span == 1).  */
+      return *this;
+    }
+
+  bool operator== (const cfa_reg other) const
+    {
+      return (reg == other.reg
+	      && span == other.span
+	      && (known_eq (span_width, other.span_width)
+		  || (span == 1
+		      && (known_eq (span_width, 0)
+			  || known_eq (other.span_width, 0)))));
+    }
+  bool operator!= (const cfa_reg other) const
+    {
+      return !(*this == other);
+    }
+};
+
 /* This is how we define the location of the CFA. We use to handle it
    as REG + OFFSET all the time,  but now it can be more complex.
    It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
@@ -120,7 +154,7 @@ struct GTY(()) dw_cfa_location {
   poly_int64_pod offset;
   poly_int64_pod base_offset;
   /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
-  unsigned int reg;
+  struct cfa_reg reg;
   BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
   BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
 };
@@ -277,6 +311,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
   (dw_cfa_location *, poly_int64);
 extern struct dw_loc_descr_node *build_cfa_aligned_loc
   (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
+extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
 extern struct dw_loc_descr_node *mem_loc_descriptor
   (rtx, machine_mode mode, machine_mode mem_mode,
    enum var_init_status);
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 981577481af..7e99f37a998 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -5193,6 +5193,7 @@ main (int argc, char **argv)
       POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("double_int", &pos));
+      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
       POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
       POS_HERE (do_scalar_typedef ("offset_int", &pos));
       POS_HERE (do_scalar_typedef ("widest_int", &pos));
diff --git a/include/dwarf2.def b/include/dwarf2.def
index d8a8cce7947..eb9f1a09455 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -704,6 +704,12 @@ DW_OP (DW_OP_PGI_omp_thread_num, 0xf8)
    to 0 except explicitly documented for one action.  Please refer AArch64 DWARF
    ABI documentation for details.  */
 DW_OP (DW_OP_AARCH64_operation, 0xea)
+
+/* AMD GCN extensions (originally for LLVM).  See
+   http://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html  */
+// This clashes with DW_OP_AARCH64_operation, so use an alias instead
+// DW_OP (DW_OP_LLVM_piece_end, 0xea)
+#define DW_OP_LLVM_piece_end DW_OP_AARCH64_operation
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-09-02 19:35   ` Andrew Stubbs
@ 2020-09-02 19:55     ` Tom Tromey
  0 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2020-09-02 19:55 UTC (permalink / raw)
  To: Andrew Stubbs; +Cc: Tom Tromey, gcc-patches

>>>>> "Andrew" == Andrew Stubbs <ams@codesourcery.com> writes:

Andrew> http://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html#composite-location-description-operations

Thanks.  Adding that to the appropriate spot in the patch would be
great.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-09-02 17:49 ` Tom Tromey
@ 2020-09-02 19:35   ` Andrew Stubbs
  2020-09-02 19:55     ` Tom Tromey
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Stubbs @ 2020-09-02 19:35 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gcc-patches

On 02/09/2020 18:49, Tom Tromey wrote:
>>>>>> "Andrew" == Andrew Stubbs <ams@codesourcery.com> writes:
> 
> Andrew> To be fair, the DWARF standard makes a similar assumption; the
> Andrew> engineers working on LLVM and GDB, at AMD, have therefore invented
> Andrew> some new DWARF operators that they plan to propose for a future
> Andrew> standard. Only one is relevant here, however:
> Andrew> DW_OP_LLVM_piece_end. (Unfortunately this clashes with an AArch64
> Andrew> extension, but I think we can cope using an alias -- only GCC dumps
> Andrew> will be confusing.)
> 
> Andrew> +/* AMD GCN extensions (originally for LLVM).  */
> Andrew> +// This clashes with DW_OP_AARCH64_operation, so use an alias instead
> Andrew> +// DW_OP (DW_OP_LLVM_piece_end, 0xea)
> Andrew> +#define DW_OP_LLVM_piece_end DW_OP_AARCH64_operation
> Andrew>  DW_END_OP
>   
> Is it too late to pick a non-clashing value?
> 
> Also, we have tried pretty hard in recent years to document all of gcc's
> DWARF extensions.  Please add a link to the documentation for this one.
> If there aren't docs -- I guess it would be ideal if you could write
> them.  Putting them on the GCC wiki is fine.

I didn't select this number; I'm just following out-of-tree LLVM and GDB 
usage from AMD. It's allocated from the user extension range, so I 
imagine it'll enter the standard with a different number (and probably 
name).

The documentation is here:

http://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html#composite-location-description-operations

Andrew

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] dwarf: Multi-register CFI address support
  2020-08-28 12:04 Andrew Stubbs
@ 2020-09-02 17:49 ` Tom Tromey
  2020-09-02 19:35   ` Andrew Stubbs
  2020-09-03 15:29 ` Andrew Stubbs
  2020-10-19  9:36 ` Jakub Jelinek
  2 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2020-09-02 17:49 UTC (permalink / raw)
  To: Andrew Stubbs; +Cc: gcc-patches

>>>>> "Andrew" == Andrew Stubbs <ams@codesourcery.com> writes:

Andrew> To be fair, the DWARF standard makes a similar assumption; the
Andrew> engineers working on LLVM and GDB, at AMD, have therefore invented
Andrew> some new DWARF operators that they plan to propose for a future
Andrew> standard. Only one is relevant here, however:
Andrew> DW_OP_LLVM_piece_end. (Unfortunately this clashes with an AArch64
Andrew> extension, but I think we can cope using an alias -- only GCC dumps
Andrew> will be confusing.)

Andrew> +/* AMD GCN extensions (originally for LLVM).  */
Andrew> +// This clashes with DW_OP_AARCH64_operation, so use an alias instead
Andrew> +// DW_OP (DW_OP_LLVM_piece_end, 0xea)
Andrew> +#define DW_OP_LLVM_piece_end DW_OP_AARCH64_operation
Andrew>  DW_END_OP
 
Is it too late to pick a non-clashing value?

Also, we have tried pretty hard in recent years to document all of gcc's
DWARF extensions.  Please add a link to the documentation for this one.
If there aren't docs -- I guess it would be ideal if you could write
them.  Putting them on the GCC wiki is fine.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] dwarf: Multi-register CFI address support
@ 2020-08-28 12:04 Andrew Stubbs
  2020-09-02 17:49 ` Tom Tromey
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Andrew Stubbs @ 2020-08-28 12:04 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1487 bytes --]

Hi all,

This patch introduces DWARF CFI support for architectures that require 
multiple registers to hold pointers, such as the stack pointer, frame 
pointer, and return address. The motivating case is the AMD GCN 
architecture which has 64-bit address pointers, but 32-bit registers.

The current implementation permits program variables to span as many 
registers as they need, but assumes that CFI expressions will only need 
a single register for each frame value.

To be fair, the DWARF standard makes a similar assumption; the engineers 
working on LLVM and GDB, at AMD, have therefore invented some new DWARF 
operators that they plan to propose for a future standard. Only one is 
relevant here, however: DW_OP_LLVM_piece_end. (Unfortunately this 
clashes with an AArch64 extension, but I think we can cope using an 
alias -- only GCC dumps will be confusing.)

My approach is to change the type representing a DWARF register 
throughout the CFI code. This permits the register span information to 
propagate to where it is needed.

I've taken advantage of C++ struct copies and operator== to minimize the 
amount of refactoring required. I'm not sure this meets the GCC 
guidelines exactly, but if not I can change that once the basic form is 
agreed. (I also considered an operator= to make assigning single dwreg 
values transparent, but that hid too many invalid assumptions.)

OK to commit? (Although, I'll hold off until AMD release the compatible 
GDB.)

Thanks

Andrew

[-- Attachment #2: 200828-dwarf-reg-spans.patch --]
[-- Type: text/x-patch, Size: 29866 bytes --]

dwarf: Multi-register CFI address support

Add support for architectures such as AMD GCN, in which the pointer size is
larger than the register size.  This allows the CFI information to include
multi-register locations for the stack pointer, frame pointer, and return
address.

Note that this uses a newly proposed DWARF operator DW_OP_LLVM_piece_end,
which is currently only recognized by the ROCGDB debugger from AMD.  The exact
name and encoding for this operator is subject to change if and when the DWARF
standard accepts it.

gcc/ChangeLog:

	* dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg.
	(dw_frame_pointer_regnum): Likewise.
	(new_cfi_row): Use set_by_dwreg.
	(get_cfa_from_loc_descr): Use set_by_dwreg.  Support register spans
	with DW_OP_piece and DW_OP_LLVM_piece_end.  Support DW_OP_lit*,
	DW_OP_const*, DW_OP_minus, and DW_OP_plus.
	(lookup_cfa_1): Use set_by_dwreg.
	(def_cfa_0): Update for cfa_reg and support register spans.
	(reg_save): Change sreg parameter to struct cfa_reg.  Support register
	spans.
	(dwf_cfa_reg): New function.
	(dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of
	dwf_regno.
	(dwarf2out_frame_debug_def_cfa): Likewise.
	(dwarf2out_frame_debug_adjust_cfa): Likewise.
	(dwarf2out_frame_debug_cfa_offset): Likewise.  Update reg_save usage.
	(dwarf2out_frame_debug_cfa_register): Likewise.
	(dwarf2out_frame_debug_expr): Likewise.
	(create_pseudo_cfg): Use set_by_dwreg.
	(initial_return_save): Use set_by_dwreg and dwf_cfa_reg,
	(create_cie_data): Use dwf_cfa_reg.
	(execute_dwarf2_frame): Use dwf_cfa_reg.
	(dump_cfi_row): Use set_by_dwreg.
	* dwarf2out.c (build_span_loc): New function.
	(build_cfa_loc): Support register spans.
	(build_cfa_aligned_loc): Update cfa_reg usage.
	(convert_cfa_to_fb_loc_list): Use set_by_dwreg.
	* dwarf2out.h (struct cfa_reg): New type.
	(struct dw_cfa_location): Use struct cfa_reg.
	(build_span_loc): New prototype.
	* gengtype.c (main): Accept poly_uint16_pod type.

include/ChangeLog:

	* dwarf2.def (DW_OP_LLVM_piece_end): New extension operator.

diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index 0d179b388e4..63cb6de5c4f 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -229,8 +229,8 @@ static vec<queued_reg_save> queued_reg_saves;
 static bool any_cfis_emitted;
 
 /* Short-hand for commonly used register numbers.  */
-static unsigned dw_stack_pointer_regnum;
-static unsigned dw_frame_pointer_regnum;
+static struct cfa_reg dw_stack_pointer_regnum;
+static struct cfa_reg dw_frame_pointer_regnum;
 \f
 /* Hook used by __throw.  */
 
@@ -430,7 +430,7 @@ new_cfi_row (void)
 {
   dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
 
-  row->cfa.reg = INVALID_REGNUM;
+  row->cfa.reg.set_by_dwreg (INVALID_REGNUM);
 
   return row;
 }
@@ -538,7 +538,11 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
   cfa->offset = 0;
   cfa->base_offset = 0;
   cfa->indirect = 0;
-  cfa->reg = -1;
+  cfa->reg.set_by_dwreg (-1);
+
+  /* Record previous register pieces here.  */
+  struct cfa_reg span;
+  span.set_by_dwreg (INVALID_REGNUM);
 
   for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
     {
@@ -578,10 +582,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_reg29:
 	case DW_OP_reg30:
 	case DW_OP_reg31:
-	  cfa->reg = op - DW_OP_reg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_reg0);
 	  break;
 	case DW_OP_regx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  break;
 	case DW_OP_breg0:
 	case DW_OP_breg1:
@@ -615,17 +619,89 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 	case DW_OP_breg29:
 	case DW_OP_breg30:
 	case DW_OP_breg31:
-	  cfa->reg = op - DW_OP_breg0;
+	  cfa->reg.set_by_dwreg (op - DW_OP_breg0);
 	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
 	  break;
 	case DW_OP_bregx:
-	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int);
 	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
 	  break;
+	case DW_OP_piece:
+	  if (span.reg != INVALID_REGNUM)
+	    {
+	      /* We only support contiguous pieces, for now.  */
+	      gcc_assert (cfa->reg.reg == span.reg + span.span);
+	      gcc_assert (known_eq (ptr->dw_loc_oprnd1.v.val_int,
+				    span.span_width));
+	      span.span++;
+	      cfa->reg = span;
+	    }
+	  else
+	    {
+	      cfa->reg.span_width = ptr->dw_loc_oprnd1.v.val_int;
+	      span = cfa->reg;
+	    }
+	  break;
+	case DW_OP_LLVM_piece_end:
+	  break;
 	case DW_OP_deref:
 	  cfa->indirect = 1;
 	  break;
+	case DW_OP_lit0:
+	case DW_OP_lit1:
+	case DW_OP_lit2:
+	case DW_OP_lit3:
+	case DW_OP_lit4:
+	case DW_OP_lit5:
+	case DW_OP_lit6:
+	case DW_OP_lit7:
+	case DW_OP_lit8:
+	case DW_OP_lit9:
+	case DW_OP_lit10:
+	case DW_OP_lit11:
+	case DW_OP_lit12:
+	case DW_OP_lit13:
+	case DW_OP_lit14:
+	case DW_OP_lit15:
+	case DW_OP_lit16:
+	case DW_OP_lit17:
+	case DW_OP_lit18:
+	case DW_OP_lit19:
+	case DW_OP_lit20:
+	case DW_OP_lit21:
+	case DW_OP_lit22:
+	case DW_OP_lit23:
+	case DW_OP_lit24:
+	case DW_OP_lit25:
+	case DW_OP_lit26:
+	case DW_OP_lit27:
+	case DW_OP_lit28:
+	case DW_OP_lit29:
+	case DW_OP_lit30:
+	case DW_OP_lit31:
+	  /* Stacked operands are not yet supported.  */
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = op - DW_OP_lit0;
+	  break;
+	case DW_OP_const1u:
+	case DW_OP_const1s:
+	case DW_OP_const2u:
+	case DW_OP_const2s:
+	case DW_OP_const4s:
+	case DW_OP_const8s:
+	case DW_OP_constu:
+	case DW_OP_consts:
+	  gcc_assert (known_eq (cfa->offset, 0));
+	  cfa->offset = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_minus:
+	  cfa->offset = -cfa->offset;
+	  break;
+	case DW_OP_plus:
+	  /* The offset is already in place.  */
+	  break;
 	case DW_OP_plus_uconst:
+	  gcc_assert (known_eq (cfa->offset, 0));
 	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
 	  break;
 	default:
@@ -648,11 +724,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
       loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_register:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       break;
     case DW_CFA_def_cfa:
     case DW_CFA_def_cfa_sf:
-      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num);
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_expression:
@@ -798,6 +874,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 
   HOST_WIDE_INT const_offset;
   if (new_cfa->reg == old_cfa->reg
+      && new_cfa->reg.span == 1
       && !new_cfa->indirect
       && !old_cfa->indirect
       && new_cfa->offset.is_constant (&const_offset))
@@ -814,7 +891,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
     }
   else if (new_cfa->offset.is_constant ()
 	   && known_eq (new_cfa->offset, old_cfa->offset)
-	   && old_cfa->reg != INVALID_REGNUM
+	   && old_cfa->reg.reg != INVALID_REGNUM
+	   && new_cfa->reg.span == 1
 	   && !new_cfa->indirect
 	   && !old_cfa->indirect)
     {
@@ -824,10 +902,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	 been set as a register plus offset rather than a general
 	 DW_CFA_def_cfa_expression.  */
       cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
     }
   else if (new_cfa->indirect == 0
-	   && new_cfa->offset.is_constant (&const_offset))
+	   && new_cfa->offset.is_constant (&const_offset)
+	   && new_cfa->reg.span == 1)
     {
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
 	 indicating the CFA register has changed to <register> with
@@ -838,7 +917,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
       else
 	cfi->dw_cfi_opc = DW_CFA_def_cfa;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg;
       cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
     }
   else
@@ -885,18 +964,18 @@ def_cfa_1 (dw_cfa_location *new_cfa)
 }
 
 /* Add the CFI for saving a register.  REG is the CFA column number.
-   If SREG is -1, the register is saved at OFFSET from the CFA;
+   If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA;
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
+reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset)
 {
   dw_fde_ref fde = cfun ? cfun->fde : NULL;
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
 
-  if (sreg == INVALID_REGNUM)
+  if (sreg.reg == INVALID_REGNUM)
     {
       HOST_WIDE_INT const_offset;
       /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
@@ -926,7 +1005,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	    = build_cfa_loc (&cur_row->cfa, offset);
 	}
     }
-  else if (sreg == reg)
+  else if (sreg.reg == reg)
     {
       /* While we could emit something like DW_CFA_same_value or
 	 DW_CFA_restore, we never expect to see something like that
@@ -934,10 +1013,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 	 can always bypass this by using REG_CFA_RESTORE directly.  */
       gcc_unreachable ();
     }
+  else if (sreg.span > 1)
+    {
+      cfi->dw_cfi_opc = DW_CFA_expression;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+      cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg);
+    }
   else
     {
       cfi->dw_cfi_opc = DW_CFA_register;
-      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg;
     }
 
   add_cfi (cfi);
@@ -1018,6 +1103,43 @@ dwf_regno (const_rtx reg)
   return DWARF_FRAME_REGNUM (REGNO (reg));
 }
 
+/* Like dwf_regno, but when the value can span multiple registers.  */
+
+static struct cfa_reg
+dwf_cfa_reg (rtx reg)
+{
+  struct cfa_reg result;
+
+  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
+
+  result.reg = dwf_regno (reg);
+  result.span = 1;
+  result.span_width = GET_MODE_SIZE (GET_MODE (reg));
+
+  rtx span = targetm.dwarf_register_span (reg);
+  if (span)
+    {
+      /* We only support the simple case of consecutive registers all with the
+	 same size.  */
+      result.span = XVECLEN (span, 0);
+      result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0)));
+
+#if CHECKING_P
+      /* Ensure that the above assumption is accurate.  */
+      for (unsigned int i = 0; i < result.span; i++)
+	{
+	  gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (XVECEXP (span,
+								  0, i))),
+				result.span_width));
+	  gcc_assert (REG_P (XVECEXP (span, 0, i)));
+	  gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i);
+	}
+#endif
+    }
+
+  return result;
+}
+
 /* Compare X and Y for equivalence.  The inputs may be REGs or PC_RTX.  */
 
 static bool
@@ -1086,7 +1208,8 @@ dwarf2out_flush_queued_reg_saves (void)
 
   FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     {
-      unsigned int reg, sreg;
+      unsigned int reg;
+      struct cfa_reg sreg;
 
       record_reg_saved_in_reg (q->saved_reg, q->reg);
 
@@ -1095,9 +1218,9 @@ dwarf2out_flush_queued_reg_saves (void)
       else
         reg = dwf_regno (q->reg);
       if (q->saved_reg)
-	sreg = dwf_regno (q->saved_reg);
+	sreg = dwf_cfa_reg (q->saved_reg);
       else
-	sreg = INVALID_REGNUM;
+	sreg.set_by_dwreg (INVALID_REGNUM);
       reg_save (reg, sreg, q->cfa_offset);
     }
 
@@ -1169,7 +1292,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
   /* ??? If this fails, we could be calling into the _loc functions to
      define a full expression.  So far no port does that.  */
   gcc_assert (REG_P (pat));
-  cur_cfa->reg = dwf_regno (pat);
+  cur_cfa->reg = dwf_cfa_reg (pat);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
@@ -1186,7 +1309,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
   switch (GET_CODE (src))
     {
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
       cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
@@ -1197,7 +1320,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
       gcc_unreachable ();
     }
 
-  cur_cfa->reg = dwf_regno (dest);
+  cur_cfa->reg = dwf_cfa_reg (dest);
   gcc_assert (cur_cfa->indirect == 0);
 }
 
@@ -1219,11 +1342,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   switch (GET_CODE (addr))
     {
     case REG:
-      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg);
       offset = -cur_cfa->offset;
       break;
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
+      gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg);
       offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
@@ -1243,8 +1366,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
+  struct cfa_reg invalid;
+  invalid.set_by_dwreg (INVALID_REGNUM);
   if (!span)
-    reg_save (sregno, INVALID_REGNUM, offset);
+    reg_save (sregno, invalid, offset);
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
@@ -1258,7 +1383,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
 	{
 	  rtx elem = XVECEXP (span, 0, par_index);
 	  sregno = dwf_regno (src);
-	  reg_save (sregno, INVALID_REGNUM, span_offset);
+	  reg_save (sregno, invalid, span_offset);
 	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	}
     }
@@ -1270,7 +1395,8 @@ static void
 dwarf2out_frame_debug_cfa_register (rtx set)
 {
   rtx src, dest;
-  unsigned sregno, dregno;
+  unsigned sregno;
+  struct cfa_reg dregno;
 
   src = XEXP (set, 1);
   dest = XEXP (set, 0);
@@ -1281,7 +1407,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
   else
     sregno = dwf_regno (src);
 
-  dregno = dwf_regno (dest);
+  dregno = dwf_cfa_reg (dest);
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
@@ -1667,7 +1793,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	{
 	  /* Setting FP from SP.  */
 	case REG:
-	  if (cur_cfa->reg == dwf_regno (src))
+	  if (cur_cfa->reg == dwf_cfa_reg (src))
 	    {
 	      /* Rule 1 */
 	      /* Update the CFA rule wrt SP or FP.  Make sure src is
@@ -1677,7 +1803,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		 ARM copies SP to a temporary register, and from there to
 		 FP.  So we just rely on the backends to only set
 		 RTX_FRAME_RELATED_P on appropriate insns.  */
-	      cur_cfa->reg = dwf_regno (dest);
+	      cur_cfa->reg = dwf_cfa_reg (dest);
 	      cur_trace->cfa_temp.reg = cur_cfa->reg;
 	      cur_trace->cfa_temp.offset = cur_cfa->offset;
 	    }
@@ -1697,7 +1823,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		  && REGNO (src) == STACK_POINTER_REGNUM)
 		gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
 			    && fde->drap_reg != INVALID_REGNUM
-			    && cur_cfa->reg != dwf_regno (src));
+			    && cur_cfa->reg != dwf_cfa_reg (src));
 	      else
 		queue_reg_save (src, dest, 0);
 	    }
@@ -1712,7 +1838,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      /* Adjusting SP.  */
 	      if (REG_P (XEXP (src, 1)))
 		{
-		  gcc_assert (dwf_regno (XEXP (src, 1))
+		  gcc_assert (dwf_cfa_reg (XEXP (src, 1))
 			      == cur_trace->cfa_temp.reg);
 		  offset = cur_trace->cfa_temp.offset;
 		}
@@ -1746,7 +1872,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      gcc_assert (frame_pointer_needed);
 
 	      gcc_assert (REG_P (XEXP (src, 0))
-			  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+			  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg);
 	      offset = rtx_to_poly_int64 (XEXP (src, 1));
 	      if (GET_CODE (src) != MINUS)
 		offset = -offset;
@@ -1759,14 +1885,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 4 */
 	      if (REG_P (XEXP (src, 0))
-		  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
+		  && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg
 		  && poly_int_rtx_p (XEXP (src, 1), &offset))
 		{
 		  /* Setting a temporary CFA register that will be copied
 		     into the FP later on.  */
 		  offset = -offset;
 		  cur_cfa->offset += offset;
-		  cur_cfa->reg = dwf_regno (dest);
+		  cur_cfa->reg = dwf_cfa_reg (dest);
 		  /* Or used to save regs to the stack.  */
 		  cur_trace->cfa_temp.reg = cur_cfa->reg;
 		  cur_trace->cfa_temp.offset = cur_cfa->offset;
@@ -1774,13 +1900,13 @@ dwarf2out_frame_debug_expr (rtx expr)
 
 	      /* Rule 5 */
 	      else if (REG_P (XEXP (src, 0))
-		       && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		       && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		       && XEXP (src, 1) == stack_pointer_rtx)
 		{
 		  /* Setting a scratch register that we will use instead
 		     of SP for saving registers to the stack.  */
 		  gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
-		  cur_trace->cfa_store.reg = dwf_regno (dest);
+		  cur_trace->cfa_store.reg = dwf_cfa_reg (dest);
 		  cur_trace->cfa_store.offset
 		    = cur_cfa->offset - cur_trace->cfa_temp.offset;
 		}
@@ -1789,7 +1915,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      else if (GET_CODE (src) == LO_SUM
 		       && poly_int_rtx_p (XEXP (src, 1),
 					  &cur_trace->cfa_temp.offset))
-		cur_trace->cfa_temp.reg = dwf_regno (dest);
+		cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	      else
 		gcc_unreachable ();
 	    }
@@ -1798,17 +1924,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 6 */
 	case CONST_INT:
 	case CONST_POLY_INT:
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
 	  break;
 
 	  /* Rule 7 */
 	case IOR:
 	  gcc_assert (REG_P (XEXP (src, 0))
-		      && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg
+		      && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg
 		      && CONST_INT_P (XEXP (src, 1)));
 
-	  cur_trace->cfa_temp.reg = dwf_regno (dest);
+	  cur_trace->cfa_temp.reg = dwf_cfa_reg (dest);
 	  if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
 			  &cur_trace->cfa_temp.offset))
 	    /* The target shouldn't generate this kind of CFI note if we
@@ -1841,14 +1967,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 	      dwarf2out_flush_queued_reg_saves ();
 
               gcc_assert (cur_trace->cfa_store.reg
-			  == dwf_regno (XEXP (src, 0)));
+			  == dwf_cfa_reg (XEXP (src, 0)));
               fde->stack_realign = 1;
               fde->stack_realignment = INTVAL (XEXP (src, 1));
               cur_trace->cfa_store.offset = 0;
 
 	      if (cur_cfa->reg != dw_stack_pointer_regnum
 		  && cur_cfa->reg != dw_frame_pointer_regnum)
-		fde->drap_reg = cur_cfa->reg;
+		{
+		  gcc_assert (cur_cfa->reg.span == 1);
+		  fde->drap_reg = cur_cfa->reg.reg;
+		}
             }
           return;
 
@@ -1924,14 +2053,14 @@ dwarf2out_frame_debug_expr (rtx expr)
 	case MINUS:
 	case LO_SUM:
 	  {
-	    unsigned int regno;
+	    struct cfa_reg regno;
 
 	    gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
 	    offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
 	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
 	      offset = -offset;
 
-	    regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
+	    regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset -= cur_cfa->offset;
@@ -1949,7 +2078,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Without an offset.  */
 	case REG:
 	  {
-	    unsigned int regno = dwf_regno (XEXP (dest, 0));
+	    struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0));
 
 	    if (cur_cfa->reg == regno)
 	      offset = -cur_cfa->offset;
@@ -1966,7 +2095,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 	  /* Rule 14 */
 	case POST_INC:
 	  gcc_assert (cur_trace->cfa_temp.reg
-		      == dwf_regno (XEXP (XEXP (dest, 0), 0)));
+		      == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)));
 	  offset = -cur_trace->cfa_temp.offset;
 	  cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
 	  break;
@@ -1984,7 +2113,7 @@ dwarf2out_frame_debug_expr (rtx expr)
       if (REG_P (src)
 	  && REGNO (src) != STACK_POINTER_REGNUM
 	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
-	  && dwf_regno (src) == cur_cfa->reg)
+	  && dwf_cfa_reg (src) == cur_cfa->reg)
 	{
 	  /* We're storing the current CFA reg into the stack.  */
 
@@ -2001,7 +2130,7 @@ dwarf2out_frame_debug_expr (rtx expr)
                   && cur_cfa->indirect == 0
                   && cur_cfa->reg != dw_frame_pointer_regnum)
                 {
-		  gcc_assert (fde->drap_reg == cur_cfa->reg);
+		  gcc_assert (fde->drap_reg == cur_cfa->reg.reg);
 
 		  cur_cfa->indirect = 1;
 		  cur_cfa->reg = dw_frame_pointer_regnum;
@@ -2028,7 +2157,7 @@ dwarf2out_frame_debug_expr (rtx expr)
 		x = XEXP (x, 0);
 	      gcc_assert (REG_P (x));
 
-	      cur_cfa->reg = dwf_regno (x);
+	      cur_cfa->reg = dwf_cfa_reg (x);
 	      cur_cfa->base_offset = offset;
 	      cur_cfa->indirect = 1;
 	      break;
@@ -2924,7 +3053,7 @@ create_pseudo_cfg (void)
   ti.head = get_insns ();
   ti.beg_row = cie_cfi_row;
   ti.cfa_store = cie_cfi_row->cfa;
-  ti.cfa_temp.reg = INVALID_REGNUM;
+  ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM);
   trace_info.quick_push (ti);
 
   if (cie_return_save)
@@ -2987,14 +3116,15 @@ create_pseudo_cfg (void)
 static void
 initial_return_save (rtx rtl)
 {
-  unsigned int reg = INVALID_REGNUM;
+  struct cfa_reg reg;
+  reg.set_by_dwreg (INVALID_REGNUM);
   poly_int64 offset = 0;
 
   switch (GET_CODE (rtl))
     {
     case REG:
       /* RA is in a register.  */
-      reg = dwf_regno (rtl);
+      reg = dwf_cfa_reg (rtl);
       break;
 
     case MEM:
@@ -3035,9 +3165,9 @@ initial_return_save (rtx rtl)
       gcc_unreachable ();
     }
 
-  if (reg != DWARF_FRAME_RETURN_COLUMN)
+  if (reg.reg != DWARF_FRAME_RETURN_COLUMN)
     {
-      if (reg != INVALID_REGNUM)
+      if (reg.reg != INVALID_REGNUM)
         record_reg_saved_in_reg (rtl, pc_rtx);
       reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset);
     }
@@ -3049,7 +3179,8 @@ create_cie_data (void)
   dw_cfa_location loc;
   dw_trace_info cie_trace;
 
-  dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
+  dw_stack_pointer_regnum =
+    dwf_cfa_reg (gen_rtx_REG (Pmode, STACK_POINTER_REGNUM));
 
   memset (&cie_trace, 0, sizeof (cie_trace));
   cur_trace = &cie_trace;
@@ -3108,7 +3239,8 @@ static unsigned int
 execute_dwarf2_frame (void)
 {
   /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
-  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+  dw_frame_pointer_regnum =
+    dwf_cfa_reg (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM));
 
   /* The first time we're called, compute the incoming frame state.  */
   if (cie_cfi_vec == NULL)
@@ -3488,7 +3620,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
     {
       dw_cfa_location dummy;
       memset (&dummy, 0, sizeof (dummy));
-      dummy.reg = INVALID_REGNUM;
+      dummy.reg.set_by_dwreg (INVALID_REGNUM);
       cfi = def_cfa_0 (&dummy, &row->cfa);
     }
   output_cfi_directive (f, cfi);
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 9deca031fc2..f779ac5d464 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -2723,6 +2723,34 @@ output_loc_sequence_raw (dw_loc_descr_ref loc)
     }
 }
 
+/* Build a dwarf location for a cfa_reg spanning multiple
+   consecutive registers.  */
+
+struct dw_loc_descr_node *
+build_span_loc (struct cfa_reg reg)
+{
+  struct dw_loc_descr_node *head = NULL;
+
+  gcc_assert (known_gt (reg.span_width, 0));
+  for (unsigned int i = 0; i < reg.span; i++)
+    {
+      unsigned int regno = reg.reg + i;
+      if (regno <= 31)
+	add_loc_descr (&head,
+		       new_loc_descr ((enum dwarf_location_atom)
+				      (DW_OP_reg0 + regno),
+				      0, 0));
+      else
+	add_loc_descr (&head, new_loc_descr (DW_OP_regx, regno, 0));
+      add_loc_descr (&head,
+		     new_loc_descr (DW_OP_piece,
+				    reg.span_width.to_constant (),
+				    0));
+    }
+
+  return head;
+}
+
 /* This function builds a dwarf location descriptor sequence from a
    dw_cfa_location, adding the given OFFSET to the result of the
    expression.  */
@@ -2734,9 +2762,20 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
 
   offset += cfa->offset;
 
-  if (cfa->indirect)
+  if (cfa->reg.span > 1)
+    {
+      head = build_span_loc (cfa->reg);
+
+      if (maybe_ne (offset, 0))
+	{
+	  add_loc_descr (&head, new_loc_descr (DW_OP_LLVM_piece_end, 0, 0));
+	  add_loc_descr (&head, new_loc_descr (DW_OP_deref, 0, 0));
+	  loc_descr_plus_const (&head, offset);
+	}
+    }
+  else if (cfa->indirect)
     {
-      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
+      head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset);
       head->dw_loc_oprnd1.val_class = dw_val_class_const;
       head->dw_loc_oprnd1.val_entry = NULL;
       tmp = new_loc_descr (DW_OP_deref, 0, 0);
@@ -2744,7 +2783,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset)
       loc_descr_plus_const (&head, offset);
     }
   else
-    head = new_reg_loc_descr (cfa->reg, offset);
+    head = new_reg_loc_descr (cfa->reg.reg, offset);
 
   return head;
 }
@@ -2762,7 +2801,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
     = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
 
   /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
-  if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
+  if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0)
     {
       head = new_reg_loc_descr (dwarf_fp, 0);
       add_loc_descr (&head, int_loc_descriptor (alignment));
@@ -20374,7 +20413,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
   list = NULL;
 
   memset (&next_cfa, 0, sizeof (next_cfa));
-  next_cfa.reg = INVALID_REGNUM;
+  next_cfa.reg.set_by_dwreg (INVALID_REGNUM);
   remember = next_cfa;
 
   start_label = fde->dw_fde_begin;
diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h
index 9571f8b4b10..5daf44ff6ba 100644
--- a/gcc/dwarf2out.h
+++ b/gcc/dwarf2out.h
@@ -111,6 +111,40 @@ struct GTY(()) dw_fde_node {
 };
 
 
+/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA
+   definitions and expressions.
+   Most architectures only need a single register number, but some (amdgcn)
+   have pointers that span multiple registers.  DWARF permits arbitrary
+   register sets, using DW_OP_piece, but existing use-cases only require
+   contiguous register sets, as represented here.  */
+struct GTY(()) cfa_reg {
+  unsigned int reg;
+  unsigned int span;
+  poly_uint16_pod span_width;  /* A.K.A. register mode size.  */
+
+  cfa_reg& set_by_dwreg (unsigned int r)
+    {
+      reg = r;
+      span = 1;
+      span_width = 0;  /* Unknown size (permitted when span == 1).  */
+      return *this;
+    }
+
+  bool operator== (const cfa_reg other) const
+    {
+      return (reg == other.reg
+	      && span == other.span
+	      && (known_eq (span_width, other.span_width)
+		  || (span == 1
+		      && (known_eq (span_width, 0)
+			  || known_eq (other.span_width, 0)))));
+    }
+  bool operator!= (const cfa_reg other) const
+    {
+      return !(*this == other);
+    }
+};
+
 /* This is how we define the location of the CFA. We use to handle it
    as REG + OFFSET all the time,  but now it can be more complex.
    It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
@@ -120,7 +154,7 @@ struct GTY(()) dw_cfa_location {
   poly_int64_pod offset;
   poly_int64_pod base_offset;
   /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space.  */
-  unsigned int reg;
+  struct cfa_reg reg;
   BOOL_BITFIELD indirect : 1;  /* 1 if CFA is accessed via a dereference.  */
   BOOL_BITFIELD in_use : 1;    /* 1 if a saved cfa is stored here.  */
 };
@@ -277,6 +311,7 @@ extern struct dw_loc_descr_node *build_cfa_loc
   (dw_cfa_location *, poly_int64);
 extern struct dw_loc_descr_node *build_cfa_aligned_loc
   (dw_cfa_location *, poly_int64, HOST_WIDE_INT);
+extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg);
 extern struct dw_loc_descr_node *mem_loc_descriptor
   (rtx, machine_mode mode, machine_mode mem_mode,
    enum var_init_status);
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 981577481af..7e99f37a998 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -5193,6 +5193,7 @@ main (int argc, char **argv)
       POS_HERE (do_scalar_typedef ("REAL_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("FIXED_VALUE_TYPE", &pos));
       POS_HERE (do_scalar_typedef ("double_int", &pos));
+      POS_HERE (do_scalar_typedef ("poly_uint16_pod", &pos));
       POS_HERE (do_scalar_typedef ("poly_int64_pod", &pos));
       POS_HERE (do_scalar_typedef ("offset_int", &pos));
       POS_HERE (do_scalar_typedef ("widest_int", &pos));
diff --git a/include/dwarf2.def b/include/dwarf2.def
index d8a8cce7947..a31ab2432d4 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -704,6 +704,11 @@ DW_OP (DW_OP_PGI_omp_thread_num, 0xf8)
    to 0 except explicitly documented for one action.  Please refer AArch64 DWARF
    ABI documentation for details.  */
 DW_OP (DW_OP_AARCH64_operation, 0xea)
+
+/* AMD GCN extensions (originally for LLVM).  */
+// This clashes with DW_OP_AARCH64_operation, so use an alias instead
+// DW_OP (DW_OP_LLVM_piece_end, 0xea)
+#define DW_OP_LLVM_piece_end DW_OP_AARCH64_operation
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2021-12-01 15:50 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-13 13:27 [PATCH] dwarf: Multi-register CFI address support Hafiz Abid Qadeer
2021-07-22 10:58 ` Hafiz Abid Qadeer
2021-08-24 15:55   ` Hafiz Abid Qadeer
2021-11-02 15:02     ` Hafiz Abid Qadeer
2021-11-09 15:59 ` Jakub Jelinek
2021-11-11 18:12   ` Hafiz Abid Qadeer
2021-12-01 15:49     ` Jakub Jelinek
  -- strict thread matches above, loose matches on Subject: below --
2020-08-28 12:04 Andrew Stubbs
2020-09-02 17:49 ` Tom Tromey
2020-09-02 19:35   ` Andrew Stubbs
2020-09-02 19:55     ` Tom Tromey
2020-09-03 15:29 ` Andrew Stubbs
2020-09-21 13:51   ` Andrew Stubbs
2020-10-05 10:07     ` Andrew Stubbs
2020-10-19  9:36 ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).