/* This file is part of the program psim. Copyright (C) 1994-1998, Andrew Cagney 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, define it */ lf_indent_suppress (file); 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"); } }