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