]>
Commit | Line | Data |
---|---|---|
6cc76c40 | 1 | /* LoongArch opcode support. |
fd67aa11 | 2 | Copyright (C) 2021-2024 Free Software Foundation, Inc. |
6cc76c40 | 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" | |
3838f0bc | 21 | #include <stdbool.h> |
6cc76c40 | 22 | #include "opcode/loongarch.h" |
23 | ||
24 | int | |
25 | is_unsigned (const char *c_str) | |
26 | { | |
27 | if (c_str[0] == '0' && (c_str[1] == 'x' || c_str[1] == 'X')) | |
28 | { | |
29 | c_str += 2; | |
30 | while (('a' <= *c_str && *c_str <= 'f') | |
31 | || ('A' <= *c_str && *c_str <= 'F') | |
32 | || ('0' <= *c_str && *c_str <= '9')) | |
33 | c_str++; | |
34 | } | |
35 | else if (*c_str == '\0') | |
36 | return 0; | |
37 | else | |
38 | while ('0' <= *c_str && *c_str <= '9') | |
39 | c_str++; | |
40 | return *c_str == '\0'; | |
41 | } | |
42 | ||
43 | int | |
44 | is_signed (const char *c_str) | |
45 | { | |
46 | return *c_str == '-' ? is_unsigned (c_str + 1) : is_unsigned (c_str); | |
47 | } | |
48 | ||
49 | int | |
50 | loongarch_get_bit_field_width (const char *bit_field, char **end) | |
51 | { | |
52 | int width = 0; | |
53 | char has_specify = 0, *bit_field_1 = (char *) bit_field; | |
54 | if (bit_field_1 && *bit_field_1 != '\0') | |
55 | while (1) | |
56 | { | |
57 | strtol (bit_field_1, &bit_field_1, 10); | |
58 | ||
59 | if (*bit_field_1 != ':') | |
60 | break; | |
61 | bit_field_1++; | |
62 | ||
63 | width += strtol (bit_field_1, &bit_field_1, 10); | |
64 | has_specify = 1; | |
65 | ||
66 | if (*bit_field_1 != '|') | |
67 | break; | |
68 | bit_field_1++; | |
69 | } | |
70 | if (end) | |
71 | *end = bit_field_1; | |
72 | return has_specify ? width : -1; | |
73 | } | |
74 | ||
75 | int32_t | |
76 | loongarch_decode_imm (const char *bit_field, insn_t insn, int si) | |
77 | { | |
78 | int32_t ret = 0; | |
79 | uint32_t t; | |
80 | int len = 0, width, b_start; | |
81 | char *bit_field_1 = (char *) bit_field; | |
82 | while (1) | |
83 | { | |
84 | b_start = strtol (bit_field_1, &bit_field_1, 10); | |
85 | if (*bit_field_1 != ':') | |
86 | break; | |
87 | width = strtol (bit_field_1 + 1, &bit_field_1, 10); | |
88 | len += width; | |
89 | ||
90 | t = insn; | |
91 | t <<= sizeof (t) * 8 - width - b_start; | |
92 | t >>= sizeof (t) * 8 - width; | |
93 | ret <<= width; | |
94 | ret |= t; | |
95 | ||
96 | if (*bit_field_1 != '|') | |
97 | break; | |
98 | bit_field_1++; | |
99 | } | |
100 | ||
101 | if (*bit_field_1 == '<' && *(++bit_field_1) == '<') | |
102 | { | |
103 | width = atoi (bit_field_1 + 1); | |
104 | ret <<= width; | |
105 | len += width; | |
106 | } | |
107 | else if (*bit_field_1 == '+') | |
108 | ret += atoi (bit_field_1 + 1); | |
109 | ||
e36144c9 | 110 | /* Extend signed bit. */ |
6cc76c40 | 111 | if (si) |
112 | { | |
e36144c9 | 113 | uint32_t sign = 1u << (len - 1); |
114 | ret = (ret ^ sign) - sign; | |
6cc76c40 | 115 | } |
e36144c9 | 116 | |
6cc76c40 | 117 | return ret; |
118 | } | |
119 | ||
120 | static insn_t | |
121 | loongarch_encode_imm (const char *bit_field, int32_t imm) | |
122 | { | |
123 | char *bit_field_1 = (char *) bit_field; | |
124 | char *t = bit_field_1; | |
125 | int width, b_start; | |
126 | insn_t ret = 0; | |
127 | uint32_t i; | |
128 | uint32_t uimm = (uint32_t)imm; | |
129 | ||
130 | width = loongarch_get_bit_field_width (t, &t); | |
131 | if (width == -1) | |
132 | return ret; | |
133 | ||
134 | if (*t == '<' && *(++t) == '<') | |
135 | width += atoi (t + 1); | |
136 | else if (*t == '+') | |
137 | uimm -= atoi (t + 1); | |
138 | ||
e36144c9 | 139 | uimm = width ? (uimm << (sizeof (uimm) * 8 - width)) : 0; |
140 | ||
6cc76c40 | 141 | while (1) |
142 | { | |
143 | b_start = strtol (bit_field_1, &bit_field_1, 10); | |
144 | if (*bit_field_1 != ':') | |
145 | break; | |
146 | width = strtol (bit_field_1 + 1, &bit_field_1, 10); | |
147 | i = uimm; | |
e36144c9 | 148 | i = width ? (i >> (sizeof (i) * 8 - width)) : 0; |
149 | i = (b_start == 32) ? 0 : (i << b_start); | |
6cc76c40 | 150 | ret |= i; |
e36144c9 | 151 | uimm = (width == 32) ? 0 : (uimm << width); |
6cc76c40 | 152 | |
153 | if (*bit_field_1 != '|') | |
154 | break; | |
155 | bit_field_1++; | |
156 | } | |
157 | return ret; | |
158 | } | |
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 | { | |
171 | size_t arg_num = 0; | |
172 | ||
173 | if (*format == '\0') | |
174 | goto end; | |
175 | ||
176 | while (1) | |
177 | { | |
178 | /* esc1 esc2 | |
179 | for "[a-zA-Z][a-zA-Z]?" */ | |
180 | if (('a' <= *format && *format <= 'z') | |
181 | || ('A' <= *format && *format <= 'Z')) | |
182 | { | |
183 | *esc1s++ = *format++; | |
184 | if (('a' <= *format && *format <= 'z') | |
185 | || ('A' <= *format && *format <= 'Z')) | |
186 | *esc2s++ = *format++; | |
187 | else | |
188 | *esc2s++ = '\0'; | |
189 | } | |
190 | else | |
191 | return -1; | |
192 | ||
193 | arg_num++; | |
194 | if (MAX_ARG_NUM_PLUS_2 - 2 < arg_num) | |
195 | /* Need larger MAX_ARG_NUM_PLUS_2. */ | |
196 | return -1; | |
197 | ||
198 | *bit_fields++ = format; | |
199 | ||
200 | if ('0' <= *format && *format <= '9') | |
201 | { | |
202 | /* For "[0-9]+:[0-9]+(\|[0-9]+:[0-9]+)*". */ | |
203 | while (1) | |
204 | { | |
205 | while ('0' <= *format && *format <= '9') | |
206 | format++; | |
207 | ||
208 | if (*format != ':') | |
209 | return -1; | |
210 | format++; | |
211 | ||
212 | if (!('0' <= *format && *format <= '9')) | |
213 | return -1; | |
214 | while ('0' <= *format && *format <= '9') | |
215 | format++; | |
216 | ||
217 | if (*format != '|') | |
218 | break; | |
219 | format++; | |
220 | } | |
221 | ||
222 | /* For "((\+|<<)[1-9][0-9]*)?". */ | |
223 | do | |
224 | { | |
225 | if (*format == '+') | |
226 | format++; | |
227 | else if (format[0] == '<' && format[1] == '<') | |
228 | format += 2; | |
229 | else | |
230 | break; | |
231 | ||
232 | if (!('1' <= *format && *format <= '9')) | |
233 | return -1; | |
234 | while ('0' <= *format && *format <= '9') | |
235 | format++; | |
236 | } | |
237 | while (0); | |
238 | } | |
239 | ||
240 | if (*format == ',') | |
241 | format++; | |
242 | else if (*format == '\0') | |
243 | break; | |
244 | else | |
245 | return -1; | |
246 | } | |
247 | ||
248 | end: | |
249 | *esc1s = '\0'; | |
250 | return 0; | |
251 | } | |
252 | ||
253 | size_t | |
254 | loongarch_split_args_by_comma (char *args, const char *arg_strs[]) | |
255 | { | |
256 | size_t num = 0; | |
257 | ||
258 | if (*args) | |
22b78fad | 259 | { |
3838f0bc | 260 | bool inquote = false; |
2d120f18 AM |
261 | arg_strs[num++] = args; |
262 | for (; *args; args++) | |
3838f0bc AM |
263 | if (*args == '"') |
264 | inquote = !inquote; | |
265 | else if (*args == ',' && !inquote) | |
2d120f18 AM |
266 | { |
267 | if (MAX_ARG_NUM_PLUS_2 - 1 == num) | |
268 | goto out; | |
269 | *args = '\0'; | |
270 | arg_strs[num++] = args + 1; | |
271 | } | |
272 | ||
273 | if (*(args - 1) == '"' && *arg_strs[num - 1] == '"') | |
274 | { | |
275 | *(args - 1) = '\0'; | |
276 | arg_strs[num - 1] += 1; | |
277 | } | |
22b78fad | 278 | } |
2d120f18 | 279 | out: |
6cc76c40 | 280 | arg_strs[num] = NULL; |
281 | return num; | |
282 | } | |
283 | ||
284 | char * | |
285 | loongarch_cat_splited_strs (const char *arg_strs[]) | |
286 | { | |
287 | char *ret; | |
288 | size_t n, l; | |
289 | ||
290 | for (l = 0, n = 0; arg_strs[n]; n++) | |
291 | l += strlen (arg_strs[n]); | |
292 | ret = malloc (l + n + 1); | |
293 | if (!ret) | |
294 | return ret; | |
295 | ||
296 | ret[0] = '\0'; | |
297 | if (0 < n) | |
298 | strcat (ret, arg_strs[0]); | |
299 | for (l = 1; l < n; l++) | |
300 | strcat (ret, ","), strcat (ret, arg_strs[l]); | |
301 | return ret; | |
302 | } | |
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 | { | |
311 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; | |
312 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; | |
313 | size_t i; | |
314 | insn_t ret = 0; | |
315 | int ok; | |
316 | ||
317 | 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 | for (i = 0; esc1s[i] && arg_strs[i]; i++) | |
321 | ; | |
322 | ok = ok && !esc1s[i] && !arg_strs[i]; | |
323 | ||
324 | if (ok && helper) | |
325 | { | |
326 | for (i = 0; arg_strs[i]; i++) | |
327 | ret |= loongarch_encode_imm (bit_fields[i], | |
328 | helper (esc1s[i], esc2s[i], | |
329 | bit_fields[i], arg_strs[i], | |
330 | context)); | |
331 | ret |= helper ('\0', '\0', NULL, NULL, context); | |
332 | } | |
333 | ||
334 | return ret; | |
335 | } | |
336 | ||
337 | int | |
338 | loongarch_check_format (const char *format) | |
339 | { | |
340 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; | |
341 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; | |
342 | ||
343 | if (!format) | |
344 | return -1; | |
345 | ||
346 | return loongarch_parse_format (format, esc1s, esc2s, bit_fields); | |
347 | } | |
348 | ||
349 | int | |
350 | loongarch_check_macro (const char *format, const char *macro) | |
351 | { | |
352 | int num_of_args; | |
353 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; | |
354 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; | |
355 | ||
356 | if (!format || !macro | |
357 | || loongarch_parse_format (format, esc1s, esc2s, bit_fields) != 0) | |
358 | return -1; | |
359 | ||
360 | for (num_of_args = 0; esc1s[num_of_args]; num_of_args++) | |
361 | ; | |
362 | ||
363 | for (; macro[0]; macro++) | |
364 | if (macro[0] == '%') | |
365 | { | |
366 | macro++; | |
367 | if ('1' <= macro[0] && macro[0] <= '9') | |
368 | { | |
369 | if (num_of_args < macro[0] - '0') | |
370 | /* Out of args num. */ | |
371 | return -1; | |
372 | } | |
373 | else if (macro[0] == 'f') | |
374 | ; | |
375 | else if (macro[0] == '%') | |
376 | ; | |
377 | else | |
378 | return -1; | |
379 | } | |
380 | return 0; | |
381 | } | |
382 | ||
383 | static const char * | |
384 | I (char esc_ch1 ATTRIBUTE_UNUSED, char esc_ch2 ATTRIBUTE_UNUSED, | |
385 | const char *c_str) | |
386 | { | |
387 | return c_str; | |
388 | } | |
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), | |
5fb13d7e | 394 | char *(*helper) (const char *const arg_strs[], void *context), void *context, |
395 | size_t len_str) | |
6cc76c40 | 396 | { |
397 | char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1]; | |
398 | const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1]; | |
399 | const char *src; | |
400 | char *dest; | |
5fb13d7e | 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 | char *buffer =(char *) malloc(1024 + 6 * len_str); | |
6cc76c40 | 406 | |
407 | if (format) | |
408 | loongarch_parse_format (format, esc1s, esc2s, bit_fields); | |
409 | ||
410 | src = macro; | |
411 | dest = buffer; | |
412 | ||
413 | while (*src) | |
414 | if (*src == '%') | |
415 | { | |
416 | src++; | |
417 | if ('1' <= *src && *src <= '9') | |
418 | { | |
419 | size_t i = *src - '1'; | |
420 | const char *t = map (esc1s[i], esc2s[i], arg_strs[i]); | |
421 | while (*t) | |
422 | *dest++ = *t++; | |
423 | } | |
424 | else if (*src == '%') | |
425 | *dest++ = '%'; | |
426 | else if (*src == 'f' && helper) | |
427 | { | |
428 | char *b, *t; | |
429 | t = b = (*helper) (arg_strs, context); | |
430 | if (b) | |
431 | { | |
432 | while (*t) | |
433 | *dest++ = *t++; | |
434 | free (b); | |
435 | } | |
436 | } | |
437 | src++; | |
438 | } | |
439 | else | |
440 | *dest++ = *src++; | |
441 | ||
442 | *dest = '\0'; | |
5fb13d7e | 443 | return buffer; |
6cc76c40 | 444 | } |
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), | |
5fb13d7e | 450 | void *context, size_t len_str) |
6cc76c40 | 451 | { |
452 | return loongarch_expand_macro_with_format_map (NULL, macro, arg_strs, I, | |
5fb13d7e | 453 | helper, context, len_str); |
6cc76c40 | 454 | } |
455 | ||
456 | size_t | |
457 | loongarch_bits_imm_needed (int64_t imm, int si) | |
458 | { | |
459 | size_t ret; | |
460 | if (si) | |
461 | { | |
462 | if (imm < 0) | |
463 | { | |
464 | uint64_t uimm = (uint64_t) imm; | |
465 | uint64_t uimax = UINT64_C (1) << 63; | |
466 | for (ret = 0; (uimm & uimax) != 0; uimm <<= 1, ret++) | |
467 | ; | |
468 | ret = 64 - ret + 1; | |
469 | } | |
470 | else | |
471 | ret = loongarch_bits_imm_needed (imm, 0) + 1; | |
472 | } | |
473 | else | |
474 | { | |
475 | uint64_t t = imm; | |
476 | for (ret = 0; t; t >>= 1, ret++) | |
477 | ; | |
478 | } | |
479 | return ret; | |
480 | } | |
481 | ||
482 | void | |
483 | loongarch_eliminate_adjacent_repeat_char (char *dest, char c) | |
484 | { | |
485 | if (c == '\0') | |
486 | return; | |
487 | char *src = dest; | |
488 | while (*dest) | |
489 | { | |
490 | while (src[0] == c && src[0] == src[1]) | |
491 | src++; | |
492 | *dest++ = *src++; | |
493 | } | |
494 | } |