mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
1004 lines
28 KiB
C
1004 lines
28 KiB
C
/* The IGEN simulator generator for GDB, the GNU Debugger.
|
|
|
|
Copyright 2002-2024 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"
|
|
|
|
#include "gen-idecode.h"
|
|
#include "gen-icache.h"
|
|
#include "gen-semantics.h"
|
|
|
|
|
|
|
|
static void
|
|
lf_print_opcodes (lf *file, const gen_entry *table)
|
|
{
|
|
if (table !=NULL)
|
|
{
|
|
while (1)
|
|
{
|
|
ASSERT (table->opcode != NULL);
|
|
lf_printf (file, "_%d_%d",
|
|
table->opcode->first, table->opcode->last);
|
|
if (table->parent == NULL)
|
|
break;
|
|
lf_printf (file, "__%d", table->opcode_nr);
|
|
table = table->parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
print_idecode_ifetch (lf *file,
|
|
int previous_nr_prefetched_words,
|
|
int current_nr_prefetched_words)
|
|
{
|
|
int word_nr;
|
|
for (word_nr = previous_nr_prefetched_words;
|
|
word_nr < current_nr_prefetched_words; word_nr++)
|
|
{
|
|
lf_printf (file,
|
|
"instruction_word instruction_%d = IMEM%d_IMMED (cia, %d);\n",
|
|
word_nr, options.insn_bit_size, word_nr);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
static void
|
|
lf_print_table_name (lf *file, const gen_entry *table)
|
|
{
|
|
lf_printf (file, "idecode_table");
|
|
lf_print_opcodes (file, table);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_idecode_table (lf *file, const gen_entry *entry, const char *result)
|
|
{
|
|
lf_printf (file, "/* prime the search */\n");
|
|
lf_printf (file, "idecode_table_entry *table = ");
|
|
lf_print_table_name (file, entry);
|
|
lf_printf (file, ";\n");
|
|
lf_printf (file, "int opcode = EXTRACTED%d (instruction, %d, %d);\n",
|
|
options.insn_bit_size,
|
|
i2target (options.hi_bit_nr, entry->opcode->first),
|
|
i2target (options.hi_bit_nr, entry->opcode->last));
|
|
lf_printf (file, "idecode_table_entry *table_entry = table + opcode;\n");
|
|
|
|
lf_printf (file, "\n");
|
|
lf_printf (file, "/* iterate until a leaf */\n");
|
|
lf_printf (file, "while (1) {\n");
|
|
lf_printf (file, " signed shift = table_entry->shift;\n");
|
|
lf_printf (file, "if (shift == function_entry) break;\n");
|
|
lf_printf (file, " if (shift >= 0) {\n");
|
|
lf_printf (file, " table = ((idecode_table_entry*)\n");
|
|
lf_printf (file, " table_entry->function_or_table);\n");
|
|
lf_printf (file, " opcode = ((instruction & table_entry->mask)\n");
|
|
lf_printf (file, " >> shift);\n");
|
|
lf_printf (file, " table_entry = table + opcode;\n");
|
|
lf_printf (file, " }\n");
|
|
lf_printf (file, " else {\n");
|
|
lf_printf (file, " /* must be a boolean */\n");
|
|
lf_printf (file, " ASSERT(table_entry->shift == boolean_entry);\n");
|
|
lf_printf (file, " opcode = ((instruction & table_entry->mask)\n");
|
|
lf_printf (file, " != table_entry->value);\n");
|
|
lf_printf (file, " table = ((idecode_table_entry*)\n");
|
|
lf_printf (file, " table_entry->function_or_table);\n");
|
|
lf_printf (file, " table_entry = table + opcode;\n");
|
|
lf_printf (file, " }\n");
|
|
lf_printf (file, "}\n");
|
|
|
|
lf_printf (file, "\n");
|
|
lf_printf (file, "/* call the leaf code */\n");
|
|
if (options.gen.code == generate_jumps)
|
|
{
|
|
lf_printf (file, "goto *table_entry->function_or_table;\n");
|
|
}
|
|
else
|
|
{
|
|
lf_printf (file, "%s ", result);
|
|
if (options.gen.icache)
|
|
{
|
|
lf_printf (file,
|
|
"(((idecode_icache*)table_entry->function_or_table)\n");
|
|
lf_printf (file, " (");
|
|
print_icache_function_actual (file, 1);
|
|
lf_printf (file, "));\n");
|
|
}
|
|
else
|
|
{
|
|
lf_printf (file,
|
|
"((idecode_semantic*)table_entry->function_or_table)\n");
|
|
lf_printf (file, " (");
|
|
print_semantic_function_actual (file, 1);
|
|
lf_printf (file, ");\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_idecode_table_start (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
ASSERT (depth == 0);
|
|
/* start of the table */
|
|
if (table->opcode_rule->gen == array_gen)
|
|
{
|
|
lf_printf (file, "\n");
|
|
lf_printf (file, "static idecode_table_entry ");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, "[] = {\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_idecode_table_leaf (lf *file,
|
|
const gen_entry *entry, int depth, void *data)
|
|
{
|
|
const gen_entry *master_entry;
|
|
ASSERT (entry->parent != NULL);
|
|
ASSERT (depth == 0);
|
|
if (entry->combined_parent == NULL)
|
|
master_entry = entry;
|
|
else
|
|
master_entry = entry->combined_parent;
|
|
|
|
/* add an entry to the table */
|
|
if (entry->parent->opcode_rule->gen == array_gen)
|
|
{
|
|
lf_printf (file, " /*%d*/ { ", entry->opcode_nr);
|
|
if (entry->opcode == NULL)
|
|
{
|
|
ASSERT (entry->nr_insns == 1);
|
|
/* table leaf entry */
|
|
lf_printf (file, "function_entry, 0, 0, ");
|
|
if (options.gen.code == generate_jumps)
|
|
{
|
|
lf_printf (file, "&&");
|
|
}
|
|
print_function_name (file,
|
|
entry->insns->insn->name,
|
|
entry->insns->insn->format_name,
|
|
NULL,
|
|
master_entry->expanded_bits,
|
|
(options.gen.icache
|
|
? function_name_prefix_icache
|
|
: function_name_prefix_semantics));
|
|
}
|
|
else if (entry->opcode_rule->gen == switch_gen
|
|
|| entry->opcode_rule->gen == goto_switch_gen
|
|
|| entry->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
/* table calling switch statement */
|
|
lf_printf (file, "function_entry, 0, 0, ");
|
|
if (options.gen.code == generate_jumps)
|
|
{
|
|
lf_printf (file, "&&");
|
|
}
|
|
lf_print_table_name (file, entry);
|
|
}
|
|
else if (entry->opcode->is_boolean)
|
|
{
|
|
/* table `calling' boolean table */
|
|
lf_printf (file, "boolean_entry, ");
|
|
lf_printf (file, "MASK32(%d, %d), ",
|
|
i2target (options.hi_bit_nr, entry->opcode->first),
|
|
i2target (options.hi_bit_nr, entry->opcode->last));
|
|
lf_printf (file, "INSERTED32(%d, %d, %d), ",
|
|
entry->opcode->boolean_constant,
|
|
i2target (options.hi_bit_nr, entry->opcode->first),
|
|
i2target (options.hi_bit_nr, entry->opcode->last));
|
|
lf_print_table_name (file, entry);
|
|
}
|
|
else
|
|
{
|
|
/* table `calling' another table */
|
|
lf_printf (file, "%d, ",
|
|
options.insn_bit_size - entry->opcode->last - 1);
|
|
lf_printf (file, "MASK%d(%d,%d), ", options.insn_bit_size,
|
|
i2target (options.hi_bit_nr, entry->opcode->first),
|
|
i2target (options.hi_bit_nr, entry->opcode->last));
|
|
lf_printf (file, "0, ");
|
|
lf_print_table_name (file, entry);
|
|
}
|
|
lf_printf (file, " },\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_idecode_table_end (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
ASSERT (depth == 0);
|
|
if (table->opcode_rule->gen == array_gen)
|
|
{
|
|
lf_printf (file, "};\n");
|
|
}
|
|
}
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
static void
|
|
print_goto_switch_name (lf *file, const gen_entry *entry)
|
|
{
|
|
lf_printf (file, "case_");
|
|
if (entry->opcode == NULL)
|
|
{
|
|
print_function_name (file,
|
|
entry->insns->insn->name,
|
|
entry->insns->insn->format_name,
|
|
NULL,
|
|
entry->expanded_bits,
|
|
(options.gen.icache
|
|
? function_name_prefix_icache
|
|
: function_name_prefix_semantics));
|
|
}
|
|
else
|
|
{
|
|
lf_print_table_name (file, entry);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_goto_switch_table_leaf (lf *file,
|
|
const gen_entry *entry, int depth, void *data)
|
|
{
|
|
ASSERT (entry->parent != NULL);
|
|
ASSERT (depth == 0);
|
|
ASSERT (entry->parent->opcode_rule->gen == goto_switch_gen);
|
|
ASSERT (entry->parent->opcode);
|
|
|
|
lf_printf (file, "/* %d */ &&", entry->opcode_nr);
|
|
if (entry->combined_parent != NULL)
|
|
print_goto_switch_name (file, entry->combined_parent);
|
|
else
|
|
print_goto_switch_name (file, entry);
|
|
lf_printf (file, ",\n");
|
|
}
|
|
|
|
static void
|
|
print_goto_switch_break (lf *file, const gen_entry *entry)
|
|
{
|
|
lf_printf (file, "goto break_");
|
|
lf_print_table_name (file, entry->parent);
|
|
lf_printf (file, ";\n");
|
|
}
|
|
|
|
|
|
static void
|
|
print_goto_switch_table (lf *file, const gen_entry *table)
|
|
{
|
|
lf_printf (file, "const static void *");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, "[] = {\n");
|
|
lf_indent (file, +2);
|
|
gen_entry_traverse_tree (file, table, 0, NULL /*start */ ,
|
|
print_goto_switch_table_leaf, NULL /*end */ ,
|
|
NULL /*data */ );
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "};\n");
|
|
}
|
|
|
|
|
|
void print_idecode_switch
|
|
(lf *file, const gen_entry *table, const char *result);
|
|
|
|
static void
|
|
print_idecode_switch_start (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
/* const char *result = data; */
|
|
ASSERT (depth == 0);
|
|
ASSERT (table->opcode_rule->gen == switch_gen
|
|
|| table->opcode_rule->gen == goto_switch_gen
|
|
|| table->opcode_rule->gen == padded_switch_gen);
|
|
|
|
if (table->opcode->is_boolean
|
|
|| table->opcode_rule->gen == switch_gen
|
|
|| table->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
lf_printf (file, "switch (EXTRACTED%d (instruction_%d, %d, %d))\n",
|
|
options.insn_bit_size,
|
|
table->opcode_rule->word_nr,
|
|
i2target (options.hi_bit_nr, table->opcode->first),
|
|
i2target (options.hi_bit_nr, table->opcode->last));
|
|
lf_indent (file, +2);
|
|
lf_printf (file, "{\n");
|
|
}
|
|
else if (table->opcode_rule->gen == goto_switch_gen)
|
|
{
|
|
if (table->parent != NULL
|
|
&& (table->parent->opcode_rule->gen == switch_gen
|
|
|| table->parent->opcode_rule->gen == goto_switch_gen
|
|
|| table->parent->opcode_rule->gen == padded_switch_gen))
|
|
{
|
|
lf_printf (file, "{\n");
|
|
lf_indent (file, +2);
|
|
}
|
|
print_goto_switch_table (file, table);
|
|
lf_printf (file, "ASSERT (EXTRACTED%d (instruction_%d, %d, %d)\n",
|
|
options.insn_bit_size,
|
|
table->opcode->word_nr,
|
|
i2target (options.hi_bit_nr, table->opcode->first),
|
|
i2target (options.hi_bit_nr, table->opcode->last));
|
|
lf_printf (file, " < (sizeof (");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, ") / sizeof(void*)));\n");
|
|
lf_printf (file, "goto *");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, "[EXTRACTED%d (instruction_%d, %d, %d)];\n",
|
|
options.insn_bit_size,
|
|
table->opcode->word_nr,
|
|
i2target (options.hi_bit_nr, table->opcode->first),
|
|
i2target (options.hi_bit_nr, table->opcode->last));
|
|
}
|
|
else
|
|
{
|
|
ASSERT ("bad switch" == NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_idecode_switch_leaf (lf *file,
|
|
const gen_entry *entry, int depth, void *data)
|
|
{
|
|
const char *result = data;
|
|
ASSERT (entry->parent != NULL);
|
|
ASSERT (depth == 0);
|
|
ASSERT (entry->parent->opcode_rule->gen == switch_gen
|
|
|| entry->parent->opcode_rule->gen == goto_switch_gen
|
|
|| entry->parent->opcode_rule->gen == padded_switch_gen);
|
|
ASSERT (entry->parent->opcode);
|
|
|
|
/* skip over any instructions combined into another entry */
|
|
if (entry->combined_parent != NULL)
|
|
return;
|
|
|
|
if (entry->parent->opcode->is_boolean && entry->opcode_nr == 0)
|
|
{
|
|
/* case: boolean false target */
|
|
lf_printf (file, "case %d:\n", entry->parent->opcode->boolean_constant);
|
|
}
|
|
else if (entry->parent->opcode->is_boolean && entry->opcode_nr != 0)
|
|
{
|
|
/* case: boolean true case */
|
|
lf_printf (file, "default:\n");
|
|
}
|
|
else if (entry->parent->opcode_rule->gen == switch_gen
|
|
|| entry->parent->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
/* case: <opcode-nr> - switch */
|
|
const gen_entry *cob;
|
|
for (cob = entry; cob != NULL; cob = cob->combined_next)
|
|
lf_printf (file, "case %d:\n", cob->opcode_nr);
|
|
}
|
|
else if (entry->parent->opcode_rule->gen == goto_switch_gen)
|
|
{
|
|
/* case: <opcode-nr> - goto-switch */
|
|
print_goto_switch_name (file, entry);
|
|
lf_printf (file, ":\n");
|
|
}
|
|
else
|
|
{
|
|
ERROR ("bad switch");
|
|
}
|
|
lf_printf (file, " {\n");
|
|
lf_indent (file, +4);
|
|
{
|
|
if (entry->opcode == NULL)
|
|
{
|
|
/* switch calling leaf */
|
|
ASSERT (entry->nr_insns == 1);
|
|
print_idecode_ifetch (file, entry->nr_prefetched_words,
|
|
entry->insns->semantic->nr_prefetched_words);
|
|
switch (options.gen.code)
|
|
{
|
|
case generate_jumps:
|
|
lf_printf (file, "goto ");
|
|
break;
|
|
case generate_calls:
|
|
lf_printf (file, "%s", result);
|
|
break;
|
|
}
|
|
print_function_name (file,
|
|
entry->insns->insn->name,
|
|
entry->insns->insn->format_name,
|
|
NULL,
|
|
entry->expanded_bits,
|
|
(options.gen.icache
|
|
? function_name_prefix_icache
|
|
: function_name_prefix_semantics));
|
|
if (options.gen.code == generate_calls)
|
|
{
|
|
lf_printf (file, " (");
|
|
print_semantic_function_actual (file,
|
|
entry->insns->semantic->
|
|
nr_prefetched_words);
|
|
lf_printf (file, ")");
|
|
}
|
|
lf_printf (file, ";\n");
|
|
}
|
|
else if (entry->opcode_rule->gen == switch_gen
|
|
|| entry->opcode_rule->gen == goto_switch_gen
|
|
|| entry->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
/* switch calling switch */
|
|
lf_printf (file, "{\n");
|
|
lf_indent (file, +2);
|
|
print_idecode_ifetch (file, entry->parent->nr_prefetched_words,
|
|
entry->nr_prefetched_words);
|
|
print_idecode_switch (file, entry, result);
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
}
|
|
else
|
|
{
|
|
/* switch looking up a table */
|
|
lf_printf (file, "{\n");
|
|
lf_indent (file, +2);
|
|
print_idecode_ifetch (file, entry->parent->nr_prefetched_words,
|
|
entry->nr_prefetched_words);
|
|
print_idecode_table (file, entry, result);
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
}
|
|
if (entry->parent->opcode->is_boolean
|
|
|| entry->parent->opcode_rule->gen == switch_gen
|
|
|| entry->parent->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
lf_printf (file, "break;\n");
|
|
}
|
|
else if (entry->parent->opcode_rule->gen == goto_switch_gen)
|
|
{
|
|
print_goto_switch_break (file, entry);
|
|
}
|
|
else
|
|
{
|
|
ERROR ("bad switch");
|
|
}
|
|
}
|
|
lf_indent (file, -4);
|
|
lf_printf (file, " }\n");
|
|
}
|
|
|
|
|
|
static void
|
|
print_idecode_switch_illegal (lf *file, const char *result)
|
|
{
|
|
lf_indent (file, +2);
|
|
print_idecode_invalid (file, result, invalid_illegal);
|
|
lf_printf (file, "break;\n");
|
|
lf_indent (file, -2);
|
|
}
|
|
|
|
static void
|
|
print_idecode_switch_end (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
const char *result = data;
|
|
ASSERT (depth == 0);
|
|
ASSERT (table->opcode_rule->gen == switch_gen
|
|
|| table->opcode_rule->gen == goto_switch_gen
|
|
|| table->opcode_rule->gen == padded_switch_gen);
|
|
ASSERT (table->opcode);
|
|
|
|
if (table->opcode->is_boolean)
|
|
{
|
|
lf_printf (file, "}\n");
|
|
lf_indent (file, -2);
|
|
}
|
|
else if (table->opcode_rule->gen == switch_gen
|
|
|| table->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
lf_printf (file, "default:\n");
|
|
lf_indent (file, +2);
|
|
if (table->nr_entries == table->opcode->nr_opcodes)
|
|
{
|
|
print_sim_engine_abort (file,
|
|
"Internal error - bad switch generated");
|
|
lf_printf (file, "%sNULL_CIA;\n", result);
|
|
lf_printf (file, "break;\n");
|
|
}
|
|
else
|
|
{
|
|
print_idecode_switch_illegal (file, result);
|
|
}
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
lf_indent (file, -2);
|
|
}
|
|
else if (table->opcode_rule->gen == goto_switch_gen)
|
|
{
|
|
lf_printf (file, "illegal_");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, ":\n");
|
|
print_idecode_invalid (file, result, invalid_illegal);
|
|
lf_printf (file, "break_");
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, ":;\n");
|
|
if (table->parent != NULL
|
|
&& (table->parent->opcode_rule->gen == switch_gen
|
|
|| table->parent->opcode_rule->gen == goto_switch_gen
|
|
|| table->parent->opcode_rule->gen == padded_switch_gen))
|
|
{
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR ("bad switch");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
print_idecode_switch (lf *file, const gen_entry *table, const char *result)
|
|
{
|
|
gen_entry_traverse_tree (file, table,
|
|
0,
|
|
print_idecode_switch_start,
|
|
print_idecode_switch_leaf,
|
|
print_idecode_switch_end, (void *) result);
|
|
}
|
|
|
|
|
|
static void
|
|
print_idecode_switch_function_header (lf *file,
|
|
const gen_entry *table,
|
|
int is_function_definition,
|
|
int nr_prefetched_words)
|
|
{
|
|
lf_printf (file, "\n");
|
|
if (options.gen.code == generate_calls)
|
|
{
|
|
lf_printf (file, "static ");
|
|
if (options.gen.icache)
|
|
{
|
|
lf_printf (file, "idecode_semantic *");
|
|
}
|
|
else
|
|
{
|
|
lf_printf (file, "unsigned_word");
|
|
}
|
|
if (is_function_definition)
|
|
{
|
|
lf_printf (file, "\n");
|
|
}
|
|
else
|
|
{
|
|
lf_printf (file, " ");
|
|
}
|
|
lf_print_table_name (file, table);
|
|
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");
|
|
}
|
|
if (options.gen.code == generate_jumps && is_function_definition)
|
|
{
|
|
lf_indent (file, -1);
|
|
lf_print_table_name (file, table);
|
|
lf_printf (file, ":\n");
|
|
lf_indent (file, +1);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
idecode_declare_if_switch (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
if ((table->opcode_rule->gen == switch_gen || table->opcode_rule->gen == goto_switch_gen || table->opcode_rule->gen == padded_switch_gen) &&table->parent != NULL /* don't declare the top one yet */
|
|
&& table->parent->opcode_rule->gen == array_gen)
|
|
{
|
|
print_idecode_switch_function_header (file,
|
|
table,
|
|
0 /*isnt function definition */ ,
|
|
0);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
idecode_expand_if_switch (lf *file,
|
|
const gen_entry *table, int depth, void *data)
|
|
{
|
|
if ((table->opcode_rule->gen == switch_gen || table->opcode_rule->gen == goto_switch_gen || table->opcode_rule->gen == padded_switch_gen) &&table->parent != NULL /* don't expand the top one yet */
|
|
&& table->parent->opcode_rule->gen == array_gen)
|
|
{
|
|
print_idecode_switch_function_header (file,
|
|
table,
|
|
1 /*is function definition */ ,
|
|
0);
|
|
if (options.gen.code == generate_calls)
|
|
{
|
|
lf_printf (file, "{\n");
|
|
lf_indent (file, +2);
|
|
}
|
|
print_idecode_switch (file, table, "return");
|
|
if (options.gen.code == generate_calls)
|
|
{
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
void
|
|
print_idecode_lookups (lf *file,
|
|
const gen_entry *table,
|
|
cache_entry *cache_rules)
|
|
{
|
|
int depth;
|
|
|
|
/* output switch function declarations where needed by tables */
|
|
gen_entry_traverse_tree (file, table, 1, idecode_declare_if_switch, /* START */
|
|
NULL, NULL, NULL);
|
|
|
|
/* output tables where needed */
|
|
for (depth = gen_entry_depth (table); depth > 0; depth--)
|
|
{
|
|
gen_entry_traverse_tree (file, table,
|
|
1 - depth,
|
|
print_idecode_table_start,
|
|
print_idecode_table_leaf,
|
|
print_idecode_table_end, NULL);
|
|
}
|
|
|
|
/* output switch functions where needed */
|
|
gen_entry_traverse_tree (file, table, 1, idecode_expand_if_switch, /* START */
|
|
NULL, NULL, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
print_idecode_body (lf *file, const gen_entry *table, const char *result)
|
|
{
|
|
if (table->opcode_rule->gen == switch_gen
|
|
|| table->opcode_rule->gen == goto_switch_gen
|
|
|| table->opcode_rule->gen == padded_switch_gen)
|
|
{
|
|
print_idecode_switch (file, table, result);
|
|
}
|
|
else
|
|
{
|
|
print_idecode_table (file, table, result);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
/* Output code to do any final checks on the decoded instruction.
|
|
This includes things like verifying any on decoded fields have the
|
|
correct value and checking that (for floating point) floating point
|
|
hardware isn't disabled */
|
|
|
|
void
|
|
print_idecode_validate (lf *file,
|
|
const insn_entry *instruction,
|
|
const insn_opcodes *opcode_paths)
|
|
{
|
|
/* Validate: unchecked instruction fields
|
|
|
|
If any constant fields in the instruction were not checked by the
|
|
idecode tables, output code to check that they have the correct
|
|
value here */
|
|
{
|
|
int nr_checks = 0;
|
|
int word_nr;
|
|
lf_printf (file, "\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#if defined (WITH_RESERVED_BITS)\n");
|
|
lf_printf (file, "/* validate: ");
|
|
print_insn_words (file, instruction);
|
|
lf_printf (file, " */\n");
|
|
for (word_nr = 0; word_nr < instruction->nr_words; word_nr++)
|
|
{
|
|
insn_uint check_mask = 0;
|
|
insn_uint check_val = 0;
|
|
insn_word_entry *word = instruction->word[word_nr];
|
|
int bit_nr;
|
|
|
|
/* form check_mask/check_val containing what needs to be checked
|
|
in the instruction */
|
|
for (bit_nr = 0; bit_nr < options.insn_bit_size; bit_nr++)
|
|
{
|
|
insn_bit_entry *bit = word->bit[bit_nr];
|
|
insn_field_entry *field = bit->field;
|
|
|
|
/* Make space for the next bit */
|
|
check_mask <<= 1;
|
|
check_val <<= 1;
|
|
|
|
/* Only need to validate constant (and reserved)
|
|
bits. Skip any others */
|
|
if (field->type != insn_field_int
|
|
&& field->type != insn_field_reserved
|
|
/* Consider a named field equal to a value to be just as
|
|
constant as an integer field. */
|
|
&& (field->type != insn_field_string
|
|
|| field->conditions == NULL
|
|
|| field->conditions->test != insn_field_cond_eq
|
|
|| field->conditions->type != insn_field_cond_value))
|
|
continue;
|
|
|
|
/* Look through the list of opcode paths that lead to this
|
|
instruction. See if any have failed to check the
|
|
relevant bit */
|
|
if (opcode_paths != NULL)
|
|
{
|
|
const insn_opcodes *entry;
|
|
for (entry = opcode_paths; entry != NULL; entry = entry->next)
|
|
{
|
|
opcode_field *opcode;
|
|
for (opcode = entry->opcode;
|
|
opcode != NULL; opcode = opcode->parent)
|
|
{
|
|
if (opcode->word_nr == word_nr
|
|
&& opcode->first <= bit_nr
|
|
&& opcode->last >= bit_nr)
|
|
/* we've decoded on this bit */
|
|
break;
|
|
}
|
|
if (opcode == NULL)
|
|
/* the bit wasn't decoded on */
|
|
break;
|
|
}
|
|
if (entry == NULL)
|
|
/* all the opcode paths decoded on BIT_NR, no need
|
|
to check it */
|
|
continue;
|
|
}
|
|
|
|
check_mask |= 1;
|
|
check_val |= bit->value;
|
|
}
|
|
|
|
/* if any bits not checked by opcode tables, output code to check them */
|
|
if (check_mask)
|
|
{
|
|
if (nr_checks == 0)
|
|
{
|
|
lf_printf (file, "if (WITH_RESERVED_BITS)\n");
|
|
lf_printf (file, " {\n");
|
|
lf_indent (file, +4);
|
|
}
|
|
nr_checks++;
|
|
if (options.insn_bit_size > 32)
|
|
{
|
|
lf_printf (file, "if ((instruction_%d\n", word_nr);
|
|
lf_printf (file, " & UNSIGNED64 (0x%08lx%08lx))\n",
|
|
(unsigned long) (check_mask >> 32),
|
|
(unsigned long) (check_mask));
|
|
lf_printf (file, " != UNSIGNED64 (0x%08lx%08lx))\n",
|
|
(unsigned long) (check_val >> 32),
|
|
(unsigned long) (check_val));
|
|
}
|
|
else
|
|
{
|
|
lf_printf (file,
|
|
"if ((instruction_%d & 0x%08lx) != 0x%08lx)\n",
|
|
word_nr, (unsigned long) (check_mask),
|
|
(unsigned long) (check_val));
|
|
}
|
|
lf_indent (file, +2);
|
|
print_idecode_invalid (file, "return", invalid_illegal);
|
|
lf_indent (file, -2);
|
|
}
|
|
}
|
|
if (nr_checks > 0)
|
|
{
|
|
lf_indent (file, -4);
|
|
lf_printf (file, " }\n");
|
|
}
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#endif\n");
|
|
}
|
|
|
|
/* Validate: Floating Point hardware
|
|
|
|
If the simulator is being built with out floating point hardware
|
|
(different to it being disabled in the MSR) then floating point
|
|
instructions are invalid */
|
|
{
|
|
if (filter_is_member (instruction->flags, "f"))
|
|
{
|
|
lf_printf (file, "\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#if defined(CURRENT_FLOATING_POINT)\n");
|
|
lf_printf (file, "/* Validate: FP hardware exists */\n");
|
|
lf_printf (file,
|
|
"if (CURRENT_FLOATING_POINT != HARD_FLOATING_POINT) {\n");
|
|
lf_indent (file, +2);
|
|
print_idecode_invalid (file, "return", invalid_illegal);
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#endif\n");
|
|
}
|
|
}
|
|
|
|
/* Validate: Floating Point available
|
|
|
|
If floating point is not available, we enter a floating point
|
|
unavailable interrupt into the cache instead of the instruction
|
|
proper.
|
|
|
|
The PowerPC spec requires a CSI after MSR[FP] is changed and when
|
|
ever a CSI occures we flush the instruction cache. */
|
|
|
|
{
|
|
if (filter_is_member (instruction->flags, "f"))
|
|
{
|
|
lf_printf (file, "\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#if defined(IS_FP_AVAILABLE)\n");
|
|
lf_printf (file, "/* Validate: FP available according to cpu */\n");
|
|
lf_printf (file, "if (!IS_FP_AVAILABLE) {\n");
|
|
lf_indent (file, +2);
|
|
print_idecode_invalid (file, "return", invalid_fp_unavailable);
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#endif\n");
|
|
}
|
|
}
|
|
|
|
/* Validate: Validate Instruction in correct slot
|
|
|
|
Some architectures place restrictions on the slot that an
|
|
instruction can be issued in */
|
|
|
|
{
|
|
if (filter_is_member (instruction->options, "s")
|
|
|| options.gen.slot_verification)
|
|
{
|
|
lf_printf (file, "\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#if defined(IS_WRONG_SLOT)\n");
|
|
lf_printf (file,
|
|
"/* Validate: Instruction issued in correct slot */\n");
|
|
lf_printf (file, "if (IS_WRONG_SLOT) {\n");
|
|
lf_indent (file, +2);
|
|
print_idecode_invalid (file, "return", invalid_wrong_slot);
|
|
lf_indent (file, -2);
|
|
lf_printf (file, "}\n");
|
|
lf_indent_suppress (file);
|
|
lf_printf (file, "#endif\n");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
void
|
|
print_idecode_issue_function_header (lf *file,
|
|
const char *processor,
|
|
function_decl_type decl_type,
|
|
int nr_prefetched_words)
|
|
{
|
|
int indent;
|
|
lf_printf (file, "\n");
|
|
switch (decl_type)
|
|
{
|
|
case is_function_declaration:
|
|
lf_print__function_type_function (file, print_semantic_function_type,
|
|
"INLINE_IDECODE", " ");
|
|
break;
|
|
case is_function_definition:
|
|
lf_print__function_type_function (file, print_semantic_function_type,
|
|
"INLINE_IDECODE", "\n");
|
|
break;
|
|
case is_function_variable:
|
|
if (lf_get_file_type (file) == lf_is_h)
|
|
lf_printf (file, "extern ");
|
|
print_semantic_function_type (file);
|
|
lf_printf (file, " (*");
|
|
break;
|
|
}
|
|
indent = print_function_name (file,
|
|
"issue",
|
|
NULL,
|
|
processor,
|
|
NULL, function_name_prefix_idecode);
|
|
switch (decl_type)
|
|
{
|
|
case is_function_definition:
|
|
indent += lf_printf (file, " (");
|
|
break;
|
|
case is_function_declaration:
|
|
lf_putstr (file, "\n(");
|
|
indent = 1;
|
|
break;
|
|
case is_function_variable:
|
|
lf_putstr (file, ")\n(");
|
|
indent = 1;
|
|
break;
|
|
}
|
|
lf_indent (file, +indent);
|
|
print_semantic_function_formal (file, nr_prefetched_words);
|
|
lf_putstr (file, ")");
|
|
lf_indent (file, -indent);
|
|
switch (decl_type)
|
|
{
|
|
case is_function_definition:
|
|
lf_printf (file, "\n");
|
|
break;
|
|
case is_function_declaration:
|
|
case is_function_variable:
|
|
lf_putstr (file, ";\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
print_idecode_globals (lf *file)
|
|
{
|
|
lf_printf (file, "enum {\n");
|
|
lf_printf (file, " /* greater or equal to zero => table */\n");
|
|
lf_printf (file, " function_entry = -1,\n");
|
|
lf_printf (file, " boolean_entry = -2,\n");
|
|
lf_printf (file, "};\n");
|
|
lf_printf (file, "\n");
|
|
lf_printf (file, "typedef struct _idecode_table_entry {\n");
|
|
lf_printf (file, " int shift;\n");
|
|
lf_printf (file, " uint%d_t mask;\n", options.insn_bit_size);
|
|
lf_printf (file, " uint%d_t value;\n", options.insn_bit_size);
|
|
lf_printf (file, " void *function_or_table;\n");
|
|
lf_printf (file, "} idecode_table_entry;\n");
|
|
}
|