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