[V2 18/36] [SFrame-V3] gas: sframe: output new FDE type SFRAME_FDE_TYPE_FLEX

Indu Bhagat indu.bhagat@oracle.com
Sat Jan 10 19:00:23 GMT 2026


On 1/7/26 12:42 AM, Indu Bhagat wrote:
> This patch updates the routines for emission of the new FDE type
> SFRAME_FDE_TYPE_FLEX in the SFrame output section.  The support for
> generating these flex FDEs themselves is added in a subsequent commit.
> 
> For flex FDE type, output_sframe_row_entry_offsets () emits two
> offsets for RA tracking, irrespective of the ABI/arch, i.e.,
> irrespecitve of whether RA-tracking is enabled.  This is because, for
> flex FDE type, RA is allowed "non-standard" recovery rules,
> e.g., RA = REG or RA = *(REG+offset).  In a follow-up patch, when
> handling for .cfi_register RA, REG is added, emission code will be
> patched up accodingly.
> 
> TBD - gas/gen-sframe.c code can be improved ?
> 
> gas/
> 	* gen-sframe.c (get_udata_size_in_bytes): Get size of unsigned
> 	int data in bytes.
> 	(get_fre_num_offsets): Get number of offsets for
> 	SFRAME_FDE_TYPE_FLEX fde type as well (new argument).
> 	(output_sframe_row_entry_offsets): Output the SFrame FREs for
> 	two different cases (fde type SFRAME_FDE_TYPE_FLEX or the
> 	default).
> 	(output_sframe_row_entry): Update caller.
> 	(output_sframe_funcdesc): Emit func type in func_info2 field.
> 	(sframe_xlate_ctx_init): Initialize flex_p in translation
> 	context object.
> 	(sframe_xlate_ctx_finalize): Likewise.
> 	(sframe_row_entry_initialize): Copy over new data members too.
> 	* gen-sframe.h (struct sframe_row_entry): Add tracking for CFA
> 	dereferencing, frame pointer dereferencing.
> 	(struct sframe_func_entry): Add tracking for flexible FDE type.
> 	(struct sframe_xlate_ctx): Likewise.
> 
> ---
> [Changes in V1]
>   - Rename the FDE type to SFRAME_FDE_TYPE_FLEX. [Jens]
>   - Fix generation of RA offsets for flex FDE.  In RFC, we were emitting
>     two offsets for flex FDE, irrespctive of whether RA tracking location
>     was REG/STACK or NONE.  This is now changed to: emit RA padding offset
>   - Bring proper implementation in now and remove FIXME in
>     output_sframe_row_entry_offsets [Jens].
>   - gas: sframe: must reset and init ra_reg appropriately.
>   - Allow reg to be any sized in the offset.  In RFC, there was a limit
>     of 5-bits.  Now its lifted.  The calculation of the `size of offset`
>     is adjusted to account for the minimum necessary size, just like the
>     other offsets.
> [End of changes in V1]
> 
> [Changes in V2]
>    - Bring stub resetting ra_reg, ra_deref_p, fp_deref_p to the rightful
>      place (from previous commit) [Indu].
>    - Remove stale comments from output_sframe_row_entry_offsets () that
>      two offsets are emitted for RA irrespective of whether its tracked
>      or not [Indu].
>    - Other minor touch ups.
> [End of changes in V2]
> ---
>   gas/gen-sframe.c | 188 +++++++++++++++++++++++++++++++++++++++--------
>   gas/gen-sframe.h |  18 ++++-
>   2 files changed, 175 insertions(+), 31 deletions(-)
> 
> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
> index a21756c82b9..6a05492c939 100644
> --- a/gas/gen-sframe.c
> +++ b/gas/gen-sframe.c
> @@ -233,6 +233,22 @@ get_offset_size_in_bytes (offsetT value)
>     return size;
>   }
>   
> +/* Given an unsigned item, return the size in bytes needed to represent it.  */
> +
> +static unsigned int
> +get_udata_size_in_bytes (unsigned int value)
> +{
> +  unsigned int size = 0;
> +
> +  if (value <= VALUE_8BIT + 1)
> +    size = 1;
> +  else if (value <= VALUE_16BIT + 1)
> +    size = 2;
> +  else
> +    size = 4;
> +
> +  return size;
> +}
>   #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B  0 /* SFRAME_FRE_OFFSET_1B.  */
>   #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B  1 /* SFRAME_FRE_OFFSET_2B.  */
>   #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B  2 /* SFRAME_FRE_OFFSET_4B.  */
> @@ -380,19 +396,36 @@ get_fre_base_reg_id (const struct sframe_row_entry *sframe_fre)
>   /* Get number of offsets necessary for the SFrame Frame Row Entry.  */
>   
>   static unsigned int
> -get_fre_num_offsets (const struct sframe_row_entry *sframe_fre)
> +get_fre_num_offsets (const struct sframe_row_entry *sframe_fre,
> +		     bool flex_p)
>   {
> -  /* Atleast 1 must always be present (to recover CFA).  */
> -  unsigned int fre_num_offsets = 1;
> +  /* For SFRAME_FDE_TYPE_FLEX FDE type, each entity (CFA, FP, RA) may carry up
> +     to two offsets.  */
> +  unsigned int count = flex_p ? 2 : 1;
>   
> -  if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
> -    fre_num_offsets++;
> -  if (sframe_ra_tracking_p ()
> -      && (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK
> -	  /* For s390x account padding RA offset, if FP without RA saved.  */
> -	  || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
> -	      && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)))
> +  /* CFA offset (or offsets when flex_p) must always be present.  */
> +  unsigned int fre_num_offsets = count;
> +
> +  /* For flexible frames encoding, there will be two offsets for RA (if RA is
> +     being tracked).  1 padding offset otherwise.  */
> +  if (flex_p)
> +    {
> +     if (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE)
> +       fre_num_offsets += count;
> +     else if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)
> +       fre_num_offsets += 1;
> +    }
> +  else if (sframe_ra_tracking_p ()
> +	   && (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE
> +	       /* For s390x account padding RA offset, if FP without RA
> +		  saved.  */
> +	       || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
> +		   && sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)))
>       fre_num_offsets++;
> +
> +  if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)
> +    fre_num_offsets += count;
> +
>     return fre_num_offsets;
>   }
>   
> @@ -400,7 +433,8 @@ get_fre_num_offsets (const struct sframe_row_entry *sframe_fre)
>      SFrame frame row entry.  */
>   
>   static unsigned int
> -sframe_get_fre_offset_size (const struct sframe_row_entry *sframe_fre)
> +sframe_get_fre_offset_size (const struct sframe_row_entry *sframe_fre,
> +			    bool flex_p)
>   {
>     unsigned int max_offset_size = 0;
>     unsigned int cfa_offset_size = 0;
> @@ -430,6 +464,32 @@ sframe_get_fre_offset_size (const struct sframe_row_entry *sframe_fre)
>     if (ra_offset_size > max_offset_size)
>       max_offset_size = ra_offset_size;
>   
> +  /* If flex FDE, account for reg data too.  */
> +  if (flex_p)
> +    {
> +      bool reg_p = (sframe_fre->cfa_base_reg != SFRAME_FRE_REG_INVALID);
> +      unsigned int data
> +	= SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->cfa_base_reg,
> +					 sframe_fre->cfa_deref_p, reg_p);
> +      unsigned int data_size = get_udata_size_in_bytes (data);
> +      if (data_size > max_offset_size)
> +	max_offset_size = data_size;
> +
> +      reg_p = (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_REG);
> +      data = SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->fp_reg,
> +					    sframe_fre->fp_deref_p, reg_p);
> +      data_size = get_udata_size_in_bytes (data);
> +      if (data_size > max_offset_size)
> +	max_offset_size = data_size;
> +

The above 5 lines accounting for size of FP data need to be guarded with:

if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_REG)

Similarly for ra below.

> +      reg_p = (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_REG);
> +      data = SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->ra_reg,
> +					    sframe_fre->ra_deref_p, reg_p);
> +      data_size = get_udata_size_in_bytes (data);
> +      if (data_size > max_offset_size)
> +	max_offset_size = data_size;
> +    }
> +
>     gas_assert (max_offset_size);
>   
>     switch (max_offset_size)
> @@ -539,6 +599,7 @@ sframe_row_entry_new (void)
>        valid register for a supported arch.  */
>     fre->cfa_base_reg = SFRAME_FRE_REG_INVALID;
>     fre->fp_reg = SFRAME_FRE_REG_INVALID;
> +  fre->ra_reg = SFRAME_FRE_REG_INVALID;
>     fre->merge_candidate = true;
>     /* Reset the mangled RA status bit to zero by default.  We will
>        initialize it in sframe_row_entry_initialize () with the sticky
> @@ -588,39 +649,91 @@ sframe_fde_free (struct sframe_func_entry *sframe_fde)
>      size FRE_OFFSET_SIZE.  Write out the offsets in order - CFA, RA, FP.  */
>   
>   static unsigned int
> -output_sframe_row_entry_offsets (const struct sframe_func_entry *sframe_fde ATTRIBUTE_UNUSED,
> +output_sframe_row_entry_offsets (const struct sframe_func_entry *sframe_fde,
>   				 const struct sframe_row_entry *sframe_fre,
>   				 unsigned int fre_offset_size)
> -
>   {
>     unsigned int fre_write_offsets = 0;
>   
>     unsigned int idx = sframe_fre_offset_func_map_index (fre_offset_size);
>     gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
>   
> -  /* Write out the offsets in order - cfa, fp, ra.  */
> -  fre_offset_func_map[idx].out_func (sframe_fre->cfa_offset);
> -  fre_write_offsets++;
> -
> -  if (sframe_ra_tracking_p ())
> +  if (sframe_fde->fde_flex_p)
>       {
> -      if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +      /* SFrame FDE of type SFRAME_FDE_TYPE_FLEX.  */
> +      /* Output CFA related FRE offsets.  */
> +      uint32_t reg_data
> +	= SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->cfa_base_reg,
> +					 sframe_fre->cfa_deref_p,
> +					 1 /* reg_p.  */);
> +      offsetT offset_data = sframe_fre->cfa_offset;
> +      fre_offset_func_map[idx].out_func (reg_data);
> +      fre_offset_func_map[idx].out_func (offset_data);
> +      fre_write_offsets += 2;
> +
> +      bool reg_p = false;
> +      if (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE)
>   	{
> -	  fre_offset_func_map[idx].out_func (sframe_fre->ra_offset);
> -	  fre_write_offsets++;
> +	  /* Output RA related FRE offsets.  */
> +	  reg_p = sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_REG;
> +	  reg_data = SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->ra_reg,
> +						    sframe_fre->ra_deref_p,
> +						    reg_p);
> +	  offset_data = sframe_fre->ra_offset;
> +	  fre_offset_func_map[idx].out_func (reg_data);
> +	  fre_offset_func_map[idx].out_func (offset_data);
> +	  fre_write_offsets += 2;
>   	}
> -      /* For s390x write padding RA offset, if FP without RA saved.  */
> -      else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
> -	       && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +      else if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)
>   	{
> +	  /* If RA is not in REG/STACK, emit RA padding if there are more
> +	     offsets to follow.  Note that, emitting
> +	     SFRAME_FRE_RA_OFFSET_INVALID is equivalent to emitting
> +	     SFRAME_V3_FLEX_FDE_REG_ENCODE (0, 0, 0).  */
>   	  fre_offset_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID);
> -	  fre_write_offsets++;
> +	  fre_write_offsets += 1;
> +	}
> +
> +      if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)
> +	{
> +	  /* Output FP related FRE offsets.  */
> +	  reg_p = sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_REG;
> +	  reg_data = SFRAME_V3_FLEX_FDE_REG_ENCODE (sframe_fre->fp_reg,
> +						    sframe_fre->fp_deref_p,
> +						    reg_p);
> +	  offset_data = sframe_fre->fp_offset;
> +	  fre_offset_func_map[idx].out_func (reg_data);
> +	  fre_offset_func_map[idx].out_func (offset_data);
> +	  fre_write_offsets += 2;
>   	}
>       }
> -  if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +  else
>       {
> -      fre_offset_func_map[idx].out_func (sframe_fre->fp_offset);
> +      /* SFrame FDE of type SFRAME_FDE_TYPE_DEFAULT.  */
> +      /* Output CFA related FRE offsets.  */
> +      fre_offset_func_map[idx].out_func (sframe_fre->cfa_offset);
>         fre_write_offsets++;
> +
> +      if (sframe_ra_tracking_p ())
> +	{
> +	  if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +	    {
> +	      fre_offset_func_map[idx].out_func (sframe_fre->ra_offset);
> +	      fre_write_offsets++;
> +	    }
> +	  /* For s390x write padding RA offset, if FP without RA saved.  */
> +	  else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
> +		   && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +	    {
> +	      fre_offset_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID);
> +	      fre_write_offsets++;
> +	    }
> +	}
> +      if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
> +	{
> +	  fre_offset_func_map[idx].out_func (sframe_fre->fp_offset);
> +	  fre_write_offsets++;
> +	}
>       }
>   
>     return fre_write_offsets;
> @@ -677,8 +790,10 @@ output_sframe_row_entry (const struct sframe_func_entry *sframe_fde,
>     else
>       {
>         fre_base_reg = get_fre_base_reg_id (sframe_fre);
> -      fre_num_offsets = get_fre_num_offsets (sframe_fre);
> -      fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
> +      fre_num_offsets = get_fre_num_offsets (sframe_fre,
> +					     sframe_fde->fde_flex_p);
> +      fre_offset_size = sframe_get_fre_offset_size (sframe_fre,
> +						    sframe_fde->fde_flex_p);
>         fre_mangled_ra_p = sframe_fre->mangled_ra_p;
>       }
>     fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
> @@ -764,7 +879,12 @@ output_sframe_funcdesc (symbolS *start_of_fre_section,
>       }
>     else
>       out_one (func_info);
> -  out_one (0);
> +
> +  uint8_t finfo2 = 0;
> +  if (sframe_fde->fde_flex_p)
> +    finfo2 = SFRAME_V3_SET_FDE_TYPE (finfo2, SFRAME_FDE_TYPE_FLEX);
> +  out_one (finfo2);
> +
>     out_one (0);
>   }
>   
> @@ -948,6 +1068,7 @@ static void
>   sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx)
>   {
>     xlate_ctx->dw_fde = NULL;
> +  xlate_ctx->flex_p = false;
>     xlate_ctx->first_fre = NULL;
>     xlate_ctx->last_fre = NULL;
>     xlate_ctx->cur_fre = NULL;
> @@ -974,6 +1095,7 @@ sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx,
>   			   struct sframe_func_entry *sframe_fde)
>   {
>     sframe_fde->dw_fde = xlate_ctx->dw_fde;
> +  sframe_fde->fde_flex_p = xlate_ctx->flex_p;
>     sframe_fde->sframe_fres = xlate_ctx->first_fre;
>     sframe_fde->num_fres = xlate_ctx->num_xlate_fres;
>     /* remember_fre is cloned copy of the applicable fre (where necessary).
> @@ -1030,10 +1152,13 @@ sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
>     gas_assert (prev_fre);
>     cur_fre->cfa_base_reg = prev_fre->cfa_base_reg;
>     cur_fre->cfa_offset = prev_fre->cfa_offset;
> +  cur_fre->cfa_deref_p = prev_fre->cfa_deref_p;
>     cur_fre->fp_loc = prev_fre->fp_loc;
>     cur_fre->fp_reg = prev_fre->fp_reg;
>     cur_fre->fp_offset = prev_fre->fp_offset;
> +  cur_fre->fp_deref_p = prev_fre->fp_deref_p;
>     cur_fre->ra_loc = prev_fre->ra_loc;
> +  cur_fre->ra_reg = prev_fre->ra_reg;
>     cur_fre->ra_offset = prev_fre->ra_offset;
>     /* Treat RA mangling as a sticky bit.  It retains its value until another
>        .cfi_negate_ra_state is seen.  */
> @@ -1255,12 +1380,15 @@ sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx,
>       {
>         sframe_fre_set_fp_track (cur_fre, cfi_insn->u.ri.offset);
>         cur_fre->fp_reg = SFRAME_FRE_REG_INVALID;
> +      cur_fre->fp_deref_p = true;
>         cur_fre->merge_candidate = false;
>       }
>     else if (sframe_ra_tracking_p ()
>   	   && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG)
>       {
>         sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
> +      cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
> +      cur_fre->ra_deref_p = true;
>         cur_fre->merge_candidate = false;
>       }
>     /* This is used to track changes to non-rsp registers, skip all others
> diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
> index fe7f3961fc9..1a1f887d508 100644
> --- a/gas/gen-sframe.h
> +++ b/gas/gen-sframe.h
> @@ -79,17 +79,27 @@ struct sframe_row_entry
>     unsigned int cfa_base_reg;
>     /* Offset from the CFA base register for recovering CFA.  */
>     offsetT cfa_offset;
> +  /* Whether CFA recovery needs dereferencing.  This is tracked for
> +     SFRAME_FDE_TYPE_FLEX FDE type.  */
> +  bool cfa_deref_p;
>   
>     /* Track FP location.  Specify whether it is in register or memory.  */
> -  unsigned int fp_reg;
>     unsigned int fp_loc;
> +  unsigned int fp_reg;
>     /* If the FP is stashed on stack, note the offset.  */
>     offsetT fp_offset;
> +  /* Whether FP recovery needs dereferencing.  This is tracked for
> +     SFRAME_FDE_TYPE_FLEX FDE type.  */
> +  bool fp_deref_p;
>   
>     /* Track RA location.  Specify whether it is in register or memory.  */
>     unsigned int ra_loc;
> +  unsigned int ra_reg;
>     /* If RA is stashed on stack, note the offset.  */
>     offsetT ra_offset;
> +  /* Whether RA recovery needs dereferencing.  This is tracked for FDE type
> +     SFRAME_FDE_TYPE_FLEX.  */
> +  bool ra_deref_p;
>   };
>   
>   /* SFrame Function Description Entry.  */
> @@ -103,6 +113,8 @@ struct sframe_func_entry
>        like the start_address and the segment is made available via this
>        member.  */
>     const struct fde_entry *dw_fde;
> +  /* Whether the current FDE will use SFRAME_FDE_TYPE_FLEX representation.  */
> +  bool fde_flex_p;
>   
>     /* Reference to the first FRE for this function.  */
>     struct sframe_row_entry *sframe_fres;
> @@ -133,6 +145,10 @@ struct sframe_xlate_ctx
>     /* Remember FRE for an eventual restore.  */
>     struct sframe_row_entry *remember_fre;
>   
> +  /* Whether the current FRE requires a more flexible frame encoding, hence
> +     needing SFRAME_FDE_TYPE_FLEX FDE type.  */
> +  bool flex_p;
> +
>     unsigned num_xlate_fres;
>   };
>   



More information about the Binutils mailing list