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

Indu Bhagat indu.bhagat@oracle.com
Tue Jan 13 11:12:22 GMT 2026


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.

Update struct sframe_row_entry to track additional state for CFA,
FP, and RA.  Modify output_sframe_row_entry_offsets () to emit
metadata/offset pairs for flexible FDEs or padding where applicable,
ensuring the usual ordering (CFA, RA, FP).  The padding data, a.k.a.
SFRAME_FRE_RA_OFFSET_INVALID is emitted in flexible FDEs when RA is
untracked but FP offsets follow.  Trailing padding offsets should not
occur.  Add a new function get_udata_size_in_bytes () to account for
sizing of unsigned register metadata.

gas/
	* gen-sframe.c (get_udata_size_in_bytes): Get size of unsigned
	int data in bytes.
	(get_fre_num_offsets): Handle SFRAME_FDE_TYPE_FLEX.
	(sframe_get_fre_offset_size): Account for register metadata
	in flexible FDEs.
	(output_sframe_row_entry_offsets): Add logic for flexible
	FDE offset pairs.
	(output_sframe_row_entry): Reset base register for flex FDEs.
	(output_sframe_funcdesc): Emit FDE type in func_info2.
	(sframe_xlate_ctx_init): Initialize flex_p.
	(sframe_xlate_ctx_finalize): Finalize flex_p status.
	(sframe_row_entry_initialize): Copy new deref tracking bits.
	(sframe_xlate_do_offset): Set deref bits during translation.
	* gen-sframe.h (struct sframe_row_entry): Add cfa_deref_p,
	fp_deref_p, and ra_deref_p.
	(struct sframe_func_entry): Add fde_flex_p.
	(struct sframe_xlate_ctx): Add flex_p.

---
[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]

[Changes in V3]
  - Use standard min/max constants [Jens]
  - In sframe_get_fre_offset_size(), do not check fp_reg_data/fp_offset
    if FP recovery rule was never saved.  Similar handling for RA.  This
    fixes a issue causing a bit more bytes in output section than
    necessary [Indu].
  - Update commit log [Jens].
[End of changes in V3]
---
 gas/gen-sframe.c | 199 ++++++++++++++++++++++++++++++++++++++++-------
 gas/gen-sframe.h |  18 ++++-
 2 files changed, 186 insertions(+), 31 deletions(-)

diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 6511ae08ed1..cc58655be9d 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -223,6 +223,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 <= UINT8_MAX)
+    size = 1;
+  else if (value <= UINT16_MAX)
+    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.  */
@@ -370,19 +386,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;
 }
 
@@ -390,7 +423,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;
@@ -420,6 +454,38 @@ 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;
+
+      if (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE)
+	{
+	  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;
+	}
+
+      if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)
+	{
+	  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;
+	}
+    }
+
   gas_assert (max_offset_size);
 
   switch (max_offset_size)
@@ -529,6 +595,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
@@ -578,39 +645,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;
@@ -667,10 +786,17 @@ 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;
     }
+
+  /* Unused for flex FDE.  Set to zero.  */
+  if (sframe_fde->fde_flex_p)
+    fre_base_reg = SFRAME_BASE_REG_FP;
+
   fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
 				  fre_offset_size, fre_mangled_ra_p);
   out_one (fre_info);
@@ -754,7 +880,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);
 }
 
@@ -938,6 +1069,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;
@@ -964,6 +1096,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).
@@ -1020,10 +1153,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.  */
@@ -1245,12 +1381,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;
 };
 
-- 
2.43.0



More information about the Binutils mailing list