ff4ff35918
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
1047 lines
26 KiB
C
1047 lines
26 KiB
C
/* kvx-parse.c -- Recursive decent parser driver for the KVX ISA
|
|
|
|
Copyright (C) 2009-2024 Free Software Foundation, Inc.
|
|
Contributed by Kalray SA.
|
|
|
|
This file is part of GAS.
|
|
|
|
GAS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the license, or
|
|
(at your option) any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING3. If not,
|
|
see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "as.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <elf/kvx_elfids.h>
|
|
#include "kvx-parse.h"
|
|
|
|
/* This is bad! */
|
|
struct node_list_s {
|
|
struct node_s *node;
|
|
struct node_list_s *nxt;
|
|
};
|
|
|
|
struct node_s {
|
|
char *val;
|
|
int len;
|
|
struct node_list_s *succs;
|
|
int nb_succs;
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
has_relocation_of_size (const struct kvx_reloc **relocs)
|
|
{
|
|
const int symbol_size = env.params.arch_size;
|
|
|
|
/*
|
|
* This is a bit hackish: in case of PCREL here, it means we are
|
|
* trying to fit a symbol in the insn, not a pseudo function
|
|
* (eg. @gotaddr, ...).
|
|
* We don't want to use a GOTADDR (pcrel) in any insn that tries to fit a symbol.
|
|
* One way to filter out these is to use the following assumption:
|
|
* - Any insn that accepts a pcrel immediate has only one immediate variant.
|
|
* Example:
|
|
* - call accepts only a pcrel27 -> allow pcrel reloc here
|
|
* - cb accepts only a pcrel17 -> allow pcrel reloc here
|
|
* - addd accepts signed10,37,64 -> deny pcrel reloc here
|
|
*
|
|
* The motivation here is to prevent the function to allow a 64bits
|
|
* symbol in a 37bits variant of any ALU insn (that would match with
|
|
* the GOTADDR 37bits reloc switch case below)
|
|
*/
|
|
|
|
if (!relocs)
|
|
return 0;
|
|
|
|
struct kvx_reloc **relocs_it = (struct kvx_reloc **) relocs;
|
|
int has_only_one_p = relocs[0] && !relocs[1];
|
|
|
|
while (*relocs_it)
|
|
{
|
|
switch ((*relocs_it)->relative)
|
|
{
|
|
/* An absolute reloc needs a full size symbol reloc */
|
|
case KVX_REL_ABS:
|
|
if ((*relocs_it)->bitsize >= symbol_size)
|
|
return 1;
|
|
break;
|
|
|
|
/* Most likely relative jumps. Let something else check size is
|
|
OK. We don't currently have several relocations for such insns */
|
|
case KVX_REL_PC:
|
|
if (has_only_one_p)
|
|
return 1;
|
|
break;
|
|
|
|
/* These relocations should be handled elsewhere with pseudo functions */
|
|
case KVX_REL_GP:
|
|
case KVX_REL_TP:
|
|
case KVX_REL_GOT:
|
|
case KVX_REL_BASE:
|
|
break;
|
|
}
|
|
relocs_it++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct pseudo_func *
|
|
kvx_get_pseudo_func2 (symbolS * sym, struct kvx_reloc **relocs);
|
|
struct pseudo_func *
|
|
kvx_get_pseudo_func2 (symbolS *sym, struct kvx_reloc **relocs)
|
|
{
|
|
if (!relocs)
|
|
return NULL;
|
|
|
|
struct kvx_reloc **relocs_it = (struct kvx_reloc **) relocs;
|
|
|
|
for (int i = 0; i < 26; i++)
|
|
{
|
|
if (sym == kvx_core_info->pseudo_funcs[i].sym)
|
|
{
|
|
relocs_it = relocs;
|
|
while (*relocs_it)
|
|
{
|
|
if (*relocs_it == kvx_core_info->pseudo_funcs[i].pseudo_relocs.kreloc
|
|
&& (env.params.arch_size == (int) kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes
|
|
|| kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes == PSEUDO_ALL))
|
|
return &kvx_core_info->pseudo_funcs[i];
|
|
relocs_it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Trie */
|
|
|
|
static
|
|
struct node_list_s *
|
|
insert_in_succ_list (struct node_s *node, struct node_s *base)
|
|
{
|
|
struct node_list_s *new_hd = NULL;
|
|
if (!(new_hd = calloc (1, sizeof (*new_hd))))
|
|
return NULL;
|
|
|
|
new_hd->node = node;
|
|
new_hd->nxt = base->succs;
|
|
base->nb_succs += 1;
|
|
return new_hd;
|
|
}
|
|
|
|
static
|
|
struct node_s *
|
|
make_node (const char *str, int len)
|
|
{
|
|
struct node_s *n = NULL;
|
|
if (!(n = calloc (1, sizeof (*n))))
|
|
goto err;
|
|
|
|
n->len = len;
|
|
n->succs = NULL;
|
|
if (!(n->val = calloc (n->len + 1, sizeof (*n->val))))
|
|
goto err1;
|
|
|
|
strncpy (n->val, str, n->len);
|
|
return n;
|
|
|
|
err1:
|
|
free (n), n = NULL;
|
|
err:
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
struct node_s *
|
|
insert (const char *str, struct node_s *node)
|
|
{
|
|
int i = 0;
|
|
int len = strlen (str);
|
|
|
|
if (!node)
|
|
{
|
|
struct node_s *n = make_node (str, len);
|
|
n->succs = insert_in_succ_list (NULL, n);
|
|
return n;
|
|
}
|
|
|
|
while (i < len && i < node->len && str[i] == node->val[i])
|
|
++i;
|
|
|
|
/* The strings share a prefix. */
|
|
if (i < len && i < node->len)
|
|
{
|
|
/* Split the current node on that common prefix. */
|
|
|
|
/* Create a new node with only the unshared suffix, and makes it inherit
|
|
the successor of the node under consideration. */
|
|
struct node_s *suf = make_node (node->val + i, node->len - i);
|
|
suf->succs = node->succs;
|
|
suf->nb_succs = node->nb_succs;
|
|
/* Insert the remainder on the other branch */
|
|
struct node_s *rem = make_node (str + i, len - i);
|
|
rem->succs = insert_in_succ_list (NULL, rem);
|
|
|
|
node->val[i] = '\0';
|
|
node->len = i;
|
|
node->succs = NULL;
|
|
node->nb_succs = 0;
|
|
node->succs = insert_in_succ_list (suf, node);
|
|
node->succs = insert_in_succ_list (rem, node);
|
|
return node;
|
|
}
|
|
|
|
/* str is a strict prefix of node->val */
|
|
if (i == len && i < node->len)
|
|
{
|
|
/* Split the current node at position */
|
|
struct node_s *suf = make_node (node->val + i, node->len - i);
|
|
suf->succs = node->succs;
|
|
suf->nb_succs = node->nb_succs;
|
|
node->val[i] = '\0';
|
|
node->len = i;
|
|
/* Insert an empty leaf */
|
|
node->succs = NULL;
|
|
node->nb_succs = 0;
|
|
node->succs = insert_in_succ_list (NULL, node);
|
|
node->succs = insert_in_succ_list (suf, node);
|
|
return node;
|
|
}
|
|
|
|
/* node->val is a prefix of str */
|
|
if (i == node->len)
|
|
{
|
|
/* Find a successor of node into which the remainder can be inserted. */
|
|
struct node_list_s *cur_succ = node->succs;
|
|
while (cur_succ)
|
|
{
|
|
struct node_s *n = cur_succ->node;
|
|
if (n && n->val && n->val[0] == str[i])
|
|
{
|
|
cur_succ->node = insert (str + i, cur_succ->node);
|
|
break;
|
|
}
|
|
cur_succ = cur_succ->nxt;
|
|
}
|
|
/* No successor shares a common prefix */
|
|
if (cur_succ == NULL)
|
|
{
|
|
struct node_s *suf = make_node (str + i, len - i);
|
|
suf->succs = insert_in_succ_list (NULL, suf);
|
|
node->succs = insert_in_succ_list (suf, node);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static
|
|
void
|
|
free_node (struct node_s *node)
|
|
{
|
|
if (!node)
|
|
return;
|
|
|
|
free (node->val);
|
|
|
|
struct node_list_s *cur_succ = node->succs;
|
|
struct node_list_s *tmp = NULL;
|
|
while ((tmp = cur_succ))
|
|
{
|
|
struct node_s *n = cur_succ->node;
|
|
if (n)
|
|
free_node (n), n = NULL;
|
|
cur_succ = cur_succ->nxt;
|
|
free (tmp);
|
|
}
|
|
|
|
free (node);
|
|
}
|
|
|
|
#define max(a,b) (((a)>(b))?(a):(b))
|
|
static
|
|
int
|
|
longest_match (const char *str, int len, struct node_s *node)
|
|
{
|
|
int i = 0;
|
|
int last_mark = 0;
|
|
struct node_s *cur = node;
|
|
|
|
while (1)
|
|
{
|
|
if (i + cur->len > len
|
|
|| strncmp (str + i, cur->val, max(0, cur->len)))
|
|
return last_mark;
|
|
|
|
i += cur->len;
|
|
struct node_list_s *cur_succ = cur->succs;
|
|
cur = NULL;
|
|
while (cur_succ)
|
|
{
|
|
struct node_s *n = cur_succ->node;
|
|
if (!n)
|
|
last_mark = i;
|
|
else if (n->val[0] == str[i])
|
|
cur = n;
|
|
cur_succ = cur_succ->nxt;
|
|
}
|
|
if (!cur)
|
|
return last_mark;
|
|
}
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static void
|
|
dump_graph_1 (FILE *fd, struct node_s *node, int id)
|
|
{
|
|
struct node_list_s *cur_succ = node->succs;
|
|
int i = 0;
|
|
|
|
if (id == 1)
|
|
fprintf (fd, "\t%d [label=\"%s\"];\n", id, node->val);
|
|
|
|
while (cur_succ)
|
|
{
|
|
if (cur_succ->node == NULL)
|
|
fprintf (fd, "\t%d -> \"()\";\n", id);
|
|
else
|
|
{
|
|
fprintf (fd, "\t%d [label=\"%s\"];\n",
|
|
node->nb_succs * id + i, cur_succ->node->val);
|
|
fprintf (fd, "\t%d -> %d;\n", id, node->nb_succs * id + i);
|
|
dump_graph_1 (fd, cur_succ->node, node->nb_succs * id + i);
|
|
}
|
|
i += 1;
|
|
cur_succ = cur_succ->nxt;
|
|
}
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static void
|
|
dump_graph (char *name, char *path, struct node_s *node)
|
|
{
|
|
FILE *fd = fopen (path, "w");
|
|
fprintf (fd, "digraph %s {\n", name);
|
|
|
|
dump_graph_1 (fd, node, 1);
|
|
|
|
fprintf (fd, "}\n");
|
|
fclose (fd);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static void
|
|
print_n (const char *str, int n)
|
|
{
|
|
for (int i = 0 ; i < n ; ++i)
|
|
putchar (str[i]);
|
|
putchar('\n');
|
|
}
|
|
|
|
|
|
int debug_level = 0;
|
|
|
|
__attribute__((unused))
|
|
static int
|
|
printf_debug (int lvl, const char *fmt, ...)
|
|
{
|
|
int ret = 0;
|
|
if (debug_level >= lvl)
|
|
{
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
ret = vprintf (fmt, args);
|
|
va_end (args);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
is_delim (char c)
|
|
{
|
|
char delims[] = { '[', ']', '?', ',', '=' };
|
|
int nb_delims = sizeof (delims) / (sizeof (*delims));
|
|
for (int i = 0; i < nb_delims; ++i)
|
|
if (c == delims[i])
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static void
|
|
print_token (struct token_s token, char *buf, int bufsz)
|
|
{
|
|
for (int i = 0; i < token.end - token.begin && i < bufsz; ++i)
|
|
buf[i] = token.insn[token.begin + i];
|
|
for (int i = token.end - token.begin ; i < bufsz; ++i)
|
|
buf[i] = 0;
|
|
}
|
|
|
|
static int64_t
|
|
promote_token (struct token_s tok)
|
|
{
|
|
int64_t cur_class = tok.class_id & -tok.class_id;
|
|
switch (tok.category)
|
|
{
|
|
case CAT_REGISTER:
|
|
case CAT_MODIFIER:
|
|
return (cur_class != tok.class_id)
|
|
? tok.class_id ^ cur_class
|
|
: tok.class_id;
|
|
case CAT_IMMEDIATE:
|
|
{
|
|
expressionS exp = { 0 };
|
|
char *ilp_save = input_line_pointer;
|
|
input_line_pointer = tok.insn + tok.begin;
|
|
expression (&exp);
|
|
input_line_pointer = ilp_save;
|
|
uint64_t val = tok.val;
|
|
uint64_t pval = ((int64_t) val) < 0 ? -val : val;
|
|
int neg_power2_p = ((int64_t) val) < 0 && !(pval & (pval - 1));
|
|
struct token_class *class = env.token_classes->imm_classes;
|
|
unsigned len = pval ? 8 * sizeof (pval) - __builtin_clzll (pval) : 0;
|
|
|
|
/* Find the imm class */
|
|
int imm_idx = 0;
|
|
for (imm_idx = 0 ; class[imm_idx].class_id ; ++imm_idx)
|
|
if (class[imm_idx].class_id == tok.class_id)
|
|
break;
|
|
|
|
while (class[imm_idx + 1].class_id != -1
|
|
&& ((unsigned int) (class[imm_idx + 1].sz < 0 ? - class[imm_idx + 1].sz - !neg_power2_p : class[imm_idx + 1].sz) < len
|
|
|| (exp.X_op == O_symbol && !has_relocation_of_size (str_hash_find (env.reloc_hash, TOKEN_NAME (class[imm_idx + 1].class_id))))
|
|
|| (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))))))
|
|
imm_idx += 1;
|
|
|
|
return class[imm_idx + 1].class_id == -1 ? class[imm_idx].class_id : class[imm_idx + 1].class_id;
|
|
}
|
|
default:
|
|
return tok.class_id;
|
|
}
|
|
}
|
|
|
|
static int
|
|
is_insn (const struct token_s *token, struct token_class *classes)
|
|
{
|
|
int res = false;
|
|
int i = 0;
|
|
int tok_sz = token->end - token->begin;
|
|
char *tok = token->insn + token->begin;
|
|
while (!res && classes[i].class_values != NULL)
|
|
{
|
|
res = !strncmp (classes[i].class_values[0], tok, tok_sz);
|
|
i += 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int64_t
|
|
get_token_class (struct token_s *token, struct token_classes *classes, int insn_p, int modifier_p)
|
|
{
|
|
int cur = 0;
|
|
int found = 0;
|
|
int tok_sz = token->end - token->begin;
|
|
char *tok = token->insn + token->begin;
|
|
expressionS exp = {0};
|
|
|
|
token->val = 0;
|
|
int token_val_p = 0;
|
|
|
|
struct token_class *class;
|
|
if (tok[0] == '$')
|
|
{
|
|
class = classes->reg_classes;
|
|
token->category = CAT_REGISTER;
|
|
}
|
|
else if (modifier_p && tok[0] == '.')
|
|
{
|
|
class = classes->mod_classes;
|
|
token->category = CAT_MODIFIER;
|
|
}
|
|
else if (isdigit (tok[0]) || tok[0] == '+' || tok[0] == '-')
|
|
{
|
|
class = classes->imm_classes;
|
|
token->category = CAT_IMMEDIATE;
|
|
char *ilp_save = input_line_pointer;
|
|
input_line_pointer = tok;
|
|
expression (&exp);
|
|
token->end = token->begin + (input_line_pointer - tok);
|
|
token->val = exp.X_add_number;
|
|
token_val_p = 1;
|
|
input_line_pointer = ilp_save;
|
|
}
|
|
else if (tok_sz == 1 && is_delim (tok[0]))
|
|
{
|
|
class = classes->sep_classes;
|
|
token->category = CAT_SEPARATOR;
|
|
}
|
|
else if (insn_p && is_insn (token, classes->insn_classes))
|
|
{
|
|
class = classes->insn_classes;
|
|
token->category = CAT_INSTRUCTION;
|
|
}
|
|
else
|
|
{
|
|
/* We are in fact dealing with a symbol. */
|
|
class = classes->imm_classes;
|
|
token->category = CAT_IMMEDIATE;
|
|
|
|
char *ilp_save = input_line_pointer;
|
|
input_line_pointer = tok;
|
|
expression (&exp);
|
|
|
|
/* If the symbol can be resolved easily takes it value now. Otherwise it
|
|
means that is either a symbol which will need a real relocation or an
|
|
internal fixup (ie, a pseudo-function, or a computation on symbols). */
|
|
if (exp.X_op != O_symbol && exp.X_op != O_pseudo_fixup)
|
|
{
|
|
token->val = exp.X_add_number;
|
|
token_val_p = 1;
|
|
}
|
|
|
|
input_line_pointer = ilp_save;
|
|
}
|
|
|
|
if (class == classes->imm_classes)
|
|
{
|
|
uint64_t uval
|
|
= (token_val_p
|
|
? token->val
|
|
: strtoull (tok + (tok[0] == '-') + (tok[0] == '+'), NULL, 0));
|
|
int64_t val = uval;
|
|
int64_t pval = val < 0 ? -uval : uval;
|
|
int neg_power2_p = val < 0 && !(pval & (pval - 1));
|
|
unsigned len = pval ? 8 * sizeof (pval) - __builtin_clzll (pval) : 0;
|
|
while (class[cur].class_id != -1
|
|
&& ((unsigned) (class[cur].sz < 0
|
|
? -class[cur].sz - !neg_power2_p
|
|
: class[cur].sz) < len
|
|
|| (exp.X_op == O_symbol
|
|
&& !(has_relocation_of_size
|
|
(str_hash_find (env.reloc_hash,
|
|
TOKEN_NAME (class[cur].class_id)))))
|
|
|| (exp.X_op == O_pseudo_fixup
|
|
&& !(kvx_get_pseudo_func2
|
|
(exp.X_op_symbol,
|
|
str_hash_find (env.reloc_hash,
|
|
TOKEN_NAME (class[cur].class_id)))))))
|
|
++cur;
|
|
|
|
token->val = uval;
|
|
// if (exp.X_op == O_pseudo_fixup)
|
|
// token->val = (uintptr_t) !kvx_get_pseudo_func2 (exp.X_op_symbol, str_hash_find (env.reloc_hash, TOKEN_NAME (class[cur].class_id)));
|
|
found = 1;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
for (int i = 0; !found && i < class[cur].sz; ++i)
|
|
{
|
|
const char *ref = class[cur].class_values[i];
|
|
found = ((long) strlen (ref) == tok_sz) && !strncmp (tok, ref, tok_sz);
|
|
token->val = i;
|
|
}
|
|
|
|
cur += !(found);
|
|
}
|
|
while (!found && class[cur].class_id != -1);
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
token->category = CAT_IMMEDIATE;
|
|
return token->class_id = classes->imm_classes[0].class_id;
|
|
}
|
|
|
|
#define unset(w, rg) ((w) & (~(1ULL << ((rg) - env.fst_reg))))
|
|
if (class == classes->reg_classes && !env.opts.allow_all_sfr)
|
|
return token->class_id = unset (class[cur].class_id, env.sys_reg);
|
|
#undef unset
|
|
|
|
return token->class_id = class[cur].class_id;
|
|
}
|
|
|
|
static int
|
|
read_token (struct token_s *tok)
|
|
{
|
|
int insn_p = tok->begin == 0;
|
|
int modifier_p = 0;
|
|
char *str = tok->insn;
|
|
int *begin = &tok->begin;
|
|
int *end = &tok->end;
|
|
int last_imm_p = 0;
|
|
|
|
/* Was the last previous token was an immediate? */
|
|
for (int i = 1; *begin - i > 0; ++i)
|
|
{
|
|
if ('0' <= str[*begin - i] && str[*begin - i] <= '9')
|
|
last_imm_p = 1;
|
|
else if (str[*begin - i] != ' ' && str[*begin - i] != '\t')
|
|
break;
|
|
}
|
|
|
|
/* Eat up all leading spaces. */
|
|
while (str[*begin] && (str[*begin] == ' ' || str[*begin] == '\n'))
|
|
*begin += 1;
|
|
|
|
*end = *begin;
|
|
|
|
if (!str[*begin])
|
|
return 0;
|
|
|
|
/* Special case, we're reading an instruction. Try to read as much as possible
|
|
as long as the prefix is a valid instruction. */
|
|
if (insn_p)
|
|
*end += longest_match (str + *begin, strlen (str + *begin), env.insns);
|
|
else
|
|
{
|
|
if (is_delim (str[*begin]))
|
|
{
|
|
*end += 1;
|
|
get_token_class (tok, env.token_classes, insn_p, modifier_p);
|
|
return 1;
|
|
}
|
|
|
|
if (str[*begin] == '.'
|
|
&& (!(*begin > 0 && (str[*begin - 1] == ' ' || is_delim(str[*begin - 1])))
|
|
|| last_imm_p))
|
|
modifier_p = 1;
|
|
|
|
/* This is a modifier or a register */
|
|
if (str[*begin] == '.' || str[*begin] == '$')
|
|
*end += 1;
|
|
|
|
/* Stop when reaching the start of the new token. */
|
|
while (!(!str[*end] || is_delim (str[*end]) || str[*end] == ' ' || (modifier_p && str[*end] == '.')))
|
|
*end += 1;
|
|
|
|
}
|
|
|
|
get_token_class (tok, env.token_classes, insn_p, modifier_p);
|
|
return 1;
|
|
}
|
|
|
|
/* Rewrite with as_bad. */
|
|
static void
|
|
rule_expect_error (int rule_id, char *buf, int bufsz __attribute__((unused)))
|
|
{
|
|
int i = 0;
|
|
int pos = 0;
|
|
int comma = 0;
|
|
pos += sprintf (buf + pos, "expected one of [");
|
|
struct steering_rule *rules = env.rules[rule_id].rules;
|
|
while (rules[i].steering != -1)
|
|
{
|
|
if ((env.opts.allow_all_sfr || rules[i].steering != env.sys_reg)
|
|
&& rules[i].steering != -3)
|
|
{
|
|
pos += sprintf (buf + pos, "%s%s", comma ? ", " : "", TOKEN_NAME (rules[i].steering));
|
|
comma = 1;
|
|
}
|
|
i += 1;
|
|
}
|
|
pos += sprintf (buf + pos, "].");
|
|
}
|
|
|
|
static struct token_list *
|
|
create_token (struct token_s tok, int len, int loc)
|
|
{
|
|
struct token_list *tl = calloc (1, sizeof *tl);
|
|
int tok_sz = tok.end - tok.begin;
|
|
tl->tok = calloc (tok_sz + 1, sizeof (char));
|
|
memcpy (tl->tok, tok.insn + tok.begin, tok_sz * sizeof (char));
|
|
tl->val = tok.val;
|
|
tl->class_id = tok.class_id;
|
|
tl->category = tok.category;
|
|
tl->next = NULL;
|
|
tl->len = len;
|
|
tl->loc = loc;
|
|
return tl;
|
|
}
|
|
|
|
void
|
|
print_token_list (struct token_list *lst)
|
|
{
|
|
struct token_list *cur = lst;
|
|
while (cur)
|
|
{
|
|
printf_debug (0, "%s (%llu : %s : %llu) / ",
|
|
cur->tok, cur->val, TOKEN_NAME (cur->class_id), cur->loc);
|
|
cur = cur->next;
|
|
}
|
|
printf_debug (0, "\n");
|
|
}
|
|
|
|
void
|
|
free_token_list (struct token_list *tok_list)
|
|
{
|
|
struct token_list *cur = tok_list;
|
|
struct token_list *tmp;
|
|
while (cur)
|
|
{
|
|
tmp = cur->next;
|
|
free (cur->tok);
|
|
free (cur);
|
|
cur = tmp;
|
|
}
|
|
}
|
|
|
|
static struct token_list *
|
|
token_list_append (struct token_list *lst1, struct token_list *lst2)
|
|
{
|
|
if (lst1 == NULL)
|
|
return lst2;
|
|
|
|
if (lst2 == NULL)
|
|
return NULL;
|
|
|
|
struct token_list *hd = lst1;
|
|
while (hd->next)
|
|
{
|
|
hd->len += lst2->len;
|
|
hd = hd->next;
|
|
}
|
|
|
|
hd->len += lst2->len;
|
|
hd->next = lst2;
|
|
return lst1;
|
|
}
|
|
|
|
struct error_list
|
|
{
|
|
int loc, rule;
|
|
struct error_list *nxt;
|
|
};
|
|
|
|
static struct error_list *
|
|
error_list_insert (int rule, int loc, struct error_list *nxt)
|
|
{
|
|
struct error_list *n = calloc (1, sizeof (*n));
|
|
n->loc = loc > 0 ? loc - 1 : loc;
|
|
n->rule = rule;
|
|
n->nxt = nxt;
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
free_error_list (struct error_list *l)
|
|
{
|
|
struct error_list *tmp, *cur_err = l;
|
|
while ((tmp = cur_err))
|
|
{
|
|
cur_err = cur_err->nxt;
|
|
free (tmp);
|
|
}
|
|
}
|
|
|
|
static int
|
|
CLASS_ID (struct token_s tok)
|
|
{
|
|
int offset = __builtin_ctzll (tok.class_id & -tok.class_id);
|
|
switch (tok.category)
|
|
{
|
|
case CAT_REGISTER:
|
|
return env.fst_reg + offset;
|
|
case CAT_MODIFIER:
|
|
return env.fst_mod + offset;
|
|
default:
|
|
return tok.class_id;
|
|
}
|
|
}
|
|
|
|
struct parser {
|
|
|
|
};
|
|
|
|
static struct token_list *
|
|
parse_with_restarts (struct token_s tok, int jump_target, struct rule rules[],
|
|
struct error_list **errs)
|
|
{
|
|
int end_of_line = 0;
|
|
struct steering_rule *cur_rule = rules[jump_target].rules;
|
|
|
|
if (!tok.insn[tok.begin])
|
|
tok.class_id = -3;
|
|
|
|
if (CLASS_ID (tok) == -1)
|
|
{
|
|
/* Unknown token */
|
|
*errs = error_list_insert (jump_target, tok.begin, *errs);
|
|
return NULL;
|
|
}
|
|
|
|
printf_debug (1, "\nEntering rule: %d (Trying to match: (%s)[%d])\n",
|
|
jump_target, TOKEN_NAME (CLASS_ID (tok)), CLASS_ID (tok));
|
|
|
|
/* 1. Find a rule that can be used with the current token. */
|
|
int i = 0;
|
|
while (cur_rule[i].steering != -1 && cur_rule[i].steering != CLASS_ID (tok))
|
|
i += 1;
|
|
|
|
printf_debug (1, "steering: %d (%s), jump_target: %d, stack_it: %d\n",
|
|
cur_rule[i].steering, TOKEN_NAME (cur_rule[i].steering),
|
|
cur_rule[i].jump_target, cur_rule[i].stack_it);
|
|
|
|
struct token_s init_tok = tok;
|
|
retry:;
|
|
tok = init_tok;
|
|
if (cur_rule[i].jump_target == -2 && cur_rule[i].stack_it == -2)
|
|
{
|
|
/* We're reading eps. */
|
|
printf_debug (1, "successfully ignored: %s\n", TOKEN_NAME (jump_target));
|
|
struct token_s tok_ =
|
|
{ (char *)".", 0, 1, CAT_MODIFIER, jump_target, 0 };
|
|
return create_token (tok_, 0, tok.begin);
|
|
}
|
|
else if (cur_rule[i].jump_target == -1 && cur_rule[i].stack_it == -1)
|
|
{
|
|
/* We're handling the rule for a terminal (not eps) */
|
|
if (cur_rule[i].steering == CLASS_ID (tok))
|
|
// && tok.begin != tok.end) -- only fails when eps is last, eg. fence.
|
|
{
|
|
/* We matched a token */
|
|
printf_debug (1, "matched %s\n", TOKEN_NAME (CLASS_ID (tok)));
|
|
tok.class_id = CLASS_ID (tok);
|
|
return create_token (tok, 1, tok.begin);
|
|
}
|
|
else
|
|
{
|
|
/* This is a mandatory modifier */
|
|
*errs = error_list_insert (jump_target, tok.begin, *errs);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Not on a terminal */
|
|
struct token_list *fst_part =
|
|
parse_with_restarts (tok, cur_rule[i].jump_target, rules, errs);
|
|
/* While parsing fails but there is hope since the current token can be
|
|
promoted. */
|
|
while (!fst_part && tok.class_id != (int64_t) promote_token (tok))
|
|
{
|
|
free_token_list (fst_part);
|
|
tok.class_id = promote_token (tok);
|
|
printf_debug (1, "> Restart with %s?\n", TOKEN_NAME (CLASS_ID (tok)));
|
|
fst_part = parse_with_restarts (tok, cur_rule[i].jump_target, rules, errs);
|
|
};
|
|
|
|
if (!fst_part)
|
|
{
|
|
i += 1;
|
|
while (cur_rule[i].steering != CLASS_ID(tok) && cur_rule[i].steering != -1)
|
|
i += 1;
|
|
if (cur_rule[i].steering != -1)
|
|
goto retry;
|
|
}
|
|
|
|
if (!fst_part)
|
|
{
|
|
printf_debug (1, "fst_part == NULL (Exiting %d)\n", jump_target);
|
|
return NULL;
|
|
}
|
|
|
|
for (int _ = 0; _ < fst_part->len; ++_)
|
|
{
|
|
tok.begin = tok.end;
|
|
end_of_line = !read_token (&tok);
|
|
}
|
|
|
|
if (end_of_line && cur_rule[i].stack_it == -1)
|
|
{
|
|
/* No more tokens and no more place to go */
|
|
printf_debug (1, "return fst_part.\n");
|
|
return fst_part;
|
|
}
|
|
else if (!end_of_line && cur_rule[i].stack_it == -1)
|
|
{
|
|
/* Too much tokens. */
|
|
printf_debug (1, "too much tokens\n");
|
|
*errs = error_list_insert (cur_rule[i].stack_it, tok.begin, *errs);
|
|
return NULL;
|
|
}
|
|
else if (cur_rule[i].stack_it == -1)
|
|
{
|
|
printf_debug (1, "return fst_part. (end of rule)\n");
|
|
return fst_part;
|
|
}
|
|
|
|
printf_debug (1, "snd_part: Trying to match: %s\n", TOKEN_NAME (CLASS_ID (tok)));
|
|
struct token_list *snd_part = parse_with_restarts (tok, cur_rule[i].stack_it, rules, errs);
|
|
while (!snd_part && tok.class_id != (int64_t) promote_token (tok))
|
|
{
|
|
tok.class_id = promote_token (tok);
|
|
printf_debug (1, ">> Restart with %s?\n", TOKEN_NAME (CLASS_ID (tok)));
|
|
snd_part = parse_with_restarts (tok, cur_rule[i].stack_it, rules, errs);
|
|
}
|
|
|
|
if (!snd_part)
|
|
{
|
|
free_token_list (fst_part);
|
|
i += 1;
|
|
tok = init_tok;
|
|
while (cur_rule[i].steering != CLASS_ID (tok) && cur_rule[i].steering != -1)
|
|
i += 1;
|
|
if (cur_rule[i].steering != -1)
|
|
goto retry;
|
|
}
|
|
|
|
if (!snd_part)
|
|
{
|
|
printf_debug (1, "snd_part == NULL (Exiting %d)\n", jump_target);
|
|
return NULL;
|
|
}
|
|
|
|
printf_debug (1, "Exiting rule: %d\n", jump_target,
|
|
TOKEN_NAME (CLASS_ID (tok)), tok.class_id);
|
|
|
|
/* Combine fst & snd parts */
|
|
return token_list_append (fst_part, snd_part);
|
|
}
|
|
|
|
/* During the parsing the modifiers and registers are handled through pseudo
|
|
classes such that each register and modifier appears in at most one pseudo
|
|
class. Since the pseudo-classes are not correlated with how the modifiers
|
|
and registers are encoded we fix that after a successful match instead of
|
|
updating it many times during the parsing.
|
|
|
|
Currently, only assigning correct values to modifiers is of interest. The
|
|
real value of registers is computed in tc-kvx.c:insert_operand. */
|
|
|
|
static void
|
|
assign_final_values (struct token_list *lst)
|
|
{
|
|
(void) lst;
|
|
struct token_list *cur = lst;
|
|
|
|
while (cur)
|
|
{
|
|
if (cur->category == CAT_MODIFIER)
|
|
{
|
|
int idx = cur->class_id - env.fst_mod;
|
|
int found = 0;
|
|
for (int i = 0 ; !found && kvx_modifiers[idx][i]; ++i)
|
|
if ((found = !strcmp (cur->tok, kvx_modifiers[idx][i])))
|
|
cur->val = i;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
struct token_list *
|
|
parse (struct token_s tok)
|
|
{
|
|
int error_code = 0;
|
|
int error_char = 0;
|
|
struct error_list *errs = NULL;
|
|
read_token (&tok);
|
|
|
|
struct token_list *tok_list =
|
|
parse_with_restarts (tok, 0, env.rules, &errs);
|
|
|
|
if (!tok_list)
|
|
{
|
|
struct error_list *cur_err = errs;
|
|
while (cur_err)
|
|
{
|
|
if (cur_err->loc > error_char)
|
|
{
|
|
error_char = cur_err->loc;
|
|
error_code = cur_err->rule;
|
|
}
|
|
cur_err = cur_err->nxt;
|
|
}
|
|
}
|
|
|
|
free_error_list (errs);
|
|
|
|
if (!tok_list)
|
|
{
|
|
if (error_code != -1)
|
|
{
|
|
char buf[256] = { 0 };
|
|
const char * msg = "Unexpected token when parsing %s.";
|
|
for (int i = 0; i < (int) (strlen (msg) + error_char + 1 - 4) ; ++i)
|
|
buf[i] = ' ';
|
|
buf[strlen (msg) + error_char + 1 - 4] = '^';
|
|
as_bad (msg, tok.insn);
|
|
if (env.opts.diagnostics)
|
|
{
|
|
as_bad ("%s", buf);
|
|
char err_buf[10000] = { 0 };
|
|
rule_expect_error (error_code, err_buf, 10000);
|
|
as_bad ("%s", err_buf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char buf[256] = { 0 };
|
|
const char * msg = "Extra token when parsing %s.";
|
|
for (int i = 0; i < (int) (strlen (msg) + error_char + 1 - 4) ; ++i)
|
|
buf[i] = ' ';
|
|
buf[strlen (msg) + error_char + 1 - 4] = '^';
|
|
as_bad (msg, tok.insn);
|
|
if (env.opts.diagnostics)
|
|
as_bad ("%s\n", buf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf_debug (1, "[PASS] Successfully matched %s\n", tok.insn);
|
|
assign_final_values (tok_list);
|
|
// print_token_list (tok_list);
|
|
// free_token_list (tok_list);
|
|
}
|
|
return tok_list;
|
|
}
|
|
|
|
void
|
|
setup (int core)
|
|
{
|
|
switch (core)
|
|
{
|
|
case ELF_KVX_CORE_KV3_1:
|
|
setup_kv3_v1 ();
|
|
break;
|
|
case ELF_KVX_CORE_KV3_2:
|
|
setup_kv3_v2 ();
|
|
break;
|
|
case ELF_KVX_CORE_KV4_1:
|
|
setup_kv4_v1 ();
|
|
break;
|
|
default:
|
|
as_bad ("Unknown architecture");
|
|
abort ();
|
|
}
|
|
|
|
for (int i = 0; env.token_classes->insn_classes[i].class_values ; ++i)
|
|
env.insns =
|
|
insert (env.token_classes->insn_classes[i].class_values[0], env.insns);
|
|
}
|
|
|
|
void
|
|
cleanup ()
|
|
{
|
|
free_node (env.insns);
|
|
}
|