binutils-gdb/sim/ppc/gen.c
1995-10-11 20:17:49 +00:00

3261 lines
78 KiB
C

/* This file is part of the program psim.
Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* BUGS:
Instead of using/abusing macro's the semantic code should be parsed
and each `cachable' expression replaced with the corresponding
value. */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include "config.h"
#include "ppc-config.h"
#undef WITH_ASSERT
#define WITH_ASSERT 1
#include "debug.h"
/****************************************************************/
static void
error (char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vprintf(msg, ap);
va_end(ap);
exit (1);
}
static void *
zmalloc(long size)
{
void *memory = malloc(size);
if (memory == NULL)
error("zmalloc failed\n");
bzero(memory, size);
return memory;
}
static void
dumpf (int indent, char *msg, ...)
{
va_list ap;
for (; indent > 0; indent--)
printf(" ");
va_start(ap, msg);
vprintf(msg, ap);
va_end(ap);
}
/****************************************************************/
int idecode_expand_semantics = WITH_IDECODE_EXPAND_SEMANTICS;
int idecode_cache = WITH_IDECODE_CACHE;
int spreg_lookup_table = WITH_SPREG_LOOKUP_TABLE;
/****************************************************************/
typedef struct {
int valid;
char *old_name;
char *new_name;
char *type;
char *expression;
} extraction_rules;
extraction_rules cachable_values[] = WITH_IDECODE_CACHE_RULES;
/****************************************************************/
typedef struct _opcode_rules {
int valid;
int first;
int last;
int force_first;
int force_last;
int force_slash;
char *force_expansion;
int use_switch;
unsigned special_mask;
unsigned special_value;
unsigned special_rule;
} opcode_rules;
/* FIXME - this should be loaded from a file */
opcode_rules opcode_table[] = WITH_IDECODE_OPCODE_RULES;
static void
dump_opcode_rule(opcode_rules *rule,
int indent)
{
printf("(opcode_rules*)%p\n", rule);
dumpf(indent, "(valid %d)\n", rule->valid);
ASSERT(rule != NULL);
if (rule->valid) {
dumpf(indent, "(first %d)\n", rule->first);
dumpf(indent, "(last %d)\n", rule->last);
dumpf(indent, "(force_first %d)\n", rule->force_first);
dumpf(indent, "(force_last %d)\n", rule->force_last);
dumpf(indent, "(force_slash %d)\n", rule->force_slash);
dumpf(indent, "(force_expansion %s)\n", rule->force_expansion);
}
}
/****************************************************************/
enum gen_constants {
insn_size = 32,
nr_of_sprs = 1024
};
char cache_idecode_formal[] = "cpu *processor,\n instruction_word instruction,\n unsigned_word cia,\n idecode_cache *cache_entry";
char cache_idecode_actual[] = "processor, instruction, cia, cache_entry";
char cache_insn_formal[] = "cpu *processor,\n idecode_cache *cache_entry,\n unsigned_word cia";
char cache_insn_actual[] = "processor, entry, cia";
char insn_formal[] = "cpu *processor,\n instruction_word instruction,\n unsigned_word cia";
char insn_actual[] = "processor, instruction, cia";
char insn_local[] = "unsigned_word nia = cia + 4;";
/****************************************************************/
static int
bin2i(char *bin, int width)
{
int i = 0;
while (*bin != '\0' && width != 0) {
i = (i << 1) + (*bin - '0');
width--;
bin++;
}
return i;
}
static int
it_is(char *flag,
char *flags)
{
int flag_len = strlen(flag);
while (*flags != '\0') {
if (!strncmp(flags, flag, flag_len)
&& (flags[flag_len] == ',' || flags[flag_len] == '\0'))
return 1;
while (*flags != ',') {
if (*flags == '\0')
return 0;
flags++;
}
flags++;
}
return 0;
}
/****************************************************************/
typedef struct _lf lf;
struct _lf {
FILE *stream;
int line_nr; /* nr complete lines written, curr line is line_nr+1 */
int indent;
int line_blank;
char *file_name;
};
static lf *
lf_open(char *name,
char *real_name)
{
/* create a file object */
lf *new_lf = zmalloc(sizeof(lf));
ASSERT(new_lf != NULL);
new_lf->file_name = (real_name == NULL
? name
: real_name);
/* attach to stdout if pipe */
if (!strcmp(name, "-")) {
new_lf->stream = stdout;
}
else {
/* create a new file */
new_lf->stream = fopen(name, "w");
ASSERT(new_lf->stream != NULL);
}
return new_lf;
}
static void
lf_close(lf *file)
{
if (file->stream != stdout) {
if (fclose(file->stream)) {
perror("lf_close.fclose");
exit(1);
}
free(file);
}
}
static void
lf_putchr(lf *file,
const char chr)
{
if (chr == '\n') {
file->line_nr += 1;
file->line_blank = 1;
}
else if (file->line_blank) {
int pad;
for (pad = file->indent; pad > 0; pad--)
putc(' ', file->stream);
file->line_blank = 0;
}
putc(chr, file->stream);
}
static void
lf_indent_suppress(lf *file)
{
file->line_blank = 0;
}
static void
lf_putstr(lf *file,
const char *string)
{
const char *chp;
if (string != NULL) {
for (chp = string; *chp != '\0'; chp++) {
lf_putchr(file, *chp);
}
}
}
static void
do_lf_putunsigned(lf *file,
unsigned u)
{
if (u > 0) {
do_lf_putunsigned(file, u / 10);
lf_putchr(file, (u % 10) + '0');
}
}
static void
lf_putint(lf *file,
int decimal)
{
if (decimal == 0)
lf_putchr(file, '0');
else if (decimal < 0) {
lf_putchr(file, '-');
do_lf_putunsigned(file, -decimal);
}
else if (decimal > 0) {
do_lf_putunsigned(file, decimal);
}
else
ASSERT(0);
}
static void
lf_printf(lf *file,
const char *fmt,
...)
{
char buf[1024];
va_list ap;
int nr_chars;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
/* FIXME - this is really stuffed but so is vsprintf() on a sun! */
ASSERT(strlen(buf) > 0 && strlen(buf) < sizeof(buf));
lf_putstr(file, buf);
va_end(ap);
}
static void
lf_print_file_line_nr(lf *file)
{
#if WITH_LINE_NUMBERS
lf_indent_suppress(file);
lf_putstr(file, "#line ");
lf_putint(file, file->line_nr+2); /*line_nr == last_line, want next */
lf_putstr(file, " \"");
lf_putstr(file, file->file_name);
lf_putstr(file, "\"\n");
#endif
}
static void
lf_indent(lf *file, int delta)
{
file->indent += delta;
}
/****************************************************************/
/* read entries from ppc.instructions */
enum {
file_table_max_fields = 6,
};
typedef struct _file_table file_table;
struct _file_table {
size_t size;
char *buffer;
char *pos;
int line_nr;
char *file_name;
};
typedef struct _file_table_entry file_table_entry;
struct _file_table_entry {
char *fields[file_table_max_fields];
int line_nr;
char *file_name;
char *annex;
};
static file_table *
file_table_open(char *file_name)
{
int fd;
struct stat stat_buf;
file_table *file;
/* create a file descriptor */
file = (file_table*)zmalloc(sizeof(file_table));
ASSERT(file != NULL);
/* save the file name */
file->file_name = (char*)zmalloc(strlen(file_name) + 1);
ASSERT(file->file_name != NULL);
strcpy(file->file_name, file_name);
/* open the file */
fd = open(file->file_name, O_RDONLY, 0);
ASSERT(fd >= 0);
/* determine the size */
if (fstat(fd, &stat_buf) < 0) {
perror("file_table_open.fstat");
exit(1);
}
file->size = stat_buf.st_size;
/* allocate this much memory */
file->buffer = (char*)zmalloc(file->size+1);
if(file->buffer == NULL) {
perror("file_table_open.calloc.file->size+1");
exit(1);
}
file->pos = file->buffer;
/* read it in */
if (read(fd, file->buffer, file->size) < file->size) {
perror("file_table_open.read");
exit(1);
}
file->buffer[file->size] = '\0';
/* done */
close(fd);
return file;
}
static file_table_entry *
file_table_read(file_table *file)
{
int field;
file_table_entry *entry;
/* skip comments/blanks */
while(1) {
/* leading white space */
while (*file->pos != '\0'
&& *file->pos != '\n'
&& isspace(*file->pos))
file->pos++;
/* comment */
if (*file->pos == '#') {
do {
file->pos++;
} while (*file->pos != '\0' && *file->pos != '\n');
}
/* end of line? */
if (*file->pos == '\n') {
file->pos++;
file->line_nr++;
}
else
break;
}
if (*file->pos == '\0')
return NULL;
/* create this new entry */
entry = (file_table_entry*)zmalloc(sizeof(file_table_entry));
ASSERT(entry != NULL);
entry->file_name = file->file_name;
/* break the line into its colon delimitered fields */
for (field = 0; field < file_table_max_fields-1; field++) {
entry->fields[field] = file->pos;
while(*file->pos && *file->pos != ':' && *file->pos != '\n')
file->pos++;
if (*file->pos == ':') {
*file->pos = '\0';
file->pos++;
}
}
/* any trailing stuff not the last field */
ASSERT(field == file_table_max_fields-1);
entry->fields[field] = file->pos;
while (*file->pos && *file->pos != '\n') {
file->pos++;
}
if (*file->pos == '\n') {
*file->pos = '\0';
file->pos++;
}
file->line_nr++;
entry->line_nr = file->line_nr;
/* if following lines tab indented, put in the annex */
if (*file->pos == '\t') {
entry->annex = file->pos;
do {
do {
file->pos++;
} while (*file->pos != '\0' && *file->pos != '\n');
if (*file->pos == '\n') {
file->pos++;
file->line_nr++;
}
} while (*file->pos != '\0' && *file->pos == '\t');
if (file->pos[-1] == '\n')
file->pos[-1] = '\0';
}
else
entry->annex = NULL;
/* return it */
return entry;
}
static void
dump_file_table_entry(file_table_entry *entry,
int indent)
{
printf("(file_table_entry*)%p\n", entry);
if (entry != NULL) {
int field;
char sep;
sep = ' ';
dumpf(indent, "(fields");
for (field = 0; field < file_table_max_fields; field++) {
printf("%c%s", sep, entry->fields[field]);
sep = ':';
}
printf(")\n");
dumpf(indent, "(line_nr %d)\n", entry->line_nr);
dumpf(indent, "(file_name %s)\n", entry->file_name);
dumpf(indent, "(annex\n%s\n", entry->annex);
dumpf(indent, " )\n");
}
}
/****************************************************************/
typedef struct _insn_field insn_field;
struct _insn_field {
int first;
int last;
int width;
int is_int;
int is_slash;
int is_string;
int val_int;
char *pos_string;
char *val_string;
insn_field *next;
insn_field *prev;
};
typedef struct _insn_fields insn_fields;
struct _insn_fields {
insn_field *bits[insn_size];
insn_field *first;
insn_field *last;
unsigned value;
};
static insn_field *
insn_field_new()
{
insn_field *new_field = (insn_field*)zmalloc(sizeof(insn_field));
ASSERT(new_field != NULL);
return new_field;
}
static insn_fields *
insn_fields_new()
{
insn_fields *new_fields = (insn_fields*)zmalloc(sizeof(insn_fields));
ASSERT(new_fields != NULL);
return new_fields;
}
static insn_fields *
parse_insn_format(file_table_entry *entry,
char *format)
{
char *chp;
insn_fields *fields = insn_fields_new();
/* create a leading sentinal */
fields->first = insn_field_new();
fields->first->first = -1;
fields->first->last = -1;
fields->first->width = 0;
/* and a trailing sentinal */
fields->last = insn_field_new();
fields->last->first = insn_size;
fields->last->last = insn_size;
fields->last->width = 0;
/* link them together */
fields->first->next = fields->last;
fields->last->prev = fields->first;
/* now work through the formats */
chp = format;
while (*chp != '\0') {
char *start_pos;
char *start_val;
int strlen_val;
int strlen_pos;
insn_field *new_field;
/* sanity check */
if (!isdigit(*chp)) {
error("%s:%d: missing position field at `%s'\n",
entry->file_name, entry->line_nr, chp);
}
/* break out the bit position */
start_pos = chp;
while (isdigit(*chp))
chp++;
strlen_pos = chp - start_pos;
if (*chp == '.' && strlen_pos > 0)
chp++;
else {
error("%s:%d: missing field value at %s\n",
entry->file_name, entry->line_nr, chp);
break;
}
/* break out the value */
start_val = chp;
while ((*start_val == '/' && *chp == '/')
|| (isdigit(*start_val) && isdigit(*chp))
|| (isalpha(*start_val) && (isalnum(*chp) || *chp == '_')))
chp++;
strlen_val = chp - start_val;
if (*chp == ',')
chp++;
else if (*chp != '\0' || strlen_val == 0) {
error("%s:%d: missing field terminator at %s\n",
entry->file_name, entry->line_nr, chp);
break;
}
/* create a new field and insert it */
new_field = insn_field_new();
new_field->next = fields->last;
new_field->prev = fields->last->prev;
new_field->next->prev = new_field;
new_field->prev->next = new_field;
/* the value */
new_field->val_string = (char*)zmalloc(strlen_val+1);
strncpy(new_field->val_string, start_val, strlen_val);
if (isdigit(*new_field->val_string)) {
new_field->val_int = atoi(new_field->val_string);
new_field->is_int = 1;
}
else if (new_field->val_string[0] == '/') {
new_field->is_slash = 1;
}
else {
new_field->is_string = 1;
}
/* the pos */
new_field->pos_string = (char*)zmalloc(strlen_pos+1);
strncpy(new_field->pos_string, start_pos, strlen_pos);
new_field->first = atoi(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(fields->first->last < 0);
ASSERT(fields->last->first >= insn_size);
fields->first = fields->first->next;
fields->last = fields->last->prev;
/* now go over this again, pointing each bit position at a field
record */
{
int i;
insn_field *field;
field = fields->first;
for (i = 0; i < insn_size; i++) {
while (field->last < i)
field = field->next;
fields->bits[i] = field;
}
}
/* go over each of the fields, and compute a `value' for the insn */
{
insn_field *field;
fields->value = 0;
for (field = fields->first;
field->last < insn_size;
field = field->next) {
fields->value <<= field->width;
if (field->is_int)
fields->value |= field->val_int;
}
}
return fields;
}
typedef enum {
field_constant_int = 1,
field_constant_slash = 2,
field_constant_string = 3
} constant_field_types;
static int
insn_field_is_constant(insn_field *field,
opcode_rules *rule)
{
/* field is an integer */
if (field->is_int)
return field_constant_int;
/* field is `/' and treating that as a constant */
if (field->is_slash && rule->force_slash)
return field_constant_slash;
/* field, though variable is on the list */
if (field->is_string && rule->force_expansion != NULL) {
char *forced_fields = rule->force_expansion;
while (*forced_fields != '\0') {
int field_len;
char *end = strchr(forced_fields, ',');
if (end == NULL)
field_len = strlen(forced_fields);
else
field_len = end-forced_fields;
if (strncmp(forced_fields, field->val_string, field_len) == 0
&& field->val_string[field_len] == '\0')
return field_constant_string;
forced_fields += field_len;
if (*forced_fields == ',')
forced_fields++;
}
}
return 0;
}
static void
dump_insn_field(insn_field *field,
int indent)
{
printf("(insn_field*)0x%x\n", (unsigned)field);
dumpf(indent, "(first %d)\n", field->first);
dumpf(indent, "(last %d)\n", field->last);
dumpf(indent, "(width %d)\n", field->width);
if (field->is_int)
dumpf(indent, "(is_int %d)\n", field->val_int);
if (field->is_slash)
dumpf(indent, "(is_slash)\n");
if (field->is_string)
dumpf(indent, "(is_string `%s')\n", field->val_string);
dumpf(indent, "(next 0x%x)\n", field->next);
dumpf(indent, "(prev 0x%x)\n", field->prev);
}
static void
dump_insn_fields(insn_fields *fields,
int indent)
{
insn_field *field;
int i;
printf("(insn_fields*)%p\n", fields);
dumpf(indent, "(first 0x%x)\n", fields->first);
dumpf(indent, "(last 0x%x)\n", fields->last);
dumpf(indent, "(value 0x%x)\n", fields->value);
for (i = 0; i < insn_size; i++) {
dumpf(indent, "(bits[%d] ", i, fields->bits[i]);
dump_insn_field(fields->bits[i], indent+1);
dumpf(indent, " )\n");
}
}
/****************************************************************/
typedef struct _opcode_field opcode_field;
struct _opcode_field {
int first;
int last;
int is_boolean;
opcode_field *parent;
};
static opcode_field *
opcode_field_new()
{
opcode_field *new_field = (opcode_field*)zmalloc(sizeof(opcode_field));
ASSERT(new_field != NULL);
new_field->first = insn_size;
new_field->last = -1;
return new_field;
}
static void
dump_opcode_field(opcode_field *field, int indent, int levels)
{
printf("(opcode_field*)%p\n", field);
if (levels && field != NULL) {
dumpf(indent, "(first %d)\n", field->first);
dumpf(indent, "(last %d)\n", field->last);
dumpf(indent, "(is_boolean %d)\n", field->is_boolean);
dumpf(indent, "(parent ");
dump_opcode_field(field->parent, indent, levels-1);
}
}
/****************************************************************/
typedef struct _insn_bits insn_bits;
struct _insn_bits {
int is_expanded;
int value;
insn_field *field;
opcode_field *opcode;
insn_bits *last;
};
static insn_bits *
insn_bits_new()
{
insn_bits *new_bits = (insn_bits*)zmalloc(sizeof(insn_bits));
ASSERT(new_bits);
return new_bits;
}
static void
dump_insn_bits(insn_bits *bits, int indent, int levels)
{
printf("(insn_bits*)%p\n", bits);
if (levels && bits != NULL) {
dumpf(indent, "(value %d)\n", bits->value);
dumpf(indent, "(opcode ");
dump_opcode_field(bits->opcode, indent+1, 0);
dumpf(indent, " )\n");
dumpf(indent, "(field ");
dump_insn_field(bits->field, indent+1);
dumpf(indent, " )\n");
dumpf(indent, "(last ");
dump_insn_bits(bits->last, indent+1, levels-1);
}
}
/****************************************************************/
typedef enum {
insn_format,
insn_form,
insn_flags,
insn_nmemonic,
insn_name,
insn_comment,
nr_insn_table_fields = file_table_max_fields,
} insn_table_fields;
char *insn_field_name[] = {
"format", "form", "flags", "nmemonic", "name", "comments"
};
typedef enum {
function_type = insn_format,
function_name = insn_name,
function_param = insn_comment,
} function_table_fields;
typedef struct _insn insn;
struct _insn {
file_table_entry *file_entry;
insn_fields *fields;
insn *next;
};
typedef struct _insn_table insn_table;
struct _insn_table {
int opcode_nr;
insn_bits *expanded_bits;
int nr_insn;
insn *insns;
insn *functions;
opcode_rules *opcode_rule;
opcode_field *opcode;
int nr_entries;
insn_table *entries;
insn_table *sibling;
insn_table *parent;
};
static insn *
insn_new()
{
insn *new_entry = ((insn*) zmalloc(sizeof(insn)));
ASSERT(new_entry != NULL);
return new_entry;
}
static insn_table *
insn_table_new()
{
insn_table *new_table = (insn_table*)zmalloc(sizeof(insn_table));
ASSERT(new_table != NULL);
return new_table;
}
static void
insn_table_insert_function(insn_table *table,
file_table_entry *file_entry)
{
insn **ptr_to_cur_function = &table->functions;
/* create a new function */
insn *new_function = insn_new();
new_function->file_entry = file_entry;
/* append it to the end of the function list */
while (*ptr_to_cur_function != NULL) {
ptr_to_cur_function = &(*ptr_to_cur_function)->next;
}
*ptr_to_cur_function = new_function;
}
static void
insn_table_insert_insn(insn_table *table,
file_table_entry *file_entry,
insn_fields *fields)
{
insn **ptr_to_cur_insn = &table->insns;
insn *cur_insn = *ptr_to_cur_insn;
/* create a new instruction */
insn *new_insn = insn_new();
new_insn->file_entry = file_entry;
new_insn->fields = fields;
/* insert it according to the order of the fields */
while (cur_insn != NULL
&& new_insn->fields->value >= cur_insn->fields->value) {
ptr_to_cur_insn = &cur_insn->next;
cur_insn = *ptr_to_cur_insn;
}
new_insn->next = cur_insn;
*ptr_to_cur_insn = new_insn;
table->nr_insn++;
}
static opcode_field *
insn_table_find_opcode_field(insn *insns,
opcode_rules *rule,
int string_only)
{
opcode_field *curr_opcode = opcode_field_new();
insn *entry;
ASSERT(rule->valid);
for (entry = insns; entry != NULL; entry = entry->next) {
insn_fields *fields = entry->fields;
opcode_field new_opcode;
/* find a start point for the opcode field */
new_opcode.first = rule->first;
while (new_opcode.first <= rule->last
&& (!string_only
|| insn_field_is_constant(fields->bits[new_opcode.first],
rule) != field_constant_string)
&& (string_only
|| !insn_field_is_constant(fields->bits[new_opcode.first],
rule)))
new_opcode.first = fields->bits[new_opcode.first]->last + 1;
ASSERT(new_opcode.first > rule->last
|| (string_only
&& insn_field_is_constant(fields->bits[new_opcode.first],
rule) == field_constant_string)
|| (!string_only
&& insn_field_is_constant(fields->bits[new_opcode.first],
rule)));
/* find the end point for the opcode field */
new_opcode.last = rule->last;
while (new_opcode.last >= rule->first
&& (!string_only
|| insn_field_is_constant(fields->bits[new_opcode.last],
rule) != field_constant_string)
&& (string_only
|| !insn_field_is_constant(fields->bits[new_opcode.last],
rule)))
new_opcode.last = fields->bits[new_opcode.last]->first - 1;
ASSERT(new_opcode.last < rule->first
|| (string_only
&& insn_field_is_constant(fields->bits[new_opcode.last],
rule) == field_constant_string)
|| (!string_only
&& insn_field_is_constant(fields->bits[new_opcode.last],
rule)));
/* now see if our current opcode needs expanding */
if (new_opcode.first <= rule->last
&& curr_opcode->first > new_opcode.first)
curr_opcode->first = new_opcode.first;
if (new_opcode.last >= rule->first
&& curr_opcode->last < new_opcode.last)
curr_opcode->last = new_opcode.last;
}
/* was any thing interesting found? */
if (curr_opcode->first > rule->last) {
ASSERT(curr_opcode->last < rule->first);
free(curr_opcode);
return NULL;
}
ASSERT(curr_opcode->last >= rule->first);
ASSERT(curr_opcode->first <= rule->last);
/* if something was found, check it includes the forced field range */
if (!string_only
&& curr_opcode->first > rule->force_first) {
curr_opcode->first = rule->force_first;
}
if (!string_only
&& curr_opcode->last < rule->force_last) {
curr_opcode->last = rule->force_last;
}
/* handle special case elminating any need to do shift after mask */
if (string_only
&& rule->force_last == insn_size-1) {
curr_opcode->last = insn_size-1;
}
/* handle any special cases */
switch (rule->special_rule) {
case 0: /* let the above apply */
break;
case 1: /* expand a limited nr of bits, ignoring the rest */
curr_opcode->first = rule->force_first;
curr_opcode->last = rule->force_last;
break;
case 2: /* boolean field */
curr_opcode->is_boolean = 1;
break;
}
return curr_opcode;
}
static void
insn_table_insert_expanded(insn_table *table,
insn *old_insn,
int new_opcode_nr,
insn_bits *new_bits)
{
insn_table **ptr_to_cur_entry = &table->entries;
insn_table *cur_entry = *ptr_to_cur_entry;
/* find the new table for this entry */
while (cur_entry != NULL
&& cur_entry->opcode_nr < new_opcode_nr) {
ptr_to_cur_entry = &cur_entry->sibling;
cur_entry = *ptr_to_cur_entry;
}
if (cur_entry == NULL || cur_entry->opcode_nr != new_opcode_nr) {
insn_table *new_entry = insn_table_new();
new_entry->opcode_nr = new_opcode_nr;
new_entry->expanded_bits = new_bits;
new_entry->opcode_rule = table->opcode_rule+1;
new_entry->sibling = cur_entry;
new_entry->parent = table;
*ptr_to_cur_entry = new_entry;
cur_entry = new_entry;
table->nr_entries++;
}
/* ASSERT new_bits == cur_entry bits */
ASSERT(cur_entry != NULL && cur_entry->opcode_nr == new_opcode_nr);
insn_table_insert_insn(cur_entry,
old_insn->file_entry,
old_insn->fields);
}
static void
insn_table_expand_opcode(insn_table *table,
insn *instruction,
int field_nr,
int opcode_nr,
insn_bits *bits)
{
if (field_nr > table->opcode->last) {
insn_table_insert_expanded(table, instruction, opcode_nr, bits);
}
else {
insn_field *field = instruction->fields->bits[field_nr];
if (field->is_int || field->is_slash) {
ASSERT(field->first >= table->opcode->first
&& field->last <= table->opcode->last);
insn_table_expand_opcode(table, instruction, field->last+1,
((opcode_nr << field->width) + field->val_int),
bits);
}
else {
int val;
int last_pos = ((field->last < table->opcode->last)
? field->last : table->opcode->last);
int first_pos = ((field->first > table->opcode->first)
? field->first : table->opcode->first);
int width = last_pos - first_pos + 1;
int last_val = (table->opcode->is_boolean
? 2 : (1 << width));
for (val = 0; val < last_val; val++) {
insn_bits *new_bits = insn_bits_new();
new_bits->field = field;
new_bits->value = val;
new_bits->last = bits;
new_bits->opcode = table->opcode;
insn_table_expand_opcode(table, instruction, last_pos+1,
((opcode_nr << width) | val),
new_bits);
}
}
}
}
static void
insn_table_insert_expanding(insn_table *table,
insn *entry)
{
insn_table_expand_opcode(table,
entry,
table->opcode->first,
0,
table->expanded_bits);
}
static void
insn_table_expand_insns(insn_table *table)
{
ASSERT(table->nr_insn >= 1);
/* determine a valid opcode */
while (table->opcode_rule->valid) {
/* specials only for single instructions */
if ((table->nr_insn > 1
&& table->opcode_rule->special_mask == 0
&& table->opcode_rule->special_rule == 0)
|| (table->nr_insn == 1
&& table->opcode_rule->special_mask != 0
&& ((table->insns->fields->value
& table->opcode_rule->special_mask)
== table->opcode_rule->special_value))
|| (idecode_expand_semantics
&& table->opcode_rule->special_mask == 0
&& table->opcode_rule->special_rule == 0))
table->opcode =
insn_table_find_opcode_field(table->insns,
table->opcode_rule,
table->nr_insn == 1/*string*/
);
if (table->opcode != NULL)
break;
table->opcode_rule++;
}
/* did we find anything */
if (table->opcode == NULL) {
return;
}
ASSERT(table->opcode != NULL);
/* back link what we found to its parent */
if (table->parent != NULL) {
ASSERT(table->parent->opcode != NULL);
table->opcode->parent = table->parent->opcode;
}
/* expand the raw instructions according to the opcode */
{
insn *entry;
for (entry = table->insns; entry != NULL; entry = entry->next) {
insn_table_insert_expanding(table, entry);
}
}
/* and do the same for the sub entries */
{
insn_table *entry;
for (entry = table->entries; entry != NULL; entry = entry->sibling) {
insn_table_expand_insns(entry);
}
}
}
static insn_table *
insn_table_load_insns(char *file_name)
{
file_table *file = file_table_open(file_name);
insn_table *table = insn_table_new();
file_table_entry *file_entry;
table->opcode_rule = opcode_table;
while ((file_entry = file_table_read(file)) != NULL) {
if (it_is("function", file_entry->fields[insn_flags])) {
insn_table_insert_function(table, file_entry);
}
else {
insn_fields *fields;
/* skip instructions that aren't relevant to the mode */
if ((it_is("64", file_entry->fields[insn_flags])
&& WITH_TARGET_WORD_BITSIZE != 64)
|| (it_is("32", file_entry->fields[insn_flags])
&& WITH_TARGET_WORD_BITSIZE != 32)
|| (it_is("f", file_entry->fields[insn_flags])
&& WITH_FLOATING_POINT == SOFT_FLOATING_POINT))
continue;
/* create/insert the new instruction */
fields = parse_insn_format(file_entry, file_entry->fields[insn_format]);
insn_table_insert_insn(table, file_entry, fields);
}
}
return table;
}
static void
dump_insn(insn *entry, int indent, int levels)
{
printf("(insn*)%p\n", entry);
if (levels && entry != NULL) {
dumpf(indent, "(file_entry ");
dump_file_table_entry(entry->file_entry, indent+1);
dumpf(indent, " )\n");
dumpf(indent, "(fields ");
dump_insn_fields(entry->fields, indent+1);
dumpf(indent, " )\n");
dumpf(indent, "(next ");
dump_insn(entry->next, indent+1, levels-1);
dumpf(indent, " )\n");
}
}
static void
dump_insn_table(insn_table *table,
int indent, int levels)
{
printf("(insn_table*)%p\n", table);
if (levels && table != NULL) {
insn *entry;
dumpf(indent, "(opcode_nr %d)\n", table->opcode_nr);
dumpf(indent, "(expanded_bits ");
dump_insn_bits(table->expanded_bits, indent+1, -1);
dumpf(indent, " )\n");
dumpf(indent, "(int nr_insn %d)\n", table->nr_insn);
dumpf(indent, "(insns ");
dump_insn(table->insns, indent+1, table->nr_insn);
dumpf(indent, " )\n");
dumpf(indent, "(opcode_rule ");
dump_opcode_rule(table->opcode_rule, indent+1);
dumpf(indent, " )\n");
dumpf(indent, "(opcode ");
dump_opcode_field(table->opcode, indent+1, 1);
dumpf(indent, " )\n");
dumpf(indent, "(nr_entries %d)\n", table->entries);
dumpf(indent, "(entries ");
dump_insn_table(table->entries, indent+1, table->nr_entries);
dumpf(indent, " )\n");
dumpf(indent, "(sibling ", table->sibling);
dump_insn_table(table->sibling, indent+1, levels-1);
dumpf(indent, " )\n");
dumpf(indent, "(parent ", table->parent);
dump_insn_table(table->parent, indent+1, 0);
dumpf(indent, " )\n");
}
}
/****************************************************************/
static void
lf_print_copyleft(lf *file)
{
lf_putstr(file, "\
/* This file is part of psim (model of the PowerPC(tm) architecture)
Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
--
PowerPC is a trademark of International Business Machines Corporation.
--
This file was generated by the program gen */
");
}
static void
lf_print_c_line_nr(lf *file, file_table_entry *entry)
{
#if WITH_LINE_NUMBERS
lf_indent_suppress(file);
lf_putstr(file, "#line ");
lf_putint(file, entry->line_nr);
lf_putstr(file, " \"");
lf_putstr(file, entry->file_name);
lf_putstr(file, "\"\n");
#endif
}
static void
lf_print_c_code(lf *file, char *code)
{
char *chp = code;
int in_bit_field = 0;
while (*chp != '\0') {
if (*chp == '\t')
chp++;
if (*chp == '#')
lf_indent_suppress(file);
while (*chp != '\0' && *chp != '\n') {
if (chp[0] == '{' && !isspace(chp[1])) {
in_bit_field = 1;
lf_putchr(file, '_');
}
else if (in_bit_field && chp[0] == ':') {
lf_putchr(file, '_');
}
else if (in_bit_field && *chp == '}') {
lf_putchr(file, '_');
in_bit_field = 0;
}
else {
lf_putchr(file, *chp);
}
chp++;
}
if (in_bit_field)
error("bit field paren miss match some where\n");
if (*chp == '\n') {
lf_putchr(file, '\n');
chp++;
}
}
lf_putchr(file, '\n');
}
static void
lf_print_binary(lf *file, int decimal, int width)
{
int bit;
ASSERT(width > 0);
for (bit = 1 << (width-1); bit != 0; bit >>= 1) {
if (decimal & bit)
lf_putchr(file, '1');
else
lf_putchr(file, '0');
}
}
static void
lf_print_insn_bits(lf *file, insn_bits *bits)
{
if (bits == NULL)
return;
lf_print_insn_bits(file, bits->last);
lf_putchr(file, '_');
lf_putstr(file, bits->field->val_string);
if (!bits->opcode->is_boolean || bits->value == 0) {
if (bits->opcode->last < bits->field->last)
lf_putint(file, bits->value << (bits->field->last - bits->opcode->last));
else
lf_putint(file, bits->value);
}
}
static void
lf_print_opcodes(lf *file,
insn_table *table)
{
if (table != NULL) {
while (1) {
lf_printf(file, "_%d_%d",
table->opcode->first,
table->opcode->last);
if (table->parent == NULL) break;
lf_printf(file, "__%d", table->opcode_nr);
table = table->parent;
}
}
}
static void
lf_print_table_name(lf *file,
insn_table *table)
{
lf_printf(file, "idecode_table");
lf_print_opcodes(file, table);
}
typedef enum {
function_name_prefix_semantics,
function_name_prefix_idecode,
function_name_prefix_none
} lf_function_name_prefixes;
static void
lf_print_function_name(lf *file,
char *basename,
insn_bits *expanded_bits,
lf_function_name_prefixes prefix)
{
/* the prefix */
switch (prefix) {
case function_name_prefix_semantics:
lf_putstr(file, "semantic_");
break;
case function_name_prefix_idecode:
lf_printf(file, "idecode_");
break;
default:
break;
}
/* the function name */
{
char *pos;
for (pos = basename;
*pos != '\0';
pos++) {
switch (*pos) {
case '/':
case '-':
break;
case ' ':
lf_putchr(file, '_');
break;
default:
lf_putchr(file, *pos);
break;
}
}
}
/* the suffix */
if (idecode_expand_semantics)
lf_print_insn_bits(file, expanded_bits);
}
static void
lf_print_idecode_table(lf *file,
insn_table *entry)
{
int can_assume_leaf;
int rule;
/* have a look at the rule table, if all table rules follow all
switch rules, I can assume that all end points are leaves */
rule = 0;
while (opcode_table[rule].valid
&& opcode_table[rule].use_switch)
rule++;
while (opcode_table[rule].valid
&& !opcode_table[rule].use_switch
&& !opcode_table[rule].special_rule)
rule++;
can_assume_leaf = !opcode_table[rule].valid;
lf_printf(file, "{\n");
lf_indent(file, +2);
{
lf_printf(file, "idecode_table_entry *table = ");
lf_print_table_name(file, entry);
lf_printf(file, ";\n");
lf_printf(file, "int opcode = EXTRACTED32(instruction, %d, %d);\n",
entry->opcode->first, entry->opcode->last);
lf_printf(file, "idecode_table_entry *table_entry = table + opcode;\n");
lf_printf(file, "while (1) {\n");
lf_indent(file, +2);
{
lf_printf(file, "while (table_entry->mask != 0) {\n");
lf_indent(file, +2);
{
lf_printf(file, "table = ((idecode_table_entry*)\n");
lf_printf(file, " table_entry->function_or_table);\n");
lf_printf(file, "opcode = ((instruction & table_entry->mask)\n");
lf_printf(file, " >> table_entry->shift);\n");
lf_printf(file, "table_entry = table + opcode;\n");
}
lf_indent(file, -2);
lf_printf(file, "}\n");
if (!idecode_cache && can_assume_leaf) {
lf_printf(file, "return (((idecode_semantic*)\n");
lf_printf(file, " table_entry->function_or_table)\n");
lf_printf(file, " (%s));\n", insn_actual);
}
else if (!idecode_cache && !can_assume_leaf) {
lf_printf(file, "if (table_entry->shift == 0)");
lf_printf(file, " return (((idecode_semantic*)\n");
lf_printf(file, " table_entry->function_or_table)\n");
lf_printf(file, " (%s));\n", insn_actual);
}
else if (idecode_cache == 1 && can_assume_leaf) {
lf_printf(file, "ASSERT(!entry->shift);\n");
lf_printf(file, "return ((idecode_semantic*)\n");
lf_printf(file, " table_entry->function_or_table);\n");
}
else if (idecode_cache == 1 && !can_assume_leaf) {
lf_printf(file, "if (table_entry->shift == 0)\n");
lf_printf(file, " return ((idecode_semantic*)\n");
lf_printf(file, " table_entry->function_or_table);\n");
lf_printf(file, "else if (table_entry->shift == -1)\n");
lf_printf(file, " return (((idecode_crack*)\n");
lf_printf(file, " table_entry->function_or_table)\n");
lf_printf(file, " (%s));\n", insn_actual);
}
else {
lf_printf(file, "if (table_entry->shift == 0)\n");
lf_printf(file, " return (((idecode_crack*)\n");
lf_printf(file, " table_entry->function_or_table)\n");
lf_printf(file, " (%s));\n", cache_idecode_actual);
}
if (!can_assume_leaf) {
lf_printf(file, "opcode = (instruction & table_entry->shift) != 0;\n");
lf_printf(file, "table = ((idecode_table_entry*)\n");
lf_printf(file, " table_entry->function_or_table);\n");
lf_printf(file, "table_entry = table + opcode;\n");
}
}
lf_indent(file, -2);
lf_printf(file, "}\n");
}
lf_indent(file, -2);
lf_printf(file, "}\n");
}
static void
lf_print_my_prefix(lf *file,
file_table_entry *file_entry)
{
lf_printf(file, "const char *const my_prefix = \n");
lf_printf(file, " \"%s:%s:%d:cache\";\n",
file_entry->file_name,
file_entry->fields[insn_name],
file_entry->line_nr);
}
static void
lf_print_ptrace(lf *file)
{
lf_printf(file, "\n");
lf_putstr(file, "ITRACE(trace_semantics, (\"cia=0x%x\\n\", cia));\n");
}
/****************************************************************/
typedef void leaf_handler
(insn_table *entry,
void *data,
int depth);
typedef void padding_handler
(insn_table *table,
void *data,
int depth,
int opcode_nr);
static void
insn_table_traverse_tree(insn_table *table,
void *data,
int depth,
leaf_handler *start,
leaf_handler *leaf,
leaf_handler *end,
padding_handler *padding)
{
insn_table *entry;
int entry_nr;
ASSERT(table != NULL
&& table->opcode != NULL
&& table->nr_entries > 0
&& table->entries != 0);
if (start != NULL && depth >= 0)
start(table, data, depth);
for (entry_nr = 0, entry = table->entries;
entry_nr < (table->opcode->is_boolean
? 2
: (1 << (table->opcode->last - table->opcode->first + 1)));
entry_nr ++) {
if (entry == NULL
|| (!table->opcode->is_boolean
&& entry_nr < entry->opcode_nr)) {
if (padding != NULL && depth >= 0)
padding(table, data, depth, entry_nr);
}
else {
ASSERT(entry != NULL && (entry->opcode_nr == entry_nr
|| table->opcode->is_boolean));
if (entry->opcode != NULL && depth != 0) {
insn_table_traverse_tree(entry, data, depth+1,
start, leaf, end, padding);
}
else if (depth >= 0) {
if (leaf != NULL)
leaf(entry, data, depth);
}
entry = entry->sibling;
}
}
if (end != NULL && depth >= 0)
end(table, data, depth);
}
typedef void function_handler
(insn_table *table,
void *data,
file_table_entry *function);
static void
insn_table_traverse_function(insn_table *table,
void *data,
function_handler *leaf)
{
insn *function;
for (function = table->functions;
function != NULL;
function = function->next) {
leaf(table, data, function->file_entry);
}
}
typedef void insn_handler
(insn_table *table,
void *data,
insn *instruction);
static void
insn_table_traverse_insn(insn_table *table,
void *data,
insn_handler *leaf)
{
insn *instruction;
for (instruction = table->insns;
instruction != NULL;
instruction = instruction->next) {
leaf(table, data, instruction);
}
}
static void
update_depth(insn_table *entry,
void *data,
int depth)
{
int *max_depth = (int*)data;
if (*max_depth < depth)
*max_depth = depth;
}
static int
insn_table_depth(insn_table *table)
{
int depth = 0;
insn_table_traverse_tree(table,
&depth,
1,
NULL, /*start*/
update_depth,
NULL, /*end*/
NULL); /*padding*/
return depth;
}
/****************************************************************/
static void
dump_traverse_start(insn_table *table,
void *data,
int depth)
{
dumpf(depth*2, "(%d\n", table->opcode_nr);
}
static void
dump_traverse_leaf(insn_table *entry,
void *data,
int depth)
{
ASSERT(entry->nr_entries == 0
&& entry->nr_insn == 1
&& entry->opcode == NULL);
dumpf(depth*2, ".%d %s\n", entry->opcode_nr,
entry->insns->file_entry->fields[insn_format]);
}
static void
dump_traverse_end(insn_table *table,
void *data,
int depth)
{
dumpf(depth*2, ")\n");
}
static void
dump_traverse_padding(insn_table *table,
void *data,
int depth,
int opcode_nr)
{
dumpf(depth*2, ".<%d>\n", opcode_nr);
}
static void
dump_traverse(insn_table *table)
{
insn_table_traverse_tree(table, NULL, 1,
dump_traverse_start,
dump_traverse_leaf,
dump_traverse_end,
dump_traverse_padding);
}
/****************************************************************/
static void
semantics_h_print_function(lf *file,
char *basename,
insn_bits *expanded_bits)
{
lf_printf(file, "\n");
lf_printf(file, "INLINE_SEMANTICS unsigned_word ");
lf_print_function_name(file,
basename,
expanded_bits,
function_name_prefix_semantics);
lf_printf(file, "\n(%s);\n",
idecode_cache > 1 ? cache_insn_formal : insn_formal);
}
static void
semantics_h_leaf(insn_table *entry,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(entry->nr_insn == 1);
semantics_h_print_function(file,
entry->insns->file_entry->fields[insn_name],
entry->expanded_bits);
}
static void
semantics_h_insn(insn_table *entry,
void *data,
insn *instruction)
{
lf *file = (lf*)data;
semantics_h_print_function(file,
instruction->file_entry->fields[insn_name],
NULL);
}
static void
semantics_h_function(insn_table *entry,
void *data,
file_table_entry *function)
{
lf *file = (lf*)data;
if (function->fields[function_type] == NULL
|| function->fields[function_type][0] == '\0') {
semantics_h_print_function(file,
function->fields[function_name],
NULL);
}
else {
lf_printf(file, "\n");
lf_printf(file, "INLINE_SEMANTICS %s %s\n(%s);\n",
function->fields[function_type],
function->fields[function_name],
function->fields[function_param]);
}
}
static void
gen_semantics_h(insn_table *table, lf *file)
{
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _SEMANTICS_H_\n");
lf_printf(file, "#define _SEMANTICS_H_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef INLINE_SEMANTICS\n");
lf_printf(file, "#define INLINE_SEMANTICS\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "\n");
/* output a declaration for all functions */
insn_table_traverse_function(table,
file,
semantics_h_function);
/* output a declaration for all instructions */
if (idecode_expand_semantics)
insn_table_traverse_tree(table,
file,
1,
NULL, /* start */
semantics_h_leaf, /* leaf */
NULL, /* end */
NULL); /* padding */
else
insn_table_traverse_insn(table,
file,
semantics_h_insn);
lf_printf(file, "\n");
lf_printf(file, "#endif /* _SEMANTICS_H_ */\n");
}
/****************************************************************/
typedef struct _icache_tree icache_tree;
struct _icache_tree {
char *name;
icache_tree *next;
icache_tree *children;
};
static icache_tree *
icache_tree_new()
{
icache_tree *new_tree = (icache_tree*)zmalloc(sizeof(icache_tree));
ASSERT(new_tree != NULL);
return new_tree;
}
static icache_tree *
icache_tree_insert(icache_tree *tree,
char *name)
{
icache_tree *new_tree;
/* find it */
icache_tree **ptr_to_cur_tree = &tree->children;
icache_tree *cur_tree = *ptr_to_cur_tree;
while (cur_tree != NULL
&& strcmp(cur_tree->name, name) < 0) {
ptr_to_cur_tree = &cur_tree->next;
cur_tree = *ptr_to_cur_tree;
}
ASSERT(cur_tree == NULL
|| strcmp(cur_tree->name, name) >= 0);
/* already in the tree */
if (cur_tree != NULL
&& strcmp(cur_tree->name, name) == 0)
return cur_tree;
/* missing, insert it */
ASSERT(cur_tree == NULL
|| strcmp(cur_tree->name, name) > 0);
new_tree = icache_tree_new();
new_tree->name = name;
new_tree->next = cur_tree;
*ptr_to_cur_tree = new_tree;
return new_tree;
}
static icache_tree *
insn_table_cache_fields(insn_table *table)
{
icache_tree *tree = icache_tree_new();
insn *instruction;
for (instruction = table->insns;
instruction != NULL;
instruction = instruction->next) {
insn_field *field;
icache_tree *form =
icache_tree_insert(tree,
instruction->file_entry->fields[insn_form]);
for (field = instruction->fields->first;
field != NULL;
field = field->next) {
if (field->is_string)
icache_tree_insert(form, field->val_string);
}
}
return tree;
}
static void
gen_icache_h(icache_tree *tree,
lf *file)
{
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _ICACHE_H_\n");
lf_printf(file, "#define _ICACHE_H_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef INLINE_ICACHE\n");
lf_printf(file, "#define INLINE_ICACHE\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "\n");
/* create an instruction cache if being used */
if (idecode_cache) {
lf_printf(file, "typedef struct _idecode_cache {\n");
lf_printf(file, " unsigned_word address;\n");
lf_printf(file, " void *semantic;\n");
if (idecode_cache == 1) {
lf_printf(file, " instruction_word instruction;\n");
}
else {
icache_tree *form;
lf_printf(file, " union {\n");
for (form = tree->children;
form != NULL;
form = form->next) {
icache_tree *field;
lf_printf(file, " struct {\n");
for (field = form->children;
field != NULL;
field = field->next) {
extraction_rules *rule;
int found_rule = 0;
for (rule = cachable_values;
rule->valid;
rule++) {
if (strcmp(field->name, rule->old_name) == 0) {
found_rule = 1;
if (rule->new_name != NULL)
lf_printf(file, " %s %s; /* %s */\n",
rule->type == NULL ? "unsigned" : rule->type,
rule->new_name, rule->old_name);
}
}
if (!found_rule)
lf_printf(file, " unsigned %s;\n", field->name);
}
lf_printf(file, " } %s;\n", form->name);
}
lf_printf(file, " } crack;\n");
}
lf_printf(file, "} idecode_cache;\n");
}
/* define various fields according to the cache */
if (idecode_cache <= 1) {
extraction_rules *rule;
lf_printf(file, "\n");
for (rule = cachable_values;
rule->valid;
rule++) {
if (rule->expression != NULL)
lf_printf(file, "#define %s %s\n",
rule->new_name, rule->expression);
}
}
lf_printf(file, "\n");
lf_printf(file, "#endif /* _ICACHE_H_ */\n");
}
/****************************************************************/
static void
lf_print_c_extraction(lf *file,
insn *instruction,
char *field_name,
char *field_type,
char *field_expression,
insn_field *cur_field,
insn_bits *bits,
int get_value_from_cache,
int put_value_in_cache)
{
ASSERT(field_name != NULL);
if (bits != NULL
&& (!bits->opcode->is_boolean || bits->value == 0)
&& strcmp(field_name, cur_field->val_string) == 0) {
ASSERT(bits->field == cur_field);
ASSERT(field_type == NULL);
lf_print_c_line_nr(file, instruction->file_entry);
lf_printf(file, "const unsigned %s = ",
field_name);
if (bits->opcode->last < bits->field->last)
lf_printf(file, "%d;\n",
bits->value << (bits->field->last - bits->opcode->last));
else
lf_printf(file, "%d;\n", bits->value);
}
else {
/* put the field in the local variable */
lf_print_c_line_nr(file, instruction->file_entry);
lf_printf(file, "%s const %s = ",
field_type == NULL ? "unsigned" : field_type,
field_name);
/* getting it from the cache */
if (get_value_from_cache || put_value_in_cache) {
lf_printf(file, "cache_entry->crack.%s.%s",
instruction->file_entry->fields[insn_form],
field_name);
if (put_value_in_cache) /* also put it in the cache? */
lf_printf(file, " = ");
}
if (!get_value_from_cache) {
if (strcmp(field_name, cur_field->val_string) == 0)
lf_printf(file, "EXTRACTED32(instruction, %d, %d)",
cur_field->first, cur_field->last);
else if (field_expression != NULL)
lf_printf(file, "%s", field_expression);
else
lf_printf(file, "eval_%s", field_name);
}
lf_printf(file, ";\n");
}
}
static void
lf_print_c_extractions(lf *file,
insn *instruction,
insn_bits *expanded_bits,
int get_value_from_cache,
int put_value_in_cache)
{
insn_field *cur_field;
/* extract instruction fields */
lf_printf(file, "/* extraction: %s */\n",
instruction->file_entry->fields[insn_format]);
for (cur_field = instruction->fields->first;
cur_field->first < insn_size;
cur_field = cur_field->next) {
if (cur_field->is_string) {
insn_bits *bits;
int found_rule = 0;
/* find any corresponding value */
for (bits = expanded_bits;
bits != NULL;
bits = bits->last) {
if (bits->field == cur_field)
break;
}
/* try the cache rule table for what to do */
if (get_value_from_cache || put_value_in_cache) {
extraction_rules *field_rule;
for (field_rule = cachable_values;
field_rule->valid;
field_rule++) {
if (strcmp(cur_field->val_string, field_rule->old_name) == 0) {
found_rule = 1;
if (field_rule->valid > 1 && put_value_in_cache)
lf_print_c_extraction(file,
instruction,
field_rule->new_name,
field_rule->type,
field_rule->expression,
cur_field,
bits,
0,
0);
else if (field_rule->valid == 1)
lf_print_c_extraction(file,
instruction,
field_rule->new_name,
field_rule->type,
field_rule->expression,
cur_field,
bits,
get_value_from_cache,
put_value_in_cache);
}
}
}
if (found_rule == 0)
lf_print_c_extraction(file,
instruction,
cur_field->val_string,
0,
0,
cur_field,
bits,
get_value_from_cache,
put_value_in_cache);
/* if any (XXX == 0), output a corresponding test */
if (instruction->file_entry->annex != NULL) {
char *field_name = cur_field->val_string;
char *is_0_ptr = instruction->file_entry->annex;
int field_len = strlen(field_name);
if (strlen(is_0_ptr) >= (strlen("_is_0") + field_len)) {
is_0_ptr += field_len;
while ((is_0_ptr = strstr(is_0_ptr, "_is_0")) != NULL) {
if (strncmp(is_0_ptr - field_len, field_name, field_len) == 0
&& !isalpha(is_0_ptr[ - field_len - 1])) {
lf_print_c_line_nr(file, instruction->file_entry);
lf_printf(file, "const unsigned %s_is_0 = (", field_name);
if (bits != NULL)
lf_printf(file, "%d", bits->value);
else
lf_printf(file, "%s", field_name);
lf_printf(file, " == 0);\n");
break;
}
is_0_ptr += strlen("_is_0");
}
}
}
/* any thing else ... */
}
}
lf_print_file_line_nr(file);
}
static void
lf_print_idecode_illegal(lf *file)
{
switch (idecode_cache) {
case 0:
lf_printf(file, "return semantic_illegal(%s);\n", insn_actual);
break;
case 1:
lf_printf(file, "return semantic_illegal;\n");
break;
default:
lf_printf(file, "return idecode_illegal(%s);\n", cache_idecode_actual);
}
}
static void
lf_print_c_validate(lf *file,
insn *instruction,
opcode_field *opcodes)
{
unsigned check_mask = 0;
unsigned check_val = 0;
insn_field *field;
opcode_field *opcode;
for (field = instruction->fields->first;
field->first < insn_size;
field = field->next) {
check_mask <<= field->width;
check_val <<= field->width;
/* is it a constant that could need validating? */
if (!field->is_int && !field->is_slash)
continue;
/* has it been checked by a table? */
for (opcode = opcodes; opcode != NULL; opcode = opcode->parent) {
if (field->first >= opcode->first
&& field->last <= opcode->last)
break;
}
if (opcode != NULL)
continue;
check_mask |= (1 << field->width)-1;
check_val |= field->val_int;
}
/* if any bits not checked by opcode tables, output code to check them */
if (check_mask) {
lf_printf(file, "\n");
lf_printf(file, "/* validate: %s */\n",
instruction->file_entry->fields[insn_format]);
lf_printf(file, "if ((instruction & 0x%x) != 0x%x)\n",
check_mask, check_val);
lf_indent(file, +2);
lf_print_idecode_illegal(file);
lf_indent(file, -2);
}
}
static void
lf_print_c_cracker(lf *file,
insn *instruction,
insn_bits *expanded_bits,
opcode_field *opcodes)
{
/* function header */
lf_printf(file, "{\n");
lf_indent(file, +2);
lf_print_my_prefix(file,
instruction->file_entry);
lf_print_ptrace(file);
lf_print_c_validate(file, instruction, opcodes);
lf_printf(file, "\n");
lf_printf(file, "{\n");
lf_indent(file, +2);
lf_print_c_extractions(file,
instruction,
expanded_bits,
0/*get_value_from_cache*/,
1/*put_value_in_cache*/);
lf_indent(file, -2);
lf_printf(file, "}\n");
/* return the function propper (main sorts this one out) */
lf_printf(file, "\n");
lf_printf(file, "/* semantic routine */\n");
lf_print_c_line_nr(file, instruction->file_entry);
lf_printf(file, "return ");
lf_print_function_name(file,
instruction->file_entry->fields[insn_name],
expanded_bits,
function_name_prefix_semantics);
lf_printf(file, ";\n");
lf_print_file_line_nr(file);
lf_indent(file, -2);
lf_printf(file, "}\n");
}
static void
lf_print_c_semantic(lf *file,
insn *instruction,
insn_bits *expanded_bits,
opcode_field *opcodes)
{
lf_printf(file, "{\n");
lf_indent(file, +2);
lf_print_my_prefix(file,
instruction->file_entry);
lf_putstr(file, insn_local);
lf_printf(file, "\n");
lf_printf(file, "\n");
lf_print_c_extractions(file,
instruction,
expanded_bits,
idecode_cache > 1/*get_value_from_cache*/,
0/*put_value_in_cache*/);
lf_print_ptrace(file);
/* generate code to check previously unchecked fields */
if (idecode_cache < 2)
lf_print_c_validate(file, instruction, opcodes);
/* if floating-point generate checks that a. floating point hardware
exists and b. floating point is enabled */
if (it_is("f", instruction->file_entry->fields[insn_flags])) {
lf_printf(file, "\n");
lf_printf(file, "/* verify: FP hardware exists */\n");
lf_printf(file, "if (CURRENT_FLOATING_POINT != HARD_FLOATING_POINT)\n");
lf_indent(file, +2);
lf_print_idecode_illegal(file);
lf_indent(file, -2);
lf_printf(file, "\n");
lf_printf(file, "/* verify: FP is enabled */\n");
lf_printf(file, "if (!IS_FP_AVAILABLE(processor))\n");
lf_indent(file, +2);
lf_printf(file, "floating_point_unavailable_interrupt(processor, cia);\n");
lf_indent(file, -2);
}
/* generate the code (or at least something */
if (instruction->file_entry->annex != NULL) {
/* true code */
lf_printf(file, "\n");
lf_print_c_line_nr(file, instruction->file_entry);
lf_printf(file, "{\n");
lf_indent(file, +2);
lf_print_c_code(file, instruction->file_entry->annex);
lf_indent(file, -2);
lf_printf(file, "}\n");
lf_print_file_line_nr(file);
}
else if (it_is("nop", instruction->file_entry->fields[insn_flags])) {
lf_print_file_line_nr(file);
}
else if (it_is("f", instruction->file_entry->fields[insn_flags])) {
/* unimplemented floating point instruction - call for assistance */
lf_printf(file, "\n");
lf_printf(file, "/* unimplemented floating point instruction - call for assistance */\n");
lf_print_c_line_nr(file, instruction->file_entry);
lf_putstr(file, "floating_point_assist_interrupt(processor, cia);\n");
lf_print_file_line_nr(file);
}
else {
/* abort so it is implemented now */
lf_print_c_line_nr(file, instruction->file_entry);
lf_putstr(file, "error(\"%s: unimplemented, cia=0x%x\\n\", my_prefix, cia);\n");
lf_print_file_line_nr(file);
lf_printf(file, "\n");
}
/* the function footer */
lf_printf(file, "return nia;\n");
lf_indent(file, -2);
lf_printf(file, "}\n");
}
static void
lf_print_c_semantic_function_header(lf *file,
char *basename,
insn_bits *expanded_bits)
{
lf_printf(file, "\n");
lf_printf(file, "INLINE_SEMANTICS unsigned_word\n");
lf_print_function_name(file,
basename,
expanded_bits,
function_name_prefix_semantics);
lf_printf(file, "\n(%s)\n",
idecode_cache > 1 ? cache_insn_formal : insn_formal);
}
static void
lf_print_c_semantic_function(lf *file,
insn *instruction,
insn_bits *expanded_bits,
opcode_field *opcodes)
{
/* build the semantic routine to execute the instruction */
lf_print_c_semantic_function_header(file,
instruction->file_entry->fields[insn_name],
expanded_bits);
lf_print_c_semantic(file,
instruction,
expanded_bits,
opcodes);
}
static void
semantics_c_leaf(insn_table *entry,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(entry->nr_insn == 1
&& entry->opcode == NULL
&& entry->parent != NULL
&& entry->parent->opcode != NULL);
lf_print_c_semantic_function(file,
entry->insns,
entry->expanded_bits,
entry->parent->opcode);
}
static void
semantics_c_insn(insn_table *table,
void *data,
insn *instruction)
{
lf *file = (lf*)data;
lf_print_c_semantic_function(file, instruction,
NULL, NULL);
}
static void
semantics_c_function(insn_table *table,
void *data,
file_table_entry *function)
{
lf *file = (lf*)data;
if (function->fields[function_type] == NULL
|| function->fields[function_type][0] == '\0') {
lf_print_c_semantic_function_header(file,
function->fields[function_name],
NULL);
}
else {
lf_printf(file, "\n");
lf_printf(file, "INLINE_SEMANTICS %s\n%s(%s)\n",
function->fields[function_type],
function->fields[function_name],
function->fields[function_param]);
}
lf_print_c_line_nr(file, function);
lf_printf(file, "{\n");
lf_indent(file, +2);
lf_print_c_code(file, function->annex);
lf_indent(file, -2);
lf_printf(file, "}\n");
lf_print_file_line_nr(file);
}
static void
gen_semantics_c(insn_table *table, lf *file)
{
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _SEMANTICS_C_\n");
lf_printf(file, "#define _SEMANTICS_C_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef STATIC_INLINE_SEMANTICS\n");
lf_printf(file, "#define STATIC_INLINE_SEMANTICS STATIC_INLINE\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "#include \"cpu.h\"\n");
lf_printf(file, "#include \"idecode.h\"\n");
lf_printf(file, "#include \"semantics.h\"\n");
lf_printf(file, "\n");
/* output a definition (c-code) for all functions */
insn_table_traverse_function(table,
file,
semantics_c_function);
/* output a definition (c-code) for all instructions */
if (idecode_expand_semantics)
insn_table_traverse_tree(table,
file,
1,
NULL, /* start */
semantics_c_leaf,
NULL, /* end */
NULL); /* padding */
else
insn_table_traverse_insn(table,
file,
semantics_c_insn);
lf_printf(file, "\n");
lf_printf(file, "#endif /* _SEMANTICS_C_ */\n");
}
/****************************************************************/
static void
gen_idecode_h(insn_table *table, lf *file)
{
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _IDECODE_H_\n");
lf_printf(file, "#define _IDECODE_H_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef INLINE_IDECODE\n");
lf_printf(file, "#define INLINE_IDECODE\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "#include \"idecode_expression.h\"\n");
lf_printf(file, "#include \"idecode_fields.h\"\n");
lf_printf(file, "#include \"idecode_branch.h\"\n");
lf_printf(file, "\n");
lf_printf(file, "#include \"icache.h\"\n");
lf_printf(file, "\n");
lf_printf(file, "typedef unsigned_word idecode_semantic\n(%s);\n",
idecode_cache < 2 ? insn_formal : cache_insn_formal);
lf_printf(file, "\n");
if (!idecode_cache)
lf_printf(file, "INLINE_IDECODE unsigned_word idecode_issue\n(%s);\n",
insn_formal);
else if (idecode_cache)
lf_printf(file, "INLINE_IDECODE idecode_semantic *idecode\n(%s);\n",
idecode_cache == 1 ? insn_formal : cache_idecode_formal);
lf_printf(file, "\n");
lf_printf(file, "#endif /* _IDECODE_H_ */\n");
}
/****************************************************************/
static void
idecode_table_start(insn_table *table,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
/* start of the table */
if (!table->opcode_rule->use_switch) {
lf_printf(file, "\n");
lf_printf(file, "static idecode_table_entry ");
lf_print_table_name(file, table);
lf_printf(file, "[] = {\n");
}
}
static void
idecode_table_leaf(insn_table *entry,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(entry->parent != NULL);
ASSERT(depth == 0);
/* add an entry to the table */
if (!entry->parent->opcode_rule->use_switch) {
if (entry->opcode == NULL) {
/* table leaf entry */
lf_printf(file, " /*%d*/ { 0, 0, ", entry->opcode_nr);
lf_print_function_name(file,
entry->insns->file_entry->fields[insn_name],
entry->expanded_bits,
(idecode_cache < 2
? function_name_prefix_semantics
: function_name_prefix_idecode));
lf_printf(file, " },\n");
}
else if (entry->opcode_rule->use_switch) {
/* table calling switch statement */
lf_printf(file, " /*%d*/ { -1, 0, ",
entry->opcode_nr);
lf_print_table_name(file, entry);
lf_printf(file, " },\n");
}
else {
/* table `calling' another table */
lf_printf(file, " /*%d*/ { ", entry->opcode_nr);
if (entry->opcode->is_boolean)
lf_printf(file, "MASK32(%d,%d), 0, ",
entry->opcode->first, entry->opcode->last);
else
lf_printf(file, "%d, MASK32(%d,%d), ",
insn_size - entry->opcode->last - 1,
entry->opcode->first, entry->opcode->last);
lf_print_table_name(file, entry);
lf_printf(file, " },\n");
}
}
}
static void
idecode_table_end(insn_table *table,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
if (!table->opcode_rule->use_switch) {
lf_printf(file, "};\n");
}
}
static void
idecode_table_padding(insn_table *table,
void *data,
int depth,
int opcode_nr)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
if (!table->opcode_rule->use_switch) {
lf_printf(file, " /*%d*/ { 0, 0, %s_illegal },\n",
opcode_nr, idecode_cache > 1 ? "idecode" : "semantic");
}
}
/****************************************************************/
void lf_print_idecode_switch
(lf *file,
insn_table *table);
static void
idecode_switch_start(insn_table *table,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
ASSERT(table->opcode_rule->use_switch);
lf_printf(file, "switch (EXTRACTED32(instruction, %d, %d)) {\n",
table->opcode->first, table->opcode->last);
}
static void
idecode_switch_leaf(insn_table *entry,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(entry->parent != NULL);
ASSERT(depth == 0);
ASSERT(entry->parent->opcode_rule->use_switch);
lf_printf(file, "case %d:\n", entry->opcode_nr);
lf_indent(file, +2);
{
if (entry->opcode == NULL) {
/* switch calling leaf */
lf_printf(file, "return ");
lf_print_function_name(file,
entry->insns->file_entry->fields[insn_name],
entry->expanded_bits,
(idecode_cache < 2
? function_name_prefix_semantics
: function_name_prefix_idecode));
if (!idecode_cache)
lf_printf(file, "(%s);\n", insn_actual);
else if (idecode_cache == 1)
lf_printf(file, ";\n");
else
lf_printf(file, "(%s);\n", cache_idecode_actual);
}
else if (entry->opcode_rule->use_switch) {
/* switch calling switch */
lf_print_idecode_switch(file, entry);
}
else {
/* switch calling table */
lf_printf(file, "return ");
lf_print_idecode_table(file, entry);
}
lf_printf(file, "break;\n");
}
lf_indent(file, -2);
}
static void
lf_print_idecode_switch_illegal(lf *file)
{
lf_indent(file, +2);
lf_print_idecode_illegal(file);
lf_printf(file, "break;\n");
lf_indent(file, -2);
}
static void
idecode_switch_end(insn_table *table,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
ASSERT(table->opcode_rule->use_switch);
if (table->opcode_rule->use_switch == 1) {
lf_printf(file, "default:\n");
lf_print_idecode_switch_illegal(file);
}
lf_printf(file, "}\n");
}
static void
idecode_switch_padding(insn_table *table,
void *data,
int depth,
int opcode_nr)
{
lf *file = (lf*)data;
ASSERT(depth == 0);
ASSERT(table->opcode_rule->use_switch);
if (table->opcode_rule->use_switch > 1) {
lf_printf(file, "case %d:\n", opcode_nr);
lf_print_idecode_switch_illegal(file);
}
}
void
lf_print_idecode_switch(lf *file,
insn_table *table)
{
insn_table_traverse_tree(table,
file,
0,
idecode_switch_start,
idecode_switch_leaf,
idecode_switch_end,
idecode_switch_padding);
}
static void
idecode_expand_if_switch(insn_table *table,
void *data,
int depth)
{
lf *file = (lf*)data;
if (table->opcode_rule->use_switch
&& table->parent != NULL /* don't expand the top one yet */
&& !table->parent->opcode_rule->use_switch) {
lf_printf(file, "\n");
lf_printf(file, "STATIC_INLINE_IDECODE void\n");
lf_print_table_name(file, table);
lf_printf(file, "\n(%s)\n",
idecode_cache ? cache_idecode_formal : insn_formal);
lf_printf(file, "{\n");
{
lf_indent(file, +2);
lf_print_idecode_switch(file, table);
lf_indent(file, -2);
}
lf_printf(file, "}\n");
}
}
static void
lf_print_c_cracker_function(lf *file,
insn *instruction,
insn_bits *expanded_bits,
opcode_field *opcodes)
{
/* if needed, generate code to enter this routine into a cache */
lf_printf(file, "\n");
lf_printf(file, "STATIC_INLINE_IDECODE idecode_semantic *\n");
lf_print_function_name(file,
instruction->file_entry->fields[insn_name],
expanded_bits,
function_name_prefix_idecode);
lf_printf(file, "\n(%s)\n", cache_idecode_formal);
lf_print_c_cracker(file,
instruction,
expanded_bits,
opcodes);
}
static void
idecode_crack_leaf(insn_table *entry,
void *data,
int depth)
{
lf *file = (lf*)data;
ASSERT(entry->nr_insn == 1
&& entry->opcode == NULL
&& entry->parent != NULL
&& entry->parent->opcode != NULL);
lf_print_c_cracker_function(file,
entry->insns,
entry->expanded_bits,
entry->opcode);
}
static void
idecode_crack_insn(insn_table *entry,
void *data,
insn *instruction)
{
lf *file = (lf*)data;
lf_print_c_cracker_function(file,
instruction,
NULL,
NULL);
}
/****************************************************************/
static void
gen_idecode_c(insn_table *table, lf *file)
{
int depth;
/* the intro */
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef _IDECODE_C_\n");
lf_printf(file, "#define _IDECODE_C_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef STATIC_INLINE_IDECODE\n");
lf_printf(file, "#define STATIC_INLINE_IDECODE STATIC_INLINE\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "#include \"cpu.h\"\n");
lf_printf(file, "#include \"idecode.h\"\n");
lf_printf(file, "#include \"semantics.h\"\n");
lf_printf(file, "\n");
lf_printf(file, "\n");
lf_printf(file, "typedef idecode_semantic *idecode_crack\n(%s);\n",
idecode_cache > 1 ? cache_idecode_formal : insn_formal);
lf_printf(file, "\n");
lf_printf(file, "typedef struct _idecode_table_entry {\n");
lf_printf(file, " unsigned shift;\n");
lf_printf(file, " unsigned mask;\n");
lf_printf(file, " void *function_or_table;\n");
lf_printf(file, "} idecode_table_entry;\n");
lf_printf(file, "\n");
lf_printf(file, "\n");
/* output cracking functions where needed */
if (idecode_cache > 1) {
if (idecode_expand_semantics)
insn_table_traverse_tree(table,
file,
1,
NULL,
idecode_crack_leaf,
NULL,
NULL);
else
insn_table_traverse_insn(table,
file,
idecode_crack_insn);
}
/* output tables where needed */
for (depth = insn_table_depth(table);
depth > 0;
depth--) {
insn_table_traverse_tree(table,
file,
1-depth,
idecode_table_start,
idecode_table_leaf,
idecode_table_end,
idecode_table_padding);
}
/* output switch functions where needed */
insn_table_traverse_tree(table,
file,
1,
idecode_expand_if_switch, /* START */
NULL, NULL, NULL);
/* output the main idecode routine */
lf_printf(file, "\n");
if (!idecode_cache)
lf_printf(file, "INLINE_IDECODE unsigned_word\nidecode_issue\n(%s)\n",
insn_formal);
else if (idecode_cache)
lf_printf(file, "INLINE_IDECODE idecode_semantic *\nidecode\n(%s)\n",
idecode_cache == 1 ? insn_formal : cache_idecode_formal);
lf_printf(file, "{\n");
lf_indent(file, +2);
if (!idecode_cache)
lf_printf(file, "cpu_increment_number_of_insns (processor);\n");
if (table->opcode_rule->use_switch)
lf_print_idecode_switch(file, table);
else
lf_print_idecode_table(file, table);
lf_indent(file, -2);
lf_printf(file, "}\n");
lf_printf(file, "\n");
lf_printf(file, "#endif\n");
}
/****************************************************************/
typedef enum {
spreg_name,
spreg_reg_nr,
spreg_readonly,
spreg_length,
nr_spreg_registers = file_table_max_fields
} spreg_fields;
typedef struct _spreg_table_entry spreg_table_entry;
struct _spreg_table_entry {
char *name;
int spreg_nr;
int is_readonly;
int length;
file_table_entry *entry;
spreg_table_entry *next;
};
typedef struct _spreg_table spreg_table;
struct _spreg_table {
spreg_table_entry *sprs;
};
static spreg_table_entry *
spreg_table_entry_new()
{
spreg_table_entry *new_entry =
(spreg_table_entry*)zmalloc(sizeof(spreg_table_entry));
ASSERT(new_entry != NULL);
return new_entry;
}
static spreg_table *
spreg_table_new()
{
spreg_table *new_table = (spreg_table*)zmalloc(sizeof(spreg_table));
ASSERT(new_table != NULL);
return new_table;
}
static void
spreg_table_insert(spreg_table *table, file_table_entry *entry)
{
/* create a new spr entry */
spreg_table_entry *new_spr = spreg_table_entry_new();
new_spr->next = NULL;
new_spr->entry = entry;
new_spr->spreg_nr = atoi(entry->fields[spreg_reg_nr]);
new_spr->is_readonly = (entry->fields[spreg_readonly]
? atoi(entry->fields[spreg_readonly])
: 0);
new_spr->length = atoi(entry->fields[spreg_length]);
new_spr->name = (char*)zmalloc(strlen(entry->fields[spreg_name]) + 1);
ASSERT(new_spr->name != NULL);
{
int i;
for (i = 0; entry->fields[spreg_name][i] != '\0'; i++) {
if (isupper(entry->fields[spreg_name][i]))
new_spr->name[i] = tolower(entry->fields[spreg_name][i]);
else
new_spr->name[i] = entry->fields[spreg_name][i];
}
}
/* insert, by spreg_nr order */
{
spreg_table_entry **ptr_to_spreg_entry = &table->sprs;
spreg_table_entry *spreg_entry = *ptr_to_spreg_entry;
while (spreg_entry != NULL && spreg_entry->spreg_nr < new_spr->spreg_nr) {
ptr_to_spreg_entry = &spreg_entry->next;
spreg_entry = *ptr_to_spreg_entry;
}
ASSERT(spreg_entry == NULL || spreg_entry->spreg_nr != new_spr->spreg_nr);
*ptr_to_spreg_entry = new_spr;
new_spr->next = spreg_entry;
}
}
static spreg_table *
spreg_table_load(char *file_name)
{
file_table *file = file_table_open(file_name);
spreg_table *table = spreg_table_new();
{
file_table_entry *entry;
while ((entry = file_table_read(file)) != NULL) {
spreg_table_insert(table, entry);
}
}
return table;
}
/****************************************************************/
char *spreg_attributes[] = {
"is_valid",
"is_readonly",
"name",
"index",
"length",
0
};
static void
gen_spreg_h(spreg_table *table, lf *file)
{
spreg_table_entry *entry;
char **attribute;
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _SPREG_H_\n");
lf_printf(file, "#define _SPREG_H_\n");
lf_printf(file, "\n");
lf_printf(file, "#ifndef INLINE_SPREG\n");
lf_printf(file, "#define INLINE_SPREG\n");
lf_printf(file, "#endif\n");
lf_printf(file, "\n");
lf_printf(file, "typedef unsigned_word spreg;\n");
lf_printf(file, "\n");
lf_printf(file, "typedef enum {\n");
for (entry = table->sprs;
entry != NULL ;
entry = entry->next) {
lf_printf(file, " spr_%s = %d,\n", entry->name, entry->spreg_nr);
}
lf_printf(file, " nr_of_sprs = %d\n", nr_of_sprs);
lf_printf(file, "} sprs;\n");
lf_printf(file, "\n");
for (attribute = spreg_attributes;
*attribute != NULL;
attribute++) {
if (strcmp(*attribute, "name") == 0)
lf_printf(file, "INLINE_SPREG char *spr_%s(sprs spr);\n",
*attribute);
else
lf_printf(file, "INLINE_SPREG int spr_%s(sprs spr);\n",
*attribute);
}
lf_printf(file, "\n");
lf_printf(file, "#endif /* _SPREG_H_ */\n");
}
static void
gen_spreg_c(spreg_table *table, lf *file)
{
spreg_table_entry *entry;
char **attribute;
int spreg_nr;
lf_print_copyleft(file);
lf_printf(file, "\n");
lf_printf(file, "#ifndef _SPREG_C_\n");
lf_printf(file, "#define _SPREG_C_\n");
lf_printf(file, "\n");
lf_printf(file, "#include \"words.h\"\n");
lf_printf(file, "#include \"spreg.h\"\n");
lf_printf(file, "\n");
lf_printf(file, "typedef struct _spreg_info {\n");
lf_printf(file, " char *name;\n");
lf_printf(file, " int is_valid;\n");
lf_printf(file, " int length;\n");
lf_printf(file, " int is_readonly;\n");
lf_printf(file, " int index;\n");
lf_printf(file, "} spreg_info;\n");
lf_printf(file, "\n");
lf_printf(file, "static spreg_info spr_info[nr_of_sprs+1] = {\n");
entry = table->sprs;
for (spreg_nr = 0; spreg_nr < nr_of_sprs+1; spreg_nr++) {
if (entry == NULL || spreg_nr < entry->spreg_nr)
lf_printf(file, " { 0, 0, 0, 0, %d},\n", spreg_nr);
else {
lf_printf(file, " { \"%s\", %d, %d, %d, spr_%s /*%d*/ },\n",
entry->name, 1, entry->length, entry->is_readonly,
entry->name, entry->spreg_nr);
entry = entry->next;
}
}
lf_printf(file, "};\n");
for (attribute = spreg_attributes;
*attribute != NULL;
attribute++) {
lf_printf(file, "\n");
if (strcmp(*attribute, "name") == 0)
lf_printf(file, "INLINE_SPREG char *\n");
else
lf_printf(file, "INLINE_SPREG int\n");
lf_printf(file, "spr_%s(sprs spr)\n", *attribute);
lf_printf(file, "{\n");
if (spreg_lookup_table
|| strcmp(*attribute, "name") == 0
|| strcmp(*attribute, "index") == 0)
lf_printf(file, " return spr_info[spr].%s;\n",
*attribute);
else {
spreg_table_entry *entry;
lf_printf(file, " switch (spr) {\n");
for (entry = table->sprs; entry != NULL; entry = entry->next) {
lf_printf(file, " case %d:\n", entry->spreg_nr);
if (strcmp(*attribute, "is_valid") == 0)
lf_printf(file, " return 1;\n");
else if (strcmp(*attribute, "is_readonly") == 0)
lf_printf(file, " return %d;\n", entry->is_readonly);
else if (strcmp(*attribute, "length") == 0)
lf_printf(file, " return %d;\n", entry->length);
else
ASSERT(0);
}
lf_printf(file, " default:\n");
lf_printf(file, " return 0;\n");
lf_printf(file, " }\n");
}
lf_printf(file, "}\n");
}
lf_printf(file, "\n");
lf_printf(file, "#endif /* _SPREG_C_ */\n");
}
/****************************************************************/
int
main(int argc,
char **argv,
char **envp)
{
insn_table *instructions = NULL;
spreg_table *sprs = NULL;
icache_tree *cache_fields = NULL;
char *real_file_name = NULL;
int ch;
while ((ch = getopt(argc, argv, "n:i:I:r:S:s:D:d:P:p:C:")) != -1) {
fprintf(stderr, "\t-%c %s\n", ch, optarg);
switch(ch) {
case 'I':
case 'i':
instructions = insn_table_load_insns(optarg);
fprintf(stderr, "\texpanding ...\n");
insn_table_expand_insns(instructions);
fprintf(stderr, "\tcache fields ...\n");
cache_fields = insn_table_cache_fields(instructions);
if (ch == 'I') {
dump_traverse(instructions);
dump_insn_table(instructions, 0, 1);
}
break;
case 'r':
sprs = spreg_table_load(optarg);
break;
case 'n':
real_file_name = strdup(optarg);
break;
default:
{
lf *file = lf_open(optarg, real_file_name);
switch (ch) {
case 'S':
gen_semantics_h(instructions, file);
break;
case 's':
gen_semantics_c(instructions, file);
break;
case 'P':
gen_spreg_h(sprs, file);
break;
case 'p':
gen_spreg_c(sprs, file);
break;
case 'D':
gen_idecode_h(instructions, file);
break;
case 'd':
gen_idecode_c(instructions, file);
break;
case 'C':
gen_icache_h(cache_fields, file);
break;
}
lf_close(file);
}
real_file_name = NULL;
}
}
return 0;
}