blob: ece75221262d95ea56bfe7ce780365d5443614bd [file] [log] [blame]
/* 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;
}