summaryrefslogtreecommitdiff
path: root/gcc/genpreds.c
diff options
context:
space:
mode:
authorzack <zack@138bc75d-0d04-0410-961f-82ee72b054a4>2004-08-12 07:49:00 +0000
committerzack <zack@138bc75d-0d04-0410-961f-82ee72b054a4>2004-08-12 07:49:00 +0000
commitcbf464bd936b926a34bbc04e8ccd39afd4c5815a (patch)
tree35550b19b3978c830f6043f43687018fe8198d2d /gcc/genpreds.c
parent307719b97143646f73530f046493efc507351385 (diff)
downloadgcc-cbf464bd936b926a34bbc04e8ccd39afd4c5815a.tar.gz
* genpreds.c: Add capability to generate predicate bodies as
well as function prototypes. Write function prototypes for the generic predicates too. (process_define_predicate, write_tm_preds_h, write_insn_preds_c) (write_predicate_subfunction, mark_mode_tests, add_mode_tests) (write_match_code, write_predicate_expr, write_one_predicate_function) (parse_option): New functions. (output_predicate_decls): Delete. (main): Read the machine description, process DEFINE_PREDICATE or DEFINE_SPECIAL_PREDICATE patterns, write tm-preds.h or insn-preds.c as appropriate. * genrecog.c (struct decision_test): Replace index with struct pred_data pointer. (next_index): Remove, unused. (pred_table, preds, special_mode_pred_table): Delete. (compute_predicate_codes, process_define_predicate): New functions. (validate_pattern, add_to_sequence, write_switch): Update for new data structures. (main): Handle DEFINE_PREDICATE and DEFINE_SPECIAL_PREDICATE. Check both error_count and have_error. * gensupport.c (in_fname, first_predicate): New globals. (define_pred_queue, define_pred_tail): New RTL-pattern queue. (predicate_table, last_predicate, old_pred_table) (old_special_pred_table): New statics. (hash_struct_pred_data, eq_struct_pred_data, lookup_predicate) (add_predicate, init_predicate_table): New functions. (process_rtx): Handle DEFINE_PREDICATE and DEFINE_SPECIAL_PREDICATE. (init_md_reader_args_cb): Use the global in_fname. No need to zero it or max_include_len. Call init_predicate_table. (read_rtx): Run the predicate queue after the attribute queue but before all the others. * gensupport.h (in_fname, struct pred_data, first_predicate) (lookup_predicate, add_predicate, FOR_ALL_PREDICATES): Declare. * rtl.def (MATCH_CODE, MATCH_TEST, DEFINE_PREDICATE) (DEFINE_SPECIAL_PREDICATE): New RTL codes. * dummy-conditions.c: Don't include bconfig.h, system.h, coretypes.h, tm.h, or system.h. Do include stddef.h. Duplicate declaration of struct c_test from gensupport.h. * Makefile.in (OBJS-common): Add insn-preds.o. (STAGESTUFF, .PRECIOUS): Add insn-preds.c. (insn-preds.c, insn-preds.o): New rules. (s-preds): Also generate insn-preds.c. (dummy-conditions.o, genpreds$(build_exeext), genpreds.o): Update dependencies. (print-rtl.o, print-rtl1.o): Correct dependencies. * recog.h: Delete prototypes of predicate functions. * doc/md.texi (Predicates): New section with complete documentation of operand/operator predicates. Remove some incomplete documentation of predicates from other places. * doc/tm.texi (Misc): Move SPECIAL_MODE_PREDICATES next to PREDICATE_CODES; indicate that both are deprecated in favor of define_predicate/define_special_predicate. * config/ia64/ia64.c: All predicate function definitions moved to ia64.md, except (small_addr_symbolic_operand, tls_symbolic_operand): Delete. (ia64_expand_load_address, ia64_expand_move): Check SYMBOL_REF_TLS_MODEL directly, don't use tls_symbolic_operand. * config/ia64/ia64.md: All predicates now defined here. (symbolic_operand): Is now a special predicate. * config/ia64/ia64.h: Declare ia64_section_threshold. (PREDICATE_CODES): Delete. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@85855 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/genpreds.c')
-rw-r--r--gcc/genpreds.c475
1 files changed, 448 insertions, 27 deletions
diff --git a/gcc/genpreds.c b/gcc/genpreds.c
index b5601208df8..833d47f907b 100644
--- a/gcc/genpreds.c
+++ b/gcc/genpreds.c
@@ -1,8 +1,8 @@
/* Generate from machine description:
- - some macros CODE_FOR_... giving the insn_code_number value
- for each of the defined standard insn names.
- Copyright (C) 1987, 1991, 1995, 1998,
- 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ - prototype declarations for operand predicates (tm-preds.h)
+ - function definitions of operand predicates, if defined new-style
+ (insn-preds.c)
+ Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
@@ -26,40 +26,461 @@ Boston, MA 02111-1307, USA. */
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
+#include "errors.h"
+#include "gensupport.h"
+#include "obstack.h"
+/* The new way to declare predicates is with (define_predicate) or
+ (define_special_predicate) expressions in the machine description.
+ This provides a function body as well as a name. */
static void
-output_predicate_decls (void)
+process_define_predicate (rtx defn)
{
-#ifdef PREDICATE_CODES
- static const struct {
- const char *const name;
- const RTX_CODE codes[NUM_RTX_CODE];
- } predicate[] = {
- PREDICATE_CODES
- };
- size_t i;
-
- puts ("#ifdef RTX_CODE\n");
- for (i = 0; i < ARRAY_SIZE (predicate); i++)
- printf ("extern int %s (rtx, enum machine_mode);\n",
- predicate[i].name);
- puts ("\n#endif /* RTX_CODE */\n");
-#endif
+ struct pred_data *pred;
+ if (XEXP (defn, 1) == 0)
+ {
+ error ("%s: must give a predicate expression", XSTR (defn, 0));
+ return;
+ }
+
+ pred = xcalloc (sizeof (struct pred_data), 1);
+ pred->name = XSTR (defn, 0);
+ pred->exp = XEXP (defn, 1);
+ pred->c_block = XSTR (defn, 2);
+
+ if (GET_CODE (defn) == DEFINE_SPECIAL_PREDICATE)
+ pred->special = true;
+
+ add_predicate (pred);
+}
+
+/* Write tm-preds.h. Unfortunately, it is impossible to forward-declare
+ an enumeration in portable C, so we have to condition all these
+ prototypes on HAVE_MACHINE_MODES. */
+static void
+write_tm_preds_h (void)
+{
+ struct pred_data *p;
+
+ printf ("\
+/* Generated automatically by the program '%s'\n\
+ from the machine description file '%s'. */\n\n", progname, in_fname);
+
+ puts ("\
+#ifndef GCC_TM_PREDS_H\n\
+#define GCC_TM_PREDS_H\n\
+\n\
+#ifdef HAVE_MACHINE_MODES");
+
+ FOR_ALL_PREDICATES (p)
+ printf ("extern int %s (rtx, enum machine_mode);\n", p->name);
+
+ puts ("\
+#endif /* HAVE_MACHINE_MODES */\n\
+#endif /* tm-preds.h */");
+}
+
+/* Given a predicate, if it has an embedded C block, write the block
+ out as a static inline subroutine, and augment the RTL test with a
+ match_test that calls that subroutine. For instance,
+
+ (define_predicate "basereg_operand"
+ (match_operand 0 "register_operand")
+ {
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_POINTER (op);
+ })
+
+ becomes
+
+ static inline int basereg_operand_1(rtx op, enum machine_mode mode)
+ {
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_POINTER (op);
+ }
+
+ (define_predicate "basereg_operand"
+ (and (match_operand 0 "register_operand")
+ (match_test "basereg_operand_1 (op, mode)")))
+
+ The only wart is that there's no way to insist on a { } string in
+ an RTL template, so we have to handle "" strings. */
+
+
+static void
+write_predicate_subfunction (struct pred_data *p)
+{
+ const char *match_test_str;
+ rtx match_test_exp, and_exp;
+
+ if (p->c_block[0] == '\0')
+ return;
+
+ /* Construct the function-call expression. */
+ obstack_grow (rtl_obstack, p->name, strlen (p->name));
+ obstack_grow (rtl_obstack, "_1 (op, mode)",
+ sizeof "_1 (op, mode)");
+ match_test_str = obstack_finish (rtl_obstack);
+
+ /* Add the function-call expression to the complete expression to be
+ evaluated. */
+ match_test_exp = rtx_alloc (MATCH_TEST);
+ XSTR (match_test_exp, 0) = match_test_str;
+
+ and_exp = rtx_alloc (AND);
+ XEXP (and_exp, 0) = p->exp;
+ XEXP (and_exp, 1) = match_test_exp;
+
+ p->exp = and_exp;
+
+ printf ("static inline int\n"
+ "%s_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)\n",
+ p->name);
+ if (p->c_block[0] == '{')
+ fputs (p->c_block, stdout);
+ else
+ printf ("{\n %s\n}", p->c_block);
+ fputs ("\n\n", stdout);
+}
+
+/* Given an RTL expression EXP, find all subexpressions which we may
+ assume to perform mode tests. Normal MATCH_OPERAND does;
+ MATCH_CODE does if and only if it accepts CONST_INT or
+ CONST_DOUBLE; and we have to assume that MATCH_TEST does not.
+ These combine in almost-boolean fashion - the only exception is
+ that (not X) must be assumed not to perform a mode test, whether or
+ not X does.
+
+ The mark is the RTL /v flag, which is true for subexpressions which
+ do *not* perform mode tests.
+*/
+#define NO_MODE_TEST(EXP) RTX_FLAG (EXP, volatil)
+static void
+mark_mode_tests (rtx exp)
+{
+ switch (GET_CODE (exp))
+ {
+ case MATCH_OPERAND:
+ {
+ struct pred_data *p = lookup_predicate (XSTR (exp, 1));
+ if (!p)
+ error ("reference to undefined predicate '%s'", XSTR (exp, 1));
+ else if (p->special)
+ NO_MODE_TEST (exp) = 1;
+ }
+ break;
+
+ case MATCH_CODE:
+ if (!strstr (XSTR (exp, 0), "const_int")
+ && !strstr (XSTR (exp, 0), "const_double"))
+ NO_MODE_TEST (exp) = 1;
+ break;
+
+ case MATCH_TEST:
+ case NOT:
+ NO_MODE_TEST (exp) = 1;
+ break;
+
+ case AND:
+ mark_mode_tests (XEXP (exp, 0));
+ mark_mode_tests (XEXP (exp, 1));
+
+ NO_MODE_TEST (exp) = (NO_MODE_TEST (XEXP (exp, 0))
+ && NO_MODE_TEST (XEXP (exp, 1)));
+ break;
+
+ case IOR:
+ mark_mode_tests (XEXP (exp, 0));
+ mark_mode_tests (XEXP (exp, 1));
+
+ NO_MODE_TEST (exp) = (NO_MODE_TEST (XEXP (exp, 0))
+ || NO_MODE_TEST (XEXP (exp, 1)));
+ break;
+
+ case IF_THEN_ELSE:
+ /* A ? B : C does a mode test if (one of A and B) does a mode
+ test, and C does too. */
+ mark_mode_tests (XEXP (exp, 0));
+ mark_mode_tests (XEXP (exp, 1));
+ mark_mode_tests (XEXP (exp, 2));
+
+ NO_MODE_TEST (exp) = ((NO_MODE_TEST (XEXP (exp, 0))
+ && NO_MODE_TEST (XEXP (exp, 1)))
+ || NO_MODE_TEST (XEXP (exp, 2)));
+ break;
+
+ default:
+ error ("'%s' cannot be used in a define_predicate expression",
+ GET_RTX_NAME (GET_CODE (exp)));
+ }
+}
+
+/* Given a predicate, work out where in its RTL expression to add
+ tests for proper modes. Special predicates do not get any such
+ tests. We try to avoid adding tests when we don't have to; in
+ particular, other normal predicates can be counted on to do it for
+ us. */
+
+static void
+add_mode_tests (struct pred_data *p)
+{
+ rtx match_test_exp, and_exp;
+ rtx *pos;
+
+ /* Don't touch special predicates. */
+ if (p->special)
+ return;
+
+ mark_mode_tests (p->exp);
+
+ /* If the whole expression already tests the mode, we're done. */
+ if (!NO_MODE_TEST (p->exp))
+ return;
+
+ match_test_exp = rtx_alloc (MATCH_TEST);
+ XSTR (match_test_exp, 0) = "mode == VOIDmode || GET_MODE (op) == mode";
+ and_exp = rtx_alloc (AND);
+ XEXP (and_exp, 1) = match_test_exp;
+
+ /* It is always correct to rewrite p->exp as
+
+ (and (...) (match_test "mode == VOIDmode || GET_MODE (op) == mode"))
+
+ but there are a couple forms where we can do better. If the
+ top-level pattern is an IOR, and one of the two branches does test
+ the mode, we can wrap just the branch that doesn't. Likewise, if
+ we have an IF_THEN_ELSE, and one side of it tests the mode, we can
+ wrap just the side that doesn't. And, of course, we can repeat this
+ descent as many times as it works. */
+
+ pos = &p->exp;
+ for (;;)
+ {
+ rtx subexp = *pos;
+ if (GET_CODE (subexp) == IOR)
+ {
+ if (NO_MODE_TEST (XEXP (subexp, 0))
+ && NO_MODE_TEST (XEXP (subexp, 1)))
+ break;
+ else if (NO_MODE_TEST (XEXP (subexp, 0)))
+ pos = &XEXP (subexp, 0);
+ else if (NO_MODE_TEST (XEXP (subexp, 1)))
+ pos = &XEXP (subexp, 1);
+ else
+ abort ();
+ }
+ else if (GET_CODE (subexp) == IF_THEN_ELSE)
+ {
+ if (NO_MODE_TEST (XEXP (subexp, 0))
+ && NO_MODE_TEST (XEXP (subexp, 1))
+ && NO_MODE_TEST (XEXP (subexp, 2)))
+ break;
+ else if (NO_MODE_TEST (XEXP (subexp, 0))
+ && NO_MODE_TEST (XEXP (subexp, 1)))
+ /* Must put it on the dependent clause, not the controlling
+ expression, or we change the meaning of the test. */
+ pos = &XEXP (subexp, 1);
+ else if (NO_MODE_TEST (XEXP (subexp, 2)))
+ pos = &XEXP (subexp, 2);
+ else
+ abort ();
+ }
+ else
+ break;
+ }
+
+ XEXP (and_exp, 0) = *pos;
+ *pos = and_exp;
+}
+
+
+/* CODES is a list of RTX codes. Write out an expression which
+ determines whether the operand has one of those codes. */
+static void
+write_match_code (const char *codes)
+{
+ const char *code;
+
+ while ((code = scan_comma_elt (&codes)) != 0)
+ {
+ fputs ("GET_CODE (op) == ", stdout);
+ while (code < codes)
+ {
+ putchar (TOUPPER (*code));
+ code++;
+ }
+
+ if (*codes == ',')
+ fputs (" || ", stdout);
+ }
+}
+
+/* EXP is an RTL (sub)expression for a predicate. Recursively
+ descend the expression and write out an equivalent C expression. */
+static void
+write_predicate_expr (const char *name, rtx exp)
+{
+ switch (GET_CODE (exp))
+ {
+ case AND:
+ putchar ('(');
+ write_predicate_expr (name, XEXP (exp, 0));
+ fputs (") && (", stdout);
+ write_predicate_expr (name, XEXP (exp, 1));
+ putchar (')');
+ break;
+
+ case IOR:
+ putchar ('(');
+ write_predicate_expr (name, XEXP (exp, 0));
+ fputs (") || (", stdout);
+ write_predicate_expr (name, XEXP (exp, 1));
+ putchar (')');
+ break;
+
+ case NOT:
+ fputs ("!(", stdout);
+ write_predicate_expr (name, XEXP (exp, 0));
+ putchar (')');
+ break;
+
+ case IF_THEN_ELSE:
+ putchar ('(');
+ write_predicate_expr (name, XEXP (exp, 0));
+ fputs (") ? (", stdout);
+ write_predicate_expr (name, XEXP (exp, 1));
+ fputs (") : (", stdout);
+ write_predicate_expr (name, XEXP (exp, 2));
+ putchar (')');
+ break;
+
+ case MATCH_OPERAND:
+ printf ("%s (op, mode)", XSTR (exp, 1));
+ break;
+
+ case MATCH_CODE:
+ write_match_code (XSTR (exp, 0));
+ break;
+
+ case MATCH_TEST:
+ fputs (XSTR (exp, 0), stdout);
+ break;
+
+ default:
+ error ("%s: cannot use '%s' in a predicate expression",
+ name, GET_RTX_NAME (GET_CODE (exp)));
+ putchar ('0');
+ }
+}
+
+/* Given a predicate, write out a complete C function to compute it. */
+static void
+write_one_predicate_function (struct pred_data *p)
+{
+ if (!p->exp)
+ return;
+
+ write_predicate_subfunction (p);
+ add_mode_tests (p);
+
+ /* A normal predicate can legitimately not look at enum machine_mode
+ if it accepts only CONST_INTs and/or CONST_DOUBLEs. */
+ printf ("int\n%s (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)\n"
+ "{\n return ",
+ p->name);
+ write_predicate_expr (p->name, p->exp);
+ fputs (";\n}\n\n", stdout);
}
+/* Write insn-preds.c.
+ N.B. the list of headers to include was copied from genrecog; it
+ may not be ideal.
+
+ FUTURE: Write #line markers referring back to the machine
+ description. (Can't practically do this now since we don't know
+ the line number of the C block - just the line number of the enclosing
+ expression.) */
+static void
+write_insn_preds_c (void)
+{
+ struct pred_data *p;
+
+ printf ("\
+/* Generated automatically by the program '%s'\n\
+ from the machine description file '%s'. */\n\n", progname, in_fname);
+
+ puts ("\
+#include \"config.h\"\n\
+#include \"system.h\"\n\
+#include \"coretypes.h\"\n\
+#include \"tm.h\"\n\
+#include \"rtl.h\"\n\
+#include \"tm_p.h\"\n\
+#include \"function.h\"\n\
+#include \"insn-config.h\"\n\
+#include \"recog.h\"\n\
+#include \"real.h\"\n\
+#include \"output.h\"\n\
+#include \"flags.h\"\n\
+#include \"hard-reg-set.h\"\n\
+#include \"resource.h\"\n\
+#include \"toplev.h\"\n\
+#include \"reload.h\"\n");
+
+ FOR_ALL_PREDICATES (p)
+ write_one_predicate_function (p);
+}
+
+/* Argument parsing. */
+static bool gen_header;
+static bool
+parse_option (const char *opt)
+{
+ if (!strcmp (opt, "-h"))
+ {
+ gen_header = true;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Master control. */
int
-main (void)
+main (int argc, char **argv)
{
- puts ("/* Generated automatically by the program `genpreds'. */\n");
- puts ("#ifndef GCC_TM_PREDS_H");
- puts ("#define GCC_TM_PREDS_H\n");
+ rtx defn;
+ int pattern_lineno, next_insn_code = 0;
- output_predicate_decls ();
+ progname = argv[0];
+ if (argc <= 1)
+ fatal ("no input file name");
+ if (init_md_reader_args_cb (argc, argv, parse_option) != SUCCESS_EXIT_CODE)
+ return FATAL_EXIT_CODE;
+
+ while ((defn = read_md_rtx (&pattern_lineno, &next_insn_code)) != 0)
+ {
+ if (GET_CODE (defn) == DEFINE_PREDICATE
+ || GET_CODE (defn) == DEFINE_SPECIAL_PREDICATE)
+ process_define_predicate (defn);
+ }
- puts ("#endif /* GCC_TM_PREDS_H */");
+ if (gen_header)
+ write_tm_preds_h ();
+ else
+ write_insn_preds_c ();
- if (ferror (stdout) || fflush (stdout) || fclose (stdout))
+ if (have_error || ferror (stdout) || fflush (stdout) || fclose (stdout))
return FATAL_EXIT_CODE;
return SUCCESS_EXIT_CODE;
}
+
+/* Dummy for debugging purposes. */
+const char *
+get_insn_name (int code ATTRIBUTE_UNUSED)
+{
+ return 0;
+}