[V2 26/36] [SFrame-V3] gas: sframe: testsuite: handle .cfi_offset for RA
Indu Bhagat
indu.bhagat@oracle.com
Thu Jan 8 08:11:24 GMT 2026
On 1/7/26 7:20 AM, Jens Remus wrote:
> On 1/7/2026 9:42 AM, Indu Bhagat wrote:
>> With the introduction of flex FDE type, handling .cfi_offset for
>> RA needs adjustment.
>>
>> On architectures like x86_64, the return address (RA) is typically saved
>> at a fixed offset from the CFA. Previous versions of the SFrame format
>> assumed this fixed offset was invariant for the entire function on such
>> architectures. Consequently, GAS would warn and suppress SFrame
>> generation if it encountered a .cfi_offset directive for the RA
>> register that deviated from this fixed default.
>>
>> SFrame V3 introduces "Flex FDEs" which allows tracking the RA location
>> explicitly even on architectures where it is usually fixed.
>>
>> This patch updates sframe_xlate_do_offset () to leverage Flex FDEs. When
>> processing a .cfi_offset for the RA register:
>> - The check for non-representable RA offsets is relaxed. If the ABI
>> supports Flex FDEs (SFrame V3), GAS proceeds instead of issuing a
>> warning.
>> - For ABIs without explicit RA tracking (like AMD64), if the RA
>> offset differs from the default fixed offset, the FDE is marked as a
>> Flex FDE, and the new stack location is recorded.
>> - Logic is added to detect when the RA is restored to its standard
>> fixed offset. In this case, the tracking state is reset (ra_loc set
>> to SFRAME_FRE_ELEM_LOC_NONE), deferring to the ABI's default fixed RA
>> offset behavior.
>>
>> gas/
>> * gen-sframe.c (sframe_xlate_do_offset): Support .cfi_offset for RA
>> by switching to Flex FDEs when necessary.
>> gas/testsuite/
>> * gas/cfi-sframe/cfi-sframe-x86_64-6.d: New test.
>> * gas/cfi-sframe/cfi-sframe-x86_64-6.s: New test to check
>> transition of location of REG_RA from register to CFA-8 (default
>> location on AMD64). Flex FDE in effect.
>> * gas/cfi-sframe/cfi-sframe.exp: Add new test.
>
> Reviewed-by: Jens Remus <jremus@linux.ibm.com>
>
>> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
>
>> @@ -1390,8 +1390,10 @@ sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx,
>>
>> /* For ABIs not tracking RA, the return address is expected to be in a
>> specific location. Explicit manourvering to a different offset (than the
>> - default offset) is non-representable in SFrame. */
>> - if (!sframe_ra_tracking_p () && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG
>> + default offset) is non-representable in SFrame, unless flex FDE generation
>> + is supported for the ABI. */
>> + if (!sframe_support_flex_fde_p () && !sframe_ra_tracking_p ()
>> + && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG
>> && cfi_insn->u.ri.offset != sframe_cfa_ra_offset ())
>> {
>> as_warn (_("no SFrame FDE emitted; %s register %u in .cfi_offset"),
>> @@ -1409,13 +1411,42 @@ sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx,
>> 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)
>> + /* Either the ABI has enabled RA tracking, in which case we must process the
>> + DW_CFA_offset opcode for REG_RA like usual. Or if the ABI has not enabled
>> + RA tracking, but flex FDE generation is supported, dintinguish between
>
> Nit: s/dintinguish/distinguish/
>
>> + whether its time to reset the ra tracking state or not. */
>
> Nit: s/ra/RA/
>
>> + else if (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;
>> + if (!sframe_ra_tracking_p ())
>> + {
>> + /* RA is restored to its standard fixed offset. */
>
> Maybe: s/RA/RA tracking info/
>
> The comment is kind of confusing as what it states is not performed
> unconditionally. Maybe change as follows:
>
> /* Reset RA tracking info, if fixed offset. */
>
>> + if (cfi_insn->u.ri.offset == sframe_cfa_ra_offset ())
>> + {
>> + cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
>> + cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_NONE;
>> + cur_fre->ra_deref_p = false;
>> + cur_fre->merge_candidate = false;
>> + }
>> + /* If flex FDE is supported, update the ra tracking info. */
>
> Nit: s/ra/RA/
>
>> + else if (sframe_support_flex_fde_p ())
>> + {
>> + sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
>> + cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
>> + cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
>> + cur_fre->ra_deref_p = true;
>> + cur_fre->merge_candidate = false;
>> +
>> + xlate_ctx->flex_p = true;
>> + }
>> + }
>> + else if (sframe_ra_tracking_p ())
>
> Can't this be simplified to:
>
> else
> or
> else /* sframe_ra_tracking_p () */
>
Yes, this is doable.
> If the sframe_ra_tracking_p () case is handled first then the else
> would be close to the if condition:
>
> if (!sframe_ra_tracking_p ())
> {
> sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
> cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
> cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
> cur_fre->ra_deref_p = true;
> cur_fre->merge_candidate = false;
> }
> else
> {
> ...
> }
>
I dont understand this part though.
>> + {
>> + sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
>> + cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
>> + cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
>
> If I am not mistaken this was not part of the old implementation. Why
> is that required?
>
Correct. In the RFC implmentation, we were not handling a few scenarios:
Case 1:
- RA tracking not enabled, e.g., AMD64, which uses fixed RA offset
- RA offset != fixed RA offset shows up
=> we should switch to flex FDE and record the RA offset
Case 2:
- RA tracking not enabled, e.g., AMD64, which uses fixed RA offset
- flex FDE in effect
- RA offset = fixed RA offset shows up
=> we should record that RA tracking info is now the same as the default
ra_reg = SFRAME_FRE_REG_INVALID;
ra_loc = SFRAME_FRE_ELEM_LOC_NONE;
ra_deref_p = false;
By doing so, we will emit no more offsets after CFA offsets when the
RA recovery is the default rule and FP is not tracked either.
See testcase gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-6.d for
testing this "transition" of RA offset for AMD64.
(PS: I have made thinkos here, I appreciate you checking.)
>> + cur_fre->ra_deref_p = true;
>> + cur_fre->merge_candidate = false;
>> + }
>> }
>> /* This is used to track changes to non-rsp registers, skip all others
>
> Unrelated to this patch: What are non-rsp registers?
>
I think this comment meant to say that all others except FP/RA are OK to
skip (with a previous comment saying "Ignore SP reg, as it can be
recovered from the CFA tracking info."). For REG_SP, as you indicated
in review on another patch, I will later address .cfi_offset REG_SP,XX
for non-s390x in a separate patch.
As for the comment above, I think we should remove it because it is
confusing:
- /* This is used to track changes to non-rsp registers, skip all others
- except FP / RA for now. */
+
return SFRAME_XLATE_OK;
>> except FP / RA for now. */
> Maybe the SFRAME_CFA_RA_REG case can be simplified as follows:
>
> else if (cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG)
> {
> if (!sframe_ra_tracking_p ()
> && cfi_insn->u.ri.offset == sframe_cfa_ra_offset ())
> {
> /* Reset RA tracking info to fixed offset. */
> cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
> cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_NONE;
> cur_fre->ra_deref_p = false;
> cur_fre->merge_candidate = false;
> }
> else
> {
> sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
> cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
> cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
> cur_fre->ra_deref_p = true;
> cur_fre->merge_candidate = false;
>
> if (!sframe_ra_tracking_p () && sframe_support_flex_fde_p ())
> xlate_ctx->flex_p = true;
> }
> }
>
OK.
> The following common statements could be moved from the inner to the
> outer clause. I left them duplicated as I found that more readable.
>
> cur_fre->ra_reg = SFRAME_FRE_REG_INVALID;
> cur_fre->merge_candidate = false;
>
Yes, I prefer the duplicated version too for now.
More information about the Binutils
mailing list