/src/binutils-gdb/gas/sframe-opt.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* sframe-opt.c - optimize FRE and FDE information in SFrame. |
2 | | Copyright (C) 2022-2025 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 | | /* The function estimates the size of a rs_sframe variant frag based on |
25 | | the current values of the symbols. It is called before the |
26 | | relaxation loop. We set fr_subtype{0:2} to the expected length. */ |
27 | | |
28 | | int |
29 | | sframe_estimate_size_before_relax (fragS *frag) |
30 | 0 | { |
31 | 0 | offsetT width; |
32 | 0 | expressionS *exp; |
33 | 0 | symbolS *widthS; |
34 | 0 | int ret; |
35 | | |
36 | | /* We are dealing with two different kind of fragments here which need |
37 | | to be fixed up: |
38 | | - first, FRE start address in each FRE, and |
39 | | - second, Function info in each FDE (function info stores the FRE type) |
40 | | The two kind of fragments can be differentiated based on the opcode |
41 | | of the symbol. */ |
42 | 0 | exp = symbol_get_value_expression (frag->fr_symbol); |
43 | 0 | gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent)); |
44 | | /* Fragment for function info in an SFrame FDE will always write |
45 | | only one byte. */ |
46 | 0 | if (exp->X_op == O_modulus) |
47 | 0 | ret = 1; |
48 | | /* Fragment for the start address in an SFrame FRE may write out |
49 | | 1/2/4 bytes depending on the value of the diff. */ |
50 | 0 | else |
51 | 0 | { |
52 | | /* Get the width expression from the symbol. */ |
53 | 0 | widthS = exp->X_op_symbol; |
54 | 0 | width = resolve_symbol_value (widthS); |
55 | |
|
56 | 0 | if (width < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT) |
57 | 0 | ret = 1; |
58 | 0 | else if (width < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT) |
59 | 0 | ret = 2; |
60 | 0 | else |
61 | 0 | ret = 4; |
62 | 0 | } |
63 | |
|
64 | 0 | frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7); |
65 | |
|
66 | 0 | return ret; |
67 | 0 | } |
68 | | |
69 | | /* This function relaxes a rs_sframe variant frag based on the current |
70 | | values of the symbols. fr_subtype{0:2} is the current length of |
71 | | the frag. This returns the change in frag length. */ |
72 | | |
73 | | int |
74 | | sframe_relax_frag (fragS *frag) |
75 | 0 | { |
76 | 0 | int oldsize, newsize; |
77 | |
|
78 | 0 | oldsize = frag->fr_subtype & 7; |
79 | 0 | if (oldsize == 7) |
80 | 0 | oldsize = -1; |
81 | 0 | newsize = sframe_estimate_size_before_relax (frag); |
82 | 0 | return newsize - oldsize; |
83 | 0 | } |
84 | | |
85 | | /* This function converts a rs_sframe variant frag into a normal fill |
86 | | frag. This is called after all relaxation has been done. |
87 | | fr_subtype{0:2} will be the desired length of the frag. */ |
88 | | |
89 | | void |
90 | | sframe_convert_frag (fragS *frag) |
91 | 0 | { |
92 | 0 | offsetT fsize; |
93 | 0 | offsetT diff; |
94 | 0 | offsetT value; |
95 | |
|
96 | 0 | offsetT rest_of_data; |
97 | 0 | uint8_t fde_type, fre_type; |
98 | 0 | uint8_t pauth_key; |
99 | |
|
100 | 0 | expressionS *exp; |
101 | 0 | symbolS *dataS; |
102 | 0 | symbolS *fsizeS, *diffS; |
103 | | |
104 | | /* We are dealing with two different kind of fragments here which need |
105 | | to be fixed up: |
106 | | - first, FRE start address in each FRE, and |
107 | | - second, Function info in each FDE (function info stores the FRE type) |
108 | | The two kind of fragments can be differentiated based on the opcode |
109 | | of the symbol. */ |
110 | 0 | exp = symbol_get_value_expression (frag->fr_symbol); |
111 | 0 | gas_assert ((exp->X_op == O_modulus) || (exp->X_op == O_absent)); |
112 | | /* Fragment for function info in an SFrame FDE. */ |
113 | 0 | if (exp->X_op == O_modulus) |
114 | 0 | { |
115 | | /* Gather the existing value of the rest of the data except |
116 | | the fre_type. */ |
117 | 0 | dataS = exp->X_add_symbol; |
118 | 0 | rest_of_data = (symbol_get_value_expression(dataS))->X_add_number; |
119 | 0 | fde_type = SFRAME_V1_FUNC_FDE_TYPE (rest_of_data); |
120 | 0 | pauth_key = SFRAME_V1_FUNC_PAUTH_KEY (rest_of_data); |
121 | 0 | gas_assert (fde_type == SFRAME_FDE_TYPE_PCINC); |
122 | | |
123 | | /* Calculate the applicable fre_type. */ |
124 | 0 | fsizeS = exp->X_op_symbol; |
125 | 0 | fsize = resolve_symbol_value (fsizeS); |
126 | 0 | if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT) |
127 | 0 | fre_type = SFRAME_FRE_TYPE_ADDR1; |
128 | 0 | else if (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT) |
129 | 0 | fre_type = SFRAME_FRE_TYPE_ADDR2; |
130 | 0 | else |
131 | 0 | fre_type = SFRAME_FRE_TYPE_ADDR4; |
132 | | |
133 | | /* Create the new function info. */ |
134 | 0 | value = SFRAME_V1_FUNC_INFO (fde_type, fre_type); |
135 | 0 | value = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, value); |
136 | |
|
137 | 0 | frag->fr_literal[frag->fr_fix] = value; |
138 | 0 | } |
139 | | /* Fragment for the start address in an SFrame FRE. */ |
140 | 0 | else |
141 | 0 | { |
142 | | /* Get the fsize expression from the symbol. */ |
143 | 0 | fsizeS = exp->X_op_symbol; |
144 | 0 | fsize = resolve_symbol_value (fsizeS); |
145 | | /* Get the diff expression from the symbol. */ |
146 | 0 | diffS= exp->X_add_symbol; |
147 | 0 | diff = resolve_symbol_value (diffS); |
148 | 0 | value = diff; |
149 | |
|
150 | 0 | switch (frag->fr_subtype & 7) |
151 | 0 | { |
152 | 0 | case 1: |
153 | 0 | gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR1_LIMIT); |
154 | 0 | frag->fr_literal[frag->fr_fix] = diff; |
155 | 0 | break; |
156 | 0 | case 2: |
157 | 0 | gas_assert (fsize < (offsetT) SFRAME_FRE_TYPE_ADDR2_LIMIT); |
158 | 0 | md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2); |
159 | 0 | break; |
160 | 0 | case 4: |
161 | 0 | md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4); |
162 | 0 | break; |
163 | 0 | default: |
164 | 0 | abort (); |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | 0 | frag->fr_fix += frag->fr_subtype & 7; |
169 | 0 | frag->fr_type = rs_fill; |
170 | 0 | frag->fr_subtype = 0; |
171 | 0 | frag->fr_offset = 0; |
172 | | /* FIXME do this now because we have evaluated and fixed up the fragments |
173 | | manually ? */ |
174 | 0 | frag->fr_symbol = 0; |
175 | 0 | } |