diff mbox series

[committed,OG10] dwarf: Multi-register CFI address support

Message ID dea194f7-f10d-464e-933d-69d1952471ca@codesourcery.com
State New
Headers show
Series [committed,OG10] dwarf: Multi-register CFI address support | expand

Commit Message

Andrew Stubbs Sept. 22, 2020, 2:22 p.m. UTC
On 03/09/2020 16:29, Andrew Stubbs wrote:
>> OK to commit? (Although, I'll hold off until AMD release the 
>> compatible GDB.)

The ROCm 3.8 ROCGDB is now released. I'm committing the attached patches 
to devel/omp/gcc-10 while I wait for review.

The first patch is the multi-register CFI support, and the second is the 
amdgcn DWARF configuration patch that relies on the first.

Andrew
diff mbox series

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.

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;
 
 /* 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)