From: Richard Kenner Date: Sun, 24 Nov 1991 19:57:16 +0000 (-0500) Subject: Initial revision X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ec65fa669004405ecf6b41c3d87ff142607c1755;p=gcc.git Initial revision From-SVN: r71 --- diff --git a/gcc/genrecog.c b/gcc/genrecog.c new file mode 100644 index 00000000000..db4bb41612e --- /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 +#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; +}