blob: 4fdb07e981e7b4ebea2c8cf6b351d97ca4cad249 [file] [log] [blame]
/* The IGEN simulator generator for GDB, the GNU Debugger.
Copyright 2002-2021 Free Software Foundation, Inc.
Contributed by Andrew Cagney.
This file is part of GDB.
This program 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.
This program 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. If not, see <http://www.gnu.org/licenses/>. */
#include "misc.h"
#include "lf.h"
#include "table.h"
#include "filter.h"
#include "igen.h"
#include "ld-insn.h"
static insn_word_entry *
parse_insn_word (line_ref *line, char *string, int word_nr)
{
char *chp;
insn_word_entry *word = ZALLOC (insn_word_entry);
/* create a leading sentinal */
word->first = ZALLOC (insn_field_entry);
word->first->first = -1;
word->first->last = -1;
word->first->width = 0;
/* and a trailing sentinal */
word->last = ZALLOC (insn_field_entry);
word->last->first = options.insn_bit_size;
word->last->last = options.insn_bit_size;
word->last->width = 0;
/* link them together */
word->first->next = word->last;
word->last->prev = word->first;
/* now work through the formats */
chp = skip_spaces (string);
while (*chp != '\0')
{
char *start_pos;
int strlen_pos;
char *start_val;
int strlen_val;
insn_field_entry *new_field;
/* create / link in the new field */
new_field = ZALLOC (insn_field_entry);
new_field->next = word->last;
new_field->prev = word->last->prev;
new_field->next->prev = new_field;
new_field->prev->next = new_field;
new_field->word_nr = word_nr;
/* break out the first field (if present) */
start_pos = chp;
chp = skip_to_separator (chp, ".,!");
strlen_pos = back_spaces (start_pos, chp) - start_pos;
/* break out the second field (if present) */
if (*chp != '.')
{
/* assume what was specified was the value (and not the start
position). Assume the value length implicitly specifies
the number of bits */
start_val = start_pos;
strlen_val = strlen_pos;
start_pos = "";
strlen_pos = 0;
}
else
{
chp++; /* skip `.' */
chp = skip_spaces (chp);
start_val = chp;
if (*chp == '/' || *chp == '*')
{
do
{
chp++;
}
while (*chp == '/' || *chp == '*');
}
else if (isalpha (*start_val))
{
do
{
chp++;
}
while (isalnum (*chp) || *chp == '_');
}
else if (isdigit (*start_val))
{
do
{
chp++;
}
while (isalnum (*chp));
}
strlen_val = chp - start_val;
chp = skip_spaces (chp);
}
if (strlen_val == 0)
error (line, "Empty value field\n");
/* break out any conditional fields - { [ "!" | "=" [ <value> | <field-name> } */
while (*chp == '!' || *chp == '=')
{
char *start;
char *end;
int len;
insn_field_cond *new_cond = ZALLOC (insn_field_cond);
/* determine the conditional test */
switch (*chp)
{
case '=':
new_cond->test = insn_field_cond_eq;
break;
case '!':
new_cond->test = insn_field_cond_ne;
break;
default:
ASSERT (0);
}
/* save the value */
chp++;
chp = skip_spaces (chp);
start = chp;
chp = skip_to_separator (chp, "+,:!=");
end = back_spaces (start, chp);
len = end - start;
if (len == 0)
error (line, "Missing or invalid conditional value\n");
new_cond->string = NZALLOC (char, len + 1);
strncpy (new_cond->string, start, len);
/* determine the conditional type */
if (isdigit (*start))
{
/* [ "!" | "=" ] <value> */
new_cond->type = insn_field_cond_value;
new_cond->value = a2i (new_cond->string);
}
else
{
/* [ "!" | "=" ] <field> - check field valid */
new_cond->type = insn_field_cond_field;
/* new_cond->field is determined in later */
}
/* Only a single `=' is permitted. */
if ((new_cond->test == insn_field_cond_eq
&& new_field->conditions != NULL)
|| (new_field->conditions != NULL
&& new_field->conditions->test == insn_field_cond_eq))
error (line, "Only single conditional when `=' allowed\n");
/* insert it */
{
insn_field_cond **last = &new_field->conditions;
while (*last != NULL)
last = &(*last)->next;
*last = new_cond;
}
}
/* NOW verify that the field was finished */
if (*chp == ',')
{
chp = skip_spaces (chp + 1);
if (*chp == '\0')
error (line, "empty field\n");
}
else if (*chp != '\0')
{
error (line, "Missing field separator\n");
}
/* copy the value */
new_field->val_string = NZALLOC (char, strlen_val + 1);
strncpy (new_field->val_string, start_val, strlen_val);
if (isdigit (new_field->val_string[0]))
{
if (strlen_pos == 0)
{
/* when the length/pos field is omited, an integer field
is always binary */
unsigned64 val = 0;
int i;
for (i = 0; i < strlen_val; i++)
{
if (new_field->val_string[i] != '0'
&& new_field->val_string[i] != '1')
error (line, "invalid binary field %s\n",
new_field->val_string);
val = (val << 1) + (new_field->val_string[i] == '1');
}
new_field->val_int = val;
new_field->type = insn_field_int;
}
else
{
new_field->val_int = a2i (new_field->val_string);
new_field->type = insn_field_int;
}
}
else if (new_field->val_string[0] == '/')
{
new_field->type = insn_field_reserved;
}
else if (new_field->val_string[0] == '*')
{
new_field->type = insn_field_wild;
}
else
{
new_field->type = insn_field_string;
if (filter_is_member (word->field_names, new_field->val_string))
error (line, "Field name %s is duplicated\n",
new_field->val_string);
filter_parse (&word->field_names, new_field->val_string);
}
if (new_field->type != insn_field_string
&& new_field->conditions != NULL)
error (line, "Conditionals can only be applied to named fields\n");
/* the copy the position */
new_field->pos_string = NZALLOC (char, strlen_pos + 1);
strncpy (new_field->pos_string, start_pos, strlen_pos);
if (strlen_pos == 0)
{
new_field->first = new_field->prev->last + 1;
if (new_field->first == 0 /* first field */
&& *chp == '\0' /* no further fields */
&& new_field->type == insn_field_string)
{
/* A single string without any position, assume that it
represents the entire instruction word */
new_field->width = options.insn_bit_size;
}
else
{
/* No explicit width/position, assume value implicitly
supplies the width */
new_field->width = strlen_val;
}
new_field->last = new_field->first + new_field->width - 1;
if (new_field->last >= options.insn_bit_size)
error (line, "Bit position %d exceed instruction bit size (%d)\n",
new_field->last, options.insn_bit_size);
}
else if (options.insn_specifying_widths)
{
new_field->first = new_field->prev->last + 1;
new_field->width = a2i (new_field->pos_string);
new_field->last = new_field->first + new_field->width - 1;
if (new_field->last >= options.insn_bit_size)
error (line, "Bit position %d exceed instruction bit size (%d)\n",
new_field->last, options.insn_bit_size);
}
else
{
new_field->first = target_a2i (options.hi_bit_nr,
new_field->pos_string);
new_field->last = new_field->next->first - 1; /* guess */
new_field->width = new_field->last - new_field->first + 1; /* guess */
new_field->prev->last = new_field->first - 1; /*fix */
new_field->prev->width = new_field->first - new_field->prev->first; /*fix */
}
}
/* fiddle first/last so that the sentinals disapear */
ASSERT (word->first->last < 0);
ASSERT (word->last->first >= options.insn_bit_size);
word->first = word->first->next;
word->last = word->last->prev;
/* check that the last field goes all the way to the last bit */
if (word->last->last != options.insn_bit_size - 1)
{
if (options.warn.width)
options.warning (line, "Instruction format is not %d bits wide\n",
options.insn_bit_size);
word->last->last = options.insn_bit_size - 1;
}
/* now go over this again, pointing each bit position at a field
record */
{
insn_field_entry *field;
for (field = word->first;
field->last < options.insn_bit_size; field = field->next)
{
int i;
for (i = field->first; i <= field->last; i++)
{
word->bit[i] = ZALLOC (insn_bit_entry);
word->bit[i]->field = field;
switch (field->type)
{
case insn_field_invalid:
ASSERT (0);
break;
case insn_field_int:
word->bit[i]->mask = 1;
word->bit[i]->value = ((field->val_int
& ((insn_uint) 1 <<
(field->last - i))) != 0);
case insn_field_reserved:
case insn_field_wild:
case insn_field_string:
/* if we encounter a constant conditional, encode
their bit value. */
if (field->conditions != NULL
&& field->conditions->test == insn_field_cond_eq
&& field->conditions->type == insn_field_cond_value)
{
word->bit[i]->mask = 1;
word->bit[i]->value = ((field->conditions->value
& ((insn_uint) 1 <<
(field->last - i))) != 0);
}
break;
}
}
}
}
return word;
}
static void
parse_insn_words (insn_entry * insn, char *formats)
{
insn_word_entry **last_word = &insn->words;
char *chp;
/* now work through the formats */
insn->nr_words = 0;
chp = formats;
while (1)
{
char *start_pos;
char *end_pos;
int strlen_pos;
char *format;
insn_word_entry *new_word;
/* skip leading spaces */
chp = skip_spaces (chp);
/* break out the format */
start_pos = chp;
chp = skip_to_separator (chp, "+");
end_pos = back_spaces (start_pos, chp);
strlen_pos = end_pos - start_pos;
/* check that something was there */
if (strlen_pos == 0)
error (insn->line, "missing or empty instruction format\n");
/* parse the field */
format = NZALLOC (char, strlen_pos + 1);
strncpy (format, start_pos, strlen_pos);
new_word = parse_insn_word (insn->line, format, insn->nr_words);
insn->nr_words++;
if (filter_is_common (insn->field_names, new_word->field_names))
error (insn->line, "Field name duplicated between two words\n");
filter_add (&insn->field_names, new_word->field_names);
/* insert it */
*last_word = new_word;
last_word = &new_word->next;
/* last format? */
if (*chp == '\0')
break;
ASSERT (*chp == '+');
chp++;
}
/* create a quick access array (indexed by word) of the same structure */
{
int i;
insn_word_entry *word;
insn->word = NZALLOC (insn_word_entry *, insn->nr_words + 1);
for (i = 0, word = insn->words;
i < insn->nr_words; i++, word = word->next)
insn->word[i] = word;
}
/* Go over all fields that have conditionals refering to other
fields. Link the fields up. Verify that the two fields have the
same size. Verify that the two fields are different */
{
int i;
for (i = 0; i < insn->nr_words; i++)
{
insn_word_entry *word = insn->word[i];
insn_field_entry *f;
for (f = word->first; f->last < options.insn_bit_size; f = f->next)
{
insn_field_cond *cond;
for (cond = f->conditions; cond != NULL; cond = cond->next)
{
if (cond->type == insn_field_cond_field)
{
int j;
if (strcmp (cond->string, f->val_string) == 0)
error (insn->line,
"Conditional `%s' of field `%s' refers to its self\n",
cond->string, f->val_string);
for (j = 0; j <= i && cond->field == NULL; j++)
{
insn_word_entry *refered_word = insn->word[j];
insn_field_entry *refered_field;
for (refered_field = refered_word->first;
refered_field != NULL && cond->field == NULL;
refered_field = refered_field->next)
{
if (refered_field->type == insn_field_string
&& strcmp (refered_field->val_string,
cond->string) == 0)
{
/* found field being refered to by conditonal */
cond->field = refered_field;
/* check refered to and this field are
the same size */
if (f->width != refered_field->width)
error (insn->line,
"Conditional `%s' of field `%s' should be of size %s\n",
cond->string, f->val_string,
refered_field->width);
}
}
}
if (cond->field == NULL)
error (insn->line,
"Conditional `%s' of field `%s' not yet defined\n",
cond->string, f->val_string);
}
}
}
}
}
}
typedef enum
{
unknown_record = 0,
insn_record, /* default */
code_record,
cache_record,
compute_record,
scratch_record,
option_record,
string_function_record,
function_record,
internal_record,
define_record,
include_record,
model_processor_record,
model_macro_record,
model_data_record,
model_static_record,
model_function_record,
model_internal_record,
}
insn_record_type;
static const name_map insn_type_map[] = {
{"option", option_record},
{"cache", cache_record},
{"compute", compute_record},
{"scratch", scratch_record},
{"define", define_record},
{"include", include_record},
{"%s", string_function_record},
{"function", function_record},
{"internal", internal_record},
{"model", model_processor_record},
{"model-macro", model_macro_record},
{"model-data", model_data_record},
{"model-static", model_static_record},
{"model-internal", model_internal_record},
{"model-function", model_function_record},
{NULL, insn_record},
};
static int
record_is_old (table_entry *entry)
{
if (entry->nr_fields > record_type_field
&& strlen (entry->field[record_type_field]) == 0)
return 1;
return 0;
}
static insn_record_type
record_type (table_entry *entry)
{
switch (entry->type)
{
case table_code_entry:
return code_record;
case table_colon_entry:
if (record_is_old (entry))
{
/* old-format? */
if (entry->nr_fields > old_record_type_field)
{
int i = name2i (entry->field[old_record_type_field],
insn_type_map);
return i;
}
else
{
return unknown_record;
}
}
else if (entry->nr_fields > record_type_field
&& entry->field[0][0] == '\0')
{
/* new-format? */
int i = name2i (entry->field[record_type_field],
insn_type_map);
return i;
}
else
return insn_record; /* default */
}
return unknown_record;
}
static int
record_prefix_is (table_entry *entry, char ch, int nr_fields)
{
if (entry->type != table_colon_entry)
return 0;
if (entry->nr_fields < nr_fields)
return 0;
if (entry->field[0][0] != ch && ch != '\0')
return 0;
return 1;
}
static table_entry *
parse_model_data_record (insn_table *isa,
table *file,
table_entry *record,
int nr_fields, model_data **list)
{
table_entry *model_record = record;
table_entry *code_record = NULL;
model_data *new_data;
if (record->nr_fields < nr_fields)
error (record->line, "Incorrect number of fields\n");
record = table_read (file);
if (record->type == table_code_entry)
{
code_record = record;
record = table_read (file);
}
/* create the new data record */
new_data = ZALLOC (model_data);
new_data->line = model_record->line;
filter_parse (&new_data->flags,
model_record->field[record_filter_flags_field]);
new_data->entry = model_record;
new_data->code = code_record;
/* append it if not filtered out */
if (!is_filtered_out (options.flags_filter,
model_record->field[record_filter_flags_field])
&& !is_filtered_out (options.model_filter,
model_record->field[record_filter_models_field]))
{
while (*list != NULL)
list = &(*list)->next;
*list = new_data;
}
return record;
}
typedef enum
{
insn_bit_size_option = 1,
insn_specifying_widths_option,
hi_bit_nr_option,
flags_filter_option,
model_filter_option,
multi_sim_option,
format_names_option,
gen_delayed_branch,
unknown_option,
}
option_names;
static const name_map option_map[] = {
{"insn-bit-size", insn_bit_size_option},
{"insn-specifying-widths", insn_specifying_widths_option},
{"hi-bit-nr", hi_bit_nr_option},
{"flags-filter", flags_filter_option},
{"model-filter", model_filter_option},
{"multi-sim", multi_sim_option},
{"format-names", format_names_option},
{"gen-delayed-branch", gen_delayed_branch},
{NULL, unknown_option},
};
static table_entry *
parse_include_record (table *file, table_entry *record)
{
/* parse the include record */
if (record->nr_fields < nr_include_fields)
error (record->line, "Incorrect nr fields for include record\n");
/* process it */
if (!is_filtered_out (options.flags_filter,
record->field[record_filter_flags_field])
&& !is_filtered_out (options.model_filter,
record->field[record_filter_models_field]))
{
table_push (file, record->line, options.include,
record->field[include_filename_field]);
}
/* nb: can't read next record until after the file has been pushed */
record = table_read (file);
return record;
}
static table_entry *
parse_option_record (table *file, table_entry *record)
{
table_entry *option_record;
/* parse the option record */
option_record = record;
if (record->nr_fields < nr_option_fields)
error (record->line, "Incorrect nr of fields for option record\n");
record = table_read (file);
/* process it */
if (!is_filtered_out (options.flags_filter,
option_record->field[record_filter_flags_field])
&& !is_filtered_out (options.model_filter,
option_record->field[record_filter_models_field]))
{
char *name = option_record->field[option_name_field];
option_names option = name2i (name, option_map);
char *value = option_record->field[option_value_field];
switch (option)
{
case insn_bit_size_option:
{
options.insn_bit_size = a2i (value);
if (options.insn_bit_size < 0
|| options.insn_bit_size > max_insn_bit_size)
error (option_record->line,
"Instruction bit size out of range\n");
if (options.hi_bit_nr != options.insn_bit_size - 1
&& options.hi_bit_nr != 0)
error (option_record->line,
"insn-bit-size / hi-bit-nr conflict\n");
break;
}
case insn_specifying_widths_option:
{
options.insn_specifying_widths = a2i (value);
break;
}
case hi_bit_nr_option:
{
options.hi_bit_nr = a2i (value);
if (options.hi_bit_nr != 0
&& options.hi_bit_nr != options.insn_bit_size - 1)
error (option_record->line,
"hi-bit-nr / insn-bit-size conflict\n");
break;
}
case flags_filter_option:
{
filter_parse (&options.flags_filter, value);
break;
}
case model_filter_option:
{
filter_parse (&options.model_filter, value);
break;
}
case multi_sim_option:
{
options.gen.multi_sim = a2i (value);
break;
}
case format_names_option:
{
filter_parse (&options.format_name_filter, value);
break;
}
case gen_delayed_branch:
{
options.gen.delayed_branch = a2i (value);
break;
}
case unknown_option:
{
error (option_record->line, "Unknown option - %s\n", name);
break;
}
}
}
return record;
}
static table_entry *
parse_function_record (table *file,
table_entry *record,
function_entry ** list,
function_entry ** list_entry,
int is_internal, model_table *model)
{
function_entry *new_function;
new_function = ZALLOC (function_entry);
new_function->line = record->line;
new_function->is_internal = is_internal;
/* parse the function header */
if (record_is_old (record))
{
if (record->nr_fields < nr_old_function_fields)
error (record->line, "Missing fields from (old) function record\n");
new_function->type = record->field[old_function_typedef_field];
new_function->type = record->field[old_function_typedef_field];
if (record->nr_fields > old_function_param_field)
new_function->param = record->field[old_function_param_field];
new_function->name = record->field[old_function_name_field];
}
else
{
if (record->nr_fields < nr_function_fields)
error (record->line, "Missing fields from function record\n");
filter_parse (&new_function->flags,
record->field[record_filter_flags_field]);
filter_parse (&new_function->models,
record->field[record_filter_models_field]);
new_function->type = record->field[function_typedef_field];
new_function->param = record->field[function_param_field];
new_function->name = record->field[function_name_field];
}
record = table_read (file);
/* parse any function-model records */
while (record != NULL
&& record_prefix_is (record, '*', nr_function_model_fields))
{
char *model_name = record->field[function_model_name_field] + 1; /*skip `*' */
filter_parse (&new_function->models, model_name);
if (!filter_is_subset (model->processors, new_function->models))
{
error (record->line, "machine model `%s' undefined\n", model_name);
}
record = table_read (file);
}
/* parse the function body */
if (record->type == table_code_entry)
{
new_function->code = record;
record = table_read (file);
}
/* insert it */
if (!filter_is_subset (options.flags_filter, new_function->flags))
{
if (options.warn.discard)
notify (new_function->line, "Discarding function %s - filter flags\n",
new_function->name);
}
else if (new_function->models != NULL
&& !filter_is_common (options.model_filter, new_function->models))
{
if (options.warn.discard)
notify (new_function->line,
"Discarding function %s - filter models\n",
new_function->name);
}
else
{
while (*list != NULL)
list = &(*list)->next;
*list = new_function;
if (list_entry != NULL)
*list_entry = new_function;
}
/* done */
return record;
}
static void
parse_insn_model_record (table *file,
table_entry *record,
insn_entry * insn, model_table *model)
{
insn_model_entry **last_insn_model;
insn_model_entry *new_insn_model = ZALLOC (insn_model_entry);
/* parse it */
new_insn_model->line = record->line;
if (record->nr_fields > insn_model_unit_data_field)
new_insn_model->unit_data = record->field[insn_model_unit_data_field];
new_insn_model->insn = insn;
/* parse the model names, verify that all were defined */
new_insn_model->names = NULL;
filter_parse (&new_insn_model->names,
record->field[insn_model_name_field] + 1 /*skip `*' */ );
if (new_insn_model->names == NULL)
{
/* No processor names - a generic model entry, enter it into all
the non-empty fields */
int index;
for (index = 0; index < model->nr_models; index++)
if (insn->model[index] == 0)
{
insn->model[index] = new_insn_model;
}
/* also add the complete processor set to this processor's set */
filter_add (&insn->processors, model->processors);
}
else
{
/* Find the corresponding master model record for each name so
that they can be linked in. */
int index;
char *name = "";
while (1)
{
name = filter_next (new_insn_model->names, name);
if (name == NULL)
break;
index = filter_is_member (model->processors, name) - 1;
if (index < 0)
{
error (new_insn_model->line,
"machine model `%s' undefined\n", name);
}
/* store it in the corresponding model array entry */
if (insn->model[index] != NULL && insn->model[index]->names != NULL)
{
warning (new_insn_model->line,
"machine model `%s' previously defined\n", name);
error (insn->model[index]->line, "earlier definition\n");
}
insn->model[index] = new_insn_model;
/* also add the name to the instructions processor set as an
alternative lookup mechanism */
filter_parse (&insn->processors, name);
}
}
/* link it in */
last_insn_model = &insn->models;
while ((*last_insn_model) != NULL)
last_insn_model = &(*last_insn_model)->next;
*last_insn_model = new_insn_model;
}
static void
parse_insn_mnemonic_record (table *file,
table_entry *record, insn_entry * insn)
{
insn_mnemonic_entry **last_insn_mnemonic;
insn_mnemonic_entry *new_insn_mnemonic = ZALLOC (insn_mnemonic_entry);
/* parse it */
new_insn_mnemonic->line = record->line;
ASSERT (record->nr_fields > insn_mnemonic_format_field);
new_insn_mnemonic->format = record->field[insn_mnemonic_format_field];
ASSERT (new_insn_mnemonic->format[0] == '"');
if (new_insn_mnemonic->format[strlen (new_insn_mnemonic->format) - 1] !=
'"')
error (new_insn_mnemonic->line,
"Missing closing double quote in mnemonic field\n");
if (record->nr_fields > insn_mnemonic_condition_field)
new_insn_mnemonic->condition =
record->field[insn_mnemonic_condition_field];
new_insn_mnemonic->insn = insn;
/* insert it */
last_insn_mnemonic = &insn->mnemonics;
while ((*last_insn_mnemonic) != NULL)
last_insn_mnemonic = &(*last_insn_mnemonic)->next;
insn->nr_mnemonics++;
*last_insn_mnemonic = new_insn_mnemonic;
}
static table_entry *
parse_macro_record (table *file, table_entry *record)
{
#if 1
error (record->line, "Macros are not implemented");
#else
/* parse the define record */
if (record->nr_fields < nr_define_fields)
error (record->line, "Incorrect nr fields for define record\n");
/* process it */
if (!is_filtered_out (options.flags_filter,
record->field[record_filter_flags_field])
&& !is_filtered_out (options.model_filter,
record->field[record_filter_models_field]))
{
table_define (file,
record->line,
record->field[macro_name_field],
record->field[macro_args_field],
record->field[macro_expr_field]);
}
record = table_read (file);
#endif
return record;
}
insn_table *
load_insn_table (char *file_name, cache_entry *cache)
{
table *file = table_open (file_name);
table_entry *record = table_read (file);
insn_table *isa = ZALLOC (insn_table);
model_table *model = ZALLOC (model_table);
isa->model = model;
isa->caches = cache;
while (record != NULL)
{
switch (record_type (record))
{
case include_record:
{
record = parse_include_record (file, record);
break;
}
case option_record:
{
if (isa->insns != NULL)
error (record->line, "Option after first instruction\n");
record = parse_option_record (file, record);
break;
}
case string_function_record:
{
function_entry *function = NULL;
record = parse_function_record (file, record,
&isa->functions,
&function, 0 /*is-internal */ ,
model);
/* convert a string function record into an internal function */
if (function != NULL)
{
char *name = NZALLOC (char,
(strlen ("str_")
+ strlen (function->name) + 1));
strcat (name, "str_");
strcat (name, function->name);
function->name = name;
function->type = "const char *";
}
break;
}
case function_record: /* function record */
{
record = parse_function_record (file, record,
&isa->functions,
NULL, 0 /*is-internal */ ,
model);
break;
}
case internal_record:
{
/* only insert it into the function list if it is unknown */
function_entry *function = NULL;
record = parse_function_record (file, record,
&isa->functions,
&function, 1 /*is-internal */ ,
model);
/* check what was inserted to see if a pseudo-instruction
entry also needs to be created */
if (function != NULL)
{
insn_entry **insn = NULL;
if (strcmp (function->name, "illegal") == 0)
{
/* illegal function save it away */
if (isa->illegal_insn != NULL)
{
warning (function->line,
"Multiple illegal instruction definitions\n");
error (isa->illegal_insn->line,
"Location of first illegal instruction\n");
}
else
insn = &isa->illegal_insn;
}
if (insn != NULL)
{
*insn = ZALLOC (insn_entry);
(*insn)->line = function->line;
(*insn)->name = function->name;
(*insn)->code = function->code;
}
}
break;
}
case scratch_record: /* cache macro records */
case cache_record:
case compute_record:
{
cache_entry *new_cache;
/* parse the cache record */
if (record->nr_fields < nr_cache_fields)
error (record->line,
"Incorrect nr of fields for scratch/cache/compute record\n");
/* create it */
new_cache = ZALLOC (cache_entry);
new_cache->line = record->line;
filter_parse (&new_cache->flags,
record->field[record_filter_flags_field]);
filter_parse (&new_cache->models,
record->field[record_filter_models_field]);
new_cache->type = record->field[cache_typedef_field];
new_cache->name = record->field[cache_name_field];
filter_parse (&new_cache->original_fields,
record->field[cache_original_fields_field]);
new_cache->expression = record->field[cache_expression_field];
/* insert it but only if not filtered out */
if (!filter_is_subset (options.flags_filter, new_cache->flags))
{
notify (new_cache->line,
"Discarding cache entry %s - filter flags\n",
new_cache->name);
}
else if (is_filtered_out (options.model_filter,
record->
field[record_filter_models_field]))
{
notify (new_cache->line,
"Discarding cache entry %s - filter models\n",
new_cache->name);
}
else
{
cache_entry **last;
last = &isa->caches;
while (*last != NULL)
last = &(*last)->next;
*last = new_cache;
}
/* advance things */
record = table_read (file);
break;
}
/* model records */
case model_processor_record:
{
model_entry *new_model;
/* parse the model */
if (record->nr_fields < nr_model_processor_fields)
error (record->line,
"Incorrect nr of fields for model record\n");
if (isa->insns != NULL)
error (record->line, "Model appears after first instruction\n");
new_model = ZALLOC (model_entry);
filter_parse (&new_model->flags,
record->field[record_filter_flags_field]);
new_model->line = record->line;
new_model->name = record->field[model_name_field];
new_model->full_name = record->field[model_full_name_field];
new_model->unit_data = record->field[model_unit_data_field];
/* only insert it if not filtered out */
if (!filter_is_subset (options.flags_filter, new_model->flags))
{
notify (new_model->line,
"Discarding processor model %s - filter flags\n",
new_model->name);
}
else if (is_filtered_out (options.model_filter,
record->
field[record_filter_models_field]))
{
notify (new_model->line,
"Discarding processor model %s - filter models\n",
new_model->name);
}
else if (filter_is_member (model->processors, new_model->name))
{
error (new_model->line, "Duplicate processor model %s\n",
new_model->name);
}
else
{
model_entry **last;
last = &model->models;
while (*last != NULL)
last = &(*last)->next;
*last = new_model;
/* count it */
model->nr_models++;
filter_parse (&model->processors, new_model->name);
}
/* advance things */
record = table_read (file);
}
break;
case model_macro_record:
record = parse_model_data_record (isa, file, record,
nr_model_macro_fields,
&model->macros);
break;
case model_data_record:
record = parse_model_data_record (isa, file, record,
nr_model_data_fields,
&model->data);
break;
case model_static_record:
record = parse_function_record (file, record,
&model->statics,
NULL, 0 /*is internal */ ,
model);
break;
case model_internal_record:
record = parse_function_record (file, record,
&model->internals,
NULL, 1 /*is internal */ ,
model);
break;
case model_function_record:
record = parse_function_record (file, record,
&model->functions,
NULL, 0 /*is internal */ ,
model);
break;
case insn_record: /* instruction records */
{
insn_entry *new_insn;
char *format;
/* parse the instruction */
if (record->nr_fields < nr_insn_fields)
error (record->line,
"Incorrect nr of fields for insn record\n");
new_insn = ZALLOC (insn_entry);
new_insn->line = record->line;
filter_parse (&new_insn->flags,
record->field[record_filter_flags_field]);
/* save the format field. Can't parse it until after the
filter-out checks. Could be filtered out because the
format is invalid */
format = record->field[insn_word_field];
new_insn->format_name = record->field[insn_format_name_field];
if (options.format_name_filter != NULL
&& !filter_is_member (options.format_name_filter,
new_insn->format_name))
error (new_insn->line,
"Unreconized instruction format name `%s'\n",
new_insn->format_name);
filter_parse (&new_insn->options,
record->field[insn_options_field]);
new_insn->name = record->field[insn_name_field];
record = table_read (file);
/* Parse any model/assember records */
new_insn->nr_models = model->nr_models;
new_insn->model =
NZALLOC (insn_model_entry *, model->nr_models + 1);
while (record != NULL)
{
if (record_prefix_is (record, '*', nr_insn_model_fields))
parse_insn_model_record (file, record, new_insn, model);
else
if (record_prefix_is (record, '"', nr_insn_mnemonic_fields))
parse_insn_mnemonic_record (file, record, new_insn);
else
break;
/* advance */
record = table_read (file);
}
/* Parse the code record */
if (record != NULL && record->type == table_code_entry)
{
new_insn->code = record;
record = table_read (file);
}
else if (options.warn.unimplemented)
notify (new_insn->line, "unimplemented\n");
/* insert it */
if (!filter_is_subset (options.flags_filter, new_insn->flags))
{
if (options.warn.discard)
notify (new_insn->line,
"Discarding instruction %s (flags-filter)\n",
new_insn->name);
}
else if (new_insn->processors != NULL
&& options.model_filter != NULL
&& !filter_is_common (options.model_filter,
new_insn->processors))
{
/* only discard an instruction based in the processor
model when both the instruction and the options are
nonempty */
if (options.warn.discard)
notify (new_insn->line,
"Discarding instruction %s (processor-model)\n",
new_insn->name);
}
else
{
insn_entry **last;
/* finish the parsing */
parse_insn_words (new_insn, format);
/* append it */
last = &isa->insns;
while (*last)
last = &(*last)->next;
*last = new_insn;
/* update global isa counters */
isa->nr_insns++;
if (isa->max_nr_words < new_insn->nr_words)
isa->max_nr_words = new_insn->nr_words;
filter_add (&isa->flags, new_insn->flags);
filter_add (&isa->options, new_insn->options);
}
break;
}
case define_record:
record = parse_macro_record (file, record);
break;
case unknown_record:
case code_record:
error (record->line, "Unknown or unexpected entry\n");
}
}
return isa;
}
void
print_insn_words (lf *file, insn_entry * insn)
{
insn_word_entry *word = insn->words;
if (word != NULL)
{
while (1)
{
insn_field_entry *field = word->first;
while (1)
{
insn_field_cond *cond;
if (options.insn_specifying_widths)
lf_printf (file, "%d.", field->width);
else
lf_printf (file, "%d.",
i2target (options.hi_bit_nr, field->first));
switch (field->type)
{
case insn_field_invalid:
ASSERT (0);
break;
case insn_field_int:
lf_printf (file, "0x%lx", (long) field->val_int);
break;
case insn_field_reserved:
lf_printf (file, "/");
break;
case insn_field_wild:
lf_printf (file, "*");
break;
case insn_field_string:
lf_printf (file, "%s", field->val_string);
if (field->conditions == NULL)
break;
if (field->conditions->test == insn_field_cond_eq)
{
if (field->conditions->type == insn_field_cond_value)
lf_printf (file, "=%ld",
(long) field->conditions->value);
else
lf_printf (file, "=%s", field->conditions->string);
/* There can be only one equality condition. */
ASSERT (field->conditions->next == NULL);
break;
}
for (cond = field->conditions;
cond != NULL;
cond = cond->next)
{
ASSERT (cond->test == insn_field_cond_ne);
if (cond->type == insn_field_cond_value)
lf_printf (file, "!%ld", (long) cond->value);
else
lf_printf (file, "!%s", cond->string);
}
break;
}
if (field == word->last)
break;
field = field->next;
lf_printf (file, ",");
}
word = word->next;
if (word == NULL)
break;
lf_printf (file, "+");
}
}
}
void
function_entry_traverse (lf *file,
function_entry * functions,
function_entry_handler * handler, void *data)
{
function_entry *function;
for (function = functions; function != NULL; function = function->next)
{
handler (file, function, data);
}
}
void
insn_table_traverse_insn (lf *file,
insn_table *isa,
insn_entry_handler * handler, void *data)
{
insn_entry *insn;
for (insn = isa->insns; insn != NULL; insn = insn->next)
{
handler (file, isa, insn, data);
}
}
static void
dump_function_entry (lf *file,
char *prefix, function_entry * entry, char *suffix)
{
lf_printf (file, "%s(function_entry *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
dump_line_ref (file, "\n(line ", entry->line, ")");
dump_filter (file, "\n(flags ", entry->flags, ")");
lf_printf (file, "\n(type \"%s\")", entry->type);
lf_printf (file, "\n(name \"%s\")", entry->name);
lf_printf (file, "\n(param \"%s\")", entry->param);
dump_table_entry (file, "\n(code ", entry->code, ")");
lf_printf (file, "\n(is_internal %d)", entry->is_internal);
lf_printf (file, "\n(next 0x%lx)", (long) entry->next);
}
lf_printf (file, "%s", suffix);
}
static void
dump_function_entries (lf *file,
char *prefix, function_entry * entry, char *suffix)
{
lf_printf (file, "%s", prefix);
lf_indent (file, +1);
while (entry != NULL)
{
dump_function_entry (file, "\n(", entry, ")");
entry = entry->next;
}
lf_indent (file, -1);
lf_printf (file, "%s", suffix);
}
static char *
cache_entry_type_to_str (cache_entry_type type)
{
switch (type)
{
case scratch_value:
return "scratch";
case cache_value:
return "cache";
case compute_value:
return "compute";
}
ERROR ("Bad switch");
return 0;
}
static void
dump_cache_entry (lf *file, char *prefix, cache_entry *entry, char *suffix)
{
lf_printf (file, "%s(cache_entry *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
dump_line_ref (file, "\n(line ", entry->line, ")");
dump_filter (file, "\n(flags ", entry->flags, ")");
lf_printf (file, "\n(entry_type \"%s\")",
cache_entry_type_to_str (entry->entry_type));
lf_printf (file, "\n(name \"%s\")", entry->name);
dump_filter (file, "\n(original_fields ", entry->original_fields, ")");
lf_printf (file, "\n(type \"%s\")", entry->type);
lf_printf (file, "\n(expression \"%s\")", entry->expression);
lf_printf (file, "\n(next 0x%lx)", (long) entry->next);
}
lf_printf (file, "%s", suffix);
}
void
dump_cache_entries (lf *file, char *prefix, cache_entry *entry, char *suffix)
{
lf_printf (file, "%s", prefix);
lf_indent (file, +1);
while (entry != NULL)
{
dump_cache_entry (file, "\n(", entry, ")");
entry = entry->next;
}
lf_indent (file, -1);
lf_printf (file, "%s", suffix);
}
static void
dump_model_data (lf *file, char *prefix, model_data *entry, char *suffix)
{
lf_printf (file, "%s(model_data *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
lf_indent (file, +1);
dump_line_ref (file, "\n(line ", entry->line, ")");
dump_filter (file, "\n(flags ", entry->flags, ")");
dump_table_entry (file, "\n(entry ", entry->entry, ")");
dump_table_entry (file, "\n(code ", entry->code, ")");
lf_printf (file, "\n(next 0x%lx)", (long) entry->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", prefix);
}
static void
dump_model_datas (lf *file, char *prefix, model_data *entry, char *suffix)
{
lf_printf (file, "%s", prefix);
lf_indent (file, +1);
while (entry != NULL)
{
dump_model_data (file, "\n(", entry, ")");
entry = entry->next;
}
lf_indent (file, -1);
lf_printf (file, "%s", suffix);
}
static void
dump_model_entry (lf *file, char *prefix, model_entry *entry, char *suffix)
{
lf_printf (file, "%s(model_entry *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
lf_indent (file, +1);
dump_line_ref (file, "\n(line ", entry->line, ")");
dump_filter (file, "\n(flags ", entry->flags, ")");
lf_printf (file, "\n(name \"%s\")", entry->name);
lf_printf (file, "\n(full_name \"%s\")", entry->full_name);
lf_printf (file, "\n(unit_data \"%s\")", entry->unit_data);
lf_printf (file, "\n(next 0x%lx)", (long) entry->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", prefix);
}
static void
dump_model_entries (lf *file, char *prefix, model_entry *entry, char *suffix)
{
lf_printf (file, "%s", prefix);
lf_indent (file, +1);
while (entry != NULL)
{
dump_model_entry (file, "\n(", entry, ")");
entry = entry->next;
}
lf_indent (file, -1);
lf_printf (file, "%s", suffix);
}
static void
dump_model_table (lf *file, char *prefix, model_table *entry, char *suffix)
{
lf_printf (file, "%s(model_table *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
lf_indent (file, +1);
dump_filter (file, "\n(processors ", entry->processors, ")");
lf_printf (file, "\n(nr_models %d)", entry->nr_models);
dump_model_entries (file, "\n(models ", entry->models, ")");
dump_model_datas (file, "\n(macros ", entry->macros, ")");
dump_model_datas (file, "\n(data ", entry->data, ")");
dump_function_entries (file, "\n(statics ", entry->statics, ")");
dump_function_entries (file, "\n(internals ", entry->functions, ")");
dump_function_entries (file, "\n(functions ", entry->functions, ")");
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
static char *
insn_field_type_to_str (insn_field_type type)
{
switch (type)
{
case insn_field_invalid:
ASSERT (0);
return "(invalid)";
case insn_field_int:
return "int";
case insn_field_reserved:
return "reserved";
case insn_field_wild:
return "wild";
case insn_field_string:
return "string";
}
ERROR ("bad switch");
return 0;
}
void
dump_insn_field (lf *file,
char *prefix, insn_field_entry *field, char *suffix)
{
char *sep = " ";
lf_printf (file, "%s(insn_field_entry *) 0x%lx", prefix, (long) field);
if (field != NULL)
{
lf_indent (file, +1);
lf_printf (file, "%s(first %d)", sep, field->first);
lf_printf (file, "%s(last %d)", sep, field->last);
lf_printf (file, "%s(width %d)", sep, field->width);
lf_printf (file, "%s(type %s)", sep,
insn_field_type_to_str (field->type));
switch (field->type)
{
case insn_field_invalid:
ASSERT (0);
break;
case insn_field_int:
lf_printf (file, "%s(val 0x%lx)", sep, (long) field->val_int);
break;
case insn_field_reserved:
/* nothing output */
break;
case insn_field_wild:
/* nothing output */
break;
case insn_field_string:
lf_printf (file, "%s(val \"%s\")", sep, field->val_string);
break;
}
lf_printf (file, "%s(next 0x%lx)", sep, (long) field->next);
lf_printf (file, "%s(prev 0x%lx)", sep, (long) field->prev);
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
void
dump_insn_word_entry (lf *file,
char *prefix, insn_word_entry *word, char *suffix)
{
lf_printf (file, "%s(insn_word_entry *) 0x%lx", prefix, (long) word);
if (word != NULL)
{
int i;
insn_field_entry *field;
lf_indent (file, +1);
lf_printf (file, "\n(first 0x%lx)", (long) word->first);
lf_printf (file, "\n(last 0x%lx)", (long) word->last);
lf_printf (file, "\n(bit");
for (i = 0; i < options.insn_bit_size; i++)
lf_printf (file, "\n ((value %d) (mask %d) (field 0x%lx))",
word->bit[i]->value, word->bit[i]->mask,
(long) word->bit[i]->field);
lf_printf (file, ")");
for (field = word->first; field != NULL; field = field->next)
dump_insn_field (file, "\n(", field, ")");
dump_filter (file, "\n(field_names ", word->field_names, ")");
lf_printf (file, "\n(next 0x%lx)", (long) word->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_word_entries (lf *file,
char *prefix, insn_word_entry *word, char *suffix)
{
lf_printf (file, "%s", prefix);
while (word != NULL)
{
dump_insn_word_entry (file, "\n(", word, ")");
word = word->next;
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_model_entry (lf *file,
char *prefix, insn_model_entry *model, char *suffix)
{
lf_printf (file, "%s(insn_model_entry *) 0x%lx", prefix, (long) model);
if (model != NULL)
{
lf_indent (file, +1);
dump_line_ref (file, "\n(line ", model->line, ")");
dump_filter (file, "\n(names ", model->names, ")");
lf_printf (file, "\n(full_name \"%s\")", model->full_name);
lf_printf (file, "\n(unit_data \"%s\")", model->unit_data);
lf_printf (file, "\n(insn (insn_entry *) 0x%lx)", (long) model->insn);
lf_printf (file, "\n(next (insn_model_entry *) 0x%lx)",
(long) model->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_model_entries (lf *file,
char *prefix, insn_model_entry *model, char *suffix)
{
lf_printf (file, "%s", prefix);
while (model != NULL)
{
dump_insn_model_entry (file, "\n", model, "");
model = model->next;
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_mnemonic_entry (lf *file,
char *prefix,
insn_mnemonic_entry *mnemonic, char *suffix)
{
lf_printf (file, "%s(insn_mnemonic_entry *) 0x%lx", prefix,
(long) mnemonic);
if (mnemonic != NULL)
{
lf_indent (file, +1);
dump_line_ref (file, "\n(line ", mnemonic->line, ")");
lf_printf (file, "\n(format \"%s\")", mnemonic->format);
lf_printf (file, "\n(condition \"%s\")", mnemonic->condition);
lf_printf (file, "\n(insn (insn_entry *) 0x%lx)",
(long) mnemonic->insn);
lf_printf (file, "\n(next (insn_mnemonic_entry *) 0x%lx)",
(long) mnemonic->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_mnemonic_entries (lf *file,
char *prefix,
insn_mnemonic_entry *mnemonic, char *suffix)
{
lf_printf (file, "%s", prefix);
while (mnemonic != NULL)
{
dump_insn_mnemonic_entry (file, "\n", mnemonic, "");
mnemonic = mnemonic->next;
}
lf_printf (file, "%s", suffix);
}
void
dump_insn_entry (lf *file, char *prefix, insn_entry * entry, char *suffix)
{
lf_printf (file, "%s(insn_entry *) 0x%lx", prefix, (long) entry);
if (entry != NULL)
{
int i;
lf_indent (file, +1);
dump_line_ref (file, "\n(line ", entry->line, ")");
dump_filter (file, "\n(flags ", entry->flags, ")");
lf_printf (file, "\n(nr_words %d)", entry->nr_words);
dump_insn_word_entries (file, "\n(words ", entry->words, ")");
lf_printf (file, "\n(word");
for (i = 0; i < entry->nr_models; i++)
lf_printf (file, " 0x%lx", (long) entry->word[i]);
lf_printf (file, ")");
dump_filter (file, "\n(field_names ", entry->field_names, ")");
lf_printf (file, "\n(format_name \"%s\")", entry->format_name);
dump_filter (file, "\n(options ", entry->options, ")");
lf_printf (file, "\n(name \"%s\")", entry->name);
lf_printf (file, "\n(nr_models %d)", entry->nr_models);
dump_insn_model_entries (file, "\n(models ", entry->models, ")");
lf_printf (file, "\n(model");
for (i = 0; i < entry->nr_models; i++)
lf_printf (file, " 0x%lx", (long) entry->model[i]);
lf_printf (file, ")");
dump_filter (file, "\n(processors ", entry->processors, ")");
dump_insn_mnemonic_entries (file, "\n(mnemonics ", entry->mnemonics,
")");
dump_table_entry (file, "\n(code ", entry->code, ")");
lf_printf (file, "\n(next 0x%lx)", (long) entry->next);
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
static void
dump_insn_entries (lf *file, char *prefix, insn_entry * entry, char *suffix)
{
lf_printf (file, "%s", prefix);
lf_indent (file, +1);
while (entry != NULL)
{
dump_insn_entry (file, "\n(", entry, ")");
entry = entry->next;
}
lf_indent (file, -1);
lf_printf (file, "%s", suffix);
}
void
dump_insn_table (lf *file, char *prefix, insn_table *isa, char *suffix)
{
lf_printf (file, "%s(insn_table *) 0x%lx", prefix, (long) isa);
if (isa != NULL)
{
lf_indent (file, +1);
dump_cache_entries (file, "\n(caches ", isa->caches, ")");
lf_printf (file, "\n(nr_insns %d)", isa->nr_insns);
lf_printf (file, "\n(max_nr_words %d)", isa->max_nr_words);
dump_insn_entries (file, "\n(insns ", isa->insns, ")");
dump_function_entries (file, "\n(functions ", isa->functions, ")");
dump_insn_entry (file, "\n(illegal_insn ", isa->illegal_insn, ")");
dump_model_table (file, "\n(model ", isa->model, ")");
dump_filter (file, "\n(flags ", isa->flags, ")");
dump_filter (file, "\n(options ", isa->options, ")");
lf_indent (file, -1);
}
lf_printf (file, "%s", suffix);
}
#ifdef MAIN
igen_options options;
int
main (int argc, char **argv)
{
insn_table *isa;
lf *l;
INIT_OPTIONS ();
if (argc == 3)
filter_parse (&options.flags_filter, argv[2]);
else if (argc != 2)
error (NULL, "Usage: insn <insn-table> [ <filter-in> ]\n");
isa = load_insn_table (argv[1], NULL);
l = lf_open ("-", "stdout", lf_omit_references, lf_is_text, "tmp-ld-insn");
dump_insn_table (l, "(isa ", isa, ")\n");
return 0;
}
#endif