|  | /* The IGEN simulator generator for GDB, the GNU Debugger. | 
|  |  | 
|  | Copyright 2002-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by Andrew Cagney. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  |  | 
|  | #include "misc.h" | 
|  | #include "lf.h" | 
|  | #include "table.h" | 
|  | #include "filter.h" | 
|  |  | 
|  | #include "igen.h" | 
|  | #include "ld-insn.h" | 
|  | #include "ld-decode.h" | 
|  | #include "gen.h" | 
|  |  | 
|  | static insn_uint | 
|  | sub_val (insn_uint val, int val_last_pos, int first_pos, int last_pos) | 
|  | { | 
|  | return ((val >> (val_last_pos - last_pos)) | 
|  | & (((insn_uint) 1 << (last_pos - first_pos + 1)) - 1)); | 
|  | } | 
|  |  | 
|  | static void | 
|  | update_depth (lf *file, const gen_entry *entry, int depth, void *data) | 
|  | { | 
|  | int *max_depth = (int *) data; | 
|  | if (*max_depth < depth) | 
|  | *max_depth = depth; | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | gen_entry_depth (const gen_entry *table) | 
|  | { | 
|  | int depth = 0; | 
|  | gen_entry_traverse_tree (NULL, table, 1, NULL,	/*start */ | 
|  | update_depth, NULL,	/*end */ | 
|  | &depth);	/* data */ | 
|  | return depth; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | print_gen_entry_path (const line_ref *line, | 
|  | const gen_entry *table, | 
|  | error_func *print) | 
|  | { | 
|  | if (table->parent == NULL) | 
|  | { | 
|  | if (table->top->model != NULL) | 
|  | print (line, "%s", table->top->model->name); | 
|  | else | 
|  | { | 
|  | /* We don't want to output things, but we want the side-effects they | 
|  | might have (e.g. checking line != NULL).  */ | 
|  | print (line, "%s", ""); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | print_gen_entry_path (line, table->parent, print); | 
|  | print (NULL, ".%d", table->opcode_nr); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_gen_entry_insns (const gen_entry *table, | 
|  | error_func *print, | 
|  | const char *first_message, | 
|  | const char *next_message) | 
|  | { | 
|  | insn_list *i; | 
|  | const char *message; | 
|  | message = first_message; | 
|  | for (i = table->insns; i != NULL; i = i->next) | 
|  | { | 
|  | insn_entry *insn = i->insn; | 
|  | print_gen_entry_path (insn->line, table, print); | 
|  | print (NULL, ": %s.%s %s\n", insn->format_name, insn->name, message); | 
|  | if (next_message != NULL) | 
|  | message = next_message; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* same as strcmp */ | 
|  | static int | 
|  | insn_field_cmp (const insn_word_entry *l, const insn_word_entry *r) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | int bit_nr; | 
|  | if (l == NULL && r == NULL) | 
|  | return 0;		/* all previous fields the same */ | 
|  | if (l == NULL) | 
|  | return -1;		/* left shorter than right */ | 
|  | if (r == NULL) | 
|  | return +1;		/* left longer than right */ | 
|  | for (bit_nr = 0; bit_nr < options.insn_bit_size; bit_nr++) | 
|  | { | 
|  | if (l->bit[bit_nr]->field->type != insn_field_string) | 
|  | continue; | 
|  | if (r->bit[bit_nr]->field->type != insn_field_string) | 
|  | continue; | 
|  | if (l->bit[bit_nr]->field->conditions == NULL) | 
|  | continue; | 
|  | if (r->bit[bit_nr]->field->conditions == NULL) | 
|  | continue; | 
|  | if (0) | 
|  | printf ("%s%s%s VS %s%s%s\n", | 
|  | l->bit[bit_nr]->field->val_string, | 
|  | l->bit[bit_nr]->field->conditions->test == | 
|  | insn_field_cond_eq ? "=" : "!", | 
|  | l->bit[bit_nr]->field->conditions->string, | 
|  | r->bit[bit_nr]->field->val_string, | 
|  | r->bit[bit_nr]->field->conditions->test == | 
|  | insn_field_cond_eq ? "=" : "!", | 
|  | r->bit[bit_nr]->field->conditions->string); | 
|  | if (l->bit[bit_nr]->field->conditions->test == insn_field_cond_eq | 
|  | && r->bit[bit_nr]->field->conditions->test == | 
|  | insn_field_cond_eq) | 
|  | { | 
|  | if (l->bit[bit_nr]->field->conditions->type == | 
|  | insn_field_cond_field | 
|  | && r->bit[bit_nr]->field->conditions->type == | 
|  | insn_field_cond_field) | 
|  | /* somewhat arbitrary */ | 
|  | { | 
|  | int cmp = strcmp (l->bit[bit_nr]->field->conditions->string, | 
|  | r->bit[bit_nr]->field->conditions-> | 
|  | string); | 
|  | if (cmp != 0) | 
|  | return cmp; | 
|  | else | 
|  | continue; | 
|  | } | 
|  | if (l->bit[bit_nr]->field->conditions->type == | 
|  | insn_field_cond_field) | 
|  | return +1; | 
|  | if (r->bit[bit_nr]->field->conditions->type == | 
|  | insn_field_cond_field) | 
|  | return -1; | 
|  | /* The case of both fields having constant values should have | 
|  | already have been handled because such fields are converted | 
|  | into normal constant fields, but we must not make this | 
|  | an assert, as we wouldn't gracefully handle an (invalid) | 
|  | duplicate insn description.  */ | 
|  | continue; | 
|  | } | 
|  | if (l->bit[bit_nr]->field->conditions->test == insn_field_cond_eq) | 
|  | return +1;		/* left = only */ | 
|  | if (r->bit[bit_nr]->field->conditions->test == insn_field_cond_eq) | 
|  | return -1;		/* right = only */ | 
|  | /* FIXME: Need to some what arbitrarily order conditional lists */ | 
|  | continue; | 
|  | } | 
|  | l = l->next; | 
|  | r = r->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* same as strcmp */ | 
|  | static int | 
|  | insn_word_cmp (const insn_word_entry *l, const insn_word_entry *r) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | int bit_nr; | 
|  | if (l == NULL && r == NULL) | 
|  | return 0;		/* all previous fields the same */ | 
|  | if (l == NULL) | 
|  | return -1;		/* left shorter than right */ | 
|  | if (r == NULL) | 
|  | return +1;		/* left longer than right */ | 
|  | for (bit_nr = 0; bit_nr < options.insn_bit_size; bit_nr++) | 
|  | { | 
|  | if (l->bit[bit_nr]->mask < r->bit[bit_nr]->mask) | 
|  | return -1; | 
|  | if (l->bit[bit_nr]->mask > r->bit[bit_nr]->mask) | 
|  | return 1; | 
|  | if (l->bit[bit_nr]->value < r->bit[bit_nr]->value) | 
|  | return -1; | 
|  | if (l->bit[bit_nr]->value > r->bit[bit_nr]->value) | 
|  | return 1; | 
|  | } | 
|  | l = l->next; | 
|  | r = r->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* same as strcmp */ | 
|  | static int | 
|  | opcode_bit_cmp (const opcode_bits *l, const opcode_bits *r) | 
|  | { | 
|  | if (l == NULL && r == NULL) | 
|  | return 0;			/* all previous bits the same */ | 
|  | if (l == NULL) | 
|  | return -1;			/* left shorter than right */ | 
|  | if (r == NULL) | 
|  | return +1;			/* left longer than right */ | 
|  | /* most significant word */ | 
|  | if (l->field->word_nr < r->field->word_nr) | 
|  | return +1;			/* left has more significant word */ | 
|  | if (l->field->word_nr > r->field->word_nr) | 
|  | return -1;			/* right has more significant word */ | 
|  | /* most significant bit? */ | 
|  | if (l->first < r->first) | 
|  | return +1;			/* left as more significant bit */ | 
|  | if (l->first > r->first) | 
|  | return -1;			/* right as more significant bit */ | 
|  | /* nr bits? */ | 
|  | if (l->last < r->last) | 
|  | return +1;			/* left as less bits */ | 
|  | if (l->last > r->last) | 
|  | return -1;			/* right as less bits */ | 
|  | /* value? */ | 
|  | if (l->value < r->value) | 
|  | return -1; | 
|  | if (l->value > r->value) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* same as strcmp */ | 
|  | static int | 
|  | opcode_bits_cmp (const opcode_bits *l, const opcode_bits *r) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | int cmp; | 
|  | if (l == NULL && r == NULL) | 
|  | return 0;		/* all previous bits the same */ | 
|  | cmp = opcode_bit_cmp (l, r); | 
|  | if (cmp != 0) | 
|  | return cmp; | 
|  | l = l->next; | 
|  | r = r->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* same as strcmp */ | 
|  | static opcode_bits * | 
|  | new_opcode_bits (opcode_bits *old_bits, | 
|  | int value, | 
|  | int first, | 
|  | int last, insn_field_entry *field, opcode_field *opcode) | 
|  | { | 
|  | opcode_bits *new_bits = ZALLOC (opcode_bits); | 
|  | new_bits->field = field; | 
|  | new_bits->value = value; | 
|  | new_bits->first = first; | 
|  | new_bits->last = last; | 
|  | new_bits->opcode = opcode; | 
|  |  | 
|  | if (old_bits != NULL) | 
|  | { | 
|  | opcode_bits *new_list; | 
|  | opcode_bits **last = &new_list; | 
|  | new_list = new_opcode_bits (old_bits->next, | 
|  | old_bits->value, | 
|  | old_bits->first, | 
|  | old_bits->last, | 
|  | old_bits->field, old_bits->opcode); | 
|  | while (*last != NULL) | 
|  | { | 
|  | int cmp = opcode_bit_cmp (new_bits, *last); | 
|  | if (cmp < 0)		/* new < new_list */ | 
|  | { | 
|  | break; | 
|  | } | 
|  | if (cmp == 0) | 
|  | { | 
|  | ERROR ("Duplicated insn bits in list"); | 
|  | } | 
|  | last = &(*last)->next; | 
|  | } | 
|  | new_bits->next = *last; | 
|  | *last = new_bits; | 
|  | return new_list; | 
|  | } | 
|  | else | 
|  | { | 
|  | return new_bits; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Same as strcmp().  */ | 
|  | static int | 
|  | name_cmp (const char *l, const char *r) | 
|  | { | 
|  | if (l == NULL && r == NULL) | 
|  | return 0; | 
|  | if (l != NULL && r == NULL) | 
|  | return -1; | 
|  | if (l == NULL && r != NULL) | 
|  | return +1; | 
|  | return strcmp (l, r); | 
|  | } | 
|  |  | 
|  |  | 
|  | typedef enum | 
|  | { | 
|  | merge_duplicate_insns, | 
|  | report_duplicate_insns, | 
|  | } | 
|  | duplicate_insn_actions; | 
|  |  | 
|  | static insn_list * | 
|  | insn_list_insert (insn_list **cur_insn_ptr, | 
|  | int *nr_insns, | 
|  | insn_entry * insn, | 
|  | opcode_bits *expanded_bits, | 
|  | opcode_field *opcodes, | 
|  | int nr_prefetched_words, | 
|  | duplicate_insn_actions duplicate_action) | 
|  | { | 
|  | /* insert it according to the order of the fields & bits */ | 
|  | for (; (*cur_insn_ptr) != NULL; cur_insn_ptr = &(*cur_insn_ptr)->next) | 
|  | { | 
|  | int cmp; | 
|  |  | 
|  | /* key#1 sort according to the constant fields of each instruction */ | 
|  | cmp = insn_word_cmp (insn->words, (*cur_insn_ptr)->insn->words); | 
|  | if (cmp < 0) | 
|  | break; | 
|  | else if (cmp > 0) | 
|  | continue; | 
|  |  | 
|  | /* key#2 sort according to the expanded bits of each instruction */ | 
|  | cmp = opcode_bits_cmp (expanded_bits, (*cur_insn_ptr)->expanded_bits); | 
|  | if (cmp < 0) | 
|  | break; | 
|  | else if (cmp > 0) | 
|  | continue; | 
|  |  | 
|  | /* key#3 sort according to the non-constant fields of each instruction */ | 
|  | cmp = insn_field_cmp (insn->words, (*cur_insn_ptr)->insn->words); | 
|  | if (cmp < 0) | 
|  | break; | 
|  | else if (cmp > 0) | 
|  | continue; | 
|  |  | 
|  | if (duplicate_action == merge_duplicate_insns) | 
|  | { | 
|  | /* key#4: If we're going to merge duplicates, also sort | 
|  | according to the format_name.  Two instructions with | 
|  | identical decode patterns, but different names, are | 
|  | considered different when merging.  Duplicates are only | 
|  | important when creating a decode table (implied by | 
|  | report_duplicate_insns) as such a table only has the | 
|  | instruction's bit code as a way of differentiating | 
|  | between instructions.  */ | 
|  | int cmp = name_cmp (insn->format_name, | 
|  | (*cur_insn_ptr)->insn->format_name); | 
|  | if (cmp < 0) | 
|  | break; | 
|  | else if (cmp > 0) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (duplicate_action == merge_duplicate_insns) | 
|  | { | 
|  | /* key#5: If we're going to merge duplicates, also sort | 
|  | according to the name.  See comment above for | 
|  | format_name.  */ | 
|  | int cmp = name_cmp (insn->name, (*cur_insn_ptr)->insn->name); | 
|  | if (cmp < 0) | 
|  | break; | 
|  | else if (cmp > 0) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* duplicate keys, report problem */ | 
|  | switch (duplicate_action) | 
|  | { | 
|  | case report_duplicate_insns: | 
|  | /* It would appear that we have two instructions with the | 
|  | same constant field values across all words and bits. | 
|  | This error can also occure when insn_field_cmp() is | 
|  | failing to differentiate between two instructions that | 
|  | differ only in their conditional fields. */ | 
|  | warning (insn->line, | 
|  | "Two instructions with identical constant fields\n"); | 
|  | error ((*cur_insn_ptr)->insn->line, | 
|  | "Location of duplicate instruction\n"); | 
|  | case merge_duplicate_insns: | 
|  | /* Add the opcode path to the instructions list */ | 
|  | if (options.trace.insn_insertion) | 
|  | { | 
|  | notify ((*cur_insn_ptr)->insn->line, | 
|  | "%s.%s: insert merge %s.%s\n", | 
|  | (*cur_insn_ptr)->insn->format_name, | 
|  | (*cur_insn_ptr)->insn->name, | 
|  | insn->format_name, | 
|  | insn->name); | 
|  | } | 
|  | if (opcodes != NULL) | 
|  | { | 
|  | insn_opcodes **last = &(*cur_insn_ptr)->opcodes; | 
|  | while (*last != NULL) | 
|  | { | 
|  | last = &(*last)->next; | 
|  | } | 
|  | (*last) = ZALLOC (insn_opcodes); | 
|  | (*last)->opcode = opcodes; | 
|  | } | 
|  | /* Use the larger nr_prefetched_words */ | 
|  | if ((*cur_insn_ptr)->nr_prefetched_words < nr_prefetched_words) | 
|  | (*cur_insn_ptr)->nr_prefetched_words = nr_prefetched_words; | 
|  | return (*cur_insn_ptr); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | /* create a new list entry and insert it */ | 
|  | { | 
|  | insn_list *new_insn = ZALLOC (insn_list); | 
|  | if (options.trace.insn_insertion) | 
|  | { | 
|  | notify (insn->line, | 
|  | "%s.%s: insert new\n", | 
|  | insn->format_name, | 
|  | insn->name); | 
|  | } | 
|  | new_insn->insn = insn; | 
|  | new_insn->expanded_bits = expanded_bits; | 
|  | new_insn->next = (*cur_insn_ptr); | 
|  | new_insn->nr_prefetched_words = nr_prefetched_words; | 
|  | if (opcodes != NULL) | 
|  | { | 
|  | new_insn->opcodes = ZALLOC (insn_opcodes); | 
|  | new_insn->opcodes->opcode = opcodes; | 
|  | } | 
|  | (*cur_insn_ptr) = new_insn; | 
|  | } | 
|  |  | 
|  | *nr_insns += 1; | 
|  |  | 
|  | return (*cur_insn_ptr); | 
|  | } | 
|  |  | 
|  |  | 
|  | extern void | 
|  | gen_entry_traverse_tree (lf *file, | 
|  | const gen_entry *table, | 
|  | int depth, | 
|  | gen_entry_handler * start, | 
|  | gen_entry_handler * leaf, | 
|  | gen_entry_handler * end, void *data) | 
|  | { | 
|  | gen_entry *entry; | 
|  |  | 
|  | ASSERT (table !=NULL); | 
|  | ASSERT (table->opcode != NULL); | 
|  | ASSERT (table->nr_entries > 0); | 
|  | ASSERT (table->entries != 0); | 
|  |  | 
|  | /* prefix */ | 
|  | if (start != NULL && depth >= 0) | 
|  | { | 
|  | start (file, table, depth, data); | 
|  | } | 
|  | /* infix leaves */ | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | if (entry->entries != NULL && depth != 0) | 
|  | { | 
|  | gen_entry_traverse_tree (file, entry, depth + 1, | 
|  | start, leaf, end, data); | 
|  | } | 
|  | else if (depth >= 0) | 
|  | { | 
|  | if (leaf != NULL) | 
|  | { | 
|  | leaf (file, entry, depth, data); | 
|  | } | 
|  | } | 
|  | } | 
|  | /* postfix */ | 
|  | if (end != NULL && depth >= 0) | 
|  | { | 
|  | end (file, table, depth, data); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* create a list element containing a single gen_table entry */ | 
|  |  | 
|  | static gen_list * | 
|  | make_table (const insn_table *isa, | 
|  | const decode_table *rules, | 
|  | const model_entry *model) | 
|  | { | 
|  | insn_entry *insn; | 
|  | gen_list *entry = ZALLOC (gen_list); | 
|  | entry->table = ZALLOC (gen_entry); | 
|  | entry->table->top = entry; | 
|  | entry->model = model; | 
|  | entry->isa = isa; | 
|  | for (insn = isa->insns; insn != NULL; insn = insn->next) | 
|  | { | 
|  | if (model == NULL | 
|  | || insn->processors == NULL | 
|  | || filter_is_member (insn->processors, model->name)) | 
|  | { | 
|  | insn_list_insert (&entry->table->insns, &entry->table->nr_insns, insn, NULL,	/* expanded_bits - none yet */ | 
|  | NULL,	/* opcodes - none yet */ | 
|  | 0,	/* nr_prefetched_words - none yet */ | 
|  | report_duplicate_insns); | 
|  | } | 
|  | } | 
|  | entry->table->opcode_rule = rules; | 
|  | return entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | gen_table * | 
|  | make_gen_tables (const insn_table *isa, const decode_table *rules) | 
|  | { | 
|  | gen_table *gen = ZALLOC (gen_table); | 
|  | gen->isa = isa; | 
|  | gen->rules = rules; | 
|  | if (options.gen.multi_sim) | 
|  | { | 
|  | gen_list **last = &gen->tables; | 
|  | model_entry *model; | 
|  | filter *processors; | 
|  | if (options.model_filter != NULL) | 
|  | processors = options.model_filter; | 
|  | else | 
|  | processors = isa->model->processors; | 
|  | for (model = isa->model->models; model != NULL; model = model->next) | 
|  | { | 
|  | if (filter_is_member (processors, model->name)) | 
|  | { | 
|  | *last = make_table (isa, rules, model); | 
|  | last = &(*last)->next; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | gen->tables = make_table (isa, rules, NULL); | 
|  | } | 
|  | return gen; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************/ | 
|  |  | 
|  | /* Is the bit, according to the decode rule, identical across all the | 
|  | instructions? */ | 
|  | static int | 
|  | insns_bit_useless (const insn_list *insns, const decode_table *rule, int bit_nr) | 
|  | { | 
|  | const insn_list *entry; | 
|  | int value = -1; | 
|  | int is_useless = 1;		/* cleared if something actually found */ | 
|  |  | 
|  | /* check the instructions for some constant value in at least one of | 
|  | the bit fields */ | 
|  | for (entry = insns; entry != NULL; entry = entry->next) | 
|  | { | 
|  | insn_word_entry *word = entry->insn->word[rule->word_nr]; | 
|  | insn_bit_entry *bit = word->bit[bit_nr]; | 
|  | switch (bit->field->type) | 
|  | { | 
|  | case insn_field_invalid: | 
|  | ASSERT (0); | 
|  | break; | 
|  | case insn_field_wild: | 
|  | case insn_field_reserved: | 
|  | /* neither useless or useful - ignore */ | 
|  | break; | 
|  | case insn_field_int: | 
|  | switch (rule->search) | 
|  | { | 
|  | case decode_find_strings: | 
|  | /* an integer isn't a string */ | 
|  | return 1; | 
|  | case decode_find_constants: | 
|  | case decode_find_mixed: | 
|  | /* an integer is useful if its value isn't the same | 
|  | between all instructions.  The first time through the | 
|  | value is saved, the second time through (if the | 
|  | values differ) it is marked as useful. */ | 
|  | if (value < 0) | 
|  | value = bit->value; | 
|  | else if (value != bit->value) | 
|  | is_useless = 0; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case insn_field_string: | 
|  | switch (rule->search) | 
|  | { | 
|  | case decode_find_strings: | 
|  | /* at least one string, keep checking */ | 
|  | is_useless = 0; | 
|  | break; | 
|  | case decode_find_constants: | 
|  | case decode_find_mixed: | 
|  | if (filter_is_member (rule->constant_field_names, | 
|  | bit->field->val_string)) | 
|  | /* a string field forced to constant? */ | 
|  | is_useless = 0; | 
|  | else if (bit->field->conditions != NULL | 
|  | && bit->field->conditions->test == insn_field_cond_eq | 
|  | && bit->field->conditions->type == insn_field_cond_value) | 
|  | { | 
|  | int shift = bit->field->last - bit_nr; | 
|  | int bitvalue = (bit->field->conditions->value >> shift) & 1; | 
|  |  | 
|  | if (value < 0) | 
|  | value = bitvalue; | 
|  | else if (value != bitvalue) | 
|  | is_useless = 0; | 
|  | } | 
|  | else if (rule->search == decode_find_constants) | 
|  | /* the string field isn't constant */ | 
|  | return 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Given only one constant value has been found, check through all | 
|  | the instructions to see if at least one conditional makes it | 
|  | useful */ | 
|  | if (value >= 0 && is_useless) | 
|  | { | 
|  | for (entry = insns; entry != NULL; entry = entry->next) | 
|  | { | 
|  | insn_word_entry *word = entry->insn->word[rule->word_nr]; | 
|  | insn_bit_entry *bit = word->bit[bit_nr]; | 
|  | switch (bit->field->type) | 
|  | { | 
|  | case insn_field_invalid: | 
|  | ASSERT (0); | 
|  | break; | 
|  | case insn_field_wild: | 
|  | case insn_field_reserved: | 
|  | case insn_field_int: | 
|  | /* already processed */ | 
|  | break; | 
|  | case insn_field_string: | 
|  | switch (rule->search) | 
|  | { | 
|  | case decode_find_strings: | 
|  | case decode_find_constants: | 
|  | /* already processed */ | 
|  | break; | 
|  | case decode_find_mixed: | 
|  | /* string field with conditions.  If this condition | 
|  | eliminates the value then the compare is useful */ | 
|  | if (bit->field->conditions != NULL) | 
|  | { | 
|  | insn_field_cond *condition; | 
|  | int shift = bit->field->last - bit_nr; | 
|  | for (condition = bit->field->conditions; | 
|  | condition != NULL; condition = condition->next) | 
|  | { | 
|  | switch (condition->type) | 
|  | { | 
|  | case insn_field_cond_value: | 
|  | switch (condition->test) | 
|  | { | 
|  | case insn_field_cond_ne: | 
|  | if (((condition->value >> shift) & 1) | 
|  | == (unsigned) value) | 
|  | /* conditional field excludes the | 
|  | current value */ | 
|  | is_useless = 0; | 
|  | break; | 
|  | case insn_field_cond_eq: | 
|  | if (((condition->value >> shift) & 1) | 
|  | != (unsigned) value) | 
|  | /* conditional field requires the | 
|  | current value */ | 
|  | is_useless = 0; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case insn_field_cond_field: | 
|  | /* are these handled separatly? */ | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return is_useless; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* go through a gen-table's list of instruction formats looking for a | 
|  | range of bits that meet the decode table RULEs requirements */ | 
|  |  | 
|  | static opcode_field * | 
|  | gen_entry_find_opcode_field (insn_list *insns, | 
|  | const decode_table *rule, int string_only) | 
|  | { | 
|  | opcode_field curr_opcode; | 
|  | ASSERT (rule != NULL); | 
|  |  | 
|  | memset (&curr_opcode, 0, sizeof (curr_opcode)); | 
|  | curr_opcode.word_nr = rule->word_nr; | 
|  | curr_opcode.first = rule->first; | 
|  | curr_opcode.last = rule->last; | 
|  |  | 
|  | /* Try to reduce the size of first..last in accordance with the | 
|  | decode rules */ | 
|  |  | 
|  | while (curr_opcode.first <= rule->last) | 
|  | { | 
|  | if (insns_bit_useless (insns, rule, curr_opcode.first)) | 
|  | curr_opcode.first++; | 
|  | else | 
|  | break; | 
|  | } | 
|  | while (curr_opcode.last >= rule->first) | 
|  | { | 
|  | if (insns_bit_useless (insns, rule, curr_opcode.last)) | 
|  | curr_opcode.last--; | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* did the final opcode field end up being empty? */ | 
|  | if (curr_opcode.first > curr_opcode.last) | 
|  | { | 
|  | return NULL; | 
|  | } | 
|  | ASSERT (curr_opcode.last >= rule->first); | 
|  | ASSERT (curr_opcode.first <= rule->last); | 
|  | ASSERT (curr_opcode.first <= curr_opcode.last); | 
|  |  | 
|  | /* Ensure that, for the non string only case, the opcode includes | 
|  | the range forced_first .. forced_last */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* For the string only case, force just the lower bound (so that the | 
|  | shift can be eliminated) */ | 
|  | if (string_only && rule->force_last == options.insn_bit_size - 1) | 
|  | { | 
|  | curr_opcode.last = options.insn_bit_size - 1; | 
|  | } | 
|  |  | 
|  | /* handle any special cases */ | 
|  | switch (rule->type) | 
|  | { | 
|  | case normal_decode_rule: | 
|  | /* let the above apply */ | 
|  | curr_opcode.nr_opcodes = | 
|  | (1 << (curr_opcode.last - curr_opcode.first + 1)); | 
|  | break; | 
|  | case boolean_rule: | 
|  | curr_opcode.is_boolean = 1; | 
|  | curr_opcode.boolean_constant = rule->constant; | 
|  | curr_opcode.nr_opcodes = 2; | 
|  | break; | 
|  | } | 
|  |  | 
|  | { | 
|  | opcode_field *new_field = ZALLOC (opcode_field); | 
|  | memcpy (new_field, &curr_opcode, sizeof (opcode_field)); | 
|  | return new_field; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | gen_entry_insert_insn (gen_entry *table, | 
|  | insn_entry * old_insn, | 
|  | int new_word_nr, | 
|  | int new_nr_prefetched_words, | 
|  | int new_opcode_nr, opcode_bits *new_bits) | 
|  | { | 
|  | gen_entry **entry = &table->entries; | 
|  |  | 
|  | /* find the new table for this entry */ | 
|  | while ((*entry) != NULL && (*entry)->opcode_nr < new_opcode_nr) | 
|  | { | 
|  | entry = &(*entry)->sibling; | 
|  | } | 
|  |  | 
|  | if ((*entry) == NULL || (*entry)->opcode_nr != new_opcode_nr) | 
|  | { | 
|  | /* insert the missing entry */ | 
|  | gen_entry *new_entry = ZALLOC (gen_entry); | 
|  | new_entry->sibling = (*entry); | 
|  | (*entry) = new_entry; | 
|  | table->nr_entries++; | 
|  | /* fill it in */ | 
|  | new_entry->top = table->top; | 
|  | new_entry->opcode_nr = new_opcode_nr; | 
|  | new_entry->word_nr = new_word_nr; | 
|  | new_entry->expanded_bits = new_bits; | 
|  | new_entry->opcode_rule = table->opcode_rule->next; | 
|  | new_entry->parent = table; | 
|  | new_entry->nr_prefetched_words = new_nr_prefetched_words; | 
|  | } | 
|  | /* ASSERT new_bits == cur_entry bits */ | 
|  | ASSERT ((*entry) != NULL && (*entry)->opcode_nr == new_opcode_nr); | 
|  | insn_list_insert (&(*entry)->insns, &(*entry)->nr_insns, old_insn, NULL,	/* expanded_bits - only in final list */ | 
|  | NULL,	/* opcodes - only in final list */ | 
|  | new_nr_prefetched_words,	/* for this table */ | 
|  | report_duplicate_insns); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | gen_entry_expand_opcode (gen_entry *table, | 
|  | insn_entry * instruction, | 
|  | int bit_nr, int opcode_nr, opcode_bits *bits) | 
|  | { | 
|  | if (bit_nr > table->opcode->last) | 
|  | { | 
|  | /* Only include the hardwired bit information with an entry IF | 
|  | that entry (and hence its functions) are being duplicated.  */ | 
|  | if (options.trace.insn_expansion) | 
|  | { | 
|  | print_gen_entry_path (table->opcode_rule->line, table, notify); | 
|  | notify (NULL, ": insert %d - %s.%s%s\n", | 
|  | opcode_nr, | 
|  | instruction->format_name, | 
|  | instruction->name, | 
|  | (table->opcode_rule-> | 
|  | with_duplicates ? " (duplicated)" : "")); | 
|  | } | 
|  | if (table->opcode_rule->with_duplicates) | 
|  | { | 
|  | gen_entry_insert_insn (table, instruction, | 
|  | table->opcode->word_nr, | 
|  | table->nr_prefetched_words, opcode_nr, bits); | 
|  | } | 
|  | else | 
|  | { | 
|  | gen_entry_insert_insn (table, instruction, | 
|  | table->opcode->word_nr, | 
|  | table->nr_prefetched_words, opcode_nr, NULL); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | insn_word_entry *word = instruction->word[table->opcode->word_nr]; | 
|  | insn_field_entry *field = word->bit[bit_nr]->field; | 
|  | 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; | 
|  | switch (field->type) | 
|  | { | 
|  | case insn_field_int: | 
|  | { | 
|  | int val; | 
|  | val = sub_val (field->val_int, field->last, first_pos, last_pos); | 
|  | gen_entry_expand_opcode (table, instruction, | 
|  | last_pos + 1, | 
|  | ((opcode_nr << width) | val), bits); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | { | 
|  | if (field->type == insn_field_reserved) | 
|  | gen_entry_expand_opcode (table, instruction, | 
|  | last_pos + 1, | 
|  | ((opcode_nr << width)), bits); | 
|  | else | 
|  | { | 
|  | int val; | 
|  | int last_val = (table->opcode->is_boolean ? 2 : (1 << width)); | 
|  | for (val = 0; val < last_val; val++) | 
|  | { | 
|  | /* check to see if the value has been precluded | 
|  | (by a conditional) in some way */ | 
|  | int is_precluded; | 
|  | insn_field_cond *condition; | 
|  | for (condition = field->conditions, is_precluded = 0; | 
|  | condition != NULL && !is_precluded; | 
|  | condition = condition->next) | 
|  | { | 
|  | switch (condition->type) | 
|  | { | 
|  | case insn_field_cond_value: | 
|  | { | 
|  | int value = | 
|  | sub_val (condition->value, field->last, | 
|  | first_pos, last_pos); | 
|  | switch (condition->test) | 
|  | { | 
|  | case insn_field_cond_ne: | 
|  | if (value == val) | 
|  | is_precluded = 1; | 
|  | break; | 
|  | case insn_field_cond_eq: | 
|  | if (value != val) | 
|  | is_precluded = 1; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | case insn_field_cond_field: | 
|  | { | 
|  | int value = -1; | 
|  | opcode_bits *bit; | 
|  | gen_entry *t = NULL; | 
|  | /* Try to find a value for the | 
|  | conditional by looking back through | 
|  | the previously defined bits for one | 
|  | that covers the designated | 
|  | conditional field */ | 
|  | for (bit = bits; bit != NULL; bit = bit->next) | 
|  | { | 
|  | if (bit->field->word_nr == | 
|  | condition->field->word_nr | 
|  | && bit->first <= condition->field->first | 
|  | && bit->last >= condition->field->last) | 
|  | { | 
|  | /* the bit field fully specified | 
|  | the conditional field's value */ | 
|  | value = sub_val (bit->value, bit->last, | 
|  | condition->field-> | 
|  | first, | 
|  | condition->field-> | 
|  | last); | 
|  | } | 
|  | } | 
|  | /* Try to find a value by looking | 
|  | through this and previous tables */ | 
|  | if (bit == NULL) | 
|  | { | 
|  | for (t = table; | 
|  | t->parent != NULL; t = t->parent) | 
|  | { | 
|  | if (t->parent->opcode->word_nr == | 
|  | condition->field->word_nr | 
|  | && t->parent->opcode->first <= | 
|  | condition->field->first | 
|  | && t->parent->opcode->last >= | 
|  | condition->field->last) | 
|  | { | 
|  | /* the table entry fully | 
|  | specified the condition | 
|  | field's value */ | 
|  | /* extract the field's value | 
|  | from the opcode */ | 
|  | value = | 
|  | sub_val (t->opcode_nr, | 
|  | t->parent->opcode->last, | 
|  | condition->field->first, | 
|  | condition->field->last); | 
|  | /* this is a requirement of | 
|  | a conditonal field | 
|  | referring to another field */ | 
|  | ASSERT ((condition->field->first - | 
|  | condition->field->last) == | 
|  | (first_pos - last_pos)); | 
|  | printf | 
|  | ("value=%d, opcode_nr=%d, last=%d, [%d..%d]\n", | 
|  | value, t->opcode_nr, | 
|  | t->parent->opcode->last, | 
|  | condition->field->first, | 
|  | condition->field->last); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (bit == NULL && t == NULL) | 
|  | error (instruction->line, | 
|  | "Conditional `%s' of field `%s' isn't expanded\n", | 
|  | condition->string, field->val_string); | 
|  | switch (condition->test) | 
|  | { | 
|  | case insn_field_cond_ne: | 
|  | if (value == val) | 
|  | is_precluded = 1; | 
|  | break; | 
|  | case insn_field_cond_eq: | 
|  | if (value != val) | 
|  | is_precluded = 1; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!is_precluded) | 
|  | { | 
|  | /* Only add additional hardwired bit | 
|  | information if the entry is not going to | 
|  | later be combined */ | 
|  | if (table->opcode_rule->with_combine) | 
|  | { | 
|  | gen_entry_expand_opcode (table, instruction, | 
|  | last_pos + 1, | 
|  | ((opcode_nr << width) | | 
|  | val), bits); | 
|  | } | 
|  | else | 
|  | { | 
|  | opcode_bits *new_bits = | 
|  | new_opcode_bits (bits, val, | 
|  | first_pos, last_pos, | 
|  | field, | 
|  | table->opcode); | 
|  | gen_entry_expand_opcode (table, instruction, | 
|  | last_pos + 1, | 
|  | ((opcode_nr << width) | | 
|  | val), new_bits); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | gen_entry_insert_expanding (gen_entry *table, insn_entry * instruction) | 
|  | { | 
|  | gen_entry_expand_opcode (table, | 
|  | instruction, | 
|  | table->opcode->first, 0, table->expanded_bits); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | insns_match_format_names (insn_list *insns, filter *format_names) | 
|  | { | 
|  | if (format_names != NULL) | 
|  | { | 
|  | insn_list *i; | 
|  | for (i = insns; i != NULL; i = i->next) | 
|  | { | 
|  | if (i->insn->format_name != NULL | 
|  | && !filter_is_member (format_names, i->insn->format_name)) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | table_matches_path (gen_entry *table, decode_path_list *paths) | 
|  | { | 
|  | if (paths == NULL) | 
|  | return 1; | 
|  | while (paths != NULL) | 
|  | { | 
|  | gen_entry *entry = table; | 
|  | decode_path *path = paths->path; | 
|  | while (1) | 
|  | { | 
|  | if (entry == NULL && path == NULL) | 
|  | return 1; | 
|  | if (entry == NULL || path == NULL) | 
|  | break; | 
|  | if (entry->opcode_nr != path->opcode_nr) | 
|  | break; | 
|  | entry = entry->parent; | 
|  | path = path->parent; | 
|  | } | 
|  | paths = paths->next; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | insns_match_conditions (insn_list *insns, decode_cond *conditions) | 
|  | { | 
|  | if (conditions != NULL) | 
|  | { | 
|  | insn_list *i; | 
|  | for (i = insns; i != NULL; i = i->next) | 
|  | { | 
|  | decode_cond *cond; | 
|  | for (cond = conditions; cond != NULL; cond = cond->next) | 
|  | { | 
|  | int bit_nr; | 
|  | if (i->insn->nr_words <= cond->word_nr) | 
|  | return 0; | 
|  | for (bit_nr = 0; bit_nr < options.insn_bit_size; bit_nr++) | 
|  | { | 
|  | if (!cond->mask[bit_nr]) | 
|  | continue; | 
|  | if (!i->insn->word[cond->word_nr]->bit[bit_nr]->mask) | 
|  | return 0; | 
|  | if ((i->insn->word[cond->word_nr]->bit[bit_nr]->value | 
|  | == cond->value[bit_nr]) == !cond->is_equal) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | insns_match_nr_words (const insn_list *insns, int nr_words) | 
|  | { | 
|  | const insn_list *i; | 
|  | for (i = insns; i != NULL; i = i->next) | 
|  | { | 
|  | if (i->insn->nr_words < nr_words) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | insn_list_cmp (const insn_list *l, const insn_list *r) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | const insn_entry *insn; | 
|  | if (l == NULL && r == NULL) | 
|  | return 0; | 
|  | if (l == NULL) | 
|  | return -1; | 
|  | if (r == NULL) | 
|  | return 1; | 
|  | if (l->insn != r->insn) | 
|  | return -1;		/* somewhat arbitrary at present */ | 
|  | /* skip this insn */ | 
|  | insn = l->insn; | 
|  | while (l != NULL && l->insn == insn) | 
|  | l = l->next; | 
|  | while (r != NULL && r->insn == insn) | 
|  | r = r->next; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | static void | 
|  | gen_entry_expand_insns (gen_entry *table) | 
|  | { | 
|  | const decode_table *opcode_rule; | 
|  |  | 
|  | ASSERT (table->nr_insns >= 1); | 
|  |  | 
|  | /* determine a valid opcode */ | 
|  | for (opcode_rule = table->opcode_rule; | 
|  | opcode_rule != NULL; opcode_rule = opcode_rule->next) | 
|  | { | 
|  | char *discard_reason; | 
|  | if (table->top->model != NULL | 
|  | && opcode_rule->model_names != NULL | 
|  | && !filter_is_member (opcode_rule->model_names, | 
|  | table->top->model->name)) | 
|  | { | 
|  | /* the rule isn't applicable to this processor */ | 
|  | discard_reason = "wrong model"; | 
|  | } | 
|  | else if (table->nr_insns == 1 && opcode_rule->conditions == NULL) | 
|  | { | 
|  | /* for safety, require a pre-codition when attempting to | 
|  | apply a rule to a single instruction */ | 
|  | discard_reason = "need pre-condition when nr-insn == 1"; | 
|  | } | 
|  | else if (table->nr_insns == 1 && !opcode_rule->with_duplicates) | 
|  | { | 
|  | /* Little point in expanding a single instruction when we're | 
|  | not duplicating the semantic functions that this table | 
|  | calls */ | 
|  | discard_reason = "need duplication with nr-insns == 1"; | 
|  | } | 
|  | else | 
|  | if (!insns_match_format_names | 
|  | (table->insns, opcode_rule->format_names)) | 
|  | { | 
|  | discard_reason = "wrong format name"; | 
|  | } | 
|  | else if (!insns_match_nr_words (table->insns, opcode_rule->word_nr + 1)) | 
|  | { | 
|  | discard_reason = "wrong nr words"; | 
|  | } | 
|  | else if (!table_matches_path (table, opcode_rule->paths)) | 
|  | { | 
|  | discard_reason = "path failed"; | 
|  | } | 
|  | else | 
|  | if (!insns_match_conditions (table->insns, opcode_rule->conditions)) | 
|  | { | 
|  | discard_reason = "condition failed"; | 
|  | } | 
|  | else | 
|  | { | 
|  | discard_reason = "no opcode field"; | 
|  | table->opcode = gen_entry_find_opcode_field (table->insns, | 
|  | opcode_rule, | 
|  | table->nr_insns == 1	/*string-only */ | 
|  | ); | 
|  | if (table->opcode != NULL) | 
|  | { | 
|  | table->opcode_rule = opcode_rule; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options.trace.rule_rejection) | 
|  | { | 
|  | print_gen_entry_path (opcode_rule->line, table, notify); | 
|  | notify (NULL, ": rule discarded - %s\n", discard_reason); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* did we find anything */ | 
|  | if (opcode_rule == NULL) | 
|  | { | 
|  | /* the decode table failed, this set of instructions haven't | 
|  | been uniquely identified */ | 
|  | if (table->nr_insns > 1) | 
|  | { | 
|  | print_gen_entry_insns (table, warning, | 
|  | "was not uniquely decoded", | 
|  | "decodes to the same entry"); | 
|  | error (NULL, "unrecoverable\n"); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Determine the number of words that must have been prefetched for | 
|  | this table to function */ | 
|  | if (table->parent == NULL) | 
|  | table->nr_prefetched_words = table->opcode_rule->word_nr + 1; | 
|  | else if (table->opcode_rule->word_nr + 1 > | 
|  | table->parent->nr_prefetched_words) | 
|  | table->nr_prefetched_words = table->opcode_rule->word_nr + 1; | 
|  | else | 
|  | table->nr_prefetched_words = table->parent->nr_prefetched_words; | 
|  |  | 
|  | /* back link what we found to its parent */ | 
|  | if (table->parent != NULL) | 
|  | { | 
|  | ASSERT (table->parent->opcode != NULL); | 
|  | table->opcode->parent = table->parent->opcode; | 
|  | } | 
|  |  | 
|  | /* report the rule being used to expand the instructions */ | 
|  | if (options.trace.rule_selection) | 
|  | { | 
|  | print_gen_entry_path (table->opcode_rule->line, table, notify); | 
|  | notify (NULL, | 
|  | ": decode - word %d, bits [%d..%d] in [%d..%d], opcodes %d, entries %d\n", | 
|  | table->opcode->word_nr, | 
|  | i2target (options.hi_bit_nr, table->opcode->first), | 
|  | i2target (options.hi_bit_nr, table->opcode->last), | 
|  | i2target (options.hi_bit_nr, table->opcode_rule->first), | 
|  | i2target (options.hi_bit_nr, table->opcode_rule->last), | 
|  | table->opcode->nr_opcodes, table->nr_entries); | 
|  | } | 
|  |  | 
|  | /* expand the raw instructions according to the opcode */ | 
|  | { | 
|  | insn_list *entry; | 
|  | for (entry = table->insns; entry != NULL; entry = entry->next) | 
|  | { | 
|  | if (options.trace.insn_expansion) | 
|  | { | 
|  | print_gen_entry_path (table->opcode_rule->line, table, notify); | 
|  | notify (NULL, ": expand - %s.%s\n", | 
|  | entry->insn->format_name, entry->insn->name); | 
|  | } | 
|  | gen_entry_insert_expanding (table, entry->insn); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* dump the results */ | 
|  | if (options.trace.entries) | 
|  | { | 
|  | gen_entry *entry; | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | insn_list *l; | 
|  | print_gen_entry_path (table->opcode_rule->line, entry, notify); | 
|  | notify (NULL, ": %d - entries %d -", | 
|  | entry->opcode_nr, entry->nr_insns); | 
|  | for (l = entry->insns; l != NULL; l = l->next) | 
|  | notify (NULL, " %s.%s", l->insn->format_name, l->insn->name); | 
|  | notify (NULL, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* perform a combine pass if needed */ | 
|  | if (table->opcode_rule->with_combine) | 
|  | { | 
|  | gen_entry *entry; | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | if (entry->combined_parent == NULL) | 
|  | { | 
|  | gen_entry **last = &entry->combined_next; | 
|  | gen_entry *alt; | 
|  | for (alt = entry->sibling; alt != NULL; alt = alt->sibling) | 
|  | { | 
|  | if (alt->combined_parent == NULL | 
|  | && insn_list_cmp (entry->insns, alt->insns) == 0) | 
|  | { | 
|  | alt->combined_parent = entry; | 
|  | *last = alt; | 
|  | last = &alt->combined_next; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (options.trace.combine) | 
|  | { | 
|  | int nr_unique = 0; | 
|  | gen_entry *entry; | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | if (entry->combined_parent == NULL) | 
|  | { | 
|  | insn_list *l; | 
|  | gen_entry *duplicate; | 
|  | nr_unique++; | 
|  | print_gen_entry_path (table->opcode_rule->line, entry, | 
|  | notify); | 
|  | for (duplicate = entry->combined_next; duplicate != NULL; | 
|  | duplicate = duplicate->combined_next) | 
|  | { | 
|  | notify (NULL, "+%d", duplicate->opcode_nr); | 
|  | } | 
|  | notify (NULL, ": entries %d -", entry->nr_insns); | 
|  | for (l = entry->insns; l != NULL; l = l->next) | 
|  | { | 
|  | notify (NULL, " %s.%s", | 
|  | l->insn->format_name, l->insn->name); | 
|  | } | 
|  | notify (NULL, "\n"); | 
|  | } | 
|  | } | 
|  | print_gen_entry_path (table->opcode_rule->line, table, notify); | 
|  | notify (NULL, | 
|  | ": combine - word %d, bits [%d..%d] in [%d..%d], opcodes %d, entries %d, unique %d\n", | 
|  | table->opcode->word_nr, i2target (options.hi_bit_nr, | 
|  | table->opcode->first), | 
|  | i2target (options.hi_bit_nr, table->opcode->last), | 
|  | i2target (options.hi_bit_nr, table->opcode_rule->first), | 
|  | i2target (options.hi_bit_nr, table->opcode_rule->last), | 
|  | table->opcode->nr_opcodes, table->nr_entries, nr_unique); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Check that the rule did more than re-arange the order of the | 
|  | instructions */ | 
|  | { | 
|  | gen_entry *entry; | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | if (entry->combined_parent == NULL) | 
|  | { | 
|  | if (insn_list_cmp (table->insns, entry->insns) == 0) | 
|  | { | 
|  | print_gen_entry_path (table->opcode_rule->line, table, | 
|  | warning); | 
|  | warning (NULL, | 
|  | ": Applying rule just copied all instructions\n"); | 
|  | print_gen_entry_insns (entry, warning, "Copied", NULL); | 
|  | error (NULL, "unrecoverable\n"); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* if some form of expanded table, fill in the missing dots */ | 
|  | switch (table->opcode_rule->gen) | 
|  | { | 
|  | case padded_switch_gen: | 
|  | case array_gen: | 
|  | case goto_switch_gen: | 
|  | if (!table->opcode->is_boolean) | 
|  | { | 
|  | gen_entry **entry = &table->entries; | 
|  | gen_entry *illegals = NULL; | 
|  | gen_entry **last_illegal = &illegals; | 
|  | int opcode_nr = 0; | 
|  | while (opcode_nr < table->opcode->nr_opcodes) | 
|  | { | 
|  | if ((*entry) == NULL || (*entry)->opcode_nr != opcode_nr) | 
|  | { | 
|  | /* missing - insert it under our feet at *entry */ | 
|  | gen_entry_insert_insn (table, table->top->isa->illegal_insn, table->opcode->word_nr, 0,	/* nr_prefetched_words == 0 for invalid */ | 
|  | opcode_nr, NULL); | 
|  | ASSERT ((*entry) != NULL); | 
|  | ASSERT ((*entry)->opcode_nr == opcode_nr); | 
|  | (*last_illegal) = *entry; | 
|  | (*last_illegal)->combined_parent = illegals; | 
|  | last_illegal = &(*last_illegal)->combined_next; | 
|  | } | 
|  | entry = &(*entry)->sibling; | 
|  | opcode_nr++; | 
|  | } | 
|  | /* oops, will have pointed the first illegal insn back to | 
|  | its self.  Fix this */ | 
|  | if (illegals != NULL) | 
|  | illegals->combined_parent = NULL; | 
|  | } | 
|  | break; | 
|  | case switch_gen: | 
|  | case invalid_gen: | 
|  | /* ignore */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* and do the same for the newly created sub entries but *only* | 
|  | expand entries that haven't been combined. */ | 
|  | { | 
|  | gen_entry *entry; | 
|  | for (entry = table->entries; entry != NULL; entry = entry->sibling) | 
|  | { | 
|  | if (entry->combined_parent == NULL) | 
|  | { | 
|  | gen_entry_expand_insns (entry); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | gen_tables_expand_insns (gen_table *gen) | 
|  | { | 
|  | gen_list *entry; | 
|  | for (entry = gen->tables; entry != NULL; entry = entry->next) | 
|  | { | 
|  | gen_entry_expand_insns (entry->table); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* create a list of all the semantic functions that need to be | 
|  | generated.  Eliminate any duplicates. Verify that the decode stage | 
|  | worked. */ | 
|  |  | 
|  | static void | 
|  | make_gen_semantics_list (lf *file, const gen_entry *entry, int depth, void *data) | 
|  | { | 
|  | gen_table *gen = (gen_table *) data; | 
|  | insn_list *insn; | 
|  | /* Not interested in an entrie that have been combined into some | 
|  | other entry at the same level */ | 
|  | if (entry->combined_parent != NULL) | 
|  | return; | 
|  |  | 
|  | /* a leaf should contain exactly one instruction. If not the decode | 
|  | stage failed. */ | 
|  | ASSERT (entry->nr_insns == 1); | 
|  |  | 
|  | /* Enter this instruction into the list of semantic functions. */ | 
|  | insn = insn_list_insert (&gen->semantics, &gen->nr_semantics, | 
|  | entry->insns->insn, | 
|  | entry->expanded_bits, | 
|  | entry->parent->opcode, | 
|  | entry->insns->nr_prefetched_words, | 
|  | merge_duplicate_insns); | 
|  | /* point the table entry at the real semantic function */ | 
|  | ASSERT (insn != NULL); | 
|  | entry->insns->semantic = insn; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | gen_tables_expand_semantics (gen_table *gen) | 
|  | { | 
|  | gen_list *entry; | 
|  | for (entry = gen->tables; entry != NULL; entry = entry->next) | 
|  | { | 
|  | gen_entry_traverse_tree (NULL, entry->table, 1,	/* depth */ | 
|  | NULL,	/* start-handler */ | 
|  | make_gen_semantics_list,	/* leaf-handler */ | 
|  | NULL,	/* end-handler */ | 
|  | gen);	/* data */ | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | #ifdef MAIN | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_opcode_field (lf *file, | 
|  | char *prefix, | 
|  | opcode_field *field, char *suffix, int levels) | 
|  | { | 
|  | lf_printf (file, "%s(opcode_field *) %p", prefix, field); | 
|  | if (levels && field != NULL) | 
|  | { | 
|  | lf_indent (file, +1); | 
|  | lf_printf (file, "\n(first %d)", field->first); | 
|  | lf_printf (file, "\n(last %d)", field->last); | 
|  | lf_printf (file, "\n(nr_opcodes %d)", field->nr_opcodes); | 
|  | lf_printf (file, "\n(is_boolean %d)", field->is_boolean); | 
|  | lf_printf (file, "\n(boolean_constant %d)", field->boolean_constant); | 
|  | dump_opcode_field (file, "\n(parent ", field->parent, ")", levels - 1); | 
|  | lf_indent (file, -1); | 
|  | } | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_opcode_bits (lf *file, | 
|  | char *prefix, opcode_bits *bits, char *suffix, int levels) | 
|  | { | 
|  | lf_printf (file, "%s(opcode_bits *) %p", prefix, bits); | 
|  |  | 
|  | if (levels && bits != NULL) | 
|  | { | 
|  | lf_indent (file, +1); | 
|  | lf_printf (file, "\n(value %d)", bits->value); | 
|  | dump_opcode_field (file, "\n(opcode ", bits->opcode, ")", 0); | 
|  | dump_insn_field (file, "\n(field ", bits->field, ")"); | 
|  | dump_opcode_bits (file, "\n(next ", bits->next, ")", levels - 1); | 
|  | lf_indent (file, -1); | 
|  | } | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_insn_list (lf *file, char *prefix, insn_list *entry, char *suffix) | 
|  | { | 
|  | lf_printf (file, "%s(insn_list *) %p", prefix, entry); | 
|  |  | 
|  | if (entry != NULL) | 
|  | { | 
|  | lf_indent (file, +1); | 
|  | dump_insn_entry (file, "\n(insn ", entry->insn, ")"); | 
|  | lf_printf (file, "\n(next %p)", entry->next); | 
|  | lf_indent (file, -1); | 
|  | } | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_insn_word_entry_list_entries (lf *file, | 
|  | char *prefix, | 
|  | insn_list *entry, char *suffix) | 
|  | { | 
|  | lf_printf (file, "%s", prefix); | 
|  | while (entry != NULL) | 
|  | { | 
|  | dump_insn_list (file, "\n(", entry, ")"); | 
|  | entry = entry->next; | 
|  | } | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_gen_entry (lf *file, | 
|  | char *prefix, gen_entry *table, char *suffix, int levels) | 
|  | { | 
|  |  | 
|  | lf_printf (file, "%s(gen_entry *) %p", prefix, table); | 
|  |  | 
|  | if (levels && table !=NULL) | 
|  | { | 
|  |  | 
|  | lf_indent (file, +1); | 
|  | lf_printf (file, "\n(opcode_nr %d)", table->opcode_nr); | 
|  | lf_printf (file, "\n(word_nr %d)", table->word_nr); | 
|  | dump_opcode_bits (file, "\n(expanded_bits ", table->expanded_bits, ")", | 
|  | -1); | 
|  | lf_printf (file, "\n(nr_insns %d)", table->nr_insns); | 
|  | dump_insn_word_entry_list_entries (file, "\n(insns ", table->insns, | 
|  | ")"); | 
|  | dump_decode_rule (file, "\n(opcode_rule ", table->opcode_rule, ")"); | 
|  | dump_opcode_field (file, "\n(opcode ", table->opcode, ")", 0); | 
|  | lf_printf (file, "\n(nr_entries %d)", table->nr_entries); | 
|  | dump_gen_entry (file, "\n(entries ", table->entries, ")", | 
|  | table->nr_entries); | 
|  | dump_gen_entry (file, "\n(sibling ", table->sibling, ")", levels - 1); | 
|  | dump_gen_entry (file, "\n(parent ", table->parent, ")", 0); | 
|  | lf_indent (file, -1); | 
|  | } | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dump_gen_list (lf *file, | 
|  | char *prefix, gen_list *entry, char *suffix, int levels) | 
|  | { | 
|  | while (entry != NULL) | 
|  | { | 
|  | lf_printf (file, "%s(gen_list *) %p", prefix, entry); | 
|  | dump_gen_entry (file, "\n(", entry->table, ")", levels); | 
|  | lf_printf (file, "\n(next (gen_list *) %p)", entry->next); | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | dump_gen_table (lf *file, | 
|  | char *prefix, gen_table *gen, char *suffix, int levels) | 
|  | { | 
|  | lf_printf (file, "%s(gen_table *) %p", prefix, gen); | 
|  | lf_printf (file, "\n(isa (insn_table *) %p)", gen->isa); | 
|  | lf_printf (file, "\n(rules (decode_table *) %p)", gen->rules); | 
|  | dump_gen_list (file, "\n(", gen->tables, ")", levels); | 
|  | lf_printf (file, "%s", suffix); | 
|  | } | 
|  |  | 
|  |  | 
|  | igen_options options; | 
|  |  | 
|  | int | 
|  | main (int argc, char **argv) | 
|  | { | 
|  | decode_table *decode_rules; | 
|  | insn_table *instructions; | 
|  | gen_table *gen; | 
|  | lf *l; | 
|  |  | 
|  | if (argc != 7) | 
|  | error (NULL, | 
|  | "Usage: insn <filter-in> <hi-bit-nr> <insn-bit-size> <widths> <decode-table> <insn-table>\n"); | 
|  |  | 
|  | INIT_OPTIONS (); | 
|  |  | 
|  | filter_parse (&options.flags_filter, argv[1]); | 
|  |  | 
|  | options.hi_bit_nr = a2i (argv[2]); | 
|  | options.insn_bit_size = a2i (argv[3]); | 
|  | options.insn_specifying_widths = a2i (argv[4]); | 
|  | ASSERT (options.hi_bit_nr < options.insn_bit_size); | 
|  |  | 
|  | instructions = load_insn_table (argv[6], NULL); | 
|  | decode_rules = load_decode_table (argv[5]); | 
|  | gen = make_gen_tables (instructions, decode_rules); | 
|  |  | 
|  | gen_tables_expand_insns (gen); | 
|  |  | 
|  | l = lf_open ("-", "stdout", lf_omit_references, lf_is_text, "tmp-ld-insn"); | 
|  |  | 
|  | dump_gen_table (l, "(", gen, ")\n", -1); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif |