diff mbox series

dwarf: Multi-register CFI address support.

Message ID 20210613132738.615611-1-abidh@codesourcery.com
State New
Headers show
Series dwarf: Multi-register CFI address support. | expand

Commit Message

Hafiz Abid Qadeer June 13, 2021, 1:27 p.m. UTC
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(-)

Comments

Hafiz Abid Qadeer July 22, 2021, 10:58 a.m. UTC | #1
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;
>  
>  /* 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 Aug. 24, 2021, 3:55 p.m. UTC | #2
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;
>>  
>>  /* 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));
>>
> 
>
diff mbox series

Patch

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