aboutsummaryrefslogtreecommitdiff
path: root/gcc/genrecog.c
diff options
context:
space:
mode:
authorRichard Kenner <kenner@gcc.gnu.org>1991-11-24 14:57:16 -0500
committerRichard Kenner <kenner@gcc.gnu.org>1991-11-24 14:57:16 -0500
commitec65fa669004405ecf6b41c3d87ff142607c1755 (patch)
tree92d3175d318a2e430f18fc90fcaadfc95f273c40 /gcc/genrecog.c
parent1af1688bcde2b1df29d8edd65676d72dc416cb48 (diff)
downloadgcc-ec65fa669004405ecf6b41c3d87ff142607c1755.zip
gcc-ec65fa669004405ecf6b41c3d87ff142607c1755.tar.gz
gcc-ec65fa669004405ecf6b41c3d87ff142607c1755.tar.bz2
Initial revision
From-SVN: r71
Diffstat (limited to 'gcc/genrecog.c')
-rw-r--r--gcc/genrecog.c1339
1 files changed, 1339 insertions, 0 deletions
diff --git a/gcc/genrecog.c b/gcc/genrecog.c
new file mode 100644
index 0000000..db4bb41
--- /dev/null
+++ b/gcc/genrecog.c
@@ -0,0 +1,1339 @@
+/* Generate code from machine description to recognize rtl as insns.
+ Copyright (C) 1987-1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This program is used to produce insn-recog.c, which contains
+ a function called `recog' plus its subroutines.
+ These functions contain a decision tree
+ that recognizes whether an rtx, the argument given to recog,
+ is a valid instruction.
+
+ recog returns -1 if the rtx is not valid.
+ If the rtx is valid, recog returns a nonnegative number
+ which is the insn code number for the pattern that matched.
+ This is the same as the order in the machine description of the
+ entry that matched. This number can be used as an index into various
+ insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands
+ (found in insn-output.c).
+
+ The third argument to recog is an optional pointer to an int.
+ If present, recog will accept a pattern if it matches except for
+ missing CLOBBER expressions at the end. In that case, the value
+ pointed to by the optional pointer will be set to the number of
+ CLOBBERs that need to be added (it should be initialized to zero by
+ the caller). If it is set nonzero, the caller should allocate a
+ PARALLEL of the appropriate size, copy the initial entries, and call
+ add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.
+
+ This program also generates the function `split_insns',
+ which returns 0 if the rtl could not be split, or
+ it returns the split rtl in a SEQUENCE. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+
+static struct obstack obstack;
+struct obstack *rtl_obstack = &obstack;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern void free ();
+
+/* Data structure for decision tree for recognizing
+ legitimate instructions. */
+
+struct decision
+{
+ int number;
+ char *position;
+ RTX_CODE code;
+ char *exact;
+ enum machine_mode mode;
+ char *tests;
+ int insn_code_number;
+ int num_clobbers_to_add;
+ struct decision *next;
+ struct decision *success;
+ int opno;
+ int dupno;
+ int test_elt_zero_int;
+ int elt_zero_int;
+ int test_elt_one_int;
+ int elt_one_int;
+ int ignmode;
+ struct decision *afterward;
+ int label_needed;
+ char *c_test;
+ char enforce_mode;
+ int veclen;
+ int subroutine_number;
+ /* Used for DEFINE_SPLITs. */
+ char *c_hook;
+ rtx split_sequence;
+};
+
+#define SUBROUTINE_THRESHOLD 50
+
+static int next_subroutine_number;
+
+/* We can write two types of subroutines: One for insn recognition and
+ one to split insns. This defines which type is being written. */
+
+enum routine_type {RECOG, SPLIT};
+
+static int try_merge_1 ();
+static int no_same_mode ();
+static int same_codes ();
+static int same_modes ();
+
+/*
+static int
+recognize (top)
+{
+ staten:
+ x = XVECEXP (top, 0, 3);
+ if (test_code (GET_CODE (x))
+ && test_mode (MODE (x))
+ && whatever_else)
+ goto statep;
+ else if (next one...)
+ goto statem:
+ goto stater;
+
+ statep:
+ actions...;
+ return 1;
+
+ statem:
+ x = stack[depth--];
+ more tests...;
+
+ stateq:
+ stack[++depth] = x;
+ x = XEXP (stack[depth], 0);
+ more tests...;
+
+ stater:
+ x = XEXP (stack[depth], 1);
+}
+
+*/
+
+static int next_number;
+
+static int next_insn_code;
+
+static int next_index;
+
+char *xmalloc ();
+static struct decision *add_to_sequence ();
+static struct decision *merge_trees ();
+static struct decision *try_merge_2 ();
+static void write_subroutine ();
+static void print_code ();
+static void clear_codes ();
+static void clear_modes ();
+static void change_state ();
+static void write_tree ();
+static char *copystr ();
+static char *concat ();
+static void fatal ();
+void fancy_abort ();
+static void mybzero ();
+
+static struct decision *first;
+
+/* Construct and return a sequence of decisions
+ that will recognize INSN. */
+
+static struct decision *
+make_insn_sequence (insn)
+ rtx insn;
+{
+ rtx x;
+ char *c_test = XSTR (insn, 2);
+ struct decision *last;
+
+ if (XVECLEN (insn, 1) == 1)
+ x = XVECEXP (insn, 1, 0);
+ else
+ {
+ x = rtx_alloc (PARALLEL);
+ XVEC (x, 0) = XVEC (insn, 1);
+ PUT_MODE (x, VOIDmode);
+ }
+
+ last = add_to_sequence (x, 0, "");
+
+ if (c_test[0])
+ last->c_test = c_test;
+ last->insn_code_number = next_insn_code;
+ last->num_clobbers_to_add = 0;
+
+ /* If X is a PARALLEL, see if it ends with a group of CLOBBERs of (hard)
+ registers or MATCH_SCRATCHes. If so, set up to recognize the pattern
+ without these CLOBBERs. */
+
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int i;
+
+ for (i = XVECLEN (x, 0); i > 0; i--)
+ if (GET_CODE (XVECEXP (x, 0, i - 1)) != CLOBBER
+ || (GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 0)) != REG
+ && GET_CODE (XEXP (XVECEXP (x, 0, i - 1), 0)) != MATCH_SCRATCH))
+ break;
+
+ if (i != XVECLEN (x, 0))
+ {
+ rtx new;
+ struct decision *previous_first = first;
+
+ if (i == 1)
+ new = XVECEXP (x, 0, 0);
+ else
+ {
+ int j;
+
+ new = rtx_alloc (PARALLEL);
+ XVEC (new, 0) = rtvec_alloc (i);
+ for (j = i - 1; j >= 0; j--)
+ XVECEXP (new, 0, j) = XVECEXP (x, 0, j);
+ }
+
+ last = add_to_sequence (new, 0, "");
+
+ if (c_test[0])
+ last->c_test = c_test;
+ last->insn_code_number = next_insn_code;
+ last->num_clobbers_to_add = XVECLEN (x, 0) - i;
+ first = merge_trees (previous_first, first);
+ }
+ }
+
+ next_insn_code++;
+ return first;
+}
+
+static struct decision *
+make_split_sequence (insn)
+ rtx insn;
+{
+ rtx x;
+ char *c_test = XSTR (insn, 1);
+ char *c_hook = XSTR (insn, 3);
+ struct decision *last;
+
+ if (XVECLEN (insn, 0) == 1)
+ x = XVECEXP (insn, 0, 0);
+ else
+ {
+ x = rtx_alloc (PARALLEL);
+ XVEC (x, 0) = XVEC (insn, 0);
+ PUT_MODE (x, VOIDmode);
+ }
+
+ last = add_to_sequence (x, 0, "");
+
+ if (c_test[0])
+ last->c_test = c_test;
+ if (c_hook != 0 && c_hook[0] != 0)
+ last->c_hook = c_hook;
+ last->split_sequence = XEXP (insn, 2);
+ last->insn_code_number = next_insn_code++;
+
+ /* Define the subroutine we will call below and emit in genemit. */
+ printf ("extern rtx gen_split_%d ();\n", last->insn_code_number);
+
+ return first;
+}
+
+static struct decision *
+add_to_sequence (pattern, last, position)
+ rtx pattern;
+ struct decision *last;
+ char *position;
+{
+ register RTX_CODE code;
+ register struct decision *new
+ = (struct decision *) xmalloc (sizeof (struct decision));
+ struct decision *this;
+ char *newpos;
+ register char *fmt;
+ register int i;
+ int depth;
+ int len;
+
+ new->number = next_number++;
+ new->position = copystr (position);
+ new->exact = 0;
+ new->next = 0;
+ new->success = 0;
+ new->insn_code_number = -1;
+ new->num_clobbers_to_add = 0;
+ new->tests = 0;
+ new->opno = -1;
+ new->dupno = -1;
+ new->test_elt_zero_int = 0;
+ new->test_elt_one_int = 0;
+ new->elt_zero_int = 0;
+ new->elt_one_int = 0;
+ new->enforce_mode = 0;
+ new->ignmode = 0;
+ new->afterward = 0;
+ new->label_needed = 0;
+ new->c_test = 0;
+ new->c_hook = 0;
+ new->split_sequence = 0;
+ new->veclen = 0;
+ new->subroutine_number = 0;
+
+ this = new;
+
+ if (last == 0)
+ first = new;
+ else
+ last->success = new;
+
+ depth = strlen (position);
+ newpos = (char *) alloca (depth + 2);
+ strcpy (newpos, position);
+ newpos[depth + 1] = 0;
+
+ restart:
+
+ if (pattern == 0)
+ {
+ new->exact = "0";
+ new->code = UNKNOWN;
+ new->mode = VOIDmode;
+ return new;
+ }
+
+ new->mode = GET_MODE (pattern);
+ new->code = code = GET_CODE (pattern);
+
+ switch (code)
+ {
+ case MATCH_OPERAND:
+ new->opno = XINT (pattern, 0);
+ new->code = UNKNOWN;
+ new->tests = XSTR (pattern, 1);
+ if (*new->tests == 0)
+ new->tests = 0;
+ return new;
+
+ case MATCH_SCRATCH:
+ new->opno = XINT (pattern, 0);
+ new->code = UNKNOWN;
+ new->tests = "scratch_operand";
+ if (*new->tests == 0)
+ new->tests = 0;
+ return new;
+
+ case MATCH_OPERATOR:
+ new->opno = XINT (pattern, 0);
+ new->code = UNKNOWN;
+ new->tests = XSTR (pattern, 1);
+ if (*new->tests == 0)
+ new->tests = 0;
+ for (i = 0; i < XVECLEN (pattern, 2); i++)
+ {
+ newpos[depth] = i + '0';
+ new = add_to_sequence (XVECEXP (pattern, 2, i), new, newpos);
+ }
+ this->success->enforce_mode = 0;
+ return new;
+
+ case MATCH_PARALLEL:
+ new->opno = XINT (pattern, 0);
+ new->code = PARALLEL;
+ new->tests = XSTR (pattern, 1);
+ if (*new->tests == 0)
+ new->tests = 0;
+ for (i = 0; i < XVECLEN (pattern, 2); i++)
+ {
+ newpos[depth] = i + 'a';
+ new = add_to_sequence (XVECEXP (pattern, 2, i), new, newpos);
+ }
+ this->success->enforce_mode = 0;
+ return new;
+
+ case MATCH_OP_DUP:
+ new->opno = XINT (pattern, 0);
+ new->dupno = XINT (pattern, 0);
+ new->code = UNKNOWN;
+ new->tests = 0;
+ for (i = 0; i < XVECLEN (pattern, 1); i++)
+ {
+ newpos[depth] = i + '0';
+ new = add_to_sequence (XVECEXP (pattern, 1, i), new, newpos);
+ }
+ this->success->enforce_mode = 0;
+ return new;
+
+ case MATCH_DUP:
+ new->dupno = XINT (pattern, 0);
+ new->code = UNKNOWN;
+ return new;
+
+ case ADDRESS:
+ pattern = XEXP (pattern, 0);
+ goto restart;
+
+ case PC:
+ new->exact = "pc_rtx";
+ return new;
+
+ case CC0:
+ new->exact = "cc0_rtx";
+ return new;
+
+ case CONST_INT:
+ if (INTVAL (pattern) == 0)
+ {
+ new->exact = "const0_rtx";
+ return new;
+ }
+ if (INTVAL (pattern) == 1)
+ {
+ new->exact = "const1_rtx";
+ return new;
+ }
+ if (INTVAL (pattern) == -1)
+ {
+ new->exact = "constm1_rtx";
+ return new;
+ }
+ if (INTVAL (pattern) == STORE_FLAG_VALUE)
+ {
+ new->exact = "const_true_rtx";
+ return new;
+ }
+ break;
+
+ case SET:
+ newpos[depth] = '0';
+ new = add_to_sequence (SET_DEST (pattern), new, newpos);
+ this->success->enforce_mode = 1;
+ newpos[depth] = '1';
+ new = add_to_sequence (SET_SRC (pattern), new, newpos);
+ return new;
+
+ case STRICT_LOW_PART:
+ newpos[depth] = '0';
+ new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+ this->success->enforce_mode = 1;
+ return new;
+
+ case SUBREG:
+ this->test_elt_one_int = 1;
+ this->elt_one_int = XINT (pattern, 1);
+ newpos[depth] = '0';
+ new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+ this->success->enforce_mode = 1;
+ return new;
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ newpos[depth] = '0';
+ new = add_to_sequence (XEXP (pattern, 0), new, newpos);
+ this->success->enforce_mode = 1;
+ newpos[depth] = '1';
+ new = add_to_sequence (XEXP (pattern, 1), new, newpos);
+ newpos[depth] = '2';
+ new = add_to_sequence (XEXP (pattern, 2), new, newpos);
+ return new;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ len = GET_RTX_LENGTH (code);
+ for (i = 0; i < len; i++)
+ {
+ newpos[depth] = '0' + i;
+ if (fmt[i] == 'e' || fmt[i] == 'u')
+ new = add_to_sequence (XEXP (pattern, i), new, newpos);
+ else if (fmt[i] == 'i' && i == 0)
+ {
+ this->test_elt_zero_int = 1;
+ this->elt_zero_int = XINT (pattern, i);
+ }
+ else if (fmt[i] == 'i' && i == 1)
+ {
+ this->test_elt_one_int = 1;
+ this->elt_one_int = XINT (pattern, i);
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ /* We do not handle a vector appearing as other than
+ the first item, just because nothing uses them
+ and by handling only the special case
+ we can use one element in newpos for either
+ the item number of a subexpression
+ or the element number in a vector. */
+ if (i != 0)
+ abort ();
+ this->veclen = XVECLEN (pattern, i);
+ for (j = 0; j < XVECLEN (pattern, i); j++)
+ {
+ newpos[depth] = 'a' + j;
+ new = add_to_sequence (XVECEXP (pattern, i, j),
+ new, newpos);
+ }
+ }
+ else if (fmt[i] != '0')
+ abort ();
+ }
+ return new;
+}
+
+/* Merge two decision trees OLD and ADD,
+ modifying OLD destructively,
+ and return the merged tree. */
+
+static struct decision *
+merge_trees (old, add)
+ register struct decision *old, *add;
+{
+ while (add)
+ {
+ register struct decision *next = add->next;
+ add->next = 0;
+ if (!try_merge_1 (old, add))
+ old = try_merge_2 (old, add);
+ add = next;
+ }
+ return old;
+}
+
+/* Merge ADD into the next-chain starting with OLD
+ only if it overlaps a condition already tested in OLD.
+ Returns 1 if successful (OLD is modified),
+ 0 if nothing has been done. */
+
+static int
+try_merge_1 (old, add)
+ register struct decision *old, *add;
+{
+ while (old)
+ {
+ if ((old->position == add->position
+ || (old->position && add->position
+ && !strcmp (old->position, add->position)))
+ && (old->tests == add->tests
+ || (old->tests && add->tests && !strcmp (old->tests, add->tests)))
+ && (old->c_test == add->c_test
+ || (old->c_test && add->c_test && !strcmp (old->c_test, add->c_test)))
+ && (old->c_hook == add->c_hook
+ || (old->c_hook && add->c_hook && !strcmp (old->c_hook, add->c_hook)))
+ && old->test_elt_zero_int == add->test_elt_zero_int
+ && old->elt_zero_int == add->elt_zero_int
+ && old->test_elt_one_int == add->test_elt_one_int
+ && old->elt_one_int == add->elt_one_int
+ && old->veclen == add->veclen
+ && old->dupno == add->dupno
+ && old->opno == add->opno
+/* In a collection of nodes that don't have predicates,
+ we can always merge a new one with any node that matches it.
+ This is because we know that two different nodes can't possibly match
+ the same RTL object. So we can reorder the tests to simplify the
+ whole collection of them.
+
+ But when predicates are involved, we have to preserve the order of
+ testing them. This means that a new node can only be merged with the
+ last existing node.
+
+ enforce_mode indicates that at this level each of the nodes
+ requires a particular mode. When this is true, then we know
+ that two nodes with different modes can't possibly both match.
+ Therefore, it is ok to merge a new node with the last node
+ that wants the same mode, even if other nodes for different modes
+ appear after it. no_same_mode tests for this condition. */
+ && (old->tests == 0
+ || (add->enforce_mode ? no_same_mode (old) : old->next == 0))
+ && old->code == add->code
+ && old->mode == add->mode
+ && (old->exact == add->exact
+ || (old->exact && add->exact && ! strcmp (old->exact, add->exact))))
+ {
+ old->success = merge_trees (old->success, add->success);
+ if (old->insn_code_number >= 0 && add->insn_code_number >= 0)
+ fatal ("Two actions at one point in tree");
+ if (old->insn_code_number == -1)
+ old->insn_code_number = add->insn_code_number;
+ return 1;
+ }
+ old = old->next;
+ }
+ return 0;
+}
+
+/* Merge ADD into the next-chain that starts with OLD,
+ preferably after something that tests the same place
+ that ADD does.
+ The next-chain of ADD itself is ignored, and it is set
+ up for entering ADD into the new chain.
+ Returns the new chain. */
+
+static struct decision *
+try_merge_2 (old, add)
+ struct decision *old, *add;
+{
+ register struct decision *p;
+ struct decision *last = 0;
+ struct decision *last_same_place = 0;
+
+ /* Put this in after the others that test the same place,
+ if there are any. If not, find the last chain element
+ and insert there.
+
+ One modification: if this one is NOT a MATCH_OPERAND,
+ put it before any MATCH_OPERANDS that test the same place.
+
+ Another: if enforce_mode (i.e. this is first operand of a SET),
+ put this after the last thing that tests the same place for
+ the same mode. */
+
+#if 0
+ int operand = 0 != add->tests;
+#endif
+
+ for (p = old; p; p = p->next)
+ {
+ if (p->position == add->position
+ || (p->position && add->position
+ && !strcmp (p->position, add->position)))
+ {
+ last_same_place = p;
+ /* If enforce_mode, segregate the modes in numerical order. */
+ if (p->enforce_mode && (int) add->mode < (int) p->mode)
+ break;
+#if 0
+ /* Keep explicit decompositions before those that test predicates.
+ If enforce_mode, do this separately within each mode. */
+ if (! p->enforce_mode || p->mode == add->mode)
+ if (!operand && p->tests)
+ break;
+#endif
+ }
+ /* If this is past the end of the decisions at the same place as ADD,
+ stop looking now; add ADD before here. */
+ else if (last_same_place)
+ break;
+ last = p;
+ }
+
+ /* Insert before P, which means after LAST. */
+
+ if (last)
+ {
+ add->next = last->next;
+ last->next = add;
+ return old;
+ }
+
+ add->next = old;
+ return add;
+}
+
+static int
+no_same_mode (node)
+ struct decision *node;
+{
+ register struct decision *p;
+ register enum machine_mode mode = node->mode;
+
+ for (p = node->next; p; p = p->next)
+ if (p->mode == mode)
+ return 0;
+
+ return 1;
+}
+
+/* Count the number of subnodes of node NODE, assumed to be the start
+ of a next-chain. If the number is high enough, make NODE start
+ a separate subroutine in the C code that is generated.
+
+ TYPE gives the type of routine we are writing. */
+
+static int
+break_out_subroutines (node, type)
+ struct decision *node;
+ enum routine_type type;
+{
+ int size = 0;
+ struct decision *sub;
+ for (sub = node; sub; sub = sub->next)
+ size += 1 + break_out_subroutines (sub->success, type);
+ if (size > SUBROUTINE_THRESHOLD)
+ {
+ node->subroutine_number = ++next_subroutine_number;
+ write_subroutine (node, type);
+ size = 1;
+ }
+ return size;
+}
+
+static void
+write_subroutine (tree, type)
+ struct decision *tree;
+ enum routine_type type;
+{
+ char *return_type = (type == SPLIT ? "rtx" : "int");
+
+ if (type == SPLIT)
+ {
+ printf ("rtx\nsplit_%d (x0, insn)\n", tree->subroutine_number);
+ printf (" register rtx x0;\n rtx insn;\n");
+ }
+ else
+ {
+ printf ("int\nrecog_%d (x0, insn, pnum_clobbers)\n",
+ tree->subroutine_number);
+ printf (" register rtx x0;\n rtx insn;\n");
+ printf (" int *pnum_clobbers;\n");
+ }
+
+ printf ("{\n");
+ printf (" register rtx *ro = &recog_operand[0];\n");
+ printf (" register rtx x1, x2, x3, x4, x5;\n rtx x6, x7, x8, x9, x10, x11;\n");
+ printf (" %s tem;\n", return_type);
+ write_tree (tree, "", 0, "", 1, type);
+ printf (" ret0: return %d;\n}\n\n", type == SPLIT ? 0 : -1);
+}
+
+/* Write out C code to perform the decisions in the tree. */
+
+static char *
+write_tree_1 (tree, prevpos, afterward, afterpos, initial, type)
+ struct decision *tree;
+ char *prevpos;
+ int afterward;
+ char *afterpos;
+ int initial;
+ enum routine_type type;
+{
+ register struct decision *p, *p1;
+ char *pos;
+ register int depth;
+ int ignmode;
+ enum anon1 { NO_SWITCH, CODE_SWITCH, MODE_SWITCH } in_switch = NO_SWITCH;
+ char modemap[NUM_MACHINE_MODES];
+ char codemap[NUM_RTX_CODE];
+
+ pos = prevpos;
+
+ tree->label_needed = 1;
+ for (p = tree; p; p = p->next)
+ {
+ /* Find the next alternative to p
+ that might be true when p is true.
+ Test that one next if p's successors fail.
+ Note that when the `tests' field is nonzero
+ it is up to the specified test-function to compare machine modes
+ and some (such as general_operand) don't always do so.
+ But when inside a switch-on-modes we ignore this and
+ consider all modes mutually exclusive. */
+ for (p1 = p->next; p1; p1 = p1->next)
+ if (((p->code == UNKNOWN || p1->code == UNKNOWN || p->code == p1->code)
+ && (p->mode == VOIDmode || p1->mode == VOIDmode
+ || p->mode == p1->mode
+ || (in_switch != MODE_SWITCH && (p->tests || p1->tests))))
+ || strcmp (p1->position, p->position))
+ break;
+ p->afterward = p1;
+ if (p1) p1->label_needed = 1;
+
+ if (in_switch == MODE_SWITCH
+ && (p->mode == VOIDmode || (! p->enforce_mode && p->tests != 0)))
+ {
+ in_switch = NO_SWITCH;
+ printf (" }\n");
+ }
+ if (in_switch == CODE_SWITCH && p->code == UNKNOWN)
+ {
+ in_switch = NO_SWITCH;
+ printf (" }\n");
+ }
+
+ if (p->label_needed)
+ printf (" L%d:\n", p->number);
+
+ if (p->success == 0 && p->insn_code_number < 0)
+ abort ();
+
+ change_state (pos, p->position);
+ pos = p->position;
+ depth = strlen (pos);
+
+ ignmode = (p->ignmode || p->tests);
+
+ if (in_switch == NO_SWITCH)
+ {
+ /* If p and its alternatives all want the same mode,
+ reject all others at once, first, then ignore the mode. */
+ if (!ignmode && p->mode != VOIDmode && p->next && same_modes (p, p->mode))
+ {
+ printf (" if (GET_MODE (x%d) != %smode)\n",
+ depth, GET_MODE_NAME (p->mode));
+ if (afterward)
+ {
+ printf (" {\n ");
+ change_state (pos, afterpos);
+ printf (" goto L%d;\n }\n", afterward);
+ }
+ else
+ printf (" goto ret0;\n");
+ clear_modes (p);
+ ignmode = 1;
+ }
+
+ /* If p and its alternatives all want the same code,
+ reject all others at once, first, then ignore the code. */
+ if (p->code != UNKNOWN && p->next && same_codes (p, p->code))
+ {
+ printf (" if (GET_CODE (x%d) != ", depth);
+ print_code (p->code);
+ printf (")\n");
+ if (afterward)
+ {
+ printf (" {");
+ change_state (pos, afterpos);
+ printf (" goto L%d; }\n", afterward);
+ }
+ else
+ printf (" goto ret0;\n");
+ clear_codes (p);
+ }
+ }
+
+ /* If p and its alternatives all have different modes
+ and there are at least 4 of them, make a switch. */
+ if (in_switch == NO_SWITCH)
+ {
+ register int i;
+ int lose = 0;
+
+ mybzero (modemap, sizeof modemap);
+ for (p1 = p, i = 0;
+ (p1 && p1->mode != VOIDmode
+ && (p1->tests == 0 || p1->enforce_mode));
+ p1 = p1->next, i++)
+ {
+ if (! p->enforce_mode && modemap[(int) p1->mode])
+ {
+ lose = 1;
+ break;
+ }
+ modemap[(int) p1->mode] = 1;
+ }
+ if (!lose && i >= 4)
+ {
+ in_switch = MODE_SWITCH;
+ printf (" switch (GET_MODE (x%d))\n {\n", depth);
+ }
+ }
+
+ if (in_switch == NO_SWITCH)
+ {
+ register int i;
+ mybzero (codemap, sizeof codemap);
+ for (p1 = p, i = 0; p1 && p1->code != UNKNOWN; p1 = p1->next, i++)
+ {
+ if (codemap[(int) p1->code])
+ break;
+ codemap[(int) p1->code] = 1;
+ }
+ if ((p1 == 0 || p1->code == UNKNOWN) && i >= 4)
+ {
+ in_switch = CODE_SWITCH;
+ printf (" switch (GET_CODE (x%d))\n {\n", depth);
+ }
+ }
+
+ if (in_switch == MODE_SWITCH)
+ {
+ if (modemap[(int) p->mode])
+ {
+ printf (" case %smode:\n", GET_MODE_NAME (p->mode));
+ modemap[(int) p->mode] = 0;
+ }
+ }
+ if (in_switch == CODE_SWITCH)
+ {
+ if (codemap[(int) p->code])
+ {
+ printf (" case ");
+ print_code (p->code);
+ printf (":\n");
+ codemap[(int) p->code] = 0;
+ }
+ }
+
+ printf (" if (");
+ if (p->exact || (p->code != UNKNOWN && in_switch != CODE_SWITCH))
+ {
+ if (p->exact)
+ printf ("x%d == %s", depth, p->exact);
+ else
+ {
+ printf ("GET_CODE (x%d) == ", depth);
+ print_code (p->code);
+ }
+ printf (" && ");
+ }
+ if (p->mode != VOIDmode && !ignmode && in_switch != MODE_SWITCH)
+ printf ("GET_MODE (x%d) == %smode && ",
+ depth, GET_MODE_NAME (p->mode));
+ if (p->test_elt_zero_int)
+ printf ("XINT (x%d, 0) == %d && ", depth, p->elt_zero_int);
+ if (p->veclen)
+ printf ("XVECLEN (x%d, 0) == %d && ", depth, p->veclen);
+ if (p->test_elt_one_int)
+ printf ("XINT (x%d, 1) == %d && ", depth, p->elt_one_int);
+ if (p->dupno >= 0)
+ printf ("rtx_equal_p (x%d, ro[%d]) && ", depth, p->dupno);
+ if (p->tests)
+ printf ("%s (x%d, %smode)", p->tests, depth,
+ GET_MODE_NAME (p->mode));
+ else
+ printf ("1");
+
+ if (p->opno >= 0)
+ printf (")\n { ro[%d] = x%d; ",
+ p->opno, depth);
+ else
+ printf (")\n ");
+
+ if (p->c_test)
+ printf ("if (%s) ", p->c_test);
+
+ if (p->insn_code_number >= 0)
+ {
+ if (type == SPLIT)
+ printf ("return gen_split_%d (operands);", p->insn_code_number);
+ else
+ {
+ if (p->num_clobbers_to_add)
+ {
+ printf ("\n {\n");
+ printf ("\tif (pnum_clobbers == 0) goto ret0; ");
+ printf ("*pnum_clobbers = %d; ", p->num_clobbers_to_add);
+ printf ("return %d;\n }", p->insn_code_number);
+ }
+ else
+ printf ("return %d;", p->insn_code_number);
+ }
+ }
+ else
+ printf ("goto L%d;", p->success->number);
+
+ if (p->opno >= 0)
+ printf (" }\n");
+ else
+ printf ("\n");
+
+ /* Now, if inside a switch, branch to next switch member
+ that might also need to be tested if this one fails. */
+
+ if (in_switch == CODE_SWITCH)
+ {
+ /* Find the next alternative to p
+ that might be applicable if p was applicable. */
+ for (p1 = p->next; p1; p1 = p1->next)
+ if (p1->code == UNKNOWN || p->code == p1->code)
+ break;
+ if (p1 == 0 || p1->code == UNKNOWN)
+ printf (" break;\n");
+ else if (p1 != p->next)
+ {
+ printf (" goto L%d;\n", p1->number);
+ p1->label_needed = 1;
+ }
+ }
+
+ if (in_switch == MODE_SWITCH)
+ {
+ /* Find the next alternative to p
+ that might be applicable if p was applicable. */
+ for (p1 = p->next; p1; p1 = p1->next)
+ if (p1->mode == VOIDmode || p->mode == p1->mode)
+ break;
+ if (p1 == 0 || p1->mode == VOIDmode)
+ printf (" break;\n");
+ else if (p1 != p->next)
+ {
+ printf (" goto L%d;\n", p1->number);
+ p1->label_needed = 1;
+ }
+ }
+ }
+
+ if (in_switch != NO_SWITCH)
+ printf (" }\n");
+
+ if (afterward)
+ {
+ change_state (pos, afterpos);
+ printf (" goto L%d;\n", afterward);
+ }
+ else
+ printf (" goto ret0;\n");
+ return pos;
+}
+
+static void
+write_tree (tree, prevpos, afterward, afterpos, initial, type)
+ struct decision *tree;
+ char *prevpos;
+ int afterward;
+ char *afterpos;
+ int initial;
+ enum routine_type type;
+{
+ register struct decision *p;
+ char *pos = prevpos;
+ char *name_prefix = (type == SPLIT ? "split" : "recog");
+ char *call_suffix = (type == SPLIT ? "" : ", pnum_clobbers");
+
+ if (tree->subroutine_number > 0 && ! initial)
+ {
+ printf (" L%d:\n", tree->number);
+
+ if (afterward)
+ {
+ printf (" tem = %s_%d (x0, insn%s);\n",
+ name_prefix, tree->subroutine_number, call_suffix);
+ printf (" if (tem >= 0) return tem;\n");
+ change_state (pos, afterpos);
+ printf (" goto L%d;\n", afterward);
+ }
+ else
+ printf (" return %s_%d (x0, insn%s);\n",
+ name_prefix, tree->subroutine_number, call_suffix);
+ return;
+ }
+
+ pos = write_tree_1 (tree, prevpos, afterward, afterpos, initial, type);
+
+ for (p = tree; p; p = p->next)
+ if (p->success)
+ {
+ pos = p->position;
+ write_tree (p->success, pos,
+ p->afterward ? p->afterward->number : afterward,
+ p->afterward ? pos : afterpos, 0, type);
+ }
+}
+
+static void
+print_code (code)
+ RTX_CODE code;
+{
+ register char *p1;
+ for (p1 = GET_RTX_NAME (code); *p1; p1++)
+ {
+ if (*p1 >= 'a' && *p1 <= 'z')
+ putchar (*p1 + 'A' - 'a');
+ else
+ putchar (*p1);
+ }
+}
+
+static int
+same_codes (p, code)
+ register struct decision *p;
+ register RTX_CODE code;
+{
+ for (; p; p = p->next)
+ if (p->code != code)
+ return 0;
+
+ return 1;
+}
+
+static void
+clear_codes (p)
+ register struct decision *p;
+{
+ for (; p; p = p->next)
+ p->code = UNKNOWN;
+}
+
+static int
+same_modes (p, mode)
+ register struct decision *p;
+ register enum machine_mode mode;
+{
+ for (; p; p = p->next)
+ if (p->mode != mode || p->tests)
+ return 0;
+
+ return 1;
+}
+
+static void
+clear_modes (p)
+ register struct decision *p;
+{
+ for (; p; p = p->next)
+ p->ignmode = 1;
+}
+
+static void
+change_state (oldpos, newpos)
+ char *oldpos;
+ char *newpos;
+{
+ int odepth = strlen (oldpos);
+ int depth = odepth;
+ int ndepth = strlen (newpos);
+
+ /* Pop up as many levels as necessary. */
+
+ while (strncmp (oldpos, newpos, depth))
+ --depth;
+
+ /* Go down to desired level. */
+
+ while (depth < ndepth)
+ {
+ if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
+ printf (" x%d = XVECEXP (x%d, 0, %d);\n",
+ depth + 1, depth, newpos[depth] - 'a');
+ else
+ printf (" x%d = XEXP (x%d, %c);\n",
+ depth + 1, depth, newpos[depth]);
+ ++depth;
+ }
+}
+
+static char *
+copystr (s1)
+ char *s1;
+{
+ register char *tem;
+
+ if (s1 == 0)
+ return 0;
+
+ tem = (char *) xmalloc (strlen (s1) + 1);
+ strcpy (tem, s1);
+
+ return tem;
+}
+
+static void
+mybzero (b, length)
+ register char *b;
+ register unsigned length;
+{
+ while (length-- > 0)
+ *b++ = 0;
+}
+
+static char *
+concat (s1, s2)
+ char *s1, *s2;
+{
+ register char *tem;
+
+ if (s1 == 0)
+ return s2;
+ if (s2 == 0)
+ return s1;
+
+ tem = (char *) xmalloc (strlen (s1) + strlen (s2) + 2);
+ strcpy (tem, s1);
+ strcat (tem, " ");
+ strcat (tem, s2);
+
+ return tem;
+}
+
+char *
+xrealloc (ptr, size)
+ char *ptr;
+ unsigned size;
+{
+ char *result = (char *) realloc (ptr, size);
+ if (!result)
+ fatal ("virtual memory exhausted");
+ return result;
+}
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *val = (char *) malloc (size);
+
+ if (val == 0)
+ fatal ("virtual memory exhausted");
+ return val;
+}
+
+static void
+fatal (s, a1, a2)
+ char *s;
+{
+ fprintf (stderr, "genrecog: ");
+ fprintf (stderr, s, a1, a2);
+ fprintf (stderr, "\n");
+ fprintf (stderr, "after %d instruction definitions\n", next_index);
+ exit (FATAL_EXIT_CODE);
+}
+
+/* More 'friendly' abort that prints the line and file.
+ config.h can #define abort fancy_abort if you like that sort of thing. */
+
+void
+fancy_abort ()
+{
+ fatal ("Internal gcc abort.");
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ rtx desc;
+ struct decision *tree = 0;
+ struct decision *split_tree = 0;
+ FILE *infile;
+ extern rtx read_rtx ();
+ register int c;
+
+ obstack_init (rtl_obstack);
+
+ if (argc <= 1)
+ fatal ("No input file name.");
+
+ infile = fopen (argv[1], "r");
+ if (infile == 0)
+ {
+ perror (argv[1]);
+ exit (FATAL_EXIT_CODE);
+ }
+
+ init_rtl ();
+ next_insn_code = 0;
+ next_index = 0;
+
+ printf ("/* Generated automatically by the program `genrecog'\n\
+from the machine description file `md'. */\n\n");
+
+ printf ("#include \"config.h\"\n");
+ printf ("#include \"rtl.h\"\n");
+ printf ("#include \"insn-config.h\"\n");
+ printf ("#include \"recog.h\"\n");
+ printf ("#include \"real.h\"\n");
+ printf ("#include \"output.h\"\n");
+ printf ("#include \"flags.h\"\n");
+ printf ("\n");
+
+ /* Read the machine description. */
+
+ while (1)
+ {
+ c = read_skip_spaces (infile);
+ if (c == EOF)
+ break;
+ ungetc (c, infile);
+
+ desc = read_rtx (infile);
+ if (GET_CODE (desc) == DEFINE_INSN)
+ tree = merge_trees (tree, make_insn_sequence (desc));
+ else if (GET_CODE (desc) == DEFINE_SPLIT)
+ split_tree = merge_trees (split_tree, make_split_sequence (desc));
+ if (GET_CODE (desc) == DEFINE_PEEPHOLE
+ || GET_CODE (desc) == DEFINE_EXPAND)
+ next_insn_code++;
+ next_index++;
+ }
+
+ printf ("\n\
+/* `recog' contains a decision tree\n\
+ that recognizes whether the rtx X0 is a valid instruction.\n\
+\n\
+ recog returns -1 if the rtx is not valid.\n\
+ If the rtx is valid, recog returns a nonnegative number\n\
+ which is the insn code number for the pattern that matched.\n");
+ printf (" This is the same as the order in the machine description of\n\
+ the entry that matched. This number can be used as an index into\n\
+ entry that matched. This number can be used as an index into various\n\
+ insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands\n\
+ (found in insn-output.c).\n\n");
+ printf (" The third argument to recog is an optional pointer to an int.\n\
+ If present, recog will accept a pattern if it matches except for\n\
+ missing CLOBBER expressions at the end. In that case, the value\n\
+ pointed to by the optional pointer will be set to the number of\n\
+ CLOBBERs that need to be added (it should be initialized to zero by\n\
+ the caller). If it is set nonzero, the caller should allocate a\n\
+ PARALLEL of the appropriate size, copy the initial entries, and call\n\
+ add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.");
+
+ if (split_tree)
+ printf ("\n\n The function split_insns returns 0 if the rtl could not\n\
+ be split or the split rtl in a SEQUENCE if it can be.");
+
+ printf ("*/\n\n");
+
+ printf ("rtx recog_operand[MAX_RECOG_OPERANDS];\n\n");
+ printf ("rtx *recog_operand_loc[MAX_RECOG_OPERANDS];\n\n");
+ printf ("rtx *recog_dup_loc[MAX_DUP_OPERANDS];\n\n");
+ printf ("char recog_dup_num[MAX_DUP_OPERANDS];\n\n");
+ printf ("#define operands recog_operand\n\n");
+
+ next_subroutine_number = 0;
+ break_out_subroutines (tree, RECOG);
+
+ printf ("int\nrecog (x0, insn, pnum_clobbers)\n");
+ printf (" register rtx x0;\n rtx insn;\n");
+ printf (" int *pnum_clobbers;\n{\n");
+ printf (" register rtx *ro = &recog_operand[0];\n");
+ printf (" register rtx x1, x2, x3, x4, x5;\n rtx x6, x7, x8, x9, x10, x11;\n");
+ printf (" int tem;\n");
+
+ if (tree)
+ write_tree (tree, "", 0, "", 1, RECOG);
+ printf (" ret0: return -1;\n}\n");
+
+ next_subroutine_number = 0;
+ break_out_subroutines (split_tree, SPLIT);
+
+ printf ("rtx\nsplit_insns (x0, insn)\n register rtx x0;\n rtx insn;\n{\n");
+ printf (" register rtx *ro = &recog_operand[0];\n");
+ printf (" register rtx x1, x2, x3, x4, x5;\n rtx x6, x7, x8, x9, x10, x11;\n");
+ printf (" rtx tem;\n");
+
+ if (split_tree)
+ write_tree (split_tree, "", 0, "", 1, SPLIT);
+ printf (" ret0: return 0;\n}\n");
+
+ fflush (stdout);
+ exit (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
+ /* NOTREACHED */
+ return 0;
+}