/src/binutils-gdb/opcodes/cgen-opc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* CGEN generic opcode support. |
2 | | |
3 | | Copyright (C) 1996-2023 Free Software Foundation, Inc. |
4 | | |
5 | | This file is part of libopcodes. |
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 along |
18 | | with this program; if not, write to the Free Software Foundation, Inc., |
19 | | 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ |
20 | | |
21 | | #include "sysdep.h" |
22 | | #include <stdio.h> |
23 | | #include "ansidecl.h" |
24 | | #include "libiberty.h" |
25 | | #include "safe-ctype.h" |
26 | | #include "bfd.h" |
27 | | #include "symcat.h" |
28 | | #include "opcode/cgen.h" |
29 | | |
30 | | static unsigned int hash_keyword_name |
31 | | (const CGEN_KEYWORD *, const char *, int); |
32 | | static unsigned int hash_keyword_value |
33 | | (const CGEN_KEYWORD *, unsigned int); |
34 | | static void build_keyword_hash_tables |
35 | | (CGEN_KEYWORD *); |
36 | | |
37 | | /* Return number of hash table entries to use for N elements. */ |
38 | 16 | #define KEYWORD_HASH_SIZE(n) ((n) <= 31 ? 17 : 31) |
39 | | |
40 | | /* Look up *NAMEP in the keyword table KT. |
41 | | The result is the keyword entry or NULL if not found. */ |
42 | | |
43 | | const CGEN_KEYWORD_ENTRY * |
44 | | cgen_keyword_lookup_name (CGEN_KEYWORD *kt, const char *name) |
45 | 0 | { |
46 | 0 | const CGEN_KEYWORD_ENTRY *ke; |
47 | 0 | const char *p,*n; |
48 | |
|
49 | 0 | if (kt->name_hash_table == NULL) |
50 | 0 | build_keyword_hash_tables (kt); |
51 | |
|
52 | 0 | ke = kt->name_hash_table[hash_keyword_name (kt, name, 0)]; |
53 | | |
54 | | /* We do case insensitive comparisons. |
55 | | If that ever becomes a problem, add an attribute that denotes |
56 | | "do case sensitive comparisons". */ |
57 | |
|
58 | 0 | while (ke != NULL) |
59 | 0 | { |
60 | 0 | n = name; |
61 | 0 | p = ke->name; |
62 | |
|
63 | 0 | while (*p |
64 | 0 | && (*p == *n |
65 | 0 | || (ISALPHA (*p) && (TOLOWER (*p) == TOLOWER (*n))))) |
66 | 0 | ++n, ++p; |
67 | |
|
68 | 0 | if (!*p && !*n) |
69 | 0 | return ke; |
70 | | |
71 | 0 | ke = ke->next_name; |
72 | 0 | } |
73 | | |
74 | 0 | if (kt->null_entry) |
75 | 0 | return kt->null_entry; |
76 | 0 | return NULL; |
77 | 0 | } |
78 | | |
79 | | /* Look up VALUE in the keyword table KT. |
80 | | The result is the keyword entry or NULL if not found. */ |
81 | | |
82 | | const CGEN_KEYWORD_ENTRY * |
83 | | cgen_keyword_lookup_value (CGEN_KEYWORD *kt, int value) |
84 | 559k | { |
85 | 559k | const CGEN_KEYWORD_ENTRY *ke; |
86 | | |
87 | 559k | if (kt->name_hash_table == NULL) |
88 | 16 | build_keyword_hash_tables (kt); |
89 | | |
90 | 559k | ke = kt->value_hash_table[hash_keyword_value (kt, value)]; |
91 | | |
92 | 594k | while (ke != NULL) |
93 | 592k | { |
94 | 592k | if (value == ke->value) |
95 | 557k | return ke; |
96 | 35.2k | ke = ke->next_value; |
97 | 35.2k | } |
98 | | |
99 | 1.90k | return NULL; |
100 | 559k | } |
101 | | |
102 | | /* Add an entry to a keyword table. */ |
103 | | |
104 | | void |
105 | | cgen_keyword_add (CGEN_KEYWORD *kt, CGEN_KEYWORD_ENTRY *ke) |
106 | 364 | { |
107 | 364 | unsigned int hash; |
108 | 364 | size_t i; |
109 | | |
110 | 364 | if (kt->name_hash_table == NULL) |
111 | 0 | build_keyword_hash_tables (kt); |
112 | | |
113 | 364 | hash = hash_keyword_name (kt, ke->name, 0); |
114 | 364 | ke->next_name = kt->name_hash_table[hash]; |
115 | 364 | kt->name_hash_table[hash] = ke; |
116 | | |
117 | 364 | hash = hash_keyword_value (kt, ke->value); |
118 | 364 | ke->next_value = kt->value_hash_table[hash]; |
119 | 364 | kt->value_hash_table[hash] = ke; |
120 | | |
121 | 364 | if (ke->name[0] == 0) |
122 | 0 | kt->null_entry = ke; |
123 | | |
124 | 1.35k | for (i = 1; i < strlen (ke->name); i++) |
125 | 988 | if (! ISALNUM (ke->name[i]) |
126 | 988 | && ! strchr (kt->nonalpha_chars, ke->name[i])) |
127 | 0 | { |
128 | 0 | size_t idx = strlen (kt->nonalpha_chars); |
129 | | |
130 | | /* If you hit this limit, please don't just |
131 | | increase the size of the field, instead |
132 | | look for a better algorithm. */ |
133 | 0 | if (idx >= sizeof (kt->nonalpha_chars) - 1) |
134 | 0 | abort (); |
135 | 0 | kt->nonalpha_chars[idx] = ke->name[i]; |
136 | 0 | kt->nonalpha_chars[idx+1] = 0; |
137 | 0 | } |
138 | 364 | } |
139 | | |
140 | | /* FIXME: Need function to return count of keywords. */ |
141 | | |
142 | | /* Initialize a keyword table search. |
143 | | SPEC is a specification of what to search for. |
144 | | A value of NULL means to find every keyword. |
145 | | Currently NULL is the only acceptable value [further specification |
146 | | deferred]. |
147 | | The result is an opaque data item used to record the search status. |
148 | | It is passed to each call to cgen_keyword_search_next. */ |
149 | | |
150 | | CGEN_KEYWORD_SEARCH |
151 | | cgen_keyword_search_init (CGEN_KEYWORD *kt, const char *spec) |
152 | 0 | { |
153 | 0 | CGEN_KEYWORD_SEARCH search; |
154 | | |
155 | | /* FIXME: Need to specify format of params. */ |
156 | 0 | if (spec != NULL) |
157 | 0 | abort (); |
158 | | |
159 | 0 | if (kt->name_hash_table == NULL) |
160 | 0 | build_keyword_hash_tables (kt); |
161 | |
|
162 | 0 | search.table = kt; |
163 | 0 | search.spec = spec; |
164 | 0 | search.current_hash = 0; |
165 | 0 | search.current_entry = NULL; |
166 | 0 | return search; |
167 | 0 | } |
168 | | |
169 | | /* Return the next keyword specified by SEARCH. |
170 | | The result is the next entry or NULL if there are no more. */ |
171 | | |
172 | | const CGEN_KEYWORD_ENTRY * |
173 | | cgen_keyword_search_next (CGEN_KEYWORD_SEARCH *search) |
174 | 0 | { |
175 | | /* Has search finished? */ |
176 | 0 | if (search->current_hash == search->table->hash_table_size) |
177 | 0 | return NULL; |
178 | | |
179 | | /* Search in progress? */ |
180 | 0 | if (search->current_entry != NULL |
181 | | /* Anything left on this hash chain? */ |
182 | 0 | && search->current_entry->next_name != NULL) |
183 | 0 | { |
184 | 0 | search->current_entry = search->current_entry->next_name; |
185 | 0 | return search->current_entry; |
186 | 0 | } |
187 | | |
188 | | /* Move to next hash chain [unless we haven't started yet]. */ |
189 | 0 | if (search->current_entry != NULL) |
190 | 0 | ++search->current_hash; |
191 | |
|
192 | 0 | while (search->current_hash < search->table->hash_table_size) |
193 | 0 | { |
194 | 0 | search->current_entry = search->table->name_hash_table[search->current_hash]; |
195 | 0 | if (search->current_entry != NULL) |
196 | 0 | return search->current_entry; |
197 | 0 | ++search->current_hash; |
198 | 0 | } |
199 | | |
200 | 0 | return NULL; |
201 | 0 | } |
202 | | |
203 | | /* Return first entry in hash chain for NAME. |
204 | | If CASE_SENSITIVE_P is non-zero, return a case sensitive hash. */ |
205 | | |
206 | | static unsigned int |
207 | | hash_keyword_name (const CGEN_KEYWORD *kt, |
208 | | const char *name, |
209 | | int case_sensitive_p) |
210 | 364 | { |
211 | 364 | unsigned int hash; |
212 | | |
213 | 364 | if (case_sensitive_p) |
214 | 0 | for (hash = 0; *name; ++name) |
215 | 0 | hash = (hash * 97) + (unsigned char) *name; |
216 | 364 | else |
217 | 1.71k | for (hash = 0; *name; ++name) |
218 | 1.35k | hash = (hash * 97) + (unsigned char) TOLOWER (*name); |
219 | 364 | return hash % kt->hash_table_size; |
220 | 364 | } |
221 | | |
222 | | /* Return first entry in hash chain for VALUE. */ |
223 | | |
224 | | static unsigned int |
225 | | hash_keyword_value (const CGEN_KEYWORD *kt, unsigned int value) |
226 | 559k | { |
227 | 559k | return value % kt->hash_table_size; |
228 | 559k | } |
229 | | |
230 | | /* Build a keyword table's hash tables. |
231 | | We probably needn't build the value hash table for the assembler when |
232 | | we're using the disassembler, but we keep things simple. */ |
233 | | |
234 | | static void |
235 | | build_keyword_hash_tables (CGEN_KEYWORD *kt) |
236 | 16 | { |
237 | 16 | int i; |
238 | | /* Use the number of compiled in entries as an estimate for the |
239 | | typical sized table [not too many added at runtime]. */ |
240 | 16 | unsigned int size = KEYWORD_HASH_SIZE (kt->num_init_entries); |
241 | | |
242 | 16 | kt->hash_table_size = size; |
243 | 16 | kt->name_hash_table = (CGEN_KEYWORD_ENTRY **) |
244 | 16 | xmalloc (size * sizeof (CGEN_KEYWORD_ENTRY *)); |
245 | 16 | memset (kt->name_hash_table, 0, size * sizeof (CGEN_KEYWORD_ENTRY *)); |
246 | 16 | kt->value_hash_table = (CGEN_KEYWORD_ENTRY **) |
247 | 16 | xmalloc (size * sizeof (CGEN_KEYWORD_ENTRY *)); |
248 | 16 | memset (kt->value_hash_table, 0, size * sizeof (CGEN_KEYWORD_ENTRY *)); |
249 | | |
250 | | /* The table is scanned backwards as we want keywords appearing earlier to |
251 | | be prefered over later ones. */ |
252 | 380 | for (i = kt->num_init_entries - 1; i >= 0; --i) |
253 | 364 | cgen_keyword_add (kt, &kt->init_entries[i]); |
254 | 16 | } |
255 | | |
256 | | /* Hardware support. */ |
257 | | |
258 | | /* Lookup a hardware element by its name. |
259 | | Returns NULL if NAME is not supported by the currently selected |
260 | | mach/isa. */ |
261 | | |
262 | | const CGEN_HW_ENTRY * |
263 | | cgen_hw_lookup_by_name (CGEN_CPU_DESC cd, const char *name) |
264 | 0 | { |
265 | 0 | unsigned int i; |
266 | 0 | const CGEN_HW_ENTRY **hw = cd->hw_table.entries; |
267 | |
|
268 | 0 | for (i = 0; i < cd->hw_table.num_entries; ++i) |
269 | 0 | if (hw[i] && strcmp (name, hw[i]->name) == 0) |
270 | 0 | return hw[i]; |
271 | | |
272 | 0 | return NULL; |
273 | 0 | } |
274 | | |
275 | | /* Lookup a hardware element by its number. |
276 | | Hardware elements are enumerated, however it may be possible to add some |
277 | | at runtime, thus HWNUM is not an enum type but rather an int. |
278 | | Returns NULL if HWNUM is not supported by the currently selected mach. */ |
279 | | |
280 | | const CGEN_HW_ENTRY * |
281 | | cgen_hw_lookup_by_num (CGEN_CPU_DESC cd, unsigned int hwnum) |
282 | 0 | { |
283 | 0 | unsigned int i; |
284 | 0 | const CGEN_HW_ENTRY **hw = cd->hw_table.entries; |
285 | | |
286 | | /* ??? This can be speeded up. */ |
287 | 0 | for (i = 0; i < cd->hw_table.num_entries; ++i) |
288 | 0 | if (hw[i] && hwnum == hw[i]->type) |
289 | 0 | return hw[i]; |
290 | | |
291 | 0 | return NULL; |
292 | 0 | } |
293 | | |
294 | | /* Operand support. */ |
295 | | |
296 | | /* Lookup an operand by its name. |
297 | | Returns NULL if NAME is not supported by the currently selected |
298 | | mach/isa. */ |
299 | | |
300 | | const CGEN_OPERAND * |
301 | | cgen_operand_lookup_by_name (CGEN_CPU_DESC cd, const char *name) |
302 | 0 | { |
303 | 0 | unsigned int i; |
304 | 0 | const CGEN_OPERAND **op = cd->operand_table.entries; |
305 | |
|
306 | 0 | for (i = 0; i < cd->operand_table.num_entries; ++i) |
307 | 0 | if (op[i] && strcmp (name, op[i]->name) == 0) |
308 | 0 | return op[i]; |
309 | | |
310 | 0 | return NULL; |
311 | 0 | } |
312 | | |
313 | | /* Lookup an operand by its number. |
314 | | Operands are enumerated, however it may be possible to add some |
315 | | at runtime, thus OPNUM is not an enum type but rather an int. |
316 | | Returns NULL if OPNUM is not supported by the currently selected |
317 | | mach/isa. */ |
318 | | |
319 | | const CGEN_OPERAND * |
320 | | cgen_operand_lookup_by_num (CGEN_CPU_DESC cd, int opnum) |
321 | 0 | { |
322 | 0 | return cd->operand_table.entries[opnum]; |
323 | 0 | } |
324 | | |
325 | | /* Instruction support. */ |
326 | | |
327 | | /* Return number of instructions. This includes any added at runtime. */ |
328 | | |
329 | | int |
330 | | cgen_insn_count (CGEN_CPU_DESC cd) |
331 | 10 | { |
332 | 10 | int count = cd->insn_table.num_init_entries; |
333 | 10 | CGEN_INSN_LIST *rt_insns = cd->insn_table.new_entries; |
334 | | |
335 | 10 | for ( ; rt_insns != NULL; rt_insns = rt_insns->next) |
336 | 0 | ++count; |
337 | | |
338 | 10 | return count; |
339 | 10 | } |
340 | | |
341 | | /* Return number of macro-instructions. |
342 | | This includes any added at runtime. */ |
343 | | |
344 | | int |
345 | | cgen_macro_insn_count (CGEN_CPU_DESC cd) |
346 | 10 | { |
347 | 10 | int count = cd->macro_insn_table.num_init_entries; |
348 | 10 | CGEN_INSN_LIST *rt_insns = cd->macro_insn_table.new_entries; |
349 | | |
350 | 10 | for ( ; rt_insns != NULL; rt_insns = rt_insns->next) |
351 | 0 | ++count; |
352 | | |
353 | 10 | return count; |
354 | 10 | } |
355 | | |
356 | | /* Cover function to read and properly byteswap an insn value. */ |
357 | | |
358 | | CGEN_INSN_INT |
359 | | cgen_get_insn_value (CGEN_CPU_DESC cd, unsigned char *buf, int length, |
360 | | int endian) |
361 | 544k | { |
362 | 544k | int big_p = (endian == CGEN_ENDIAN_BIG); |
363 | 544k | int insn_chunk_bitsize = cd->insn_chunk_bitsize; |
364 | 544k | CGEN_INSN_INT value = 0; |
365 | | |
366 | 544k | if (insn_chunk_bitsize != 0 && insn_chunk_bitsize < length) |
367 | 0 | { |
368 | | /* We need to divide up the incoming value into insn_chunk_bitsize-length |
369 | | segments, and endian-convert them, one at a time. */ |
370 | 0 | int i; |
371 | | |
372 | | /* Enforce divisibility. */ |
373 | 0 | if ((length % insn_chunk_bitsize) != 0) |
374 | 0 | abort (); |
375 | | |
376 | 0 | for (i = 0; i < length; i += insn_chunk_bitsize) /* NB: i == bits */ |
377 | 0 | { |
378 | 0 | int bit_index; |
379 | 0 | bfd_vma this_value; |
380 | |
|
381 | 0 | bit_index = i; /* NB: not dependent on endianness; opposite of cgen_put_insn_value! */ |
382 | 0 | this_value = bfd_get_bits (& buf[bit_index / 8], insn_chunk_bitsize, big_p); |
383 | 0 | value = (value << insn_chunk_bitsize) | this_value; |
384 | 0 | } |
385 | 0 | } |
386 | 544k | else |
387 | 544k | { |
388 | 544k | value = bfd_get_bits (buf, length, endian == CGEN_ENDIAN_BIG); |
389 | 544k | } |
390 | | |
391 | 544k | return value; |
392 | 544k | } |
393 | | |
394 | | /* Cover function to store an insn value properly byteswapped. */ |
395 | | |
396 | | void |
397 | | cgen_put_insn_value (CGEN_CPU_DESC cd, |
398 | | unsigned char *buf, |
399 | | int length, |
400 | | CGEN_INSN_INT value, |
401 | | int endian) |
402 | 0 | { |
403 | 0 | int big_p = (endian == CGEN_ENDIAN_BIG); |
404 | 0 | int insn_chunk_bitsize = cd->insn_chunk_bitsize; |
405 | |
|
406 | 0 | if (insn_chunk_bitsize != 0 && insn_chunk_bitsize < length) |
407 | 0 | { |
408 | | /* We need to divide up the incoming value into insn_chunk_bitsize-length |
409 | | segments, and endian-convert them, one at a time. */ |
410 | 0 | int i; |
411 | | |
412 | | /* Enforce divisibility. */ |
413 | 0 | if ((length % insn_chunk_bitsize) != 0) |
414 | 0 | abort (); |
415 | | |
416 | 0 | for (i = 0; i < length; i += insn_chunk_bitsize) /* NB: i == bits */ |
417 | 0 | { |
418 | 0 | int bit_index; |
419 | |
|
420 | 0 | bit_index = (length - insn_chunk_bitsize - i); /* NB: not dependent on endianness! */ |
421 | 0 | bfd_put_bits ((bfd_vma) value, & buf[bit_index / 8], insn_chunk_bitsize, big_p); |
422 | 0 | value >>= insn_chunk_bitsize; |
423 | 0 | } |
424 | 0 | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | bfd_put_bits ((bfd_vma) value, buf, length, big_p); |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | /* Look up instruction INSN_*_VALUE and extract its fields. |
432 | | INSN_INT_VALUE is used if CGEN_INT_INSN_P. |
433 | | Otherwise INSN_BYTES_VALUE is used. |
434 | | INSN, if non-null, is the insn table entry. |
435 | | Otherwise INSN_*_VALUE is examined to compute it. |
436 | | LENGTH is the bit length of INSN_*_VALUE if known, otherwise 0. |
437 | | 0 is only valid if `insn == NULL && ! CGEN_INT_INSN_P'. |
438 | | If INSN != NULL, LENGTH must be valid. |
439 | | ALIAS_P is non-zero if alias insns are to be included in the search. |
440 | | |
441 | | The result is a pointer to the insn table entry, or NULL if the instruction |
442 | | wasn't recognized. */ |
443 | | |
444 | | /* ??? Will need to be revisited for VLIW architectures. */ |
445 | | |
446 | | const CGEN_INSN * |
447 | | cgen_lookup_insn (CGEN_CPU_DESC cd, |
448 | | const CGEN_INSN *insn, |
449 | | CGEN_INSN_INT insn_int_value, |
450 | | /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ |
451 | | unsigned char *insn_bytes_value, |
452 | | int length, |
453 | | CGEN_FIELDS *fields, |
454 | | int alias_p) |
455 | 0 | { |
456 | 0 | CGEN_EXTRACT_INFO ex_info; |
457 | 0 | CGEN_EXTRACT_INFO *info; |
458 | |
|
459 | 0 | if (cd->int_insn_p) |
460 | 0 | { |
461 | 0 | info = NULL; |
462 | 0 | insn_bytes_value = (unsigned char *) xmalloc (cd->max_insn_bitsize / 8); |
463 | 0 | cgen_put_insn_value (cd, insn_bytes_value, length, insn_int_value, |
464 | 0 | cd->insn_endian); |
465 | 0 | } |
466 | 0 | else |
467 | 0 | { |
468 | 0 | info = &ex_info; |
469 | 0 | ex_info.dis_info = NULL; |
470 | 0 | ex_info.insn_bytes = insn_bytes_value; |
471 | 0 | ex_info.valid = -1; |
472 | 0 | insn_int_value = cgen_get_insn_value (cd, insn_bytes_value, length, |
473 | 0 | cd->insn_endian); |
474 | 0 | } |
475 | |
|
476 | 0 | if (!insn) |
477 | 0 | { |
478 | 0 | const CGEN_INSN_LIST *insn_list; |
479 | | |
480 | | /* The instructions are stored in hash lists. |
481 | | Pick the first one and keep trying until we find the right one. */ |
482 | |
|
483 | 0 | insn_list = cgen_dis_lookup_insn (cd, (char *) insn_bytes_value, |
484 | 0 | insn_int_value); |
485 | 0 | while (insn_list != NULL) |
486 | 0 | { |
487 | 0 | insn = insn_list->insn; |
488 | |
|
489 | 0 | if (alias_p |
490 | | /* FIXME: Ensure ALIAS attribute always has same index. */ |
491 | 0 | || ! CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_ALIAS)) |
492 | 0 | { |
493 | | /* Basic bit mask must be correct. */ |
494 | | /* ??? May wish to allow target to defer this check until the |
495 | | extract handler. */ |
496 | 0 | if ((insn_int_value & CGEN_INSN_BASE_MASK (insn)) |
497 | 0 | == CGEN_INSN_BASE_VALUE (insn)) |
498 | 0 | { |
499 | | /* ??? 0 is passed for `pc' */ |
500 | 0 | int elength = CGEN_EXTRACT_FN (cd, insn) |
501 | 0 | (cd, insn, info, insn_int_value, fields, (bfd_vma) 0); |
502 | 0 | if (elength > 0) |
503 | 0 | { |
504 | | /* sanity check */ |
505 | 0 | if (length != 0 && length != elength) |
506 | 0 | abort (); |
507 | 0 | break; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | 0 | insn_list = insn_list->next; |
513 | 0 | } |
514 | 0 | } |
515 | 0 | else |
516 | 0 | { |
517 | | /* Sanity check: can't pass an alias insn if ! alias_p. */ |
518 | 0 | if (! alias_p |
519 | 0 | && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_ALIAS)) |
520 | 0 | abort (); |
521 | | /* Sanity check: length must be correct. */ |
522 | 0 | if (length != CGEN_INSN_BITSIZE (insn)) |
523 | 0 | abort (); |
524 | | |
525 | | /* ??? 0 is passed for `pc' */ |
526 | 0 | length = CGEN_EXTRACT_FN (cd, insn) |
527 | 0 | (cd, insn, info, insn_int_value, fields, (bfd_vma) 0); |
528 | | /* Sanity check: must succeed. |
529 | | Could relax this later if it ever proves useful. */ |
530 | 0 | if (length == 0) |
531 | 0 | abort (); |
532 | 0 | } |
533 | | |
534 | 0 | if (cd->int_insn_p) |
535 | 0 | free (insn_bytes_value); |
536 | |
|
537 | 0 | return insn; |
538 | 0 | } |
539 | | |
540 | | /* Fill in the operand instances used by INSN whose operands are FIELDS. |
541 | | INDICES is a pointer to a buffer of MAX_OPERAND_INSTANCES ints to be filled |
542 | | in. */ |
543 | | |
544 | | void |
545 | | cgen_get_insn_operands (CGEN_CPU_DESC cd, |
546 | | const CGEN_INSN *insn, |
547 | | const CGEN_FIELDS *fields, |
548 | | int *indices) |
549 | 0 | { |
550 | 0 | const CGEN_OPINST *opinst; |
551 | 0 | int i; |
552 | |
|
553 | 0 | if (insn->opinst == NULL) |
554 | 0 | abort (); |
555 | 0 | for (i = 0, opinst = insn->opinst; opinst->type != CGEN_OPINST_END; ++i, ++opinst) |
556 | 0 | { |
557 | 0 | enum cgen_operand_type op_type = opinst->op_type; |
558 | 0 | if (op_type == CGEN_OPERAND_NIL) |
559 | 0 | indices[i] = opinst->index; |
560 | 0 | else |
561 | 0 | indices[i] = (*cd->get_int_operand) (cd, op_type, fields); |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | /* Cover function to cgen_get_insn_operands when either INSN or FIELDS |
566 | | isn't known. |
567 | | The INSN, INSN_*_VALUE, and LENGTH arguments are passed to |
568 | | cgen_lookup_insn unchanged. |
569 | | INSN_INT_VALUE is used if CGEN_INT_INSN_P. |
570 | | Otherwise INSN_BYTES_VALUE is used. |
571 | | |
572 | | The result is the insn table entry or NULL if the instruction wasn't |
573 | | recognized. */ |
574 | | |
575 | | const CGEN_INSN * |
576 | | cgen_lookup_get_insn_operands (CGEN_CPU_DESC cd, |
577 | | const CGEN_INSN *insn, |
578 | | CGEN_INSN_INT insn_int_value, |
579 | | /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ |
580 | | unsigned char *insn_bytes_value, |
581 | | int length, |
582 | | int *indices, |
583 | | CGEN_FIELDS *fields) |
584 | 0 | { |
585 | | /* Pass non-zero for ALIAS_P only if INSN != NULL. |
586 | | If INSN == NULL, we want a real insn. */ |
587 | 0 | insn = cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, |
588 | 0 | length, fields, insn != NULL); |
589 | 0 | if (! insn) |
590 | 0 | return NULL; |
591 | | |
592 | 0 | cgen_get_insn_operands (cd, insn, fields, indices); |
593 | 0 | return insn; |
594 | 0 | } |
595 | | |
596 | | /* Allow signed overflow of instruction fields. */ |
597 | | void |
598 | | cgen_set_signed_overflow_ok (CGEN_CPU_DESC cd) |
599 | 0 | { |
600 | 0 | cd->signed_overflow_ok_p = 1; |
601 | 0 | } |
602 | | |
603 | | /* Generate an error message if a signed field in an instruction overflows. */ |
604 | | void |
605 | | cgen_clear_signed_overflow_ok (CGEN_CPU_DESC cd) |
606 | 0 | { |
607 | 0 | cd->signed_overflow_ok_p = 0; |
608 | 0 | } |
609 | | |
610 | | /* Will an error message be generated if a signed field in an instruction overflows ? */ |
611 | | unsigned int |
612 | | cgen_signed_overflow_ok_p (CGEN_CPU_DESC cd) |
613 | 0 | { |
614 | 0 | return cd->signed_overflow_ok_p; |
615 | 0 | } |