1 1.1 christos /* CGEN generic assembler support code. 2 1.10 christos Copyright (C) 1996-2025 Free Software Foundation, Inc. 3 1.1 christos 4 1.1 christos This file is part of libopcodes. 5 1.1 christos 6 1.1 christos This library is free software; you can redistribute it and/or modify 7 1.1 christos it under the terms of the GNU General Public License as published by 8 1.1 christos the Free Software Foundation; either version 3, or (at your option) 9 1.1 christos any later version. 10 1.1 christos 11 1.1 christos It is distributed in the hope that it will be useful, but WITHOUT 12 1.1 christos ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 1.1 christos or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 1.1 christos License for more details. 15 1.1 christos 16 1.1 christos 17 1.1 christos You should have received a copy of the GNU General Public License along 18 1.1 christos with this program; if not, write to the Free Software Foundation, Inc., 19 1.1 christos 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 20 1.1 christos 21 1.1 christos #include "sysdep.h" 22 1.1 christos #include <stdio.h> 23 1.1 christos #include "ansidecl.h" 24 1.1 christos #include "libiberty.h" 25 1.1 christos #include "safe-ctype.h" 26 1.1 christos #include "bfd.h" 27 1.1 christos #include "symcat.h" 28 1.1 christos #include "opcode/cgen.h" 29 1.1 christos #include "opintl.h" 30 1.1 christos 31 1.1 christos static CGEN_INSN_LIST * hash_insn_array (CGEN_CPU_DESC, const CGEN_INSN *, int, int, CGEN_INSN_LIST **, CGEN_INSN_LIST *); 32 1.1 christos static CGEN_INSN_LIST * hash_insn_list (CGEN_CPU_DESC, const CGEN_INSN_LIST *, CGEN_INSN_LIST **, CGEN_INSN_LIST *); 33 1.1 christos static void build_asm_hash_table (CGEN_CPU_DESC); 34 1.1 christos 35 1.1 christos /* Set the cgen_parse_operand_fn callback. */ 36 1.1 christos 37 1.1 christos void 38 1.1 christos cgen_set_parse_operand_fn (CGEN_CPU_DESC cd, cgen_parse_operand_fn fn) 39 1.1 christos { 40 1.1 christos cd->parse_operand_fn = fn; 41 1.1 christos } 42 1.1 christos 43 1.1 christos /* Called whenever starting to parse an insn. */ 44 1.1 christos 45 1.1 christos void 46 1.1 christos cgen_init_parse_operand (CGEN_CPU_DESC cd) 47 1.1 christos { 48 1.1 christos /* This tells the callback to re-initialize. */ 49 1.1 christos (void) (* cd->parse_operand_fn) 50 1.1 christos (cd, CGEN_PARSE_OPERAND_INIT, NULL, 0, 0, NULL, NULL); 51 1.1 christos } 52 1.1 christos 53 1.1 christos /* Subroutine of build_asm_hash_table to add INSNS to the hash table. 54 1.1 christos 55 1.1 christos COUNT is the number of elements in INSNS. 56 1.1 christos ENTSIZE is sizeof (CGEN_IBASE) for the target. 57 1.1 christos ??? No longer used but leave in for now. 58 1.1 christos HTABLE points to the hash table. 59 1.1 christos HENTBUF is a pointer to sufficiently large buffer of hash entries. 60 1.1 christos The result is a pointer to the next entry to use. 61 1.1 christos 62 1.1 christos The table is scanned backwards as additions are made to the front of the 63 1.8 christos list and we want earlier ones to be preferred. */ 64 1.1 christos 65 1.1 christos static CGEN_INSN_LIST * 66 1.1 christos hash_insn_array (CGEN_CPU_DESC cd, 67 1.1 christos const CGEN_INSN *insns, 68 1.1 christos int count, 69 1.1 christos int entsize ATTRIBUTE_UNUSED, 70 1.1 christos CGEN_INSN_LIST **htable, 71 1.1 christos CGEN_INSN_LIST *hentbuf) 72 1.1 christos { 73 1.1 christos int i; 74 1.1 christos 75 1.1 christos for (i = count - 1; i >= 0; --i, ++hentbuf) 76 1.1 christos { 77 1.1 christos unsigned int hash; 78 1.1 christos const CGEN_INSN *insn = &insns[i]; 79 1.1 christos 80 1.1 christos if (! (* cd->asm_hash_p) (insn)) 81 1.1 christos continue; 82 1.1 christos hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (insn)); 83 1.1 christos hentbuf->next = htable[hash]; 84 1.1 christos hentbuf->insn = insn; 85 1.1 christos htable[hash] = hentbuf; 86 1.1 christos } 87 1.1 christos 88 1.1 christos return hentbuf; 89 1.1 christos } 90 1.1 christos 91 1.1 christos /* Subroutine of build_asm_hash_table to add INSNS to the hash table. 92 1.1 christos This function is identical to hash_insn_array except the insns are 93 1.1 christos in a list. */ 94 1.1 christos 95 1.1 christos static CGEN_INSN_LIST * 96 1.1 christos hash_insn_list (CGEN_CPU_DESC cd, 97 1.1 christos const CGEN_INSN_LIST *insns, 98 1.1 christos CGEN_INSN_LIST **htable, 99 1.1 christos CGEN_INSN_LIST *hentbuf) 100 1.1 christos { 101 1.1 christos const CGEN_INSN_LIST *ilist; 102 1.1 christos 103 1.1 christos for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf) 104 1.1 christos { 105 1.1 christos unsigned int hash; 106 1.1 christos 107 1.1 christos if (! (* cd->asm_hash_p) (ilist->insn)) 108 1.1 christos continue; 109 1.1 christos hash = (* cd->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn)); 110 1.1 christos hentbuf->next = htable[hash]; 111 1.1 christos hentbuf->insn = ilist->insn; 112 1.1 christos htable[hash] = hentbuf; 113 1.1 christos } 114 1.1 christos 115 1.1 christos return hentbuf; 116 1.1 christos } 117 1.1 christos 118 1.1 christos /* Build the assembler instruction hash table. */ 119 1.1 christos 120 1.1 christos static void 121 1.1 christos build_asm_hash_table (CGEN_CPU_DESC cd) 122 1.1 christos { 123 1.1 christos int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd); 124 1.1 christos CGEN_INSN_TABLE *insn_table = &cd->insn_table; 125 1.1 christos CGEN_INSN_TABLE *macro_insn_table = &cd->macro_insn_table; 126 1.1 christos unsigned int hash_size = cd->asm_hash_size; 127 1.1 christos CGEN_INSN_LIST *hash_entry_buf; 128 1.1 christos CGEN_INSN_LIST **asm_hash_table; 129 1.1 christos CGEN_INSN_LIST *asm_hash_table_entries; 130 1.1 christos 131 1.1 christos /* The space allocated for the hash table consists of two parts: 132 1.1 christos the hash table and the hash lists. */ 133 1.1 christos 134 1.1 christos asm_hash_table = (CGEN_INSN_LIST **) 135 1.1 christos xmalloc (hash_size * sizeof (CGEN_INSN_LIST *)); 136 1.1 christos memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *)); 137 1.1 christos asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *) 138 1.1 christos xmalloc (count * sizeof (CGEN_INSN_LIST)); 139 1.1 christos 140 1.1 christos /* Add compiled in insns. 141 1.1 christos Don't include the first one as it is a reserved entry. */ 142 1.1 christos /* ??? It was the end of all hash chains, and also the special 143 1.1 christos "invalid insn" marker. May be able to do it differently now. */ 144 1.1 christos 145 1.1 christos hash_entry_buf = hash_insn_array (cd, 146 1.1 christos insn_table->init_entries + 1, 147 1.1 christos insn_table->num_init_entries - 1, 148 1.1 christos insn_table->entry_size, 149 1.1 christos asm_hash_table, hash_entry_buf); 150 1.1 christos 151 1.1 christos /* Add compiled in macro-insns. */ 152 1.1 christos 153 1.1 christos hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries, 154 1.1 christos macro_insn_table->num_init_entries, 155 1.1 christos macro_insn_table->entry_size, 156 1.1 christos asm_hash_table, hash_entry_buf); 157 1.1 christos 158 1.1 christos /* Add runtime added insns. 159 1.8 christos Later added insns will be preferred over earlier ones. */ 160 1.1 christos 161 1.1 christos hash_entry_buf = hash_insn_list (cd, insn_table->new_entries, 162 1.1 christos asm_hash_table, hash_entry_buf); 163 1.1 christos 164 1.1 christos /* Add runtime added macro-insns. */ 165 1.1 christos 166 1.1 christos hash_insn_list (cd, macro_insn_table->new_entries, 167 1.1 christos asm_hash_table, hash_entry_buf); 168 1.1 christos 169 1.1 christos cd->asm_hash_table = asm_hash_table; 170 1.1 christos cd->asm_hash_table_entries = asm_hash_table_entries; 171 1.1 christos } 172 1.1 christos 173 1.1 christos /* Return the first entry in the hash list for INSN. */ 174 1.1 christos 175 1.1 christos CGEN_INSN_LIST * 176 1.1 christos cgen_asm_lookup_insn (CGEN_CPU_DESC cd, const char *insn) 177 1.1 christos { 178 1.1 christos unsigned int hash; 179 1.1 christos 180 1.1 christos if (cd->asm_hash_table == NULL) 181 1.1 christos build_asm_hash_table (cd); 182 1.1 christos 183 1.1 christos hash = (* cd->asm_hash) (insn); 184 1.1 christos return cd->asm_hash_table[hash]; 185 1.1 christos } 186 1.1 christos 187 1.1 christos /* Keyword parser. 189 1.1 christos The result is NULL upon success or an error message. 190 1.1 christos If successful, *STRP is updated to point passed the keyword. 191 1.1 christos 192 1.1 christos ??? At present we have a static notion of how to pick out a keyword. 193 1.1 christos Later we can allow a target to customize this if necessary [say by 194 1.1 christos recording something in the keyword table]. */ 195 1.1 christos 196 1.1 christos const char * 197 1.1 christos cgen_parse_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 198 1.1 christos const char **strp, 199 1.1 christos CGEN_KEYWORD *keyword_table, 200 1.1 christos long *valuep) 201 1.1 christos { 202 1.1 christos const CGEN_KEYWORD_ENTRY *ke; 203 1.1 christos char buf[256]; 204 1.1 christos const char *p,*start; 205 1.1 christos 206 1.1 christos if (keyword_table->name_hash_table == NULL) 207 1.1 christos (void) cgen_keyword_search_init (keyword_table, NULL); 208 1.1 christos 209 1.1 christos p = start = *strp; 210 1.1 christos 211 1.1 christos /* Allow any first character. This is to make life easier for 212 1.1 christos the fairly common case of suffixes, eg. 'ld.b.w', where the first 213 1.1 christos character of the suffix ('.') is special. */ 214 1.1 christos if (*p) 215 1.3 christos ++p; 216 1.1 christos 217 1.1 christos /* Allow letters, digits, and any special characters. */ 218 1.1 christos while (((p - start) < (int) sizeof (buf)) 219 1.1 christos && *p 220 1.1 christos && (ISALNUM (*p) 221 1.1 christos || *p == '_' 222 1.1 christos || strchr (keyword_table->nonalpha_chars, *p))) 223 1.1 christos ++p; 224 1.1 christos 225 1.1 christos if (p - start >= (int) sizeof (buf)) 226 1.1 christos { 227 1.1 christos /* All non-empty CGEN keywords can fit into BUF. The only thing 228 1.1 christos we can match here is the empty keyword. */ 229 1.1 christos buf[0] = 0; 230 1.1 christos } 231 1.1 christos else 232 1.1 christos { 233 1.1 christos memcpy (buf, start, p - start); 234 1.1 christos buf[p - start] = 0; 235 1.1 christos } 236 1.1 christos 237 1.1 christos ke = cgen_keyword_lookup_name (keyword_table, buf); 238 1.1 christos 239 1.1 christos if (ke != NULL) 240 1.1 christos { 241 1.1 christos *valuep = ke->value; 242 1.1 christos /* Don't advance pointer if we recognized the null keyword. */ 243 1.1 christos if (ke->name[0] != 0) 244 1.1 christos *strp = p; 245 1.1 christos return NULL; 246 1.1 christos } 247 1.1 christos 248 1.1 christos return "unrecognized keyword/register name"; 249 1.1 christos } 250 1.1 christos 251 1.1 christos /* Parse a small signed integer parser. 252 1.1 christos ??? VALUEP is not a bfd_vma * on purpose, though this is confusing. 253 1.1 christos Note that if the caller expects a bfd_vma result, it should call 254 1.1 christos cgen_parse_address. */ 255 1.1 christos 256 1.1 christos const char * 257 1.1 christos cgen_parse_signed_integer (CGEN_CPU_DESC cd, 258 1.1 christos const char **strp, 259 1.1 christos int opindex, 260 1.1 christos long *valuep) 261 1.1 christos { 262 1.1 christos bfd_vma value; 263 1.1 christos enum cgen_parse_operand_result result; 264 1.1 christos const char *errmsg; 265 1.1 christos 266 1.1 christos errmsg = (* cd->parse_operand_fn) 267 1.1 christos (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE, 268 1.1 christos &result, &value); 269 1.1 christos /* FIXME: Examine `result'. */ 270 1.1 christos if (!errmsg) 271 1.1 christos { 272 1.1 christos /* Handle the case where a hex value is parsed on a 64-bit host. 273 1.1 christos A value like 0xffffe000 is clearly intended to be a negative 274 1.1 christos 16-bit value, but on a 64-bit host it will be parsed by gas 275 1.1 christos as 0x00000000ffffe000. 276 1.1 christos 277 1.1 christos The shifts below are designed not to produce compile time 278 1.1 christos warnings on a 32-bit host. */ 279 1.1 christos if (sizeof (value) > 4 280 1.1 christos && result == CGEN_PARSE_OPERAND_RESULT_NUMBER 281 1.1 christos && value > 0 282 1.1 christos && (value & 0x80000000) 283 1.3 christos && ((value >> 31) == 1)) 284 1.1 christos value |= ((bfd_vma) -1) << 31; 285 1.1 christos 286 1.1 christos *valuep = value; 287 1.1 christos } 288 1.1 christos return errmsg; 289 1.1 christos } 290 1.1 christos 291 1.1 christos /* Parse a small unsigned integer parser. 292 1.1 christos ??? VALUEP is not a bfd_vma * on purpose, though this is confusing. 293 1.1 christos Note that if the caller expects a bfd_vma result, it should call 294 1.1 christos cgen_parse_address. */ 295 1.1 christos 296 1.1 christos const char * 297 1.1 christos cgen_parse_unsigned_integer (CGEN_CPU_DESC cd, 298 1.1 christos const char **strp, 299 1.1 christos int opindex, 300 1.1 christos unsigned long *valuep) 301 1.1 christos { 302 1.1 christos bfd_vma value; 303 1.1 christos enum cgen_parse_operand_result result; 304 1.1 christos const char *errmsg; 305 1.1 christos 306 1.1 christos errmsg = (* cd->parse_operand_fn) 307 1.1 christos (cd, CGEN_PARSE_OPERAND_INTEGER, strp, opindex, BFD_RELOC_NONE, 308 1.1 christos &result, &value); 309 1.1 christos /* FIXME: Examine `result'. */ 310 1.1 christos if (!errmsg) 311 1.1 christos *valuep = value; 312 1.1 christos return errmsg; 313 1.1 christos } 314 1.1 christos 315 1.1 christos /* Address parser. */ 316 1.1 christos 317 1.1 christos const char * 318 1.1 christos cgen_parse_address (CGEN_CPU_DESC cd, 319 1.1 christos const char **strp, 320 1.1 christos int opindex, 321 1.1 christos int opinfo, 322 1.1 christos enum cgen_parse_operand_result *resultp, 323 1.1 christos bfd_vma *valuep) 324 1.1 christos { 325 1.1 christos bfd_vma value; 326 1.1 christos enum cgen_parse_operand_result result_type; 327 1.1 christos const char *errmsg; 328 1.1 christos 329 1.1 christos errmsg = (* cd->parse_operand_fn) 330 1.1 christos (cd, CGEN_PARSE_OPERAND_ADDRESS, strp, opindex, opinfo, 331 1.1 christos &result_type, &value); 332 1.1 christos /* FIXME: Examine `result'. */ 333 1.1 christos if (!errmsg) 334 1.1 christos { 335 1.1 christos if (resultp != NULL) 336 1.1 christos *resultp = result_type; 337 1.1 christos *valuep = value; 338 1.1 christos } 339 1.1 christos return errmsg; 340 1.1 christos } 341 1.1 christos 342 1.1 christos /* Signed integer validation routine. */ 344 1.1 christos 345 1.1 christos const char * 346 1.1 christos cgen_validate_signed_integer (long value, long min, long max) 347 1.1 christos { 348 1.1 christos if (value < min || value > max) 349 1.1 christos { 350 1.1 christos static char buf[100]; 351 1.1 christos 352 1.1 christos /* xgettext:c-format */ 353 1.1 christos sprintf (buf, _("operand out of range (%ld not between %ld and %ld)"), 354 1.1 christos value, min, max); 355 1.1 christos return buf; 356 1.1 christos } 357 1.1 christos 358 1.1 christos return NULL; 359 1.1 christos } 360 1.1 christos 361 1.1 christos /* Unsigned integer validation routine. 362 1.1 christos Supplying `min' here may seem unnecessary, but we also want to handle 363 1.1 christos cases where min != 0 (and max > LONG_MAX). */ 364 1.1 christos 365 1.1 christos const char * 366 1.1 christos cgen_validate_unsigned_integer (unsigned long value, 367 1.1 christos unsigned long min, 368 1.1 christos unsigned long max) 369 1.1 christos { 370 1.1 christos if (value < min || value > max) 371 1.1 christos { 372 1.1 christos static char buf[100]; 373 1.1 christos 374 1.1 christos /* xgettext:c-format */ 375 1.1 christos sprintf (buf, _("operand out of range (%lu not between %lu and %lu)"), 376 1.1 christos value, min, max); 377 1.1 christos return buf; 378 1.1 christos } 379 1.1 christos 380 return NULL; 381 } 382