[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