binutils-gdb/sim/ppc/gen-icache.c
Mike Frysinger 3d52735bab sim: include stdlib.h for atoi()
Make sure the files using atoi() include stdlib.h for its prototype.
These files were relying on it being included implicitly by others
which isn't guaranteed, and newer toolchains produce warnings.
2021-01-04 20:17:37 -05:00

676 lines
19 KiB
C

/* 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 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");
}
}