/* 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; }