| /* This file is part of the program psim. |
| |
| Copyright (C) 1994-1997, 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 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, see <http://www.gnu.org/licenses/>. |
| |
| */ |
| |
| #include <stdlib.h> |
| |
| #include "misc.h" |
| #include "lf.h" |
| #include "table.h" |
| |
| #include "filter.h" |
| |
| #include "ld-decode.h" |
| #include "ld-cache.h" |
| #include "ld-insn.h" |
| |
| #include "igen.h" |
| |
| #include "gen-semantics.h" |
| #include "gen-idecode.h" |
| #include "gen-icache.h" |
| |
| |
| |
| static void |
| print_icache_function_header(lf *file, |
| const char *basename, |
| insn_bits *expanded_bits, |
| int is_function_definition) |
| { |
| lf_printf(file, "\n"); |
| lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", " "); |
| print_function_name(file, |
| basename, |
| expanded_bits, |
| function_name_prefix_icache); |
| lf_printf(file, "\n(%s)", ICACHE_FUNCTION_FORMAL); |
| if (!is_function_definition) |
| lf_printf(file, ";"); |
| lf_printf(file, "\n"); |
| } |
| |
| |
| void |
| print_icache_declaration(insn_table *entry, |
| lf *file, |
| void *data, |
| insn *instruction, |
| int depth) |
| { |
| if (generate_expanded_instructions) { |
| ASSERT(entry->nr_insn == 1); |
| print_icache_function_header(file, |
| entry->insns->file_entry->fields[insn_name], |
| entry->expanded_bits, |
| 0/* is not function definition */); |
| } |
| else { |
| print_icache_function_header(file, |
| instruction->file_entry->fields[insn_name], |
| NULL, |
| 0/* is not function definition */); |
| } |
| } |
| |
| |
| |
| static void |
| print_icache_extraction(lf *file, |
| insn *instruction, |
| const char *entry_name, |
| const char *entry_type, |
| const char *entry_expression, |
| const char *original_name, |
| const char *file_name, |
| int line_nr, |
| insn_field *cur_field, |
| insn_bits *bits, |
| icache_decl_type what_to_declare, |
| icache_body_type what_to_do, |
| const char *reason) |
| { |
| const char *expression; |
| ASSERT(entry_name != NULL); |
| |
| /* Define a storage area for the cache element */ |
| if (what_to_declare == undef_variables) { |
| /* We've finished with the value - destory it */ |
| lf_indent_suppress(file); |
| lf_printf(file, "#undef %s\n", entry_name); |
| return; |
| } |
| else if (what_to_declare == define_variables) { |
| lf_indent_suppress(file); |
| lf_printf(file, "#define %s ", entry_name); |
| } |
| else { |
| if (file_name != NULL) |
| lf_print__external_reference(file, line_nr, file_name); |
| lf_printf(file, "%s const %s ATTRIBUTE_UNUSED = ", |
| entry_type == NULL ? "unsigned" : entry_type, |
| entry_name); |
| } |
| |
| /* define a value for that storage area as determined by what is in |
| the cache */ |
| if (bits != NULL |
| && strcmp(entry_name, cur_field->val_string) == 0 |
| && ((bits->opcode->is_boolean && bits->value == 0) |
| || (!bits->opcode->is_boolean))) { |
| /* The simple field has been made constant (as a result of |
| expanding instructions or similar). Remember that for a |
| boolean field, value is either 0 (implying the required |
| boolean_constant) or nonzero (implying some other value and |
| handled later below) - Define the variable accordingly */ |
| expression = "constant field"; |
| ASSERT(bits->field == cur_field); |
| ASSERT(entry_type == NULL); |
| if (bits->opcode->is_boolean) |
| 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 |
| && original_name != NULL |
| && strncmp(entry_name, |
| original_name, strlen(original_name)) == 0 |
| && strncmp(entry_name + strlen(original_name), |
| "_is_", strlen("_is_")) == 0 |
| && ((bits->opcode->is_boolean |
| && (atol(entry_name + strlen(original_name) + strlen("_is_")) |
| == bits->opcode->boolean_constant)) |
| || (!bits->opcode->is_boolean))) { |
| expression = "constant compare"; |
| /* An entry, derived from ORIGINAL_NAME, is testing to see of the |
| ORIGINAL_NAME has a specific constant value. That value |
| matching a boolean or constant field */ |
| if (bits->opcode->is_boolean) |
| lf_printf(file, "%d /* %s == %d */", |
| bits->value == 0, |
| original_name, |
| bits->opcode->boolean_constant); |
| else if (bits->opcode->last < bits->field->last) |
| lf_printf(file, "%d /* %s == %d */", |
| (atol(entry_name + strlen(original_name) + strlen("_is_")) |
| == (bits->value << (bits->field->last - bits->opcode->last))), |
| original_name, |
| (bits->value << (bits->field->last - bits->opcode->last))); |
| else |
| lf_printf(file, "%d /* %s == %d */", |
| (atol(entry_name + strlen(original_name) + strlen("_is_")) |
| == bits->value), |
| original_name, |
| 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", |
| instruction->file_entry->fields[insn_form], |
| 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 && strcmp(entry_name, cur_field->val_string) == 0) |
| lf_printf(file, "EXTRACTED32(instruction, %d, %d)", |
| i2target(hi_bit_nr, cur_field->first), |
| i2target(hi_bit_nr, cur_field->last)); |
| else if (entry_expression != NULL) |
| lf_printf(file, "%s", entry_expression); |
| else |
| lf_printf(file, "eval_%s", entry_name); |
| } |
| } |
| |
| if (!((what_to_declare == define_variables) |
| || (what_to_declare == undef_variables))) |
| lf_printf(file, ";"); |
| if (reason != NULL) |
| lf_printf(file, " /* %s - %s */", reason, expression); |
| lf_printf(file, "\n"); |
| } |
| |
| |
| void |
| print_icache_body(lf *file, |
| insn *instruction, |
| insn_bits *expanded_bits, |
| cache_table *cache_rules, |
| icache_decl_type what_to_declare, |
| icache_body_type what_to_do) |
| { |
| insn_field *cur_field; |
| |
| /* extract instruction fields */ |
| lf_printf(file, "/* extraction: %s ", |
| instruction->file_entry->fields[insn_format]); |
| 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"); |
| |
| for (cur_field = instruction->fields->first; |
| cur_field->first < insn_bit_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 */ |
| { |
| cache_table *cache_rule; |
| for (cache_rule = cache_rules; |
| cache_rule != NULL; |
| cache_rule = cache_rule->next) { |
| if (strcmp(cur_field->val_string, cache_rule->field_name) == 0) { |
| found_rule = 1; |
| if (cache_rule->type == scratch_value |
| && ((what_to_do & put_values_in_icache) |
| || what_to_do == do_not_use_icache)) |
| print_icache_extraction(file, |
| instruction, |
| cache_rule->derived_name, |
| cache_rule->type_def, |
| cache_rule->expression, |
| cache_rule->field_name, |
| cache_rule->file_entry->file_name, |
| cache_rule->file_entry->line_nr, |
| cur_field, |
| bits, |
| what_to_declare, |
| do_not_use_icache, |
| "icache scratch"); |
| else if (cache_rule->type == compute_value |
| && ((what_to_do & get_values_from_icache) |
| || what_to_do == do_not_use_icache)) |
| print_icache_extraction(file, |
| instruction, |
| cache_rule->derived_name, |
| cache_rule->type_def, |
| cache_rule->expression, |
| cache_rule->field_name, |
| cache_rule->file_entry->file_name, |
| cache_rule->file_entry->line_nr, |
| cur_field, |
| bits, |
| what_to_declare, |
| do_not_use_icache, |
| "semantic compute"); |
| else if (cache_rule->type == cache_value |
| && ((what_to_declare != undef_variables) |
| || !(what_to_do & put_values_in_icache))) |
| print_icache_extraction(file, |
| instruction, |
| cache_rule->derived_name, |
| cache_rule->type_def, |
| cache_rule->expression, |
| cache_rule->field_name, |
| cache_rule->file_entry->file_name, |
| cache_rule->file_entry->line_nr, |
| cur_field, |
| bits, |
| ((what_to_do & put_values_in_icache) |
| ? declare_variables |
| : what_to_declare), |
| what_to_do, |
| "in icache"); |
| } |
| } |
| } |
| /* No rule at all, assume that this is needed in the semantic |
| function (when values are extracted from the icache) and |
| hence must be put into the cache */ |
| if (found_rule == 0 |
| && ((what_to_declare != undef_variables) |
| || !(what_to_do & put_values_in_icache))) |
| print_icache_extraction(file, |
| instruction, |
| cur_field->val_string, |
| NULL, NULL, NULL, /* type, exp, orig */ |
| instruction->file_entry->file_name, |
| instruction->file_entry->line_nr, |
| cur_field, |
| bits, |
| ((what_to_do & put_values_in_icache) |
| ? declare_variables |
| : what_to_declare), |
| what_to_do, |
| "default in icache"); |
| /* any thing else ... */ |
| } |
| } |
| |
| lf_print__internal_reference(file); |
| |
| if ((code & generate_with_insn_in_icache)) { |
| lf_printf(file, "\n"); |
| print_icache_extraction(file, |
| instruction, |
| "insn", |
| "instruction_word", |
| "instruction", |
| NULL, /* origin */ |
| NULL, 0, /* file_name & line_nr */ |
| NULL, NULL, |
| what_to_declare, |
| what_to_do, |
| NULL); |
| } |
| } |
| |
| |
| |
| typedef struct _icache_tree icache_tree; |
| struct _icache_tree { |
| char *name; |
| icache_tree *next; |
| icache_tree *children; |
| }; |
| |
| 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 = ZALLOC(icache_tree); |
| 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 = ZALLOC(icache_tree); |
| 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; |
| } |
| |
| |
| |
| extern void |
| print_icache_struct(insn_table *instructions, |
| cache_table *cache_rules, |
| lf *file) |
| { |
| icache_tree *tree = insn_table_cache_fields(instructions); |
| |
| lf_printf(file, "\n"); |
| lf_printf(file, "#define WITH_IDECODE_CACHE_SIZE %d\n", |
| (code & generate_with_icache) ? icache_size : 0); |
| lf_printf(file, "\n"); |
| |
| /* create an instruction cache if being used */ |
| if ((code & generate_with_icache)) { |
| icache_tree *form; |
| lf_printf(file, "typedef struct _idecode_cache {\n"); |
| lf_printf(file, " unsigned_word address;\n"); |
| lf_printf(file, " void *semantic;\n"); |
| lf_printf(file, " union {\n"); |
| for (form = tree->children; |
| form != NULL; |
| form = form->next) { |
| icache_tree *field; |
| lf_printf(file, " struct {\n"); |
| if (code & generate_with_insn_in_icache) |
| lf_printf(file, " instruction_word insn;\n"); |
| for (field = form->children; |
| field != NULL; |
| field = field->next) { |
| cache_table *cache_rule; |
| int found_rule = 0; |
| for (cache_rule = cache_rules; |
| cache_rule != NULL; |
| cache_rule = cache_rule->next) { |
| if (strcmp(field->name, cache_rule->field_name) == 0) { |
| found_rule = 1; |
| if (cache_rule->derived_name != NULL) |
| lf_printf(file, " %s %s; /* %s */\n", |
| (cache_rule->type_def == NULL |
| ? "unsigned" |
| : cache_rule->type_def), |
| cache_rule->derived_name, |
| cache_rule->field_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"); |
| } |
| 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 idecode_cache;\n"); |
| } |
| lf_printf(file, "\n"); |
| } |
| |
| |
| |
| static void |
| print_icache_function(lf *file, |
| insn *instruction, |
| insn_bits *expanded_bits, |
| opcode_field *opcodes, |
| cache_table *cache_rules) |
| { |
| int indent; |
| |
| /* generate code to enter decoded instruction into the icache */ |
| lf_printf(file, "\n"); |
| lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "EXTERN_ICACHE", "\n"); |
| indent = print_function_name(file, |
| instruction->file_entry->fields[insn_name], |
| expanded_bits, |
| function_name_prefix_icache); |
| lf_indent(file, +indent); |
| lf_printf(file, "(%s)\n", ICACHE_FUNCTION_FORMAL); |
| lf_indent(file, -indent); |
| |
| /* function header */ |
| lf_printf(file, "{\n"); |
| lf_indent(file, +2); |
| |
| print_my_defines(file, expanded_bits, instruction->file_entry); |
| print_itrace(file, instruction->file_entry, 1/*putting-value-in-cache*/); |
| |
| print_idecode_validate(file, instruction, opcodes); |
| |
| lf_printf(file, "\n"); |
| lf_printf(file, "{\n"); |
| lf_indent(file, +2); |
| if ((code & generate_with_semantic_icache)) |
| lf_printf(file, "unsigned_word nia;\n"); |
| print_icache_body(file, |
| instruction, |
| expanded_bits, |
| cache_rules, |
| ((code & generate_with_direct_access) |
| ? define_variables |
| : declare_variables), |
| ((code & generate_with_semantic_icache) |
| ? both_values_and_icache |
| : put_values_in_icache)); |
| |
| lf_printf(file, "\n"); |
| lf_printf(file, "cache_entry->address = cia;\n"); |
| lf_printf(file, "cache_entry->semantic = "); |
| print_function_name(file, |
| instruction->file_entry->fields[insn_name], |
| expanded_bits, |
| function_name_prefix_semantics); |
| lf_printf(file, ";\n"); |
| lf_printf(file, "\n"); |
| |
| if ((code & generate_with_semantic_icache)) { |
| lf_printf(file, "/* semantic routine */\n"); |
| print_semantic_body(file, |
| instruction, |
| expanded_bits, |
| opcodes); |
| lf_printf(file, "return nia;\n"); |
| } |
| |
| if (!(code & generate_with_semantic_icache)) { |
| lf_printf(file, "/* return the function proper */\n"); |
| lf_printf(file, "return "); |
| print_function_name(file, |
| instruction->file_entry->fields[insn_name], |
| expanded_bits, |
| function_name_prefix_semantics); |
| lf_printf(file, ";\n"); |
| } |
| |
| if ((code & generate_with_direct_access)) |
| print_icache_body(file, |
| instruction, |
| expanded_bits, |
| cache_rules, |
| undef_variables, |
| ((code & generate_with_semantic_icache) |
| ? both_values_and_icache |
| : put_values_in_icache)); |
| |
| lf_indent(file, -2); |
| lf_printf(file, "}\n"); |
| lf_indent(file, -2); |
| lf_printf(file, "}\n"); |
| } |
| |
| |
| void |
| print_icache_definition(insn_table *entry, |
| lf *file, |
| void *data, |
| insn *instruction, |
| int depth) |
| { |
| cache_table *cache_rules = (cache_table*)data; |
| if (generate_expanded_instructions) { |
| ASSERT(entry->nr_insn == 1 |
| && entry->opcode == NULL |
| && entry->parent != NULL |
| && entry->parent->opcode != NULL); |
| ASSERT(entry->nr_insn == 1 |
| && entry->opcode == NULL |
| && entry->parent != NULL |
| && entry->parent->opcode != NULL |
| && entry->parent->opcode_rule != NULL); |
| print_icache_function(file, |
| entry->insns, |
| entry->expanded_bits, |
| entry->opcode, |
| cache_rules); |
| } |
| else { |
| print_icache_function(file, |
| instruction, |
| NULL, |
| NULL, |
| cache_rules); |
| } |
| } |
| |
| |
| |
| void |
| print_icache_internal_function_declaration(insn_table *table, |
| lf *file, |
| void *data, |
| table_entry *function) |
| { |
| ASSERT((code & generate_with_icache) != 0); |
| if (it_is("internal", function->fields[insn_flags])) { |
| lf_printf(file, "\n"); |
| lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "PSIM_INLINE_ICACHE", |
| "\n"); |
| print_function_name(file, |
| function->fields[insn_name], |
| NULL, |
| function_name_prefix_icache); |
| lf_printf(file, "\n(%s);\n", ICACHE_FUNCTION_FORMAL); |
| } |
| } |
| |
| |
| void |
| print_icache_internal_function_definition(insn_table *table, |
| lf *file, |
| void *data, |
| table_entry *function) |
| { |
| ASSERT((code & generate_with_icache) != 0); |
| if (it_is("internal", function->fields[insn_flags])) { |
| lf_printf(file, "\n"); |
| lf_print_function_type(file, ICACHE_FUNCTION_TYPE, "PSIM_INLINE_ICACHE", |
| "\n"); |
| print_function_name(file, |
| function->fields[insn_name], |
| NULL, |
| function_name_prefix_icache); |
| lf_printf(file, "\n(%s)\n", ICACHE_FUNCTION_FORMAL); |
| lf_printf(file, "{\n"); |
| lf_indent(file, +2); |
| lf_printf(file, "/* semantic routine */\n"); |
| table_entry_print_cpp_line_nr(file, function); |
| if ((code & generate_with_semantic_icache)) { |
| lf_print__c_code(file, function->annex); |
| 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->fields[insn_name], |
| NULL, |
| function_name_prefix_semantics); |
| lf_printf(file, ";\n"); |
| } |
| |
| lf_print__internal_reference(file); |
| lf_indent(file, -2); |
| lf_printf(file, "}\n"); |
| } |
| } |