/src/binutils-gdb/opcodes/loongarch-coder.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* LoongArch opcode support. |
2 | | Copyright (C) 2021-2025 Free Software Foundation, Inc. |
3 | | Contributed by Loongson Ltd. |
4 | | |
5 | | This file is part of the GNU opcodes library. |
6 | | |
7 | | This library is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3, or (at your option) |
10 | | any later version. |
11 | | |
12 | | It is distributed in the hope that it will be useful, but WITHOUT |
13 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
15 | | License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program; see the file COPYING3. If not, |
19 | | see <http://www.gnu.org/licenses/>. */ |
20 | | #include "sysdep.h" |
21 | | #include <stdbool.h> |
22 | | #include "opcode/loongarch.h" |
23 | | |
24 | | int |
25 | | is_unsigned (const char *c_str) |
26 | 0 | { |
27 | 0 | if (c_str[0] == '0' && (c_str[1] == 'x' || c_str[1] == 'X')) |
28 | 0 | { |
29 | 0 | c_str += 2; |
30 | 0 | while (('a' <= *c_str && *c_str <= 'f') |
31 | 0 | || ('A' <= *c_str && *c_str <= 'F') |
32 | 0 | || ('0' <= *c_str && *c_str <= '9')) |
33 | 0 | c_str++; |
34 | 0 | } |
35 | 0 | else if (*c_str == '\0') |
36 | 0 | return 0; |
37 | 0 | else |
38 | 0 | while ('0' <= *c_str && *c_str <= '9') |
39 | 0 | c_str++; |
40 | 0 | return *c_str == '\0'; |
41 | 0 | } |
42 | | |
43 | | int |
44 | | is_signed (const char *c_str) |
45 | 0 | { |
46 | 0 | return *c_str == '-' ? is_unsigned (c_str + 1) : is_unsigned (c_str); |
47 | 0 | } |
48 | | |
49 | | int |
50 | | loongarch_get_bit_field_width (const char *bit_field, char **end) |
51 | 30.0k | { |
52 | 30.0k | int width = 0; |
53 | 30.0k | char has_specify = 0, *bit_field_1 = (char *) bit_field; |
54 | 30.0k | if (bit_field_1 && *bit_field_1 != '\0') |
55 | 32.5k | while (1) |
56 | 32.5k | { |
57 | 32.5k | strtol (bit_field_1, &bit_field_1, 10); |
58 | | |
59 | 32.5k | if (*bit_field_1 != ':') |
60 | 0 | break; |
61 | 32.5k | bit_field_1++; |
62 | | |
63 | 32.5k | width += strtol (bit_field_1, &bit_field_1, 10); |
64 | 32.5k | has_specify = 1; |
65 | | |
66 | 32.5k | if (*bit_field_1 != '|') |
67 | 30.0k | break; |
68 | 2.53k | bit_field_1++; |
69 | 2.53k | } |
70 | 30.0k | if (end) |
71 | 30.0k | *end = bit_field_1; |
72 | 30.0k | return has_specify ? width : -1; |
73 | 30.0k | } |
74 | | |
75 | | int32_t |
76 | | loongarch_decode_imm (const char *bit_field, insn_t insn, int si) |
77 | 60.0k | { |
78 | 60.0k | int32_t ret = 0; |
79 | 60.0k | uint32_t t; |
80 | 60.0k | int len = 0, width, b_start; |
81 | 60.0k | char *bit_field_1 = (char *) bit_field; |
82 | 65.0k | while (1) |
83 | 65.0k | { |
84 | 65.0k | b_start = strtol (bit_field_1, &bit_field_1, 10); |
85 | 65.0k | if (*bit_field_1 != ':') |
86 | 0 | break; |
87 | 65.0k | width = strtol (bit_field_1 + 1, &bit_field_1, 10); |
88 | 65.0k | len += width; |
89 | | |
90 | 65.0k | t = insn; |
91 | 65.0k | t <<= sizeof (t) * 8 - width - b_start; |
92 | 65.0k | t >>= sizeof (t) * 8 - width; |
93 | 65.0k | ret <<= width; |
94 | 65.0k | ret |= t; |
95 | | |
96 | 65.0k | if (*bit_field_1 != '|') |
97 | 60.0k | break; |
98 | 5.06k | bit_field_1++; |
99 | 5.06k | } |
100 | | |
101 | 60.0k | if (*bit_field_1 == '<' && *(++bit_field_1) == '<') |
102 | 12.3k | { |
103 | 12.3k | width = atoi (bit_field_1 + 1); |
104 | 12.3k | ret <<= width; |
105 | 12.3k | len += width; |
106 | 12.3k | } |
107 | 47.6k | else if (*bit_field_1 == '+') |
108 | 648 | ret += atoi (bit_field_1 + 1); |
109 | | |
110 | | /* Extend signed bit. */ |
111 | 60.0k | if (si) |
112 | 30.0k | { |
113 | 30.0k | uint32_t sign = 1u << (len - 1); |
114 | 30.0k | ret = (ret ^ sign) - sign; |
115 | 30.0k | } |
116 | | |
117 | 60.0k | return ret; |
118 | 60.0k | } |
119 | | |
120 | | static insn_t |
121 | | loongarch_encode_imm (const char *bit_field, int32_t imm) |
122 | 30.0k | { |
123 | 30.0k | char *bit_field_1 = (char *) bit_field; |
124 | 30.0k | char *t = bit_field_1; |
125 | 30.0k | int width, b_start; |
126 | 30.0k | insn_t ret = 0; |
127 | 30.0k | uint32_t i; |
128 | 30.0k | uint32_t uimm = (uint32_t)imm; |
129 | | |
130 | 30.0k | width = loongarch_get_bit_field_width (t, &t); |
131 | 30.0k | if (width == -1) |
132 | 0 | return ret; |
133 | | |
134 | 30.0k | if (*t == '<' && *(++t) == '<') |
135 | 6.18k | width += atoi (t + 1); |
136 | 23.8k | else if (*t == '+') |
137 | 324 | uimm -= atoi (t + 1); |
138 | | |
139 | 30.0k | uimm = width ? (uimm << (sizeof (uimm) * 8 - width)) : 0; |
140 | | |
141 | 32.5k | while (1) |
142 | 32.5k | { |
143 | 32.5k | b_start = strtol (bit_field_1, &bit_field_1, 10); |
144 | 32.5k | if (*bit_field_1 != ':') |
145 | 0 | break; |
146 | 32.5k | width = strtol (bit_field_1 + 1, &bit_field_1, 10); |
147 | 32.5k | i = uimm; |
148 | 32.5k | i = width ? (i >> (sizeof (i) * 8 - width)) : 0; |
149 | 32.5k | i = (b_start == 32) ? 0 : (i << b_start); |
150 | 32.5k | ret |= i; |
151 | 32.5k | uimm = (width == 32) ? 0 : (uimm << width); |
152 | | |
153 | 32.5k | if (*bit_field_1 != '|') |
154 | 30.0k | break; |
155 | 2.53k | bit_field_1++; |
156 | 2.53k | } |
157 | 30.0k | return ret; |
158 | 30.0k | } |
159 | | |
160 | | /* Parse such FORMAT |
161 | | "" |
162 | | "u" |
163 | | "v0:5,r5:5,s10:10<<2" |
164 | | "r0:5,r5:5,r10:5,u15:2+1" |
165 | | "r,r,u0:5+32,u0:5+1" |
166 | | */ |
167 | | static int |
168 | | loongarch_parse_format (const char *format, char *esc1s, char *esc2s, |
169 | | const char **bit_fields) |
170 | 11.1k | { |
171 | 11.1k | size_t arg_num = 0; |
172 | | |
173 | 11.1k | if (*format == '\0') |
174 | 36 | goto end; |
175 | | |
176 | 30.0k | while (1) |
177 | 30.0k | { |
178 | | /* esc1 esc2 |
179 | | for "[a-zA-Z][a-zA-Z]?" */ |
180 | 30.0k | if (('a' <= *format && *format <= 'z') |
181 | 30.0k | || ('A' <= *format && *format <= 'Z')) |
182 | 30.0k | { |
183 | 30.0k | *esc1s++ = *format++; |
184 | 30.0k | if (('a' <= *format && *format <= 'z') |
185 | 30.0k | || ('A' <= *format && *format <= 'Z')) |
186 | 6.65k | *esc2s++ = *format++; |
187 | 23.3k | else |
188 | 23.3k | *esc2s++ = '\0'; |
189 | 30.0k | } |
190 | 0 | else |
191 | 0 | return -1; |
192 | | |
193 | 30.0k | arg_num++; |
194 | 30.0k | if (MAX_ARG_NUM_PLUS_2 - 2 < arg_num) |
195 | | /* Need larger MAX_ARG_NUM_PLUS_2. */ |
196 | 0 | return -1; |
197 | | |
198 | 30.0k | *bit_fields++ = format; |
199 | | |
200 | 30.0k | if ('0' <= *format && *format <= '9') |
201 | 30.0k | { |
202 | | /* For "[0-9]+:[0-9]+(\|[0-9]+:[0-9]+)*". */ |
203 | 32.5k | while (1) |
204 | 32.5k | { |
205 | 77.0k | while ('0' <= *format && *format <= '9') |
206 | 44.4k | format++; |
207 | | |
208 | 32.5k | if (*format != ':') |
209 | 0 | return -1; |
210 | 32.5k | format++; |
211 | | |
212 | 32.5k | if (!('0' <= *format && *format <= '9')) |
213 | 0 | return -1; |
214 | 74.0k | while ('0' <= *format && *format <= '9') |
215 | 41.4k | format++; |
216 | | |
217 | 32.5k | if (*format != '|') |
218 | 30.0k | break; |
219 | 2.53k | format++; |
220 | 2.53k | } |
221 | | |
222 | | /* For "((\+|<<)[1-9][0-9]*)?". */ |
223 | 30.0k | do |
224 | 30.0k | { |
225 | 30.0k | if (*format == '+') |
226 | 324 | format++; |
227 | 29.6k | else if (format[0] == '<' && format[1] == '<') |
228 | 6.18k | format += 2; |
229 | 23.5k | else |
230 | 23.5k | break; |
231 | | |
232 | 6.51k | if (!('1' <= *format && *format <= '9')) |
233 | 0 | return -1; |
234 | 13.0k | while ('0' <= *format && *format <= '9') |
235 | 6.51k | format++; |
236 | 6.51k | } |
237 | 30.0k | while (0); |
238 | 30.0k | } |
239 | | |
240 | 30.0k | if (*format == ',') |
241 | 18.8k | format++; |
242 | 11.1k | else if (*format == '\0') |
243 | 11.1k | break; |
244 | 0 | else |
245 | 0 | return -1; |
246 | 30.0k | } |
247 | | |
248 | 11.1k | end: |
249 | 11.1k | *esc1s = '\0'; |
250 | 11.1k | return 0; |
251 | 11.1k | } |
252 | | |
253 | | size_t |
254 | | loongarch_split_args_by_comma (char *args, const char *arg_strs[]) |
255 | 11.1k | { |
256 | 11.1k | size_t num = 0; |
257 | | |
258 | 11.1k | if (*args) |
259 | 11.1k | { |
260 | 11.1k | bool inquote = false; |
261 | 11.1k | arg_strs[num++] = args; |
262 | 206k | for (; *args; args++) |
263 | 195k | if (*args == '"') |
264 | 0 | inquote = !inquote; |
265 | 195k | else if (*args == ',' && !inquote) |
266 | 18.8k | { |
267 | 18.8k | if (MAX_ARG_NUM_PLUS_2 - 1 == num) |
268 | 0 | goto out; |
269 | 18.8k | *args = '\0'; |
270 | 18.8k | arg_strs[num++] = args + 1; |
271 | 18.8k | } |
272 | | |
273 | 11.1k | if (*(args - 1) == '"' && *arg_strs[num - 1] == '"') |
274 | 0 | { |
275 | 0 | *(args - 1) = '\0'; |
276 | 0 | arg_strs[num - 1] += 1; |
277 | 0 | } |
278 | 11.1k | } |
279 | 11.1k | out: |
280 | 11.1k | arg_strs[num] = NULL; |
281 | 11.1k | return num; |
282 | 11.1k | } |
283 | | |
284 | | char * |
285 | | loongarch_cat_splited_strs (const char *arg_strs[]) |
286 | 0 | { |
287 | 0 | char *ret; |
288 | 0 | size_t n, l; |
289 | |
|
290 | 0 | for (l = 0, n = 0; arg_strs[n]; n++) |
291 | 0 | l += strlen (arg_strs[n]); |
292 | 0 | ret = malloc (l + n + 1); |
293 | 0 | if (!ret) |
294 | 0 | return ret; |
295 | | |
296 | 0 | ret[0] = '\0'; |
297 | 0 | if (0 < n) |
298 | 0 | strcat (ret, arg_strs[0]); |
299 | 0 | for (l = 1; l < n; l++) |
300 | 0 | strcat (ret, ","), strcat (ret, arg_strs[l]); |
301 | 0 | return ret; |
302 | 0 | } |
303 | | |
304 | | insn_t |
305 | | loongarch_foreach_args (const char *format, const char *arg_strs[], |
306 | | int32_t (*helper) (char esc1, char esc2, |
307 | | const char *bit_field, |
308 | | const char *arg, void *context), |
309 | | void *context) |
310 | 11.1k | { |
311 | 11.1k | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; |
312 | 11.1k | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; |
313 | 11.1k | size_t i; |
314 | 11.1k | insn_t ret = 0; |
315 | 11.1k | int ok; |
316 | | |
317 | 11.1k | ok = loongarch_parse_format (format, esc1s, esc2s, bit_fields) == 0; |
318 | | |
319 | | /* Make sure the num of actual args is equal to the num of escape. */ |
320 | 41.1k | for (i = 0; esc1s[i] && arg_strs[i]; i++) |
321 | 30.0k | ; |
322 | 11.1k | ok = ok && !esc1s[i] && !arg_strs[i]; |
323 | | |
324 | 11.1k | if (ok && helper) |
325 | 11.1k | { |
326 | 41.1k | for (i = 0; arg_strs[i]; i++) |
327 | 30.0k | ret |= loongarch_encode_imm (bit_fields[i], |
328 | 30.0k | helper (esc1s[i], esc2s[i], |
329 | 30.0k | bit_fields[i], arg_strs[i], |
330 | 30.0k | context)); |
331 | 11.1k | ret |= helper ('\0', '\0', NULL, NULL, context); |
332 | 11.1k | } |
333 | | |
334 | 11.1k | return ret; |
335 | 11.1k | } |
336 | | |
337 | | int |
338 | | loongarch_check_format (const char *format) |
339 | 0 | { |
340 | 0 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; |
341 | 0 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; |
342 | |
|
343 | 0 | if (!format) |
344 | 0 | return -1; |
345 | | |
346 | 0 | return loongarch_parse_format (format, esc1s, esc2s, bit_fields); |
347 | 0 | } |
348 | | |
349 | | int |
350 | | loongarch_check_macro (const char *format, const char *macro) |
351 | 0 | { |
352 | 0 | int num_of_args; |
353 | 0 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; |
354 | 0 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; |
355 | |
|
356 | 0 | if (!format || !macro |
357 | 0 | || loongarch_parse_format (format, esc1s, esc2s, bit_fields) != 0) |
358 | 0 | return -1; |
359 | | |
360 | 0 | for (num_of_args = 0; esc1s[num_of_args]; num_of_args++) |
361 | 0 | ; |
362 | |
|
363 | 0 | for (; macro[0]; macro++) |
364 | 0 | if (macro[0] == '%') |
365 | 0 | { |
366 | 0 | macro++; |
367 | 0 | if ('1' <= macro[0] && macro[0] <= '9') |
368 | 0 | { |
369 | 0 | if (num_of_args < macro[0] - '0') |
370 | | /* Out of args num. */ |
371 | 0 | return -1; |
372 | 0 | } |
373 | 0 | else if (macro[0] == 'f') |
374 | 0 | ; |
375 | 0 | else if (macro[0] == '%') |
376 | 0 | ; |
377 | 0 | else |
378 | 0 | return -1; |
379 | 0 | } |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | | static const char * |
384 | | I (char esc_ch1 ATTRIBUTE_UNUSED, char esc_ch2 ATTRIBUTE_UNUSED, |
385 | | const char *c_str) |
386 | 0 | { |
387 | 0 | return c_str; |
388 | 0 | } |
389 | | |
390 | | char * |
391 | | loongarch_expand_macro_with_format_map ( |
392 | | const char *format, const char *macro, const char *const arg_strs[], |
393 | | const char *(*map) (char esc1, char esc2, const char *arg), |
394 | | char *(*helper) (const char *const arg_strs[], void *context), void *context, |
395 | | size_t len_str) |
396 | 0 | { |
397 | 0 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; |
398 | 0 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; |
399 | 0 | const char *src; |
400 | 0 | char *dest; |
401 | | |
402 | | /* The expanded macro character length does not exceed 1000, and number of |
403 | | label is 6 at most in the expanded macro. The len_str is the length of |
404 | | str. */ |
405 | 0 | char *buffer =(char *) malloc(1024 + 6 * len_str); |
406 | |
|
407 | 0 | if (format) |
408 | 0 | loongarch_parse_format (format, esc1s, esc2s, bit_fields); |
409 | |
|
410 | 0 | src = macro; |
411 | 0 | dest = buffer; |
412 | |
|
413 | 0 | while (*src) |
414 | 0 | if (*src == '%') |
415 | 0 | { |
416 | 0 | src++; |
417 | 0 | if ('1' <= *src && *src <= '9') |
418 | 0 | { |
419 | 0 | size_t i = *src - '1'; |
420 | 0 | const char *t = map (esc1s[i], esc2s[i], arg_strs[i]); |
421 | 0 | while (*t) |
422 | 0 | *dest++ = *t++; |
423 | 0 | } |
424 | 0 | else if (*src == '%') |
425 | 0 | *dest++ = '%'; |
426 | 0 | else if (*src == 'f' && helper) |
427 | 0 | { |
428 | 0 | char *b, *t; |
429 | 0 | t = b = (*helper) (arg_strs, context); |
430 | 0 | if (b) |
431 | 0 | { |
432 | 0 | while (*t) |
433 | 0 | *dest++ = *t++; |
434 | 0 | free (b); |
435 | 0 | } |
436 | 0 | } |
437 | 0 | src++; |
438 | 0 | } |
439 | 0 | else |
440 | 0 | *dest++ = *src++; |
441 | |
|
442 | 0 | *dest = '\0'; |
443 | 0 | return buffer; |
444 | 0 | } |
445 | | |
446 | | char * |
447 | | loongarch_expand_macro (const char *macro, const char *const arg_strs[], |
448 | | char *(*helper) (const char *const arg_strs[], |
449 | | void *context), |
450 | | void *context, size_t len_str) |
451 | 0 | { |
452 | 0 | return loongarch_expand_macro_with_format_map (NULL, macro, arg_strs, I, |
453 | 0 | helper, context, len_str); |
454 | 0 | } |
455 | | |
456 | | size_t |
457 | | loongarch_bits_imm_needed (int64_t imm, int si) |
458 | 0 | { |
459 | 0 | size_t ret; |
460 | 0 | if (si) |
461 | 0 | { |
462 | 0 | if (imm < 0) |
463 | 0 | { |
464 | 0 | uint64_t uimm = (uint64_t) imm; |
465 | 0 | uint64_t uimax = UINT64_C (1) << 63; |
466 | 0 | for (ret = 0; (uimm & uimax) != 0; uimm <<= 1, ret++) |
467 | 0 | ; |
468 | 0 | ret = 64 - ret + 1; |
469 | 0 | } |
470 | 0 | else |
471 | 0 | ret = loongarch_bits_imm_needed (imm, 0) + 1; |
472 | 0 | } |
473 | 0 | else |
474 | 0 | { |
475 | 0 | uint64_t t = imm; |
476 | 0 | for (ret = 0; t; t >>= 1, ret++) |
477 | 0 | ; |
478 | 0 | } |
479 | 0 | return ret; |
480 | 0 | } |
481 | | |
482 | | void |
483 | | loongarch_eliminate_adjacent_repeat_char (char *dest, char c) |
484 | 0 | { |
485 | 0 | if (c == '\0') |
486 | 0 | return; |
487 | 0 | char *src = dest; |
488 | 0 | while (*dest) |
489 | 0 | { |
490 | 0 | while (src[0] == c && src[0] == src[1]) |
491 | 0 | src++; |
492 | 0 | *dest++ = *src++; |
493 | 0 | } |
494 | 0 | } |