|  | /* sframe-opt.c - optimize FRE and FDE information in SFrame. | 
|  | Copyright (C) 2022-2024 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GAS, the GNU Assembler. | 
|  |  | 
|  | GAS is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | GAS is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with GAS; see the file COPYING.  If not, write to the Free | 
|  | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA | 
|  | 02110-1301, USA.  */ | 
|  |  | 
|  | #include "as.h" | 
|  | #include "sframe.h" | 
|  |  | 
|  | /* The function estimates the size of a rs_sframe variant frag based on | 
|  | the current values of the symbols.  It is called before the | 
|  | relaxation loop.  We set fr_subtype{0:2} to the expected length.  */ | 
|  |  | 
|  | int | 
|  | sframe_estimate_size_before_relax (fragS *frag) | 
|  | { | 
|  | offsetT width; | 
|  | expressionS *exp; | 
|  | symbolS *widthS; | 
|  | int ret; | 
|  |  | 
|  | /* We are dealing with two different kind of fragments here which need | 
|  | to be fixed up: | 
|  | - first, FRE start address in each FRE, and | 
|  | - second, Function info in each FDE (function info stores the FRE type) | 
|  | The two kind of fragments can be differentiated based on the opcode | 
|  | of the symbol.  */ | 
|  | exp = symbol_get_value_expression (frag->fr_symbol); | 
|  | gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent)); | 
|  | /* Fragment for function info in an SFrame FDE will always write | 
|  | only one byte.  */ | 
|  | if (exp->X_op == O_modulus) | 
|  | ret = 1; | 
|  | /* Fragment for the start address in an SFrame FRE may write out | 
|  | 1/2/4 bytes depending on the value of the diff.  */ | 
|  | else | 
|  | { | 
|  | /* Get the width expression from the symbol.  */ | 
|  | widthS = exp->X_op_symbol; | 
|  | width = resolve_symbol_value (widthS); | 
|  |  | 
|  | if (width < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT) | 
|  | ret = 1; | 
|  | else if (width < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT) | 
|  | ret = 2; | 
|  | else | 
|  | ret = 4; | 
|  | } | 
|  |  | 
|  | frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* This function relaxes a rs_sframe variant frag based on the current | 
|  | values of the symbols.  fr_subtype{0:2} is the current length of | 
|  | the frag.  This returns the change in frag length.  */ | 
|  |  | 
|  | int | 
|  | sframe_relax_frag (fragS *frag) | 
|  | { | 
|  | int oldsize, newsize; | 
|  |  | 
|  | oldsize = frag->fr_subtype & 7; | 
|  | if (oldsize == 7) | 
|  | oldsize = -1; | 
|  | newsize = sframe_estimate_size_before_relax (frag); | 
|  | return newsize - oldsize; | 
|  | } | 
|  |  | 
|  | /* This function converts a rs_sframe variant frag into a normal fill | 
|  | frag.  This is called after all relaxation has been done. | 
|  | fr_subtype{0:2} will be the desired length of the frag.  */ | 
|  |  | 
|  | void | 
|  | sframe_convert_frag (fragS *frag) | 
|  | { | 
|  | offsetT fsize; | 
|  | offsetT diff; | 
|  | offsetT value; | 
|  |  | 
|  | offsetT rest_of_data; | 
|  | uint8_t fde_type, fre_type; | 
|  | uint8_t pauth_key; | 
|  |  | 
|  | expressionS *exp; | 
|  | symbolS *dataS; | 
|  | symbolS *fsizeS, *diffS; | 
|  |  | 
|  | /* We are dealing with two different kind of fragments here which need | 
|  | to be fixed up: | 
|  | - first, FRE start address in each FRE, and | 
|  | - second, Function info in each FDE (function info stores the FRE type) | 
|  | The two kind of fragments can be differentiated based on the opcode | 
|  | of the symbol.  */ | 
|  | exp = symbol_get_value_expression (frag->fr_symbol); | 
|  | gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent)); | 
|  | /* Fragment for function info in an SFrame FDE.  */ | 
|  | if (exp->X_op == O_modulus) | 
|  | { | 
|  | /* Gather the existing value of the rest of the data except | 
|  | the fre_type.  */ | 
|  | dataS = exp->X_add_symbol; | 
|  | rest_of_data = (symbol_get_value_expression(dataS))->X_add_number; | 
|  | fde_type = SFRAME_V1_FUNC_FDE_TYPE (rest_of_data); | 
|  | pauth_key = SFRAME_V1_FUNC_PAUTH_KEY (rest_of_data); | 
|  | gas_assert (fde_type == SFRAME_FDE_TYPE_PCINC); | 
|  |  | 
|  | /* Calculate the applicable fre_type.  */ | 
|  | fsizeS = exp->X_op_symbol; | 
|  | fsize = resolve_symbol_value (fsizeS); | 
|  | if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT) | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR1; | 
|  | else if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT) | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR2; | 
|  | else | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR4; | 
|  |  | 
|  | /* Create the new function info.  */ | 
|  | value = SFRAME_V1_FUNC_INFO (fde_type, fre_type); | 
|  | value = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, value); | 
|  |  | 
|  | frag->fr_literal[frag->fr_fix] = value; | 
|  | } | 
|  | /* Fragment for the start address in an SFrame FRE.  */ | 
|  | else | 
|  | { | 
|  | /* Get the fsize expression from the symbol.  */ | 
|  | fsizeS = exp->X_op_symbol; | 
|  | fsize = resolve_symbol_value (fsizeS); | 
|  | /* Get the diff expression from the symbol.  */ | 
|  | diffS= exp->X_add_symbol; | 
|  | diff = resolve_symbol_value (diffS); | 
|  | value = diff; | 
|  |  | 
|  | switch (frag->fr_subtype & 7) | 
|  | { | 
|  | case 1: | 
|  | gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT); | 
|  | frag->fr_literal[frag->fr_fix] = diff; | 
|  | break; | 
|  | case 2: | 
|  | gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT); | 
|  | md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2); | 
|  | break; | 
|  | case 4: | 
|  | md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4); | 
|  | break; | 
|  | default: | 
|  | abort (); | 
|  | } | 
|  | } | 
|  |  | 
|  | frag->fr_fix += frag->fr_subtype & 7; | 
|  | frag->fr_type = rs_fill; | 
|  | frag->fr_subtype = 0; | 
|  | frag->fr_offset = 0; | 
|  | /* FIXME do this now because we have evaluated and fixed up the fragments | 
|  | manually ?  */ | 
|  | frag->fr_symbol = 0; | 
|  | } |