8sa1-gcc/gcc/genopinit.c
Richard Sandiford 0016d8d91c [gen/AArch64] Generate helpers for substituting iterator values into pattern names
Given a pattern like:

  (define_insn "aarch64_frecpe<mode>" ...)

the SVE ACLE implementation wants to generate the pattern for a
particular (non-constant) mode.  This patch automatically generates
helpers to do that, specifically:

  // Return CODE_FOR_nothing on failure.
  insn_code maybe_code_for_aarch64_frecpe (machine_mode);

  // Assert that the code exists.
  insn_code code_for_aarch64_frecpe (machine_mode);

  // Return NULL_RTX on failure.
  rtx maybe_gen_aarch64_frecpe (machine_mode, rtx, rtx);

  // Assert that generation succeeds.
  rtx gen_aarch64_frecpe (machine_mode, rtx, rtx);

Many patterns don't have sensible names when all <...>s are removed.
E.g. "<optab><mode>2" would give a base name "2".  The new functions
therefore require explicit opt-in, which should also help to reduce
code bloat.

The (arbitrary) opt-in syntax I went for was to prefix the pattern
name with '@', similarly to the existing '*' marker.

The patch also makes config/aarch64 use the new routines in cases where
they obviously apply.  This was mostly straight-forward, but it seemed
odd that we defined:

   aarch64_reload_movcp<...><P:mode>

but then only used it with DImode, never SImode.  If we should be
using Pmode instead of DImode, then that's a simple change,
but should probably be a separate patch.

2018-08-02  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* doc/md.texi: Expand the documentation of instruction names
	to mention port-local uses.  Document '@' in pattern names.
	* read-md.h (overloaded_instance, overloaded_name): New structs.
	(mapping): Declare.
	(md_reader::handle_overloaded_name): New member function.
	(md_reader::get_overloads): Likewise.
	(md_reader::m_first_overload): New member variable.
	(md_reader::m_next_overload_ptr): Likewise.
	(md_reader::m_overloads_htab): Likewise.
	* read-md.c (md_reader::md_reader): Initialize m_first_overload,
	m_next_overload_ptr and m_overloads_htab.
	* read-rtl.c (iterator_group): Add "type" and "get_c_token" fields.
	(get_mode_token, get_code_token, get_int_token): New functions.
	(map_attr_string): Add an optional argument that passes back
	the associated iterator.
	(overloaded_name_hash, overloaded_name_eq_p, named_rtx_p):
	(md_reader::handle_overloaded_name, add_overload_instance): New
	functions.
	(apply_iterators): Handle '@' names.  Report an error if '@'
	is used without iterators.
	(initialize_iterators): Initialize the new iterator_group fields.
	* genopinit.c (handle_overloaded_code_for)
	(handle_overloaded_gen): New functions.
	(main): Use them to print declarations of maybe_code_for_* and
	maybe_gen_* functions, and inline definitions of code_for_* and gen_*.
	* genemit.c (print_overload_arguments, print_overload_test)
	(handle_overloaded_code_for, handle_overloaded_gen): New functions.
	(main): Use it to print definitions of maybe_code_for_* and
	maybe_gen_* functions.
	* config/aarch64/aarch64.c (aarch64_split_128bit_move): Use
	gen_aarch64_mov{low,high}_di and gen_aarch64_movdi_{low,high}
	instead of explicit mode checks.
	(aarch64_split_simd_combine): Likewise gen_aarch64_simd_combine.
	(aarch64_split_simd_move): Likewise gen_aarch64_split_simd_mov.
	(aarch64_emit_load_exclusive): Likewise gen_aarch64_load_exclusive.
	(aarch64_emit_store_exclusive): Likewise gen_aarch64_store_exclusive.
	(aarch64_expand_compare_and_swap): Likewise
	gen_aarch64_compare_and_swap and gen_aarch64_compare_and_swap_lse
	(aarch64_gen_atomic_cas): Likewise gen_aarch64_atomic_cas.
	(aarch64_emit_atomic_swap): Likewise gen_aarch64_atomic_swp.
	(aarch64_constant_pool_reload_icode): Delete.
	(aarch64_secondary_reload): Use code_for_aarch64_reload_movcp
	instead of aarch64_constant_pool_reload_icode.  Use
	code_for_aarch64_reload_mov instead of explicit mode checks.
	(rsqrte_type, get_rsqrte_type, rsqrts_type, get_rsqrts_type): Delete.
	(aarch64_emit_approx_sqrt): Use gen_aarch64_rsqrte instead of
	get_rsqrte_type and gen_aarch64_rsqrts instead of gen_rqrts_type.
	(recpe_type, get_recpe_type, recps_type, get_recps_type): Delete.
	(aarch64_emit_approx_div): Use gen_aarch64_frecpe instead of
	get_recpe_type and gen_aarch64_frecps instead of get_recps_type.
	(aarch64_atomic_load_op_code): Delete.
	(aarch64_emit_atomic_load_op): Likewise.
	(aarch64_gen_atomic_ldop): Use UNSPECV_ATOMIC_* instead of
	aarch64_atomic_load_op_code.  Use gen_aarch64_atomic_load
	instead of aarch64_emit_atomic_load_op.
	* config/aarch64/aarch64.md (aarch64_reload_movcp<GPF_TF:mode><P:mode>)
	(aarch64_reload_movcp<VALL:mode><P:mode>, aarch64_reload_mov<mode>)
	(aarch64_movdi_<mode>low, aarch64_movdi_<mode>high)
	(aarch64_mov<mode>high_di, aarch64_mov<mode>low_di): Add a '@'
	character before the pattern name.
	* config/aarch64/aarch64-simd.md (aarch64_split_simd_mov<mode>)
	(aarch64_rsqrte<mode>, aarch64_rsqrts<mode>)
	(aarch64_simd_combine<mode>, aarch64_frecpe<mode>)
	(aarch64_frecps<mode>): Likewise.
	* config/aarch64/atomics.md (atomic_compare_and_swap<mode>)
	(aarch64_compare_and_swap<mode>, aarch64_compare_and_swap<mode>_lse)
	(aarch64_load_exclusive<mode>, aarch64_store_exclusive<mode>)
	(aarch64_atomic_swp<mode>, aarch64_atomic_cas<mode>)
	(aarch64_atomic_load<atomic_ldop><mode>): Likewise.

From-SVN: r263251
2018-08-02 10:59:35 +00:00

457 lines
14 KiB
C

/* Generate code to initialize optabs from machine description.
Copyright (C) 1993-2018 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 "gensupport.h"
#define DEF_RTL_EXPR(V, N, X, C) #V,
static const char * const rtx_upname[] = {
#include "rtl.def"
};
#undef DEF_RTL_EXPR
/* Vector in which to collect insns that match. */
static vec<optab_pattern> patterns;
static void
gen_insn (md_rtx_info *info)
{
optab_pattern p;
if (find_optab (&p, XSTR (info->def, 0)))
patterns.safe_push (p);
}
static int
pattern_cmp (const void *va, const void *vb)
{
const optab_pattern *a = (const optab_pattern *)va;
const optab_pattern *b = (const optab_pattern *)vb;
return a->sort_num - b->sort_num;
}
static int
optab_kind_cmp (const void *va, const void *vb)
{
const optab_def *a = (const optab_def *)va;
const optab_def *b = (const optab_def *)vb;
int diff = a->kind - b->kind;
if (diff == 0)
diff = a->op - b->op;
return diff;
}
static int
optab_rcode_cmp (const void *va, const void *vb)
{
const optab_def *a = (const optab_def *)va;
const optab_def *b = (const optab_def *)vb;
return a->rcode - b->rcode;
}
static const char *header_file_name = "init-opinit.h";
static const char *source_file_name = "init-opinit.c";
static bool
handle_arg (const char *arg)
{
switch (arg[1])
{
case 'h':
header_file_name = &arg[2];
return true;
case 'c':
source_file_name = &arg[2];
return true;
default:
return false;
}
}
static FILE *
open_outfile (const char *file_name)
{
FILE *f = fopen (file_name, "w");
if (!f)
fatal ("cannot open file %s: %s", file_name, xstrerror (errno));
fprintf (f,
"/* Generated automatically by the program `genopinit'\n"
" from the machine description file `md'. */\n\n");
return f;
}
/* Declare the maybe_code_for_* function for ONAME, and provide
an inline definition of the assserting code_for_* wrapper. */
static void
handle_overloaded_code_for (FILE *file, overloaded_name *oname)
{
fprintf (file, "\nextern insn_code maybe_code_for_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%s%s", i == 0 ? "" : ", ", oname->arg_types[i]);
fprintf (file, ");\n");
fprintf (file, "inline insn_code\ncode_for_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
fprintf (file, ")\n{\n insn_code code = maybe_code_for_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
fprintf (file,
");\n"
" gcc_assert (code != CODE_FOR_nothing);\n"
" return code;\n"
"}\n");
}
/* Declare the maybe_gen_* function for ONAME, and provide
an inline definition of the assserting gen_* wrapper. */
static void
handle_overloaded_gen (FILE *file, overloaded_name *oname)
{
pattern_stats stats;
get_pattern_stats (&stats, XVEC (oname->first_instance->insn, 1));
fprintf (file, "\nextern rtx maybe_gen_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%s%s", i == 0 ? "" : ", ", oname->arg_types[i]);
for (int i = 0; i < stats.num_generator_args; ++i)
fprintf (file, ", rtx");
fprintf (file, ");\n");
fprintf (file, "inline rtx\ngen_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%s%s arg%d", i == 0 ? "" : ", ", oname->arg_types[i], i);
for (int i = 0; i < stats.num_generator_args; ++i)
fprintf (file, ", rtx x%d", i);
fprintf (file, ")\n{\n rtx res = maybe_gen_%s (", oname->name);
for (unsigned int i = 0; i < oname->arg_types.length (); ++i)
fprintf (file, "%sarg%d", i == 0 ? "" : ", ", i);
for (int i = 0; i < stats.num_generator_args; ++i)
fprintf (file, ", x%d", i);
fprintf (file,
");\n"
" gcc_assert (res);\n"
" return res;\n"
"}\n");
}
int
main (int argc, const char **argv)
{
FILE *h_file, *s_file;
unsigned int i, j, n, last_kind[5];
optab_pattern *p;
progname = "genopinit";
if (NUM_OPTABS > 0xffff || MAX_MACHINE_MODE >= 0xff)
fatal ("genopinit range assumptions invalid");
if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
return (FATAL_EXIT_CODE);
h_file = open_outfile (header_file_name);
s_file = open_outfile (source_file_name);
/* Read the machine description. */
md_rtx_info info;
while (read_md_rtx (&info))
switch (GET_CODE (info.def))
{
case DEFINE_INSN:
case DEFINE_EXPAND:
gen_insn (&info);
break;
default:
break;
}
/* Sort the collected patterns. */
patterns.qsort (pattern_cmp);
/* Now that we've handled the "extra" patterns, eliminate them from
the optabs array. That way they don't get in the way below. */
n = num_optabs;
for (i = 0; i < n; )
if (optabs[i].base == NULL)
optabs[i] = optabs[--n];
else
++i;
/* Sort the (real) optabs. Better than forcing the optabs.def file to
remain sorted by kind. We also scrogged any real ordering with the
purging of the X patterns above. */
qsort (optabs, n, sizeof (optab_def), optab_kind_cmp);
fprintf (h_file, "#ifndef GCC_INSN_OPINIT_H\n");
fprintf (h_file, "#define GCC_INSN_OPINIT_H 1\n");
/* Emit the optab enumeration for the header file. */
fprintf (h_file, "enum optab_tag {\n");
for (i = j = 0; i < n; ++i)
{
optabs[i].op = i;
fprintf (h_file, " %s,\n", optabs[i].name);
if (optabs[i].kind != j)
last_kind[j++] = i - 1;
}
fprintf (h_file, " FIRST_CONV_OPTAB = %s,\n", optabs[last_kind[0]+1].name);
fprintf (h_file, " LAST_CONVLIB_OPTAB = %s,\n", optabs[last_kind[1]].name);
fprintf (h_file, " LAST_CONV_OPTAB = %s,\n", optabs[last_kind[2]].name);
fprintf (h_file, " FIRST_NORM_OPTAB = %s,\n", optabs[last_kind[2]+1].name);
fprintf (h_file, " LAST_NORMLIB_OPTAB = %s,\n", optabs[last_kind[3]].name);
fprintf (h_file, " LAST_NORM_OPTAB = %s\n", optabs[i-1].name);
fprintf (h_file, "};\n\n");
fprintf (h_file, "#define NUM_OPTABS %u\n", n);
fprintf (h_file, "#define NUM_CONVLIB_OPTABS %u\n",
last_kind[1] - last_kind[0]);
fprintf (h_file, "#define NUM_NORMLIB_OPTABS %u\n",
last_kind[3] - last_kind[2]);
fprintf (h_file, "#define NUM_OPTAB_PATTERNS %u\n",
(unsigned) patterns.length ());
fprintf (h_file,
"typedef enum optab_tag optab;\n"
"typedef enum optab_tag convert_optab;\n"
"typedef enum optab_tag direct_optab;\n"
"\n"
"struct optab_libcall_d\n"
"{\n"
" char libcall_suffix;\n"
" const char *libcall_basename;\n"
" void (*libcall_gen) (optab, const char *name,\n"
" char suffix, machine_mode);\n"
"};\n"
"\n"
"struct convert_optab_libcall_d\n"
"{\n"
" const char *libcall_basename;\n"
" void (*libcall_gen) (convert_optab, const char *name,\n"
" machine_mode, machine_mode);\n"
"};\n"
"\n"
"/* Given an enum insn_code, access the function to construct\n"
" the body of that kind of insn. */\n"
"#define GEN_FCN(CODE) (insn_data[CODE].genfun)\n"
"\n"
"#ifdef NUM_RTX_CODE\n"
"/* Contains the optab used for each rtx code, and vice-versa. */\n"
"extern const optab code_to_optab_[NUM_RTX_CODE];\n"
"extern const enum rtx_code optab_to_code_[NUM_OPTABS];\n"
"\n"
"static inline optab\n"
"code_to_optab (enum rtx_code code)\n"
"{\n"
" return code_to_optab_[code];\n"
"}\n"
"\n"
"static inline enum rtx_code\n"
"optab_to_code (optab op)\n"
"{\n"
" return optab_to_code_[op];\n"
"}\n");
for (overloaded_name *oname = rtx_reader_ptr->get_overloads ();
oname; oname = oname->next)
{
handle_overloaded_code_for (h_file, oname);
handle_overloaded_gen (h_file, oname);
}
fprintf (h_file,
"#endif\n"
"\n"
"extern const struct convert_optab_libcall_d convlib_def[NUM_CONVLIB_OPTABS];\n"
"extern const struct optab_libcall_d normlib_def[NUM_NORMLIB_OPTABS];\n"
"\n"
"/* Returns the active icode for the given (encoded) optab. */\n"
"extern enum insn_code raw_optab_handler (unsigned);\n"
"extern bool swap_optab_enable (optab, machine_mode, bool);\n"
"\n"
"/* Target-dependent globals. */\n"
"struct target_optabs {\n"
" /* Patterns that are used by optabs that are enabled for this target. */\n"
" bool pat_enable[NUM_OPTAB_PATTERNS];\n"
"\n"
" /* Cache if the target supports vec_gather_load for at least one vector\n"
" mode. */\n"
" bool supports_vec_gather_load;\n"
" bool supports_vec_gather_load_cached;\n"
" bool supports_vec_scatter_store;\n"
" bool supports_vec_scatter_store_cached;\n"
"};\n"
"extern void init_all_optabs (struct target_optabs *);\n"
"\n"
"extern struct target_optabs default_target_optabs;\n"
"extern struct target_optabs *this_fn_optabs;\n"
"#if SWITCHABLE_TARGET\n"
"extern struct target_optabs *this_target_optabs;\n"
"#else\n"
"#define this_target_optabs (&default_target_optabs)\n"
"#endif\n");
fprintf (s_file,
"#define IN_TARGET_CODE 1\n"
"#include \"config.h\"\n"
"#include \"system.h\"\n"
"#include \"coretypes.h\"\n"
"#include \"backend.h\"\n"
"#include \"predict.h\"\n"
"#include \"tree.h\"\n"
"#include \"rtl.h\"\n"
"#include \"alias.h\"\n"
"#include \"varasm.h\"\n"
"#include \"stor-layout.h\"\n"
"#include \"calls.h\"\n"
"#include \"memmodel.h\"\n"
"#include \"tm_p.h\"\n"
"#include \"flags.h\"\n"
"#include \"insn-config.h\"\n"
"#include \"expmed.h\"\n"
"#include \"dojump.h\"\n"
"#include \"explow.h\"\n"
"#include \"emit-rtl.h\"\n"
"#include \"stmt.h\"\n"
"#include \"expr.h\"\n"
"#include \"insn-codes.h\"\n"
"#include \"optabs.h\"\n"
"\n"
"struct optab_pat {\n"
" unsigned scode;\n"
" enum insn_code icode;\n"
"};\n\n");
fprintf (s_file,
"static const struct optab_pat pats[NUM_OPTAB_PATTERNS] = {\n");
for (i = 0; patterns.iterate (i, &p); ++i)
fprintf (s_file, " { %#08x, CODE_FOR_%s },\n", p->sort_num, p->name);
fprintf (s_file, "};\n\n");
fprintf (s_file, "void\ninit_all_optabs (struct target_optabs *optabs)\n{\n");
fprintf (s_file, " bool *ena = optabs->pat_enable;\n");
for (i = 0; patterns.iterate (i, &p); ++i)
fprintf (s_file, " ena[%u] = HAVE_%s;\n", i, p->name);
fprintf (s_file, "}\n\n");
/* Perform a binary search on a pre-encoded optab+mode*2. */
/* ??? Perhaps even better to generate a minimal perfect hash.
Using gperf directly is awkward since it's so geared to working
with strings. Plus we have no visibility into the ordering of
the hash entries, which complicates the pat_enable array. */
fprintf (s_file,
"static int\n"
"lookup_handler (unsigned scode)\n"
"{\n"
" int l = 0, h = ARRAY_SIZE (pats), m;\n"
" while (h > l)\n"
" {\n"
" m = (h + l) / 2;\n"
" if (scode == pats[m].scode)\n"
" return m;\n"
" else if (scode < pats[m].scode)\n"
" h = m;\n"
" else\n"
" l = m + 1;\n"
" }\n"
" return -1;\n"
"}\n\n");
fprintf (s_file,
"enum insn_code\n"
"raw_optab_handler (unsigned scode)\n"
"{\n"
" int i = lookup_handler (scode);\n"
" return (i >= 0 && this_fn_optabs->pat_enable[i]\n"
" ? pats[i].icode : CODE_FOR_nothing);\n"
"}\n\n");
fprintf (s_file,
"bool\n"
"swap_optab_enable (optab op, machine_mode m, bool set)\n"
"{\n"
" unsigned scode = (op << 16) | m;\n"
" int i = lookup_handler (scode);\n"
" if (i >= 0)\n"
" {\n"
" bool ret = this_fn_optabs->pat_enable[i];\n"
" this_fn_optabs->pat_enable[i] = set;\n"
" return ret;\n"
" }\n"
" else\n"
" {\n"
" gcc_assert (!set);\n"
" return false;\n"
" }\n"
"}\n\n");
/* C++ (even G++) does not support (non-trivial) designated initializers.
To work around that, generate these arrays programatically rather than
by our traditional multiple inclusion of def files. */
fprintf (s_file,
"const struct convert_optab_libcall_d "
"convlib_def[NUM_CONVLIB_OPTABS] = {\n");
for (i = last_kind[0] + 1; i <= last_kind[1]; ++i)
fprintf (s_file, " { %s, %s },\n", optabs[i].base, optabs[i].libcall);
fprintf (s_file, "};\n\n");
fprintf (s_file,
"const struct optab_libcall_d "
"normlib_def[NUM_NORMLIB_OPTABS] = {\n");
for (i = last_kind[2] + 1; i <= last_kind[3]; ++i)
fprintf (s_file, " { %s, %s, %s },\n",
optabs[i].suffix, optabs[i].base, optabs[i].libcall);
fprintf (s_file, "};\n\n");
fprintf (s_file, "enum rtx_code const optab_to_code_[NUM_OPTABS] = {\n");
for (i = 0; i < n; ++i)
fprintf (s_file, " %s,\n", rtx_upname[optabs[i].fcode]);
fprintf (s_file, "};\n\n");
qsort (optabs, n, sizeof (optab_def), optab_rcode_cmp);
fprintf (s_file, "const optab code_to_optab_[NUM_RTX_CODE] = {\n");
for (j = 0; optabs[j].rcode == UNKNOWN; ++j)
continue;
for (i = 0; i < NON_GENERATOR_NUM_RTX_CODE; ++i)
{
if (j < n && optabs[j].rcode == i)
fprintf (s_file, " %s,\n", optabs[j++].name);
else
fprintf (s_file, " unknown_optab,\n");
}
fprintf (s_file, "};\n\n");
fprintf (h_file, "#endif\n");
return (fclose (h_file) == 0 && fclose (s_file) == 0
? SUCCESS_EXIT_CODE : FATAL_EXIT_CODE);
}