blob: 63d57976ef28651d6ed2fb87a2275ef56730cdca [file] [log] [blame]
/* Generate insn-target-def.h, an automatically-generated part of targetm.
Copyright (C) 1987-2021 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "bconfig.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "errors.h"
#include "read-md.h"
#include "gensupport.h"
#include "hash-table.h"
/* This class hashes define_insns and define_expands by name. */
struct insn_hasher : nofree_ptr_hash <rtx_def>
{
typedef rtx value_type;
typedef const char *compare_type;
static inline hashval_t hash (rtx);
static inline bool equal (rtx, const char *);
};
hashval_t
insn_hasher::hash (rtx x)
{
return htab_hash_string (XSTR (x, 0));
}
bool
insn_hasher::equal (rtx x, const char *y)
{
return strcmp (XSTR (x, 0), y) == 0;
}
/* All define_insns and define_expands, hashed by name. */
static hash_table <insn_hasher> *insns;
/* Records the prototype suffix X for each invalid_X stub that has been
generated. */
static hash_table <nofree_string_hash> *stubs;
/* Records which C conditions have been wrapped in functions, as a mapping
from the C condition to the function name. */
static hash_map <nofree_string_hash, const char *> *have_funcs;
/* Return true if the part of the prototype at P is for an argument
name. If so, point *END_OUT to the first character after the name.
If OPNO_OUT is nonnull, set *OPNO_OUT to the number of the associated
operand. If REQUIRED_OUT is nonnull, set *REQUIRED_OUT to whether the
.md pattern is required to match the operand. */
static bool
parse_argument (const char *p, const char **end_out,
unsigned int *opno_out = 0,
bool *required_out = 0)
{
while (ISSPACE (*p))
p++;
if (p[0] == 'x' && ISDIGIT (p[1]))
{
p += 1;
if (required_out)
*required_out = true;
}
else if (p[0] == 'o' && p[1] == 'p' && p[2] == 't' && ISDIGIT (p[3]))
{
p += 3;
if (required_out)
*required_out = false;
}
else
return false;
char *endptr;
unsigned int opno = strtol (p, &endptr, 10);
if (opno_out)
*opno_out = opno;
*end_out = endptr;
return true;
}
/* Output hook definitions for pattern NAME, which has target-insns.def
prototype PROTOTYPE. */
static void
def_target_insn (const char *name, const char *prototype)
{
/* Get an upper-case form of NAME. */
unsigned int i;
char *upper_name = XALLOCAVEC (char, strlen (name) + 1);
for (i = 0; name[i]; ++i)
upper_name[i] = TOUPPER (name[i]);
upper_name[i] = 0;
/* Check that the prototype is valid and concatenate the types
together to get a suffix. */
char *suffix = XALLOCAVEC (char, strlen (prototype) + 1);
i = 0;
unsigned int opno = 0;
unsigned int required_ops = 0;
unsigned int this_opno;
bool required_p;
for (const char *p = prototype; *p; ++p)
if (parse_argument (p, &p, &this_opno, &required_p))
{
if (this_opno != opno || (*p != ',' && *p != ')'))
{
error ("invalid prototype for '%s'", name);
exit (FATAL_EXIT_CODE);
}
if (required_p && required_ops < opno)
{
error ("prototype for '%s' has required operands after"
" optional operands", name);
exit (FATAL_EXIT_CODE);
}
opno += 1;
if (required_p)
required_ops = opno;
/* Skip over ')'s. */
if (*p == ',')
suffix[i++] = '_';
}
else if (*p == ')' || *p == ',')
{
/* We found the end of a parameter without finding a
parameter name. */
if (strcmp (prototype, "(void)") != 0)
{
error ("argument %d of '%s' did not have the expected name",
opno, name);
exit (FATAL_EXIT_CODE);
}
}
else if (*p != '(' && !ISSPACE (*p))
suffix[i++] = *p;
suffix[i] = 0;
/* See whether we have an implementation of this pattern. */
hashval_t hash = htab_hash_string (name);
int truth = 0;
const char *have_name = name;
if (rtx insn = insns->find_with_hash (name, hash))
{
pattern_stats stats;
get_pattern_stats (&stats, XVEC (insn, 1));
unsigned int actual_ops = stats.num_generator_args;
if (opno == required_ops && opno != actual_ops)
error_at (get_file_location (insn),
"'%s' must have %d operands (excluding match_dups)",
name, required_ops);
else if (actual_ops < required_ops)
error_at (get_file_location (insn),
"'%s' must have at least %d operands (excluding match_dups)",
name, required_ops);
else if (actual_ops > opno)
error_at (get_file_location (insn),
"'%s' must have no more than %d operands"
" (excluding match_dups)", name, opno);
const char *test = XSTR (insn, 2);
truth = maybe_eval_c_test (test);
gcc_assert (truth != 0);
if (truth < 0)
{
/* Try to reuse an existing function that performs the same test. */
bool existed;
const char *&entry = have_funcs->get_or_insert (test, &existed);
if (!existed)
{
entry = name;
printf ("\nstatic bool\n");
printf ("target_have_%s (void)\n", name);
printf ("{\n");
printf (" return ");
rtx_reader_ptr->print_c_condition (test);
printf (";\n");
printf ("}\n");
}
have_name = entry;
}
printf ("\nstatic rtx_insn *\n");
printf ("target_gen_%s ", name);
/* Print the prototype with the argument names after ACTUAL_OPS
removed. */
const char *p = prototype, *end;
while (*p)
if (parse_argument (p, &end, &this_opno) && this_opno >= actual_ops)
p = end;
else
fputc (*p++, stdout);
printf ("\n{\n");
if (truth < 0)
printf (" gcc_checking_assert (targetm.have_%s ());\n", name);
printf (" return insnify (gen_%s (", name);
for (i = 0; i < actual_ops; ++i)
printf ("%s%s%d", i == 0 ? "" : ", ",
i < required_ops ? "x" : "opt", i);
printf ("));\n");
printf ("}\n");
}
else
{
const char **slot = stubs->find_slot (suffix, INSERT);
if (!*slot)
{
*slot = xstrdup (suffix);
printf ("\nstatic rtx_insn *\n");
printf ("invalid_%s ", suffix);
/* Print the prototype with the argument names removed. */
const char *p = prototype;
while (*p)
if (!parse_argument (p, &p))
fputc (*p++, stdout);
printf ("\n{\n");
printf (" gcc_unreachable ();\n");
printf ("}\n");
}
}
printf ("\n#undef TARGET_HAVE_%s\n", upper_name);
printf ("#define TARGET_HAVE_%s ", upper_name);
if (truth == 0)
printf ("hook_bool_void_false\n");
else if (truth == 1)
printf ("hook_bool_void_true\n");
else
printf ("target_have_%s\n", have_name);
printf ("#undef TARGET_GEN_%s\n", upper_name);
printf ("#define TARGET_GEN_%s ", upper_name);
if (truth == 0)
printf ("invalid_%s\n", suffix);
else
printf ("target_gen_%s\n", name);
printf ("#undef TARGET_CODE_FOR_%s\n", upper_name);
printf ("#define TARGET_CODE_FOR_%s ", upper_name);
if (truth == 0)
printf ("CODE_FOR_nothing\n");
else
printf ("CODE_FOR_%s\n", name);
}
/* Record the DEFINE_INSN or DEFINE_EXPAND described by INFO. */
static void
add_insn (md_rtx_info *info)
{
rtx def = info->def;
const char *name = XSTR (def, 0);
if (name[0] == 0 || name[0] == '*')
return;
hashval_t hash = htab_hash_string (name);
rtx *slot = insns->find_slot_with_hash (name, hash, INSERT);
if (*slot)
error_at (info->loc, "duplicate definition of '%s'", name);
else
*slot = def;
}
int
main (int argc, const char **argv)
{
progname = "gentarget-def";
if (!init_rtx_reader_args (argc, argv))
return (FATAL_EXIT_CODE);
insns = new hash_table <insn_hasher> (31);
stubs = new hash_table <nofree_string_hash> (31);
have_funcs = new hash_map <nofree_string_hash, const char *>;
md_rtx_info info;
while (read_md_rtx (&info))
switch (GET_CODE (info.def))
{
case DEFINE_INSN:
case DEFINE_EXPAND:
add_insn (&info);
break;
default:
break;
}
printf ("/* Generated automatically by the program `gentarget-def'. */\n");
printf ("#ifndef GCC_INSN_TARGET_DEF_H\n");
printf ("#define GCC_INSN_TARGET_DEF_H\n");
/* Output a routine to convert an rtx to an rtx_insn sequence.
??? At some point the gen_* functions themselves should return
rtx_insns. */
printf ("\nstatic inline rtx_insn *\n");
printf ("insnify (rtx x)\n");
printf ("{\n");
printf (" if (!x)\n");
printf (" return NULL;\n");
printf (" if (rtx_insn *insn = dyn_cast <rtx_insn *> (x))\n");
printf (" return insn;\n");
printf (" start_sequence ();\n");
printf (" emit (x, false);\n");
printf (" rtx_insn *res = get_insns ();\n");
printf (" end_sequence ();\n");
printf (" return res;\n");
printf ("}\n");
#define DEF_TARGET_INSN(INSN, ARGS) \
def_target_insn (#INSN, #ARGS);
#include "target-insns.def"
#undef DEF_TARGET_INSN
printf ("\n#endif /* GCC_INSN_TARGET_DEF_H */\n");
if (have_error || ferror (stdout) || fflush (stdout) || fclose (stdout))
return FATAL_EXIT_CODE;
return SUCCESS_EXIT_CODE;
}