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