diff options
author | Zack Weinberg <zack@gcc.gnu.org> | 2004-08-12 07:49:00 +0000 |
---|---|---|
committer | Zack Weinberg <zack@gcc.gnu.org> | 2004-08-12 07:49:00 +0000 |
commit | e543e219eb1865d4230278c3db3d2bedd60b1de0 (patch) | |
tree | 35550b19b3978c830f6043f43687018fe8198d2d /gcc/genpreds.c | |
parent | b4d49f49bf21837bef59aa30788e2e9bf2ce2e3b (diff) | |
download | gcc-e543e219eb1865d4230278c3db3d2bedd60b1de0.zip gcc-e543e219eb1865d4230278c3db3d2bedd60b1de0.tar.gz gcc-e543e219eb1865d4230278c3db3d2bedd60b1de0.tar.bz2 |
genpreds.c: Add capability to generate predicate bodies as well as function prototypes.
* 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.
From-SVN: r85855
Diffstat (limited to 'gcc/genpreds.c')
-rw-r--r-- | gcc/genpreds.c | 475 |
1 files changed, 448 insertions, 27 deletions
diff --git a/gcc/genpreds.c b/gcc/genpreds.c index b560120..833d47f9 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; +} |