| /* sframe-opt.c - optimize FRE and FDE information in SFrame. | 
 |    Copyright (C) 2022-2023 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; | 
 | } |