xref: /netbsd-src/external/gpl3/binutils/dist/gas/sframe-opt.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1*cb63e24eSchristos /* sframe-opt.c - optimize FRE and FDE information in SFrame.
2*cb63e24eSchristos    Copyright (C) 2022-2024 Free Software Foundation, Inc.
3*cb63e24eSchristos 
4*cb63e24eSchristos    This file is part of GAS, the GNU Assembler.
5*cb63e24eSchristos 
6*cb63e24eSchristos    GAS is free software; you can redistribute it and/or modify
7*cb63e24eSchristos    it under the terms of the GNU General Public License as published by
8*cb63e24eSchristos    the Free Software Foundation; either version 3, or (at your option)
9*cb63e24eSchristos    any later version.
10*cb63e24eSchristos 
11*cb63e24eSchristos    GAS is distributed in the hope that it will be useful,
12*cb63e24eSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*cb63e24eSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*cb63e24eSchristos    GNU General Public License for more details.
15*cb63e24eSchristos 
16*cb63e24eSchristos    You should have received a copy of the GNU General Public License
17*cb63e24eSchristos    along with GAS; see the file COPYING.  If not, write to the Free
18*cb63e24eSchristos    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
19*cb63e24eSchristos    02110-1301, USA.  */
20*cb63e24eSchristos 
21*cb63e24eSchristos #include "as.h"
22*cb63e24eSchristos #include "sframe.h"
23*cb63e24eSchristos 
24*cb63e24eSchristos /* The function estimates the size of a rs_sframe variant frag based on
25*cb63e24eSchristos    the current values of the symbols.  It is called before the
26*cb63e24eSchristos    relaxation loop.  We set fr_subtype{0:2} to the expected length.  */
27*cb63e24eSchristos 
28*cb63e24eSchristos int
sframe_estimate_size_before_relax(fragS * frag)29*cb63e24eSchristos sframe_estimate_size_before_relax (fragS *frag)
30*cb63e24eSchristos {
31*cb63e24eSchristos   offsetT width;
32*cb63e24eSchristos   expressionS *exp;
33*cb63e24eSchristos   symbolS *widthS;
34*cb63e24eSchristos   int ret;
35*cb63e24eSchristos 
36*cb63e24eSchristos   /* We are dealing with two different kind of fragments here which need
37*cb63e24eSchristos      to be fixed up:
38*cb63e24eSchristos        - first, FRE start address in each FRE, and
39*cb63e24eSchristos        - second, Function info in each FDE (function info stores the FRE type)
40*cb63e24eSchristos      The two kind of fragments can be differentiated based on the opcode
41*cb63e24eSchristos      of the symbol.  */
42*cb63e24eSchristos   exp = symbol_get_value_expression (frag->fr_symbol);
43*cb63e24eSchristos   gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent));
44*cb63e24eSchristos   /* Fragment for function info in an SFrame FDE will always write
45*cb63e24eSchristos      only one byte.  */
46*cb63e24eSchristos   if (exp->X_op == O_modulus)
47*cb63e24eSchristos     ret = 1;
48*cb63e24eSchristos   /* Fragment for the start address in an SFrame FRE may write out
49*cb63e24eSchristos      1/2/4 bytes depending on the value of the diff.  */
50*cb63e24eSchristos   else
51*cb63e24eSchristos     {
52*cb63e24eSchristos       /* Get the width expression from the symbol.  */
53*cb63e24eSchristos       widthS = exp->X_op_symbol;
54*cb63e24eSchristos       width = resolve_symbol_value (widthS);
55*cb63e24eSchristos 
56*cb63e24eSchristos       if (width < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT)
57*cb63e24eSchristos 	ret = 1;
58*cb63e24eSchristos       else if (width < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT)
59*cb63e24eSchristos 	ret = 2;
60*cb63e24eSchristos       else
61*cb63e24eSchristos 	ret = 4;
62*cb63e24eSchristos     }
63*cb63e24eSchristos 
64*cb63e24eSchristos   frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
65*cb63e24eSchristos 
66*cb63e24eSchristos   return ret;
67*cb63e24eSchristos }
68*cb63e24eSchristos 
69*cb63e24eSchristos /* This function relaxes a rs_sframe variant frag based on the current
70*cb63e24eSchristos    values of the symbols.  fr_subtype{0:2} is the current length of
71*cb63e24eSchristos    the frag.  This returns the change in frag length.  */
72*cb63e24eSchristos 
73*cb63e24eSchristos int
sframe_relax_frag(fragS * frag)74*cb63e24eSchristos sframe_relax_frag (fragS *frag)
75*cb63e24eSchristos {
76*cb63e24eSchristos   int oldsize, newsize;
77*cb63e24eSchristos 
78*cb63e24eSchristos   oldsize = frag->fr_subtype & 7;
79*cb63e24eSchristos   if (oldsize == 7)
80*cb63e24eSchristos     oldsize = -1;
81*cb63e24eSchristos   newsize = sframe_estimate_size_before_relax (frag);
82*cb63e24eSchristos   return newsize - oldsize;
83*cb63e24eSchristos }
84*cb63e24eSchristos 
85*cb63e24eSchristos /* This function converts a rs_sframe variant frag into a normal fill
86*cb63e24eSchristos    frag.  This is called after all relaxation has been done.
87*cb63e24eSchristos    fr_subtype{0:2} will be the desired length of the frag.  */
88*cb63e24eSchristos 
89*cb63e24eSchristos void
sframe_convert_frag(fragS * frag)90*cb63e24eSchristos sframe_convert_frag (fragS *frag)
91*cb63e24eSchristos {
92*cb63e24eSchristos   offsetT fsize;
93*cb63e24eSchristos   offsetT diff;
94*cb63e24eSchristos   offsetT value;
95*cb63e24eSchristos 
96*cb63e24eSchristos   offsetT rest_of_data;
97*cb63e24eSchristos   uint8_t fde_type, fre_type;
98*cb63e24eSchristos   uint8_t pauth_key;
99*cb63e24eSchristos 
100*cb63e24eSchristos   expressionS *exp;
101*cb63e24eSchristos   symbolS *dataS;
102*cb63e24eSchristos   symbolS *fsizeS, *diffS;
103*cb63e24eSchristos 
104*cb63e24eSchristos   /* We are dealing with two different kind of fragments here which need
105*cb63e24eSchristos      to be fixed up:
106*cb63e24eSchristos        - first, FRE start address in each FRE, and
107*cb63e24eSchristos        - second, Function info in each FDE (function info stores the FRE type)
108*cb63e24eSchristos      The two kind of fragments can be differentiated based on the opcode
109*cb63e24eSchristos      of the symbol.  */
110*cb63e24eSchristos   exp = symbol_get_value_expression (frag->fr_symbol);
111*cb63e24eSchristos   gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent));
112*cb63e24eSchristos   /* Fragment for function info in an SFrame FDE.  */
113*cb63e24eSchristos   if (exp->X_op == O_modulus)
114*cb63e24eSchristos     {
115*cb63e24eSchristos       /* Gather the existing value of the rest of the data except
116*cb63e24eSchristos 	 the fre_type.  */
117*cb63e24eSchristos       dataS = exp->X_add_symbol;
118*cb63e24eSchristos       rest_of_data = (symbol_get_value_expression(dataS))->X_add_number;
119*cb63e24eSchristos       fde_type = SFRAME_V1_FUNC_FDE_TYPE (rest_of_data);
120*cb63e24eSchristos       pauth_key = SFRAME_V1_FUNC_PAUTH_KEY (rest_of_data);
121*cb63e24eSchristos       gas_assert (fde_type == SFRAME_FDE_TYPE_PCINC);
122*cb63e24eSchristos 
123*cb63e24eSchristos       /* Calculate the applicable fre_type.  */
124*cb63e24eSchristos       fsizeS = exp->X_op_symbol;
125*cb63e24eSchristos       fsize = resolve_symbol_value (fsizeS);
126*cb63e24eSchristos       if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT)
127*cb63e24eSchristos 	fre_type = SFRAME_FRE_TYPE_ADDR1;
128*cb63e24eSchristos       else if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT)
129*cb63e24eSchristos 	fre_type = SFRAME_FRE_TYPE_ADDR2;
130*cb63e24eSchristos       else
131*cb63e24eSchristos 	fre_type = SFRAME_FRE_TYPE_ADDR4;
132*cb63e24eSchristos 
133*cb63e24eSchristos       /* Create the new function info.  */
134*cb63e24eSchristos       value = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
135*cb63e24eSchristos       value = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, value);
136*cb63e24eSchristos 
137*cb63e24eSchristos       frag->fr_literal[frag->fr_fix] = value;
138*cb63e24eSchristos     }
139*cb63e24eSchristos   /* Fragment for the start address in an SFrame FRE.  */
140*cb63e24eSchristos   else
141*cb63e24eSchristos     {
142*cb63e24eSchristos       /* Get the fsize expression from the symbol.  */
143*cb63e24eSchristos       fsizeS = exp->X_op_symbol;
144*cb63e24eSchristos       fsize = resolve_symbol_value (fsizeS);
145*cb63e24eSchristos       /* Get the diff expression from the symbol.  */
146*cb63e24eSchristos       diffS= exp->X_add_symbol;
147*cb63e24eSchristos       diff = resolve_symbol_value (diffS);
148*cb63e24eSchristos       value = diff;
149*cb63e24eSchristos 
150*cb63e24eSchristos       switch (frag->fr_subtype & 7)
151*cb63e24eSchristos 	{
152*cb63e24eSchristos 	case 1:
153*cb63e24eSchristos 	  gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT);
154*cb63e24eSchristos 	  frag->fr_literal[frag->fr_fix] = diff;
155*cb63e24eSchristos 	  break;
156*cb63e24eSchristos 	case 2:
157*cb63e24eSchristos 	  gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT);
158*cb63e24eSchristos 	  md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
159*cb63e24eSchristos 	  break;
160*cb63e24eSchristos 	case 4:
161*cb63e24eSchristos 	  md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
162*cb63e24eSchristos 	  break;
163*cb63e24eSchristos 	default:
164*cb63e24eSchristos 	  abort ();
165*cb63e24eSchristos 	}
166*cb63e24eSchristos     }
167*cb63e24eSchristos 
168*cb63e24eSchristos   frag->fr_fix += frag->fr_subtype & 7;
169*cb63e24eSchristos   frag->fr_type = rs_fill;
170*cb63e24eSchristos   frag->fr_subtype = 0;
171*cb63e24eSchristos   frag->fr_offset = 0;
172*cb63e24eSchristos   /* FIXME do this now because we have evaluated and fixed up the fragments
173*cb63e24eSchristos      manually ?  */
174*cb63e24eSchristos   frag->fr_symbol = 0;
175*cb63e24eSchristos }
176