1 1.1 christos /* kvx-parse.c -- Recursive decent parser driver for the KVX ISA 2 1.1 christos 3 1.1.1.2 christos Copyright (C) 2009-2025 Free Software Foundation, Inc. 4 1.1 christos Contributed by Kalray SA. 5 1.1 christos 6 1.1 christos This file is part of GAS. 7 1.1 christos 8 1.1 christos GAS is free software; you can redistribute it and/or modify 9 1.1 christos it under the terms of the GNU General Public License as published by 10 1.1 christos the Free Software Foundation; either version 3 of the license, or 11 1.1 christos (at your option) any later version. 12 1.1 christos 13 1.1 christos GAS is distributed in the hope that it will be useful, 14 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 15 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 1.1 christos GNU General Public License for more details. 17 1.1 christos 18 1.1 christos You should have received a copy of the GNU General Public License 19 1.1 christos along with this program; see the file COPYING3. If not, 20 1.1 christos see <http://www.gnu.org/licenses/>. */ 21 1.1 christos 22 1.1 christos #include "as.h" 23 1.1 christos #include <stdio.h> 24 1.1 christos #include <stdlib.h> 25 1.1 christos #include <string.h> 26 1.1 christos #include <ctype.h> 27 1.1 christos #include <stdarg.h> 28 1.1 christos #include <elf/kvx_elfids.h> 29 1.1 christos #include "kvx-parse.h" 30 1.1 christos 31 1.1 christos /* This is bad! */ 32 1.1 christos struct node_list_s { 33 1.1 christos struct node_s *node; 34 1.1 christos struct node_list_s *nxt; 35 1.1 christos }; 36 1.1 christos 37 1.1 christos struct node_s { 38 1.1 christos char *val; 39 1.1 christos int len; 40 1.1 christos struct node_list_s *succs; 41 1.1 christos int nb_succs; 42 1.1 christos }; 43 1.1 christos 44 1.1 christos 45 1.1 christos 47 1.1 christos static int 48 1.1 christos has_relocation_of_size (const struct kvx_reloc **relocs) 49 1.1 christos { 50 1.1 christos const int symbol_size = env.params.arch_size; 51 1.1 christos 52 1.1 christos /* 53 1.1 christos * This is a bit hackish: in case of PCREL here, it means we are 54 1.1 christos * trying to fit a symbol in the insn, not a pseudo function 55 1.1 christos * (eg. @gotaddr, ...). 56 1.1 christos * We don't want to use a GOTADDR (pcrel) in any insn that tries to fit a symbol. 57 1.1 christos * One way to filter out these is to use the following assumption: 58 1.1 christos * - Any insn that accepts a pcrel immediate has only one immediate variant. 59 1.1 christos * Example: 60 1.1 christos * - call accepts only a pcrel27 -> allow pcrel reloc here 61 1.1 christos * - cb accepts only a pcrel17 -> allow pcrel reloc here 62 1.1 christos * - addd accepts signed10,37,64 -> deny pcrel reloc here 63 1.1 christos * 64 1.1 christos * The motivation here is to prevent the function to allow a 64bits 65 1.1 christos * symbol in a 37bits variant of any ALU insn (that would match with 66 1.1 christos * the GOTADDR 37bits reloc switch case below) 67 1.1 christos */ 68 1.1 christos 69 1.1 christos if (!relocs) 70 1.1 christos return 0; 71 1.1.1.2 christos 72 1.1 christos const struct kvx_reloc **relocs_it = relocs; 73 1.1 christos int has_only_one_p = relocs[0] && !relocs[1]; 74 1.1 christos 75 1.1 christos while (*relocs_it) 76 1.1 christos { 77 1.1 christos switch ((*relocs_it)->relative) 78 1.1 christos { 79 1.1 christos /* An absolute reloc needs a full size symbol reloc */ 80 1.1 christos case KVX_REL_ABS: 81 1.1 christos if ((*relocs_it)->bitsize >= symbol_size) 82 1.1 christos return 1; 83 1.1 christos break; 84 1.1 christos 85 1.1 christos /* Most likely relative jumps. Let something else check size is 86 1.1 christos OK. We don't currently have several relocations for such insns */ 87 1.1 christos case KVX_REL_PC: 88 1.1 christos if (has_only_one_p) 89 1.1 christos return 1; 90 1.1 christos break; 91 1.1 christos 92 1.1 christos /* These relocations should be handled elsewhere with pseudo functions */ 93 1.1 christos case KVX_REL_GP: 94 1.1 christos case KVX_REL_TP: 95 1.1 christos case KVX_REL_GOT: 96 1.1 christos case KVX_REL_BASE: 97 1.1 christos break; 98 1.1 christos } 99 1.1 christos relocs_it++; 100 1.1 christos } 101 1.1 christos 102 1.1 christos return 0; 103 1.1 christos } 104 1.1.1.2 christos 105 1.1.1.2 christos static struct pseudo_func * 106 1.1 christos kvx_get_pseudo_func2 (symbolS *sym, const struct kvx_reloc **relocs) 107 1.1 christos { 108 1.1 christos if (!relocs) 109 1.1 christos return NULL; 110 1.1.1.2 christos 111 1.1 christos const struct kvx_reloc **relocs_it = relocs; 112 1.1 christos 113 1.1 christos for (int i = 0; i < 26; i++) 114 1.1 christos { 115 1.1 christos if (sym == kvx_core_info->pseudo_funcs[i].sym) 116 1.1 christos { 117 1.1 christos relocs_it = relocs; 118 1.1 christos while (*relocs_it) 119 1.1 christos { 120 1.1 christos if (*relocs_it == kvx_core_info->pseudo_funcs[i].pseudo_relocs.kreloc 121 1.1 christos && (env.params.arch_size == (int) kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes 122 1.1 christos || kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes == PSEUDO_ALL)) 123 1.1 christos return &kvx_core_info->pseudo_funcs[i]; 124 1.1 christos relocs_it++; 125 1.1 christos } 126 1.1 christos } 127 1.1 christos } 128 1.1 christos 129 1.1 christos return NULL; 130 1.1 christos } 131 1.1 christos 132 1.1 christos /* Trie */ 134 1.1 christos 135 1.1 christos static 136 1.1 christos struct node_list_s * 137 1.1 christos insert_in_succ_list (struct node_s *node, struct node_s *base) 138 1.1 christos { 139 1.1 christos struct node_list_s *new_hd = NULL; 140 1.1 christos if (!(new_hd = calloc (1, sizeof (*new_hd)))) 141 1.1 christos return NULL; 142 1.1 christos 143 1.1 christos new_hd->node = node; 144 1.1 christos new_hd->nxt = base->succs; 145 1.1 christos base->nb_succs += 1; 146 1.1 christos return new_hd; 147 1.1 christos } 148 1.1 christos 149 1.1 christos static 150 1.1 christos struct node_s * 151 1.1 christos make_node (const char *str, int len) 152 1.1 christos { 153 1.1 christos struct node_s *n = NULL; 154 1.1 christos if (!(n = calloc (1, sizeof (*n)))) 155 1.1 christos goto err; 156 1.1 christos 157 1.1 christos n->len = len; 158 1.1 christos n->succs = NULL; 159 1.1 christos if (!(n->val = calloc (n->len + 1, sizeof (*n->val)))) 160 1.1 christos goto err1; 161 1.1 christos 162 1.1 christos strncpy (n->val, str, n->len); 163 1.1 christos return n; 164 1.1 christos 165 1.1 christos err1: 166 1.1 christos free (n), n = NULL; 167 1.1 christos err: 168 1.1 christos return NULL; 169 1.1 christos } 170 1.1 christos 171 1.1 christos static 172 1.1 christos struct node_s * 173 1.1 christos insert (const char *str, struct node_s *node) 174 1.1 christos { 175 1.1 christos int i = 0; 176 1.1 christos int len = strlen (str); 177 1.1 christos 178 1.1 christos if (!node) 179 1.1 christos { 180 1.1 christos struct node_s *n = make_node (str, len); 181 1.1 christos n->succs = insert_in_succ_list (NULL, n); 182 1.1 christos return n; 183 1.1 christos } 184 1.1 christos 185 1.1 christos while (i < len && i < node->len && str[i] == node->val[i]) 186 1.1 christos ++i; 187 1.1 christos 188 1.1 christos /* The strings share a prefix. */ 189 1.1 christos if (i < len && i < node->len) 190 1.1 christos { 191 1.1 christos /* Split the current node on that common prefix. */ 192 1.1 christos 193 1.1 christos /* Create a new node with only the unshared suffix, and makes it inherit 194 1.1 christos the successor of the node under consideration. */ 195 1.1 christos struct node_s *suf = make_node (node->val + i, node->len - i); 196 1.1 christos suf->succs = node->succs; 197 1.1 christos suf->nb_succs = node->nb_succs; 198 1.1 christos /* Insert the remainder on the other branch */ 199 1.1 christos struct node_s *rem = make_node (str + i, len - i); 200 1.1 christos rem->succs = insert_in_succ_list (NULL, rem); 201 1.1 christos 202 1.1 christos node->val[i] = '\0'; 203 1.1 christos node->len = i; 204 1.1 christos node->succs = NULL; 205 1.1 christos node->nb_succs = 0; 206 1.1 christos node->succs = insert_in_succ_list (suf, node); 207 1.1 christos node->succs = insert_in_succ_list (rem, node); 208 1.1 christos return node; 209 1.1 christos } 210 1.1 christos 211 1.1 christos /* str is a strict prefix of node->val */ 212 1.1 christos if (i == len && i < node->len) 213 1.1 christos { 214 1.1 christos /* Split the current node at position */ 215 1.1 christos struct node_s *suf = make_node (node->val + i, node->len - i); 216 1.1 christos suf->succs = node->succs; 217 1.1 christos suf->nb_succs = node->nb_succs; 218 1.1 christos node->val[i] = '\0'; 219 1.1 christos node->len = i; 220 1.1 christos /* Insert an empty leaf */ 221 1.1 christos node->succs = NULL; 222 1.1 christos node->nb_succs = 0; 223 1.1 christos node->succs = insert_in_succ_list (NULL, node); 224 1.1 christos node->succs = insert_in_succ_list (suf, node); 225 1.1 christos return node; 226 1.1 christos } 227 1.1 christos 228 1.1 christos /* node->val is a prefix of str */ 229 1.1 christos if (i == node->len) 230 1.1 christos { 231 1.1 christos /* Find a successor of node into which the remainder can be inserted. */ 232 1.1 christos struct node_list_s *cur_succ = node->succs; 233 1.1 christos while (cur_succ) 234 1.1 christos { 235 1.1 christos struct node_s *n = cur_succ->node; 236 1.1 christos if (n && n->val && n->val[0] == str[i]) 237 1.1 christos { 238 1.1 christos cur_succ->node = insert (str + i, cur_succ->node); 239 1.1 christos break; 240 1.1 christos } 241 1.1 christos cur_succ = cur_succ->nxt; 242 1.1 christos } 243 1.1 christos /* No successor shares a common prefix */ 244 1.1 christos if (cur_succ == NULL) 245 1.1 christos { 246 1.1 christos struct node_s *suf = make_node (str + i, len - i); 247 1.1 christos suf->succs = insert_in_succ_list (NULL, suf); 248 1.1 christos node->succs = insert_in_succ_list (suf, node); 249 1.1 christos } 250 1.1 christos return node; 251 1.1 christos } 252 1.1 christos 253 1.1 christos return node; 254 1.1 christos } 255 1.1 christos 256 1.1 christos static 257 1.1 christos void 258 1.1 christos free_node (struct node_s *node) 259 1.1 christos { 260 1.1 christos if (!node) 261 1.1 christos return; 262 1.1 christos 263 1.1 christos free (node->val); 264 1.1 christos 265 1.1 christos struct node_list_s *cur_succ = node->succs; 266 1.1 christos struct node_list_s *tmp = NULL; 267 1.1 christos while ((tmp = cur_succ)) 268 1.1 christos { 269 1.1 christos struct node_s *n = cur_succ->node; 270 1.1 christos if (n) 271 1.1 christos free_node (n), n = NULL; 272 1.1 christos cur_succ = cur_succ->nxt; 273 1.1 christos free (tmp); 274 1.1 christos } 275 1.1 christos 276 1.1 christos free (node); 277 1.1 christos } 278 1.1 christos 279 1.1 christos #define max(a,b) (((a)>(b))?(a):(b)) 280 1.1 christos static 281 1.1 christos int 282 1.1 christos longest_match (const char *str, int len, struct node_s *node) 283 1.1 christos { 284 1.1 christos int i = 0; 285 1.1 christos int last_mark = 0; 286 1.1 christos struct node_s *cur = node; 287 1.1 christos 288 1.1 christos while (1) 289 1.1 christos { 290 1.1 christos if (i + cur->len > len 291 1.1 christos || strncmp (str + i, cur->val, max(0, cur->len))) 292 1.1 christos return last_mark; 293 1.1 christos 294 1.1 christos i += cur->len; 295 1.1 christos struct node_list_s *cur_succ = cur->succs; 296 1.1 christos cur = NULL; 297 1.1 christos while (cur_succ) 298 1.1 christos { 299 1.1 christos struct node_s *n = cur_succ->node; 300 1.1 christos if (!n) 301 1.1 christos last_mark = i; 302 1.1 christos else if (n->val[0] == str[i]) 303 1.1 christos cur = n; 304 1.1 christos cur_succ = cur_succ->nxt; 305 1.1 christos } 306 1.1 christos if (!cur) 307 1.1 christos return last_mark; 308 1.1 christos } 309 1.1 christos } 310 1.1 christos 311 1.1 christos __attribute__((unused)) 312 1.1 christos static void 313 1.1 christos dump_graph_1 (FILE *fd, struct node_s *node, int id) 314 1.1 christos { 315 1.1 christos struct node_list_s *cur_succ = node->succs; 316 1.1 christos int i = 0; 317 1.1 christos 318 1.1 christos if (id == 1) 319 1.1 christos fprintf (fd, "\t%d [label=\"%s\"];\n", id, node->val); 320 1.1 christos 321 1.1 christos while (cur_succ) 322 1.1 christos { 323 1.1 christos if (cur_succ->node == NULL) 324 1.1 christos fprintf (fd, "\t%d -> \"()\";\n", id); 325 1.1 christos else 326 1.1 christos { 327 1.1 christos fprintf (fd, "\t%d [label=\"%s\"];\n", 328 1.1 christos node->nb_succs * id + i, cur_succ->node->val); 329 1.1 christos fprintf (fd, "\t%d -> %d;\n", id, node->nb_succs * id + i); 330 1.1 christos dump_graph_1 (fd, cur_succ->node, node->nb_succs * id + i); 331 1.1 christos } 332 1.1 christos i += 1; 333 1.1 christos cur_succ = cur_succ->nxt; 334 1.1 christos } 335 1.1 christos } 336 1.1 christos 337 1.1 christos __attribute__((unused)) 338 1.1 christos static void 339 1.1 christos dump_graph (char *name, char *path, struct node_s *node) 340 1.1 christos { 341 1.1 christos FILE *fd = fopen (path, "w"); 342 1.1 christos fprintf (fd, "digraph %s {\n", name); 343 1.1 christos 344 1.1 christos dump_graph_1 (fd, node, 1); 345 1.1 christos 346 1.1 christos fprintf (fd, "}\n"); 347 1.1 christos fclose (fd); 348 1.1 christos } 349 1.1 christos 350 1.1 christos __attribute__((unused)) 351 1.1 christos static void 352 1.1 christos print_n (const char *str, int n) 353 1.1 christos { 354 1.1 christos for (int i = 0 ; i < n ; ++i) 355 1.1 christos putchar (str[i]); 356 1.1 christos putchar('\n'); 357 1.1 christos } 358 1.1 christos 359 1.1 christos 360 1.1 christos int debug_level = 0; 362 1.1 christos 363 1.1 christos __attribute__((unused)) 364 1.1 christos static int 365 1.1 christos printf_debug (int lvl, const char *fmt, ...) 366 1.1 christos { 367 1.1 christos int ret = 0; 368 1.1 christos if (debug_level >= lvl) 369 1.1 christos { 370 1.1 christos va_list args; 371 1.1 christos va_start (args, fmt); 372 1.1 christos ret = vprintf (fmt, args); 373 1.1 christos va_end (args); 374 1.1 christos } 375 1.1 christos 376 1.1 christos return ret; 377 1.1 christos } 378 1.1 christos 379 1.1 christos static int 380 1.1 christos is_delim (char c) 381 1.1 christos { 382 1.1 christos char delims[] = { '[', ']', '?', ',', '=' }; 383 1.1 christos int nb_delims = sizeof (delims) / (sizeof (*delims)); 384 1.1 christos for (int i = 0; i < nb_delims; ++i) 385 1.1 christos if (c == delims[i]) 386 1.1 christos return 1; 387 1.1 christos return 0; 388 1.1 christos } 389 1.1 christos 390 1.1 christos __attribute__((unused)) 391 1.1 christos static void 392 1.1 christos print_token (struct token_s token, char *buf, int bufsz) 393 1.1 christos { 394 1.1 christos for (int i = 0; i < token.end - token.begin && i < bufsz; ++i) 395 1.1 christos buf[i] = token.insn[token.begin + i]; 396 1.1 christos for (int i = token.end - token.begin ; i < bufsz; ++i) 397 1.1 christos buf[i] = 0; 398 1.1 christos } 399 1.1 christos 400 1.1 christos static int64_t 401 1.1 christos promote_token (struct token_s tok) 402 1.1 christos { 403 1.1 christos int64_t cur_class = tok.class_id & -tok.class_id; 404 1.1 christos switch (tok.category) 405 1.1 christos { 406 1.1 christos case CAT_REGISTER: 407 1.1 christos case CAT_MODIFIER: 408 1.1 christos return (cur_class != tok.class_id) 409 1.1 christos ? tok.class_id ^ cur_class 410 1.1.1.2 christos : tok.class_id; 411 1.1 christos case CAT_IMMEDIATE: 412 1.1 christos { 413 1.1 christos expressionS exp; 414 1.1 christos char *ilp_save = input_line_pointer; 415 1.1.1.2 christos input_line_pointer = tok.insn + tok.begin; 416 1.1.1.2 christos expression (&exp); 417 1.1.1.2 christos input_line_pointer = ilp_save; 418 1.1.1.2 christos uint64_t val = tok.val; 419 1.1.1.2 christos uint64_t pval = ((int64_t) val) < 0 ? -val : val; 420 1.1.1.2 christos int neg_power2_p = ((int64_t) val) < 0 && !(pval & (pval - 1)); 421 1.1.1.2 christos struct token_class *class = env.token_classes->imm_classes; 422 1.1.1.2 christos unsigned len = pval ? 8 * sizeof (pval) - __builtin_clzll (pval) : 0; 423 1.1.1.2 christos 424 1.1.1.2 christos /* Find the imm class */ 425 1.1.1.2 christos int imm_idx = 0; 426 1.1.1.2 christos for (imm_idx = 0 ; class[imm_idx].class_id ; ++imm_idx) 427 1.1.1.2 christos if (class[imm_idx].class_id == tok.class_id) 428 1.1.1.2 christos break; 429 1.1.1.2 christos 430 1.1.1.2 christos while (class[imm_idx + 1].class_id != -1 431 1.1.1.2 christos && ((unsigned int) (class[imm_idx + 1].sz < 0 ? - class[imm_idx + 1].sz - !neg_power2_p : class[imm_idx + 1].sz) < len 432 1.1.1.2 christos || (exp.X_op == O_symbol && !has_relocation_of_size (str_hash_find (env.reloc_hash, TOKEN_NAME (class[imm_idx + 1].class_id)))) 433 1.1.1.2 christos || (exp.X_op == 64 && !kvx_get_pseudo_func2 (exp.X_op_symbol, str_hash_find (env.reloc_hash, TOKEN_NAME (class[imm_idx + 1].class_id)))))) 434 1.1 christos imm_idx += 1; 435 1.1 christos 436 1.1 christos return class[imm_idx + 1].class_id == -1 ? class[imm_idx].class_id : class[imm_idx + 1].class_id; 437 1.1 christos } 438 1.1 christos default: 439 1.1 christos return tok.class_id; 440 1.1 christos } 441 1.1 christos } 442 1.1 christos 443 1.1 christos static int 444 1.1 christos is_insn (const struct token_s *token, struct token_class *classes) 445 1.1 christos { 446 1.1 christos int res = false; 447 1.1 christos int i = 0; 448 1.1 christos int tok_sz = token->end - token->begin; 449 1.1 christos char *tok = token->insn + token->begin; 450 1.1 christos while (!res && classes[i].class_values != NULL) 451 1.1 christos { 452 1.1 christos res = !strncmp (classes[i].class_values[0], tok, tok_sz); 453 1.1 christos i += 1; 454 1.1 christos } 455 1.1 christos 456 1.1 christos return res; 457 1.1 christos } 458 1.1 christos 459 1.1 christos static int64_t 460 1.1 christos get_token_class (struct token_s *token, struct token_classes *classes, int insn_p, int modifier_p) 461 1.1.1.2 christos { 462 1.1 christos int cur = 0; 463 1.1.1.2 christos int found = 0; 464 1.1 christos size_t tok_sz = token->end - token->begin; 465 1.1 christos char *tok = token->insn + token->begin; 466 1.1 christos expressionS exp; 467 1.1 christos 468 1.1 christos token->val = 0; 469 1.1 christos int token_val_p = 0; 470 1.1 christos 471 1.1 christos struct token_class *class; 472 1.1 christos if (tok[0] == '$') 473 1.1 christos { 474 1.1 christos class = classes->reg_classes; 475 1.1 christos token->category = CAT_REGISTER; 476 1.1 christos } 477 1.1 christos else if (modifier_p && tok[0] == '.') 478 1.1 christos { 479 1.1 christos class = classes->mod_classes; 480 1.1 christos token->category = CAT_MODIFIER; 481 1.1 christos } 482 1.1 christos else if (isdigit (tok[0]) || tok[0] == '+' || tok[0] == '-') 483 1.1 christos { 484 1.1 christos class = classes->imm_classes; 485 1.1 christos token->category = CAT_IMMEDIATE; 486 1.1.1.2 christos char *ilp_save = input_line_pointer; 487 1.1 christos input_line_pointer = tok; 488 1.1 christos expression (&exp); 489 1.1 christos token->end = token->begin + (input_line_pointer - tok); 490 1.1 christos token->val = exp.X_add_number; 491 1.1 christos token_val_p = 1; 492 1.1 christos input_line_pointer = ilp_save; 493 1.1 christos } 494 1.1 christos else if (tok_sz == 1 && is_delim (tok[0])) 495 1.1 christos { 496 1.1 christos class = classes->sep_classes; 497 1.1 christos token->category = CAT_SEPARATOR; 498 1.1 christos } 499 1.1 christos else if (insn_p && is_insn (token, classes->insn_classes)) 500 1.1 christos { 501 1.1 christos class = classes->insn_classes; 502 1.1 christos token->category = CAT_INSTRUCTION; 503 1.1 christos } 504 1.1 christos else 505 1.1 christos { 506 1.1 christos /* We are in fact dealing with a symbol. */ 507 1.1 christos class = classes->imm_classes; 508 1.1 christos token->category = CAT_IMMEDIATE; 509 1.1 christos 510 1.1 christos char *ilp_save = input_line_pointer; 511 1.1 christos input_line_pointer = tok; 512 1.1 christos expression (&exp); 513 1.1 christos 514 1.1 christos /* If the symbol can be resolved easily takes it value now. Otherwise it 515 1.1 christos means that is either a symbol which will need a real relocation or an 516 1.1 christos internal fixup (ie, a pseudo-function, or a computation on symbols). */ 517 1.1 christos if (exp.X_op != O_symbol && exp.X_op != O_pseudo_fixup) 518 1.1 christos { 519 1.1 christos token->val = exp.X_add_number; 520 1.1 christos token_val_p = 1; 521 1.1 christos } 522 1.1 christos 523 1.1 christos input_line_pointer = ilp_save; 524 1.1 christos } 525 1.1 christos 526 1.1 christos if (class == classes->imm_classes) 527 1.1 christos { 528 1.1 christos uint64_t uval 529 1.1 christos = (token_val_p 530 1.1 christos ? token->val 531 1.1.1.2 christos : strtoull (tok + (tok[0] == '-') + (tok[0] == '+'), NULL, 0)); 532 1.1 christos int64_t val = uval; 533 1.1 christos int64_t pval = val < 0 ? -uval : uval; 534 1.1 christos int neg_power2_p = val < 0 && !(pval & (pval - 1)); 535 1.1 christos unsigned len = pval ? 8 * sizeof (pval) - __builtin_clzll (pval) : 0; 536 1.1 christos while (class[cur].class_id != -1 537 1.1 christos && ((unsigned) (class[cur].sz < 0 538 1.1 christos ? -class[cur].sz - !neg_power2_p 539 1.1 christos : class[cur].sz) < len 540 1.1 christos || (exp.X_op == O_symbol 541 1.1 christos && !(has_relocation_of_size 542 1.1 christos (str_hash_find (env.reloc_hash, 543 1.1 christos TOKEN_NAME (class[cur].class_id))))) 544 1.1 christos || (exp.X_op == O_pseudo_fixup 545 1.1 christos && !(kvx_get_pseudo_func2 546 1.1 christos (exp.X_op_symbol, 547 1.1 christos str_hash_find (env.reloc_hash, 548 1.1 christos TOKEN_NAME (class[cur].class_id))))))) 549 1.1 christos ++cur; 550 1.1 christos 551 1.1 christos token->val = uval; 552 1.1 christos // if (exp.X_op == O_pseudo_fixup) 553 1.1 christos // token->val = (uintptr_t) !kvx_get_pseudo_func2 (exp.X_op_symbol, str_hash_find (env.reloc_hash, TOKEN_NAME (class[cur].class_id))); 554 1.1 christos found = 1; 555 1.1 christos } 556 1.1 christos else 557 1.1 christos { 558 1.1 christos do 559 1.1 christos { 560 1.1.1.2 christos for (int i = 0; !found && i < class[cur].sz; ++i) 561 1.1 christos { 562 1.1 christos const char *ref = class[cur].class_values[i]; 563 1.1 christos found = (strlen (ref) == tok_sz) && !strncmp (tok, ref, tok_sz); 564 1.1 christos token->val = i; 565 1.1 christos } 566 1.1 christos 567 1.1 christos cur += !(found); 568 1.1 christos } 569 1.1 christos while (!found && class[cur].class_id != -1); 570 1.1 christos } 571 1.1 christos 572 1.1 christos if (!found) 573 1.1 christos { 574 1.1 christos token->category = CAT_IMMEDIATE; 575 1.1 christos return token->class_id = classes->imm_classes[0].class_id; 576 1.1 christos } 577 1.1 christos 578 1.1 christos #define unset(w, rg) ((w) & (~(1ULL << ((rg) - env.fst_reg)))) 579 1.1 christos if (class == classes->reg_classes && !env.opts.allow_all_sfr) 580 1.1 christos return token->class_id = unset (class[cur].class_id, env.sys_reg); 581 1.1 christos #undef unset 582 1.1 christos 583 1.1 christos return token->class_id = class[cur].class_id; 584 1.1 christos } 585 1.1 christos 586 1.1 christos static int 587 1.1 christos read_token (struct token_s *tok) 588 1.1 christos { 589 1.1 christos int insn_p = tok->begin == 0; 590 1.1 christos int modifier_p = 0; 591 1.1.1.2 christos char *str = tok->insn; 592 1.1.1.2 christos int *begin = &tok->begin; 593 1.1.1.2 christos int *end = &tok->end; 594 1.1.1.2 christos int last_imm_p = 0; 595 1.1.1.2 christos 596 1.1.1.2 christos /* Was the last previous token was an immediate? */ 597 1.1.1.2 christos for (int i = 1; *begin - i > 0; ++i) 598 1.1.1.2 christos { 599 1.1.1.2 christos if ('0' <= str[*begin - i] && str[*begin - i] <= '9') 600 1.1.1.2 christos last_imm_p = 1; 601 1.1 christos else if (!is_whitespace (str[*begin - i])) 602 1.1 christos break; 603 1.1.1.2 christos } 604 1.1 christos 605 1.1 christos /* Eat up all leading spaces. */ 606 1.1 christos while (str[*begin] && (is_whitespace (str[*begin]) || str[*begin] == '\n')) 607 1.1 christos *begin += 1; 608 1.1 christos 609 1.1 christos *end = *begin; 610 1.1 christos 611 1.1 christos if (!str[*begin]) 612 1.1 christos return 0; 613 1.1 christos 614 1.1 christos /* Special case, we're reading an instruction. Try to read as much as possible 615 1.1 christos as long as the prefix is a valid instruction. */ 616 1.1 christos if (insn_p) 617 1.1 christos *end += longest_match (str + *begin, strlen (str + *begin), env.insns); 618 1.1 christos else 619 1.1 christos { 620 1.1 christos if (is_delim (str[*begin])) 621 1.1 christos { 622 1.1 christos *end += 1; 623 1.1 christos get_token_class (tok, env.token_classes, insn_p, modifier_p); 624 1.1.1.2 christos return 1; 625 1.1.1.2 christos } 626 1.1.1.2 christos 627 1.1.1.2 christos if (str[*begin] == '.' 628 1.1.1.2 christos && (!(*begin > 0 629 1.1 christos && (is_whitespace (str[*begin - 1]) 630 1.1 christos || is_delim (str[*begin - 1]))) 631 1.1 christos || last_imm_p)) 632 1.1 christos modifier_p = 1; 633 1.1 christos 634 1.1 christos /* This is a modifier or a register */ 635 1.1 christos if (str[*begin] == '.' || str[*begin] == '$') 636 1.1.1.2 christos *end += 1; 637 1.1.1.2 christos 638 1.1 christos /* Stop when reaching the start of the new token. */ 639 1.1 christos while (!(!str[*end] || is_delim (str[*end]) || is_whitespace (str[*end]) 640 1.1 christos || (modifier_p && str[*end] == '.'))) 641 1.1 christos *end += 1; 642 1.1 christos 643 1.1 christos } 644 1.1 christos 645 1.1 christos get_token_class (tok, env.token_classes, insn_p, modifier_p); 646 1.1 christos return 1; 647 1.1 christos } 648 1.1 christos 649 1.1 christos /* Rewrite with as_bad. */ 650 1.1 christos static void 651 1.1 christos rule_expect_error (int rule_id, char *buf, int bufsz __attribute__((unused))) 652 1.1 christos { 653 1.1 christos int i = 0; 654 1.1 christos int pos = 0; 655 1.1 christos int comma = 0; 656 1.1 christos pos += sprintf (buf + pos, "expected one of ["); 657 1.1 christos struct steering_rule *rules = env.rules[rule_id].rules; 658 1.1 christos while (rules[i].steering != -1) 659 1.1 christos { 660 1.1 christos if ((env.opts.allow_all_sfr || rules[i].steering != env.sys_reg) 661 1.1 christos && rules[i].steering != -3) 662 1.1 christos { 663 1.1 christos pos += sprintf (buf + pos, "%s%s", comma ? ", " : "", TOKEN_NAME (rules[i].steering)); 664 1.1 christos comma = 1; 665 1.1 christos } 666 1.1 christos i += 1; 667 1.1 christos } 668 1.1 christos pos += sprintf (buf + pos, "]."); 669 1.1 christos } 670 1.1 christos 671 1.1 christos static struct token_list * 672 1.1.1.2 christos create_token (struct token_s tok, int len, int loc) 673 1.1 christos { 674 1.1 christos struct token_list *tl = calloc (1, sizeof *tl); 675 1.1 christos size_t tok_sz = tok.end - tok.begin; 676 1.1 christos tl->tok = calloc (tok_sz + 1, sizeof (char)); 677 1.1 christos memcpy (tl->tok, tok.insn + tok.begin, tok_sz * sizeof (char)); 678 1.1 christos tl->val = tok.val; 679 1.1 christos tl->class_id = tok.class_id; 680 1.1 christos tl->category = tok.category; 681 1.1 christos tl->next = NULL; 682 1.1 christos tl->len = len; 683 1.1 christos tl->loc = loc; 684 1.1 christos return tl; 685 1.1 christos } 686 1.1 christos 687 1.1 christos void 688 1.1 christos print_token_list (struct token_list *lst) 689 1.1 christos { 690 1.1.1.2 christos struct token_list *cur = lst; 691 1.1 christos while (cur) 692 1.1 christos { 693 1.1 christos printf_debug (0, "%s (%llu : %s : %llu) / ", 694 1.1.1.2 christos cur->tok, cur->val, TOKEN_NAME (cur->class_id), cur->loc); 695 1.1 christos cur = cur->next; 696 1.1 christos } 697 1.1 christos printf_debug (0, "\n"); 698 1.1 christos } 699 1.1 christos 700 1.1 christos void 701 1.1 christos free_token_list (struct token_list *tok_list) 702 1.1 christos { 703 1.1 christos struct token_list *cur = tok_list; 704 1.1 christos struct token_list *tmp; 705 1.1 christos while (cur) 706 1.1 christos { 707 1.1 christos tmp = cur->next; 708 1.1 christos free (cur->tok); 709 1.1 christos free (cur); 710 1.1 christos cur = tmp; 711 1.1 christos } 712 1.1 christos } 713 1.1 christos 714 1.1 christos static struct token_list * 715 1.1 christos token_list_append (struct token_list *lst1, struct token_list *lst2) 716 1.1 christos { 717 1.1 christos if (lst1 == NULL) 718 1.1 christos return lst2; 719 1.1 christos 720 1.1 christos if (lst2 == NULL) 721 1.1 christos return NULL; 722 1.1 christos 723 1.1 christos struct token_list *hd = lst1; 724 1.1 christos while (hd->next) 725 1.1 christos { 726 1.1 christos hd->len += lst2->len; 727 1.1 christos hd = hd->next; 728 1.1 christos } 729 1.1 christos 730 1.1 christos hd->len += lst2->len; 731 1.1 christos hd->next = lst2; 732 1.1 christos return lst1; 733 1.1 christos } 734 1.1 christos 735 1.1 christos struct error_list 736 1.1 christos { 737 1.1 christos int loc, rule; 738 1.1 christos struct error_list *nxt; 739 1.1 christos }; 740 1.1 christos 741 1.1 christos static struct error_list * 742 1.1 christos error_list_insert (int rule, int loc, struct error_list *nxt) 743 1.1 christos { 744 1.1 christos struct error_list *n = calloc (1, sizeof (*n)); 745 1.1 christos n->loc = loc > 0 ? loc - 1 : loc; 746 1.1 christos n->rule = rule; 747 1.1 christos n->nxt = nxt; 748 1.1 christos return n; 749 1.1 christos } 750 1.1 christos 751 1.1 christos static void 752 1.1 christos free_error_list (struct error_list *l) 753 1.1 christos { 754 1.1 christos struct error_list *tmp, *cur_err = l; 755 1.1 christos while ((tmp = cur_err)) 756 1.1 christos { 757 1.1 christos cur_err = cur_err->nxt; 758 1.1 christos free (tmp); 759 1.1 christos } 760 1.1 christos } 761 1.1 christos 762 1.1 christos static int 763 1.1 christos CLASS_ID (struct token_s tok) 764 1.1 christos { 765 1.1 christos int offset = __builtin_ctzll (tok.class_id & -tok.class_id); 766 1.1 christos switch (tok.category) 767 1.1 christos { 768 1.1 christos case CAT_REGISTER: 769 1.1 christos return env.fst_reg + offset; 770 1.1 christos case CAT_MODIFIER: 771 1.1 christos return env.fst_mod + offset; 772 1.1 christos default: 773 1.1 christos return tok.class_id; 774 1.1 christos } 775 1.1 christos } 776 1.1 christos 777 1.1 christos struct parser { 778 1.1 christos 779 1.1 christos }; 780 1.1 christos 781 1.1 christos static struct token_list * 782 1.1 christos parse_with_restarts (struct token_s tok, int jump_target, struct rule rules[], 783 1.1 christos struct error_list **errs) 784 1.1 christos { 785 1.1 christos int end_of_line = 0; 786 1.1 christos struct steering_rule *cur_rule = rules[jump_target].rules; 787 1.1 christos 788 1.1 christos if (!tok.insn[tok.begin]) 789 1.1 christos tok.class_id = -3; 790 1.1 christos 791 1.1 christos if (CLASS_ID (tok) == -1) 792 1.1 christos { 793 1.1 christos /* Unknown token */ 794 1.1 christos *errs = error_list_insert (jump_target, tok.begin, *errs); 795 1.1 christos return NULL; 796 1.1 christos } 797 1.1 christos 798 1.1 christos printf_debug (1, "\nEntering rule: %d (Trying to match: (%s)[%d])\n", 799 1.1 christos jump_target, TOKEN_NAME (CLASS_ID (tok)), CLASS_ID (tok)); 800 1.1 christos 801 1.1 christos /* 1. Find a rule that can be used with the current token. */ 802 1.1 christos int i = 0; 803 1.1 christos while (cur_rule[i].steering != -1 && cur_rule[i].steering != CLASS_ID (tok)) 804 1.1 christos i += 1; 805 1.1 christos 806 1.1 christos printf_debug (1, "steering: %d (%s), jump_target: %d, stack_it: %d\n", 807 1.1 christos cur_rule[i].steering, TOKEN_NAME (cur_rule[i].steering), 808 1.1 christos cur_rule[i].jump_target, cur_rule[i].stack_it); 809 1.1 christos 810 1.1 christos struct token_s init_tok = tok; 811 1.1 christos retry:; 812 1.1 christos tok = init_tok; 813 1.1 christos if (cur_rule[i].jump_target == -2 && cur_rule[i].stack_it == -2) 814 1.1 christos { 815 1.1 christos /* We're reading eps. */ 816 1.1 christos printf_debug (1, "successfully ignored: %s\n", TOKEN_NAME (jump_target)); 817 1.1 christos struct token_s tok_ = 818 1.1 christos { (char *)".", 0, 1, CAT_MODIFIER, jump_target, 0 }; 819 1.1 christos return create_token (tok_, 0, tok.begin); 820 1.1 christos } 821 1.1 christos else if (cur_rule[i].jump_target == -1 && cur_rule[i].stack_it == -1) 822 1.1 christos { 823 1.1 christos /* We're handling the rule for a terminal (not eps) */ 824 1.1 christos if (cur_rule[i].steering == CLASS_ID (tok)) 825 1.1 christos // && tok.begin != tok.end) -- only fails when eps is last, eg. fence. 826 1.1 christos { 827 1.1 christos /* We matched a token */ 828 1.1 christos printf_debug (1, "matched %s\n", TOKEN_NAME (CLASS_ID (tok))); 829 1.1 christos tok.class_id = CLASS_ID (tok); 830 1.1 christos return create_token (tok, 1, tok.begin); 831 1.1 christos } 832 1.1 christos else 833 1.1 christos { 834 1.1 christos /* This is a mandatory modifier */ 835 1.1 christos *errs = error_list_insert (jump_target, tok.begin, *errs); 836 1.1 christos return NULL; 837 1.1 christos } 838 1.1 christos } 839 1.1 christos 840 1.1 christos /* Not on a terminal */ 841 1.1 christos struct token_list *fst_part = 842 1.1 christos parse_with_restarts (tok, cur_rule[i].jump_target, rules, errs); 843 1.1 christos /* While parsing fails but there is hope since the current token can be 844 1.1 christos promoted. */ 845 1.1 christos while (!fst_part && tok.class_id != (int64_t) promote_token (tok)) 846 1.1 christos { 847 1.1 christos free_token_list (fst_part); 848 1.1 christos tok.class_id = promote_token (tok); 849 1.1 christos printf_debug (1, "> Restart with %s?\n", TOKEN_NAME (CLASS_ID (tok))); 850 1.1 christos fst_part = parse_with_restarts (tok, cur_rule[i].jump_target, rules, errs); 851 1.1 christos }; 852 1.1 christos 853 1.1 christos if (!fst_part) 854 1.1 christos { 855 1.1 christos i += 1; 856 1.1 christos while (cur_rule[i].steering != CLASS_ID(tok) && cur_rule[i].steering != -1) 857 1.1 christos i += 1; 858 1.1 christos if (cur_rule[i].steering != -1) 859 1.1 christos goto retry; 860 1.1 christos } 861 1.1 christos 862 1.1 christos if (!fst_part) 863 1.1 christos { 864 1.1 christos printf_debug (1, "fst_part == NULL (Exiting %d)\n", jump_target); 865 1.1 christos return NULL; 866 1.1 christos } 867 1.1 christos 868 1.1 christos for (int _ = 0; _ < fst_part->len; ++_) 869 1.1 christos { 870 1.1 christos tok.begin = tok.end; 871 1.1 christos end_of_line = !read_token (&tok); 872 1.1 christos } 873 1.1 christos 874 1.1 christos if (end_of_line && cur_rule[i].stack_it == -1) 875 1.1 christos { 876 1.1 christos /* No more tokens and no more place to go */ 877 1.1 christos printf_debug (1, "return fst_part.\n"); 878 1.1 christos return fst_part; 879 1.1 christos } 880 1.1 christos else if (!end_of_line && cur_rule[i].stack_it == -1) 881 1.1 christos { 882 1.1 christos /* Too much tokens. */ 883 1.1 christos printf_debug (1, "too much tokens\n"); 884 1.1 christos *errs = error_list_insert (cur_rule[i].stack_it, tok.begin, *errs); 885 1.1 christos return NULL; 886 1.1 christos } 887 1.1 christos else if (cur_rule[i].stack_it == -1) 888 1.1 christos { 889 1.1 christos printf_debug (1, "return fst_part. (end of rule)\n"); 890 1.1 christos return fst_part; 891 1.1 christos } 892 1.1 christos 893 1.1 christos printf_debug (1, "snd_part: Trying to match: %s\n", TOKEN_NAME (CLASS_ID (tok))); 894 1.1 christos struct token_list *snd_part = parse_with_restarts (tok, cur_rule[i].stack_it, rules, errs); 895 1.1 christos while (!snd_part && tok.class_id != (int64_t) promote_token (tok)) 896 1.1 christos { 897 1.1 christos tok.class_id = promote_token (tok); 898 1.1 christos printf_debug (1, ">> Restart with %s?\n", TOKEN_NAME (CLASS_ID (tok))); 899 1.1 christos snd_part = parse_with_restarts (tok, cur_rule[i].stack_it, rules, errs); 900 1.1 christos } 901 1.1 christos 902 1.1 christos if (!snd_part) 903 1.1 christos { 904 1.1 christos free_token_list (fst_part); 905 1.1 christos i += 1; 906 1.1 christos tok = init_tok; 907 1.1 christos while (cur_rule[i].steering != CLASS_ID (tok) && cur_rule[i].steering != -1) 908 1.1 christos i += 1; 909 1.1 christos if (cur_rule[i].steering != -1) 910 1.1 christos goto retry; 911 1.1 christos } 912 1.1 christos 913 1.1 christos if (!snd_part) 914 1.1 christos { 915 1.1 christos printf_debug (1, "snd_part == NULL (Exiting %d)\n", jump_target); 916 1.1 christos return NULL; 917 1.1 christos } 918 1.1 christos 919 1.1 christos printf_debug (1, "Exiting rule: %d\n", jump_target, 920 1.1 christos TOKEN_NAME (CLASS_ID (tok)), tok.class_id); 921 1.1 christos 922 1.1 christos /* Combine fst & snd parts */ 923 1.1 christos return token_list_append (fst_part, snd_part); 924 1.1 christos } 925 1.1 christos 926 1.1 christos /* During the parsing the modifiers and registers are handled through pseudo 927 1.1 christos classes such that each register and modifier appears in at most one pseudo 928 1.1 christos class. Since the pseudo-classes are not correlated with how the modifiers 929 1.1 christos and registers are encoded we fix that after a successful match instead of 930 1.1 christos updating it many times during the parsing. 931 1.1 christos 932 1.1 christos Currently, only assigning correct values to modifiers is of interest. The 933 1.1 christos real value of registers is computed in tc-kvx.c:insert_operand. */ 934 1.1 christos 935 1.1 christos static void 936 1.1 christos assign_final_values (struct token_list *lst) 937 1.1 christos { 938 1.1 christos (void) lst; 939 1.1 christos struct token_list *cur = lst; 940 1.1 christos 941 1.1 christos while (cur) 942 1.1 christos { 943 1.1 christos if (cur->category == CAT_MODIFIER) 944 1.1 christos { 945 1.1 christos int idx = cur->class_id - env.fst_mod; 946 1.1 christos int found = 0; 947 1.1 christos for (int i = 0 ; !found && kvx_modifiers[idx][i]; ++i) 948 1.1 christos if ((found = !strcmp (cur->tok, kvx_modifiers[idx][i]))) 949 1.1 christos cur->val = i; 950 1.1 christos } 951 1.1 christos cur = cur->next; 952 1.1 christos } 953 1.1 christos } 954 1.1 christos 955 1.1 christos struct token_list * 956 1.1 christos parse (struct token_s tok) 957 1.1 christos { 958 1.1 christos int error_code = 0; 959 1.1 christos int error_char = 0; 960 1.1 christos struct error_list *errs = NULL; 961 1.1 christos read_token (&tok); 962 1.1 christos 963 1.1 christos struct token_list *tok_list = 964 1.1 christos parse_with_restarts (tok, 0, env.rules, &errs); 965 1.1 christos 966 1.1 christos if (!tok_list) 967 1.1 christos { 968 1.1 christos struct error_list *cur_err = errs; 969 1.1 christos while (cur_err) 970 1.1 christos { 971 1.1 christos if (cur_err->loc > error_char) 972 1.1 christos { 973 1.1 christos error_char = cur_err->loc; 974 1.1 christos error_code = cur_err->rule; 975 1.1 christos } 976 1.1 christos cur_err = cur_err->nxt; 977 1.1 christos } 978 1.1 christos } 979 1.1 christos 980 1.1 christos free_error_list (errs); 981 1.1 christos 982 1.1 christos if (!tok_list) 983 1.1 christos { 984 1.1 christos if (error_code != -1) 985 1.1 christos { 986 1.1 christos char buf[256] = { 0 }; 987 1.1 christos const char * msg = "Unexpected token when parsing %s."; 988 1.1 christos for (int i = 0; i < (int) (strlen (msg) + error_char + 1 - 4) ; ++i) 989 1.1 christos buf[i] = ' '; 990 1.1 christos buf[strlen (msg) + error_char + 1 - 4] = '^'; 991 1.1 christos as_bad (msg, tok.insn); 992 1.1 christos if (env.opts.diagnostics) 993 1.1 christos { 994 1.1 christos as_bad ("%s", buf); 995 1.1 christos char err_buf[10000] = { 0 }; 996 1.1 christos rule_expect_error (error_code, err_buf, 10000); 997 1.1 christos as_bad ("%s", err_buf); 998 1.1 christos } 999 1.1 christos } 1000 1.1 christos else 1001 1.1 christos { 1002 1.1 christos char buf[256] = { 0 }; 1003 1.1 christos const char * msg = "Extra token when parsing %s."; 1004 1.1 christos for (int i = 0; i < (int) (strlen (msg) + error_char + 1 - 4) ; ++i) 1005 1.1 christos buf[i] = ' '; 1006 1.1 christos buf[strlen (msg) + error_char + 1 - 4] = '^'; 1007 1.1 christos as_bad (msg, tok.insn); 1008 1.1 christos if (env.opts.diagnostics) 1009 1.1 christos as_bad ("%s\n", buf); 1010 1.1 christos } 1011 1.1 christos } 1012 1.1 christos else 1013 1.1 christos { 1014 1.1 christos printf_debug (1, "[PASS] Successfully matched %s\n", tok.insn); 1015 1.1 christos assign_final_values (tok_list); 1016 1.1 christos // print_token_list (tok_list); 1017 1.1 christos // free_token_list (tok_list); 1018 1.1 christos } 1019 1.1 christos return tok_list; 1020 1.1 christos } 1021 1.1 christos 1022 1.1 christos void 1023 1.1 christos setup (int core) 1024 1.1 christos { 1025 1.1 christos switch (core) 1026 1.1 christos { 1027 1.1 christos case ELF_KVX_CORE_KV3_1: 1028 1.1 christos setup_kv3_v1 (); 1029 1.1 christos break; 1030 1.1 christos case ELF_KVX_CORE_KV3_2: 1031 1.1 christos setup_kv3_v2 (); 1032 1.1 christos break; 1033 1.1 christos case ELF_KVX_CORE_KV4_1: 1034 1.1 christos setup_kv4_v1 (); 1035 1.1 christos break; 1036 1.1 christos default: 1037 1.1 christos as_bad ("Unknown architecture"); 1038 1.1 christos abort (); 1039 1.1 christos } 1040 1.1 christos 1041 1.1 christos for (int i = 0; env.token_classes->insn_classes[i].class_values ; ++i) 1042 1.1 christos env.insns = 1043 1.1 christos insert (env.token_classes->insn_classes[i].class_values[0], env.insns); 1044 1.1 christos } 1045 1.1 christos 1046 1.1 christos void 1047 1.1 christos cleanup () 1048 { 1049 free_node (env.insns); 1050 } 1051