diff options
Diffstat (limited to 'sim/igen/gen-icache.c')
-rw-r--r-- | sim/igen/gen-icache.c | 859 |
1 files changed, 859 insertions, 0 deletions
diff --git a/sim/igen/gen-icache.c b/sim/igen/gen-icache.c new file mode 100644 index 0000000..88d5d41 --- /dev/null +++ b/sim/igen/gen-icache.c @@ -0,0 +1,859 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1998, 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. + + */ + + +#include "misc.h" +#include "lf.h" +#include "table.h" +#include "filter.h" +#include "igen.h" + +#include "ld-insn.h" +#include "ld-decode.h" + +#include "gen.h" + +#include "gen-semantics.h" +#include "gen-idecode.h" +#include "gen-icache.h" + + + +static void +print_icache_function_header (lf *file, + const char *basename, + const char *format_name, + opcode_bits *expanded_bits, + int is_function_definition, + int nr_prefetched_words) +{ + lf_printf(file, "\n"); + lf_print__function_type_function (file, print_icache_function_type, + "EXTERN_ICACHE", " "); + print_function_name (file, + basename, format_name, NULL, + expanded_bits, + function_name_prefix_icache); + lf_printf (file, "\n("); + print_icache_function_formal (file, nr_prefetched_words); + lf_printf (file, ")"); + if (!is_function_definition) + lf_printf (file, ";"); + lf_printf (file, "\n"); +} + + +void +print_icache_declaration (lf *file, + insn_entry *insn, + opcode_bits *expanded_bits, + insn_opcodes *opcodes, + int nr_prefetched_words) +{ + print_icache_function_header (file, + insn->name, + insn->format_name, + expanded_bits, + 0/* is not function definition */, + nr_prefetched_words); +} + + + +static void +print_icache_extraction (lf *file, + const char *format_name, + cache_entry_type cache_type, + const char *entry_name, + const char *entry_type, + const char *entry_expression, + char *single_insn_field, + line_ref *line, + insn_field_entry *cur_field, + opcode_bits *expanded_bits, + icache_decl_type what_to_declare, + icache_body_type what_to_do) +{ + const char *expression; + opcode_bits *bits; + char *reason; + ASSERT (format_name != NULL); + ASSERT (entry_name != NULL); + + /* figure out exactly what should be going on here */ + switch (cache_type) + { + case scratch_value: + if ((what_to_do & put_values_in_icache) + || what_to_do == do_not_use_icache) + { + reason = "scratch"; + what_to_do = do_not_use_icache; + } + else + return; + break; + case compute_value: + if ((what_to_do & get_values_from_icache) + || what_to_do == do_not_use_icache) + { + reason = "compute"; + what_to_do = do_not_use_icache; + } + else + return; + break; + case cache_value: + if ((what_to_declare != undef_variables) + || !(what_to_do & put_values_in_icache)) + { + reason = "cache"; + what_to_declare = ((what_to_do & put_values_in_icache) + ? declare_variables + : what_to_declare); + } + else + return; + break; + } + + /* For the type, default to a simple unsigned */ + if (entry_type == NULL || strlen (entry_type) == 0) + entry_type = "unsigned"; + + /* look through the set of expanded sub fields to see if this field + has been given a constant value */ + for (bits = expanded_bits; + bits != NULL; + bits = bits->next) + { + if (bits->field == cur_field) + break; + } + + /* Define a storage area for the cache element */ + switch (what_to_declare) + { + case undef_variables: + /* We've finished with the #define value - destory it */ + lf_indent_suppress (file); + lf_printf (file, "#undef %s\n", entry_name); + return; + case define_variables: + /* Using direct access for this entry, clear any prior + definition, then define it */ + lf_indent_suppress (file); + lf_printf (file, "#undef %s\n", entry_name); + /* Don't type cast pointer types! */ + lf_indent_suppress (file); + if (strchr (entry_type, '*') != NULL) + lf_printf (file, "#define %s (", entry_name); + else + lf_printf (file, "#define %s ((%s) ", entry_name, entry_type); + break; + case declare_variables: + /* using variables to define the value */ + if (line != NULL) + lf_print__line_ref (file, line); + lf_printf (file, "%s const %s UNUSED = ", entry_type, entry_name); + break; + } + + + /* define a value for that storage area as determined by what is in + the cache */ + if (bits != NULL + && single_insn_field != NULL + && strcmp (entry_name, single_insn_field) == 0 + && strcmp (entry_name, cur_field->val_string) == 0 + && ((bits->opcode->is_boolean && bits->value == 0) + || (!bits->opcode->is_boolean))) + { + /* The cache rule is specifying what to do with a simple + instruction field. + + Because of instruction expansion, the field is either a + constant value or equal to the specified constant (boolean + comparison). (The latter indicated by bits->value == 0). + + The case of a field not being equal to the specified boolean + value is handled later. */ + expression = "constant field"; + ASSERT (bits->field == cur_field); + if (bits->opcode->is_boolean) + { + ASSERT (bits->value == 0); + lf_printf (file, "%d", bits->opcode->boolean_constant); + } + else if (bits->opcode->last < bits->field->last) + { + lf_printf (file, "%d", + bits->value << (bits->field->last - bits->opcode->last)); + } + else + { + lf_printf (file, "%d", bits->value); + } + } + else if (bits != NULL + && single_insn_field != NULL + && strncmp (entry_name, + single_insn_field, + strlen (single_insn_field)) == 0 + && strncmp (entry_name + strlen (single_insn_field), + "_is_", + strlen ("_is_")) == 0 + && ((bits->opcode->is_boolean + && ((unsigned) atol (entry_name + strlen (single_insn_field) + strlen ("_is_")) + == bits->opcode->boolean_constant)) + || (!bits->opcode->is_boolean))) + { + /* The cache rule defines an entry for the comparison between a + single instruction field and a constant. The value of the + comparison in someway matches that of the opcode field that + was made constant through expansion. */ + expression = "constant compare"; + if (bits->opcode->is_boolean) + { + lf_printf (file, "%d /* %s == %d */", + bits->value == 0, + single_insn_field, + bits->opcode->boolean_constant); + } + else if (bits->opcode->last < bits->field->last) + { + lf_printf (file, "%d /* %s == %d */", + (atol (entry_name + strlen (single_insn_field) + strlen ("_is_")) + == (bits->value << (bits->field->last - bits->opcode->last))), + single_insn_field, + (bits->value << (bits->field->last - bits->opcode->last))); + } + else + { + lf_printf (file, "%d /* %s == %d */", + (atol (entry_name + strlen (single_insn_field) + strlen ("_is_")) + == bits->value), + single_insn_field, + bits->value); + } + } + else + { + /* put the field in the local variable, possibly also enter it + into the cache */ + expression = "extraction"; + /* handle the cache */ + if ((what_to_do & get_values_from_icache) + || (what_to_do & put_values_in_icache)) + { + lf_printf (file, "cache_entry->crack.%s.%s", + format_name, + entry_name); + if (what_to_do & put_values_in_icache) /* also put it in the cache? */ + { + lf_printf (file, " = "); + } + } + if ((what_to_do & put_values_in_icache) + || what_to_do == do_not_use_icache) + { + if (cur_field != NULL) + { + if (entry_expression != NULL && strlen (entry_expression) > 0) + error (line, "Instruction field entry with nonempty expression\n"); + if (cur_field->first == 0 && cur_field->last == options.insn_bit_size - 1) + lf_printf (file, "(instruction_%d)", + cur_field->word_nr); + else if (cur_field->last == options.insn_bit_size - 1) + lf_printf (file, "MASKED%d (instruction_%d, %d, %d)", + options.insn_bit_size, + cur_field->word_nr, + i2target (options.hi_bit_nr, cur_field->first), + i2target (options.hi_bit_nr, cur_field->last)); + else + lf_printf (file, "EXTRACTED%d (instruction_%d, %d, %d)", + options.insn_bit_size, + cur_field->word_nr, + i2target (options.hi_bit_nr, cur_field->first), + i2target (options.hi_bit_nr, cur_field->last)); + } + else + { + lf_printf (file, "%s", entry_expression); + } + } + } + + switch (what_to_declare) + { + case define_variables: + lf_printf (file, ")"); + break; + case undef_variables: + break; + case declare_variables: + lf_printf (file, ";"); + break; + } + + ASSERT (reason != NULL && expression != NULL); + lf_printf (file, " /* %s - %s */\n", reason, expression); +} + + +void +print_icache_body (lf *file, + insn_entry *instruction, + opcode_bits *expanded_bits, + cache_entry *cache_rules, + icache_decl_type what_to_declare, + icache_body_type what_to_do, + int nr_prefetched_words) +{ + /* extract instruction fields */ + lf_printf (file, "/* Extraction: %s\n", instruction->name); + lf_printf (file, " "); + switch (what_to_declare) + { + case define_variables: + lf_printf (file, "#define"); + break; + case declare_variables: + lf_printf (file, "declare"); + break; + case undef_variables: + lf_printf (file, "#undef"); + break; + } + lf_printf (file, " "); + switch (what_to_do) + { + case get_values_from_icache: + lf_printf (file, "get-values-from-icache"); + break; + case put_values_in_icache: + lf_printf (file, "put-values-in-icache"); + break; + case both_values_and_icache: + lf_printf (file, "get-values-from-icache|put-values-in-icache"); + break; + case do_not_use_icache: + lf_printf (file, "do-not-use-icache"); + break; + } + lf_printf (file, "\n "); + print_insn_words (file, instruction); + lf_printf(file, " */\n"); + + /* pass zero - fetch from memory any missing instructions. + + Some of the instructions will have already been fetched (in the + instruction array), others will still need fetching. */ + switch (what_to_do) + { + case get_values_from_icache: + break; + case put_values_in_icache: + case both_values_and_icache: + case do_not_use_icache: + { + int word_nr; + switch (what_to_declare) + { + case undef_variables: + break; + case define_variables: + case declare_variables: + for (word_nr = nr_prefetched_words; + word_nr < instruction->nr_words; + word_nr++) + { + /* FIXME - should be using print_icache_extraction? */ + lf_printf (file, "%sinstruction_word instruction_%d UNUSED = ", + options.module.global.prefix.l, + word_nr); + lf_printf (file, "IMEM%d_IMMED (cia, %d)", + options.insn_bit_size, word_nr); + lf_printf (file, ";\n"); + } + } + } + } + + /* if putting the instruction words in the cache, define references + for them */ + if (options.gen.insn_in_icache) { + /* FIXME: is the instruction_word type correct? */ + print_icache_extraction (file, + instruction->format_name, + cache_value, + "insn", /* name */ + "instruction_word", /* type */ + "instruction", /* expression */ + NULL, /* origin */ + NULL, /* line */ + NULL, NULL, + what_to_declare, + what_to_do); + } + lf_printf(file, "\n"); + + /* pass one - process instruction fields. + + If there is no cache rule, the default is to enter the field into + the cache */ + { + insn_word_entry *word; + for (word = instruction->words; + word != NULL; + word = word->next) + { + insn_field_entry *cur_field; + for (cur_field = word->first; + cur_field->first < options.insn_bit_size; + cur_field = cur_field->next) + { + if (cur_field->type == insn_field_string) + { + cache_entry *cache_rule; + cache_entry_type value_type = cache_value; + line_ref *value_line = instruction->line; + /* check the cache table to see if it contains a rule + overriding the default cache action for an + instruction field */ + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) + { + if (filter_is_subset (instruction->field_names, + cache_rule->original_fields) + && strcmp (cache_rule->name, cur_field->val_string) == 0) + { + value_type = cache_rule->entry_type; + value_line = cache_rule->line; + if (value_type == compute_value) + { + options.warning (cache_rule->line, + "instruction field of type `compute' changed to `cache'\n"); + cache_rule->entry_type = cache_value; + } + break; + } + } + /* Define an entry for the field within the + instruction */ + print_icache_extraction (file, + instruction->format_name, + value_type, + cur_field->val_string, /* name */ + NULL, /* type */ + NULL, /* expression */ + cur_field->val_string, /* insn field */ + value_line, + cur_field, + expanded_bits, + what_to_declare, + what_to_do); + } + } + } + } + + /* pass two - any cache fields not processed above */ + { + cache_entry *cache_rule; + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) + { + if (filter_is_subset (instruction->field_names, + cache_rule->original_fields) + && !filter_is_member (instruction->field_names, + cache_rule->name)) + { + char *single_field = filter_next (cache_rule->original_fields, ""); + if (filter_next (cache_rule->original_fields, single_field) != NULL) + single_field = NULL; + print_icache_extraction (file, + instruction->format_name, + cache_rule->entry_type, + cache_rule->name, + cache_rule->type, + cache_rule->expression, + single_field, + cache_rule->line, + NULL, /* cur_field */ + expanded_bits, + what_to_declare, + what_to_do); + } + } + } + + lf_print__internal_ref (file); +} + + + +typedef struct _form_fields form_fields; +struct _form_fields { + char *name; + filter *fields; + form_fields *next; +}; + +static form_fields * +insn_table_cache_fields (insn_table *isa) +{ + form_fields *forms = NULL; + insn_entry *insn; + for (insn = isa->insns; + insn != NULL; + insn = insn->next) { + form_fields **form = &forms; + while (1) + { + if (*form == NULL) + { + /* new format name, add it */ + form_fields *new_form = ZALLOC (form_fields); + new_form->name = insn->format_name; + filter_add (&new_form->fields, insn->field_names); + *form = new_form; + break; + } + else if (strcmp ((*form)->name, insn->format_name) == 0) + { + /* already present, add field names to the existing list */ + filter_add (&(*form)->fields, insn->field_names); + break; + } + form = &(*form)->next; + } + } + return forms; +} + + + +extern void +print_icache_struct (lf *file, + insn_table *isa, + cache_entry *cache_rules) +{ + /* Create a list of all the different instruction formats with their + corresponding field names. */ + form_fields *formats = insn_table_cache_fields (isa); + + lf_printf (file, "\n"); + lf_printf (file, "#define WITH_%sIDECODE_CACHE_SIZE %d\n", + options.module.global.prefix.u, + (options.gen.icache ? options.gen.icache_size : 0)); + lf_printf (file, "\n"); + + /* create an instruction cache if being used */ + if (options.gen.icache) { + lf_printf (file, "typedef struct _%sidecode_cache {\n", + options.module.global.prefix.l); + lf_indent (file, +2); + { + form_fields *format; + lf_printf (file, "unsigned_word address;\n"); + lf_printf (file, "void *semantic;\n"); + lf_printf (file, "union {\n"); + lf_indent (file, +2); + for (format = formats; + format != NULL; + format = format->next) + { + lf_printf (file, "struct {\n"); + lf_indent (file, +2); + { + cache_entry *cache_rule; + char *field; + /* space for any instruction words */ + if (options.gen.insn_in_icache) + lf_printf (file, "instruction_word insn[%d];\n", isa->max_nr_words); + /* define an entry for any applicable cache rules */ + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) + { + /* nb - sort of correct - should really check against + individual instructions */ + if (filter_is_subset (format->fields, cache_rule->original_fields)) + { + char *memb; + lf_printf (file, "%s %s;", + (cache_rule->type == NULL + ? "unsigned" + : cache_rule->type), + cache_rule->name); + lf_printf (file, " /*"); + for (memb = filter_next (cache_rule->original_fields, ""); + memb != NULL; + memb = filter_next (cache_rule->original_fields, memb)) + { + lf_printf (file, " %s", memb); + } + lf_printf (file, " */\n"); + } + } + /* define an entry for any fields not covered by a cache rule */ + for (field = filter_next (format->fields, ""); + field != NULL; + field = filter_next (format->fields, field)) + { + cache_entry *cache_rule; + int found_rule = 0; + for (cache_rule = cache_rules; + cache_rule != NULL; + cache_rule = cache_rule->next) + { + if (strcmp (cache_rule->name, field) == 0) + { + found_rule = 1; + break; + } + } + if (!found_rule) + lf_printf (file, "unsigned %s; /* default */\n", field); + } + } + lf_indent (file, -2); + lf_printf (file, "} %s;\n", format->name); + } + lf_indent (file, -2); + lf_printf (file, "} crack;\n"); + } + lf_indent (file, -2); + lf_printf (file, "} %sidecode_cache;\n", options.module.global.prefix.l); + } + else + { + /* alernativly, since no cache, emit a dummy definition for + idecode_cache so that code refering to the type can still compile */ + lf_printf(file, "typedef void %sidecode_cache;\n", + options.module.global.prefix.l); + } + lf_printf (file, "\n"); +} + + + +static void +print_icache_function (lf *file, + insn_entry *instruction, + opcode_bits *expanded_bits, + insn_opcodes *opcodes, + cache_entry *cache_rules, + int nr_prefetched_words) +{ + int indent; + + /* generate code to enter decoded instruction into the icache */ + lf_printf(file, "\n"); + lf_print__function_type_function (file, print_icache_function_type, + "EXTERN_ICACHE", "\n"); + indent = print_function_name (file, + instruction->name, + instruction->format_name, + NULL, + expanded_bits, + function_name_prefix_icache); + indent += lf_printf (file, " "); + lf_indent (file, +indent); + lf_printf (file, "("); + print_icache_function_formal (file, nr_prefetched_words); + lf_printf (file, ")\n"); + lf_indent (file, -indent); + + /* function header */ + lf_printf (file, "{\n"); + lf_indent (file, +2); + + print_my_defines (file, + instruction->name, + instruction->format_name, + expanded_bits); + print_itrace (file, instruction, 1/*putting-value-in-cache*/); + + print_idecode_validate (file, instruction, opcodes); + + lf_printf (file, "\n"); + lf_printf (file, "{\n"); + lf_indent (file, +2); + if (options.gen.semantic_icache) + lf_printf (file, "unsigned_word nia;\n"); + print_icache_body (file, + instruction, + expanded_bits, + cache_rules, + (options.gen.direct_access + ? define_variables + : declare_variables), + (options.gen.semantic_icache + ? both_values_and_icache + : put_values_in_icache), + nr_prefetched_words); + + lf_printf (file, "\n"); + lf_printf (file, "cache_entry->address = cia;\n"); + lf_printf (file, "cache_entry->semantic = "); + print_function_name (file, + instruction->name, + instruction->format_name, + NULL, + expanded_bits, + function_name_prefix_semantics); + lf_printf (file, ";\n"); + lf_printf (file, "\n"); + + if (options.gen.semantic_icache) { + lf_printf (file, "/* semantic routine */\n"); + print_semantic_body (file, + instruction, + expanded_bits, + opcodes); + lf_printf (file, "return nia;\n"); + } + + if (!options.gen.semantic_icache) + { + lf_printf (file, "/* return the function proper */\n"); + lf_printf (file, "return "); + print_function_name (file, + instruction->name, + instruction->format_name, + NULL, + expanded_bits, + function_name_prefix_semantics); + lf_printf (file, ";\n"); + } + + if (options.gen.direct_access) + { + print_icache_body (file, + instruction, + expanded_bits, + cache_rules, + undef_variables, + (options.gen.semantic_icache + ? both_values_and_icache + : put_values_in_icache), + nr_prefetched_words); + } + + lf_indent (file, -2); + lf_printf (file, "}\n"); + lf_indent (file, -2); + lf_printf (file, "}\n"); +} + + +void +print_icache_definition (lf *file, + insn_entry *insn, + opcode_bits *expanded_bits, + insn_opcodes *opcodes, + cache_entry *cache_rules, + int nr_prefetched_words) +{ + print_icache_function (file, + insn, + expanded_bits, + opcodes, + cache_rules, + nr_prefetched_words); +} + + + +void +print_icache_internal_function_declaration (lf *file, + function_entry *function, + void *data) +{ + ASSERT (options.gen.icache); + if (function->is_internal) + { + lf_printf (file, "\n"); + lf_print__function_type_function (file, print_icache_function_type, + "INLINE_ICACHE", "\n"); + print_function_name (file, + function->name, + NULL, + NULL, + NULL, + function_name_prefix_icache); + lf_printf (file, "\n("); + print_icache_function_formal (file, 0); + lf_printf (file, ");\n"); + } +} + + +void +print_icache_internal_function_definition (lf *file, + function_entry *function, + void *data) +{ + ASSERT (options.gen.icache); + if (function->is_internal) + { + lf_printf (file, "\n"); + lf_print__function_type_function (file, print_icache_function_type, + "INLINE_ICACHE", "\n"); + print_function_name (file, + function->name, + NULL, + NULL, + NULL, + function_name_prefix_icache); + lf_printf (file, "\n("); + print_icache_function_formal (file, 0); + lf_printf (file, ")\n"); + lf_printf (file, "{\n"); + lf_indent (file, +2); + lf_printf (file, "/* semantic routine */\n"); + if (options.gen.semantic_icache) + { + lf_print__line_ref (file, function->code->line); + table_print_code (file, function->code); + lf_printf (file, "error (\"Internal function must longjump\\n\");\n"); + lf_printf (file, "return 0;\n"); + } + else + { + lf_printf (file, "return "); + print_function_name (file, + function->name, + NULL, + NULL, + NULL, + function_name_prefix_semantics); + lf_printf (file, ";\n"); + } + + lf_print__internal_ref (file); + lf_indent (file, -2); + lf_printf (file, "}\n"); + } +} |