1991-11-24 20:57:16 +01:00
|
|
|
|
/* Generate code from machine description to recognize rtl as insns.
|
2000-02-26 14:55:09 +01:00
|
|
|
|
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1997, 1998,
|
|
|
|
|
1999, 2000 Free Software Foundation, Inc.
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
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, 59 Temple Place - Suite 330,
|
|
|
|
|
Boston, MA 02111-1307, 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_template,
|
|
|
|
|
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
|
1991-11-24 20:57:16 +01:00
|
|
|
|
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
|
1999-10-05 07:18:54 +02:00
|
|
|
|
PARALLEL of the appropriate size, copy the initial entries, and
|
|
|
|
|
call add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
This program also generates the function `peephole2_insns', which
|
|
|
|
|
returns 0 if the rtl could not be matched. If there was a match,
|
|
|
|
|
the new rtl is returned in a SEQUENCE, and LAST_INSN will point
|
|
|
|
|
to the last recognized insn in the old sequence. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1992-10-13 05:40:12 +01:00
|
|
|
|
#include "hconfig.h"
|
Cutover various gen*.c files to using system.h:
* Makefile.in (genconfig.o, genflags.o, gencodes.o, genemit.o,
genopinit.o, genrecog.o, genextract.o, genpeep.o, genattr.o,
genattrtab.o, genoutput.o): Depend on system.h.
* genattr.c: Include system.h. Add arguments to various function
prototypes. Remove redundant prototype of read_rtx().
* genattrtab.c: Likewise.
* gencodes.c: Likewise.
* genconfig.c: Likewise.
* genemit.c: Likewise.
* genextract.c: Likewise.
* genflags.c: Likewise.
* genopinit.c: Likewise.
* genoutput.c: Likewise.
* genpeep.c: Likewise.
* genrecog.c: Likewise.
From-SVN: r18794
1998-03-24 11:16:53 +01:00
|
|
|
|
#include "system.h"
|
1991-11-24 20:57:16 +01:00
|
|
|
|
#include "rtl.h"
|
|
|
|
|
#include "obstack.h"
|
errors.c: New file...
1999-08-27 00:27 -0700 Zack Weinberg <zack@bitmover.com>
* errors.c: New file; defines functions error, warning, and
fatal, variables have_error and progname.
* errors.h: New file; prototypes and decls for stuff in errors.c.
* Makefile: Add rules to build errors.o and
$(HOST_PREFIX)errors.o. Link genconfig, gencodes, genemit,
genopinit, genrecog, genextract, genpeep, genattr, and
genoutput with errors.o. Add errors.h to deps of genconfig.o,
gencodes.o, genemit.o, genopinit.o, genrecog.o, genextract.o,
genpeep.o, genattr.o, and genoutput.o.
* genconfig.c, gencodes.c, genemit.c, genopinit.c, genrecog.c,
genextract.c, genpeep.c, genattr.c: Include errors.h. Don't
define or prototype fatal. Set progname at beginning of main.
* genoutput.c: Likewise, and don't define or prototype error
either.
From-SVN: r28925
1999-08-27 09:47:17 +02:00
|
|
|
|
#include "errors.h"
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-01-04 08:48:37 +01:00
|
|
|
|
#define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
|
|
|
|
|
printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
|
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
static struct obstack obstack;
|
|
|
|
|
struct obstack *rtl_obstack = &obstack;
|
|
|
|
|
|
|
|
|
|
#define obstack_chunk_alloc xmalloc
|
|
|
|
|
#define obstack_chunk_free free
|
|
|
|
|
|
1999-01-19 21:24:22 +01:00
|
|
|
|
/* Holds an array of names indexed by insn_code_number. */
|
1999-09-12 14:46:08 +02:00
|
|
|
|
static char **insn_name_ptr = 0;
|
|
|
|
|
static int insn_name_ptr_size = 0;
|
1998-04-15 11:23:14 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* A listhead of decision trees. The alternatives to a node are kept
|
|
|
|
|
in a doublely-linked list so we can easily add nodes to the proper
|
|
|
|
|
place when merging. */
|
|
|
|
|
|
|
|
|
|
struct decision_head
|
|
|
|
|
{
|
|
|
|
|
struct decision *first;
|
|
|
|
|
struct decision *last;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* A single test. The two accept types aren't tests per-se, but
|
|
|
|
|
their equality (or lack thereof) does affect tree merging so
|
|
|
|
|
it is convenient to keep them here. */
|
|
|
|
|
|
|
|
|
|
struct decision_test
|
|
|
|
|
{
|
|
|
|
|
/* A linked list through the tests attached to a node. */
|
|
|
|
|
struct decision_test *next;
|
|
|
|
|
|
|
|
|
|
/* These types are roughly in the order in which we'd like to test them. */
|
|
|
|
|
enum decision_type {
|
|
|
|
|
DT_mode, DT_code, DT_veclen,
|
|
|
|
|
DT_elt_zero_int, DT_elt_one_int, DT_elt_zero_wide,
|
|
|
|
|
DT_dup, DT_pred, DT_c_test,
|
|
|
|
|
DT_accept_op, DT_accept_insn
|
|
|
|
|
} type;
|
|
|
|
|
|
|
|
|
|
union
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode mode; /* Machine mode of node. */
|
|
|
|
|
RTX_CODE code; /* Code to test. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
const char *name; /* Predicate to call. */
|
|
|
|
|
int index; /* Index into `preds' or -1. */
|
|
|
|
|
enum machine_mode mode; /* Machine mode for node. */
|
|
|
|
|
} pred;
|
|
|
|
|
|
|
|
|
|
const char *c_test; /* Additional test to perform. */
|
|
|
|
|
int veclen; /* Length of vector. */
|
|
|
|
|
int dup; /* Number of operand to compare against. */
|
|
|
|
|
HOST_WIDE_INT intval; /* Value for XINT for XWINT. */
|
|
|
|
|
int opno; /* Operand number matched. */
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
int code_number; /* Insn number matched. */
|
1999-10-14 05:43:49 +02:00
|
|
|
|
int lineno; /* Line number of the insn. */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
int num_clobbers_to_add; /* Number of CLOBBERs to be added. */
|
|
|
|
|
} insn;
|
|
|
|
|
} u;
|
|
|
|
|
};
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Data structure for decision tree for recognizing legitimate insns. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
struct decision
|
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision_head success; /* Nodes to test on success. */
|
|
|
|
|
struct decision *next; /* Node to test on failure. */
|
|
|
|
|
struct decision *prev; /* Node whose failure tests us. */
|
|
|
|
|
struct decision *afterward; /* Node to test on success,
|
|
|
|
|
but failure of successor nodes. */
|
|
|
|
|
|
|
|
|
|
const char *position; /* String denoting position in pattern. */
|
|
|
|
|
|
|
|
|
|
struct decision_test *tests; /* The tests for this node. */
|
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
int number; /* Node number, used for labels */
|
|
|
|
|
int subroutine_number; /* Number of subroutine this node starts */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
int need_label; /* Label needs to be output. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
};
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
#define SUBROUTINE_THRESHOLD 100
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
static int next_subroutine_number;
|
|
|
|
|
|
1999-08-31 22:37:09 +02:00
|
|
|
|
/* We can write three types of subroutines: One for insn recognition,
|
|
|
|
|
one to split insns, and one for peephole-type optimizations. This
|
|
|
|
|
defines which type is being written. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
enum routine_type {
|
|
|
|
|
RECOG, SPLIT, PEEPHOLE2
|
|
|
|
|
};
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
#define IS_SPLIT(X) ((X) != RECOG)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Next available node number for tree nodes. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static int next_number;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Next number to use as an insn_code. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static int next_insn_code;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Similar, but counts all expressions in the MD file; used for
|
1996-07-04 00:07:53 +02:00
|
|
|
|
error messages. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static int next_index;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Record the highest depth we ever have so we know how many variables to
|
|
|
|
|
allocate in each subroutine we make. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static int max_depth;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
|
|
|
|
/* The line number of the start of the pattern currently being processed. */
|
|
|
|
|
static int pattern_lineno;
|
|
|
|
|
|
|
|
|
|
/* Count of errors. */
|
|
|
|
|
static int error_count;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
/* This table contains a list of the rtl codes that can possibly match a
|
1999-10-05 07:18:54 +02:00
|
|
|
|
predicate defined in recog.c. The function `maybe_both_true' uses it to
|
1991-12-28 12:36:37 +01:00
|
|
|
|
deduce that there are no expressions that can be matches by certain pairs
|
|
|
|
|
of tree nodes. Also, if a predicate can match only one code, we can
|
|
|
|
|
hardwire that code into the node testing the predicate. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static struct pred_table
|
|
|
|
|
{
|
genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattrtab.c (fatal, attr_printf, attr_string, write_attr_set,
write_unit_name, write_eligible_delay, expand_units,
make_length_attrs, write_attr_case, find_attr,
make_internal_attr): Likewise.
* gencheck.c (tree_codes): Likewise.
* gencodes.c (fatal): Likewise.
* genconfig.c (fatal): Likewise.
* genemit.c (fatal): Likewise.
* genextract.c (fatal, walk_rtx, copystr): Likewise.
* genflags.c (fatal): Likewise.
* genopinit.c (fatal, optabs, gen_insn): Likewise.
* genoutput.c (fatal, error, predicates): Likewise.
* genpeep.c (fatal): Likewise.
* genrecog.c (fatal, decision, pred_table, add_to_sequence,
write_tree_1, write_tree, change_state, copystr, indents): Likewise.
From-SVN: r24377
1998-12-19 08:04:42 +01:00
|
|
|
|
const char *name;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
RTX_CODE codes[NUM_RTX_CODE];
|
1999-10-05 07:18:54 +02:00
|
|
|
|
} preds[] = {
|
|
|
|
|
{"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM}},
|
1991-12-28 12:36:37 +01:00
|
|
|
|
#ifdef PREDICATE_CODES
|
1999-10-05 07:18:54 +02:00
|
|
|
|
PREDICATE_CODES
|
1991-12-28 12:36:37 +01:00
|
|
|
|
#endif
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM, PLUS, MINUS, MULT}},
|
|
|
|
|
{"register_operand", {SUBREG, REG}},
|
1999-10-15 03:52:29 +02:00
|
|
|
|
{"pmode_register_operand", {SUBREG, REG}},
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{"scratch_operand", {SCRATCH, REG}},
|
|
|
|
|
{"immediate_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF}},
|
|
|
|
|
{"const_int_operand", {CONST_INT}},
|
|
|
|
|
{"const_double_operand", {CONST_INT, CONST_DOUBLE}},
|
|
|
|
|
{"nonimmediate_operand", {SUBREG, REG, MEM}},
|
|
|
|
|
{"nonmemory_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG}},
|
|
|
|
|
{"push_operand", {MEM}},
|
|
|
|
|
{"pop_operand", {MEM}},
|
|
|
|
|
{"memory_operand", {SUBREG, MEM}},
|
|
|
|
|
{"indirect_operand", {SUBREG, MEM}},
|
genrecog.c (pred): Update comparison_operator for the unordered operators.
* genrecog.c (pred): Update comparison_operator for the unordered
operators.
* config/i386/i386.c (no_comparison_operator): Disallow unordered
operators.
(fcmov_comparison_operator): Allow UNORDERED/ORDERED.
(uno_comparison_operator): New.
(put_condition_code): Handle UNORDERED/ORDERED.
(unsigned_comparison): Likewise.
(ix86_fp_compare_mode): Broken out of ix86_expand_fp_compare.
(ix86_use_fcomi_compare, ix86_prepare_fp_compare_args): Likewise.
(ix86_expand_fp_compare): Use them. Take scratch as argument,
update all callers. Handle all 8 unordered operators.
(ix86_expand_setcc): Lose the unordered argument, update all callers.
(ix86_expand_branch): Likewise. Don't fully expand fp branches.
* config/i386/i386.h (PREDICATE_CODES): Update.
* config/i386/i386-protos.h (ix86_expand_fp_compare): Declare.
(ix86_expand_branch, ix86_expand_setcc): Update.
* config/i386/i386.md (sunordered, sordered): New.
(suneq, sunge, sungt, sunle, sunlt, sltgt): New.
(bunordered, bordered): New.
(buneq, bunge, bungt, bunle, bunlt, bltgt): New.
(*fp_jcc_1, *fp_jcc_2, *fp_jcc_3, *fp_jcc_4): New.
(*fp_jcc_5, *fp_jcc_6, and splitters): New.
From-SVN: r33046
2000-04-09 22:26:42 +02:00
|
|
|
|
{"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
|
|
|
|
|
UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
|
|
|
|
|
UNLT, LTGT}},
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM}}
|
|
|
|
|
};
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
#define NUM_KNOWN_PREDS (sizeof preds / sizeof preds[0])
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
static const char * special_mode_pred_table[] = {
|
|
|
|
|
#ifdef SPECIAL_MODE_PREDICATES
|
|
|
|
|
SPECIAL_MODE_PREDICATES
|
|
|
|
|
#endif
|
1999-10-15 03:52:29 +02:00
|
|
|
|
"pmode_register_operand"
|
1999-10-14 11:37:31 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define NUM_SPECIAL_MODE_PREDS \
|
1999-10-15 03:52:29 +02:00
|
|
|
|
(sizeof (special_mode_pred_table) / sizeof (special_mode_pred_table[0]))
|
1999-10-14 11:37:31 +02:00
|
|
|
|
|
1999-10-15 08:56:28 +02:00
|
|
|
|
static void message_with_line
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((int, const char *, ...)) ATTRIBUTE_PRINTF_2;
|
1999-10-15 08:56:28 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision *new_decision
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((const char *, struct decision_head *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision_test *new_decision_test
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((enum decision_type, struct decision_test ***));
|
1999-10-14 11:37:31 +02:00
|
|
|
|
static rtx find_operand
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((rtx, int));
|
1999-10-14 11:37:31 +02:00
|
|
|
|
static void validate_pattern
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((rtx, rtx, rtx));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision *add_to_sequence
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((rtx, struct decision_head *, const char *, enum routine_type, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static int maybe_both_true_2
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int maybe_both_true_1
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int maybe_both_true
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, struct decision *, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static int nodes_identical_1
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int nodes_identical
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, struct decision *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void merge_accept_insn
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, struct decision *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void merge_trees
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, struct decision_head *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static void factor_tests
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void simplify_tests
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int break_out_subroutines
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void find_afterward
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, struct decision *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static void change_state
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((const char *, const char *, struct decision *, const char *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void print_code
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((enum rtx_code));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_afterward
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, struct decision *, const char *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision *write_switch
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_cond
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, int, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_action
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, int, int, struct decision *,
|
1999-10-05 07:18:54 +02:00
|
|
|
|
enum routine_type));
|
|
|
|
|
static int is_unconditional
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int write_node
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, int, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_tree_1
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, int, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_tree
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, const char *, enum routine_type, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_subroutine
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_subroutines
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void write_header
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((void));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static struct decision_head make_insn_sequence
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((rtx, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void process_tree
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static void record_insn_name
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((int, const char *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
1999-10-24 21:23:11 +02:00
|
|
|
|
static void debug_decision_0
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, int, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void debug_decision_1
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *, int));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void debug_decision_2
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision_test *));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
extern void debug_decision
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *));
|
1999-10-24 21:23:11 +02:00
|
|
|
|
extern void debug_decision_list
|
2000-01-17 16:17:38 +01:00
|
|
|
|
PARAMS ((struct decision *));
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-14 05:43:49 +02:00
|
|
|
|
static void
|
2000-01-17 16:17:38 +01:00
|
|
|
|
message_with_line VPARAMS ((int lineno, const char *msg, ...))
|
1999-10-14 05:43:49 +02:00
|
|
|
|
{
|
|
|
|
|
#ifndef ANSI_PROTOTYPES
|
|
|
|
|
int lineno;
|
|
|
|
|
const char *msg;
|
|
|
|
|
#endif
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
VA_START (ap, msg);
|
|
|
|
|
|
|
|
|
|
#ifndef ANSI_PROTOTYPES
|
|
|
|
|
lineno = va_arg (ap, int);
|
|
|
|
|
msg = va_arg (ap, const char *);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
fprintf (stderr, "%s:%d: ", read_rtx_filename, lineno);
|
|
|
|
|
vfprintf (stderr, msg, ap);
|
|
|
|
|
fputc ('\n', stderr);
|
|
|
|
|
|
|
|
|
|
va_end (ap);
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Create a new node in sequence after LAST. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision *
|
|
|
|
|
new_decision (position, last)
|
|
|
|
|
const char *position;
|
|
|
|
|
struct decision_head *last;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
register struct decision *new
|
|
|
|
|
= (struct decision *) xmalloc (sizeof (struct decision));
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
memset (new, 0, sizeof (*new));
|
|
|
|
|
new->success = *last;
|
|
|
|
|
new->position = xstrdup (position);
|
|
|
|
|
new->number = next_number++;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
last->first = last->last = new;
|
|
|
|
|
return new;
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Create a new test and link it in at PLACE. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision_test *
|
|
|
|
|
new_decision_test (type, pplace)
|
|
|
|
|
enum decision_type type;
|
|
|
|
|
struct decision_test ***pplace;
|
|
|
|
|
{
|
|
|
|
|
struct decision_test **place = *pplace;
|
|
|
|
|
struct decision_test *test;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
test = (struct decision_test *) xmalloc (sizeof (*test));
|
|
|
|
|
test->next = *place;
|
|
|
|
|
test->type = type;
|
|
|
|
|
*place = test;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
place = &test->next;
|
|
|
|
|
*pplace = place;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return test;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
/* Search for and return operand N. */
|
|
|
|
|
|
|
|
|
|
static rtx
|
|
|
|
|
find_operand (pattern, n)
|
|
|
|
|
rtx pattern;
|
|
|
|
|
int n;
|
|
|
|
|
{
|
|
|
|
|
const char *fmt;
|
|
|
|
|
RTX_CODE code;
|
|
|
|
|
int i, j, len;
|
|
|
|
|
rtx r;
|
|
|
|
|
|
|
|
|
|
code = GET_CODE (pattern);
|
|
|
|
|
if ((code == MATCH_SCRATCH
|
|
|
|
|
|| code == MATCH_INSN
|
|
|
|
|
|| code == MATCH_OPERAND
|
|
|
|
|
|| code == MATCH_OPERATOR
|
|
|
|
|
|| code == MATCH_PARALLEL)
|
|
|
|
|
&& XINT (pattern, 0) == n)
|
|
|
|
|
return pattern;
|
|
|
|
|
|
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
|
|
|
|
if ((r = find_operand (XEXP (pattern, i), n)) != NULL_RTX)
|
|
|
|
|
return r;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
for (j = 0; j < XVECLEN (pattern, i); j++)
|
|
|
|
|
if ((r = find_operand (XVECEXP (pattern, i, j), n)) != NULL_RTX)
|
|
|
|
|
return r;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'i': case 'w': case '0': case 's':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-15 08:56:28 +02:00
|
|
|
|
/* Check for various errors in patterns. SET is nonnull for a destination,
|
|
|
|
|
and is the complete set pattern. */
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
|
|
|
|
static void
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (pattern, insn, set)
|
1999-10-14 05:43:49 +02:00
|
|
|
|
rtx pattern;
|
1999-10-14 11:37:31 +02:00
|
|
|
|
rtx insn;
|
1999-10-15 08:56:28 +02:00
|
|
|
|
rtx set;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
{
|
|
|
|
|
const char *fmt;
|
|
|
|
|
RTX_CODE code;
|
1999-10-14 11:37:31 +02:00
|
|
|
|
size_t i, len;
|
|
|
|
|
int j;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
|
|
|
|
code = GET_CODE (pattern);
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case MATCH_SCRATCH:
|
|
|
|
|
return;
|
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
case MATCH_INSN:
|
1999-10-14 05:43:49 +02:00
|
|
|
|
case MATCH_OPERAND:
|
1999-10-14 11:37:31 +02:00
|
|
|
|
case MATCH_OPERATOR:
|
1999-10-14 05:43:49 +02:00
|
|
|
|
{
|
|
|
|
|
const char *pred_name = XSTR (pattern, 1);
|
1999-10-14 11:37:31 +02:00
|
|
|
|
int allows_non_lvalue = 1, allows_non_const = 1;
|
|
|
|
|
int special_mode_pred = 0;
|
|
|
|
|
const char *c_test;
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (insn) == DEFINE_INSN)
|
|
|
|
|
c_test = XSTR (insn, 2);
|
|
|
|
|
else
|
|
|
|
|
c_test = XSTR (insn, 1);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
|
|
|
|
if (pred_name[0] != 0)
|
|
|
|
|
{
|
1999-10-14 11:37:31 +02:00
|
|
|
|
for (i = 0; i < NUM_KNOWN_PREDS; i++)
|
1999-10-14 05:43:49 +02:00
|
|
|
|
if (! strcmp (preds[i].name, pred_name))
|
|
|
|
|
break;
|
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
if (i < NUM_KNOWN_PREDS)
|
1999-10-14 05:43:49 +02:00
|
|
|
|
{
|
1999-10-14 11:37:31 +02:00
|
|
|
|
int j;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
allows_non_lvalue = allows_non_const = 0;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
for (j = 0; preds[i].codes[j] != 0; j++)
|
|
|
|
|
{
|
1999-10-14 11:37:31 +02:00
|
|
|
|
RTX_CODE c = preds[i].codes[j];
|
|
|
|
|
if (c != LABEL_REF
|
|
|
|
|
&& c != SYMBOL_REF
|
|
|
|
|
&& c != CONST_INT
|
|
|
|
|
&& c != CONST_DOUBLE
|
|
|
|
|
&& c != CONST
|
|
|
|
|
&& c != HIGH
|
|
|
|
|
&& c != CONSTANT_P_RTX)
|
|
|
|
|
allows_non_const = 1;
|
|
|
|
|
|
|
|
|
|
if (c != REG
|
|
|
|
|
&& c != SUBREG
|
|
|
|
|
&& c != MEM
|
|
|
|
|
&& c != CONCAT
|
|
|
|
|
&& c != PARALLEL
|
|
|
|
|
&& c != STRICT_LOW_PART)
|
|
|
|
|
allows_non_lvalue = 1;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#ifdef PREDICATE_CODES
|
|
|
|
|
/* If the port has a list of the predicates it uses but
|
|
|
|
|
omits one, warn. */
|
1999-10-14 11:37:31 +02:00
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: `%s' not in PREDICATE_CODES",
|
|
|
|
|
pred_name);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
#endif
|
|
|
|
|
}
|
1999-10-14 11:37:31 +02:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
|
|
|
|
|
if (strcmp (pred_name, special_mode_pred_table[i]) == 0)
|
|
|
|
|
{
|
|
|
|
|
special_mode_pred = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-15 08:56:28 +02:00
|
|
|
|
/* A MATCH_OPERAND that is a SET should have an output reload. */
|
|
|
|
|
if (set
|
|
|
|
|
&& code == MATCH_OPERAND
|
|
|
|
|
&& XSTR (pattern, 2)[0] != '\0'
|
|
|
|
|
&& XSTR (pattern, 2)[0] != '='
|
|
|
|
|
&& XSTR (pattern, 2)[0] != '+')
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"operand %d missing output reload",
|
|
|
|
|
XINT (pattern, 0));
|
|
|
|
|
error_count++;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
/* Allowing non-lvalues in destinations -- particularly CONST_INT --
|
|
|
|
|
while not likely to occur at runtime, results in less efficient
|
|
|
|
|
code from insn-recog.c. */
|
1999-10-15 08:56:28 +02:00
|
|
|
|
if (set
|
1999-10-14 11:37:31 +02:00
|
|
|
|
&& pred_name[0] != '\0'
|
|
|
|
|
&& allows_non_lvalue)
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
1999-10-15 08:56:28 +02:00
|
|
|
|
"warning: destination operand %d allows non-lvalue",
|
1999-10-14 13:01:59 +02:00
|
|
|
|
XINT (pattern, 0));
|
1999-10-14 11:37:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A modeless MATCH_OPERAND can be handy when we can
|
|
|
|
|
check for multiple modes in the c_test. In most other cases,
|
|
|
|
|
it is a mistake. Only DEFINE_INSN is eligible, since SPLIT
|
1999-10-15 03:52:29 +02:00
|
|
|
|
and PEEP2 can FAIL within the output pattern. Exclude
|
|
|
|
|
address_operand, since its mode is related to the mode of
|
1999-10-15 08:56:28 +02:00
|
|
|
|
the memory not the operand. Exclude the SET_DEST of a call
|
|
|
|
|
instruction, as that is a common idiom. */
|
1999-10-14 11:37:31 +02:00
|
|
|
|
|
|
|
|
|
if (GET_MODE (pattern) == VOIDmode
|
|
|
|
|
&& code == MATCH_OPERAND
|
1999-10-15 03:52:29 +02:00
|
|
|
|
&& GET_CODE (insn) == DEFINE_INSN
|
1999-10-14 11:37:31 +02:00
|
|
|
|
&& allows_non_const
|
|
|
|
|
&& ! special_mode_pred
|
1999-10-15 03:52:29 +02:00
|
|
|
|
&& pred_name[0] != '\0'
|
|
|
|
|
&& strcmp (pred_name, "address_operand") != 0
|
1999-10-15 08:56:28 +02:00
|
|
|
|
&& strstr (c_test, "operands") == NULL
|
|
|
|
|
&& ! (set
|
|
|
|
|
&& GET_CODE (set) == SET
|
|
|
|
|
&& GET_CODE (SET_SRC (set)) == CALL))
|
1999-10-14 11:37:31 +02:00
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: operand %d missing mode?",
|
|
|
|
|
XINT (pattern, 0));
|
1999-10-14 05:43:49 +02:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SET:
|
1999-10-14 11:37:31 +02:00
|
|
|
|
{
|
|
|
|
|
enum machine_mode dmode, smode;
|
|
|
|
|
rtx dest, src;
|
|
|
|
|
|
|
|
|
|
dest = SET_DEST (pattern);
|
|
|
|
|
src = SET_SRC (pattern);
|
|
|
|
|
|
|
|
|
|
/* Find the referant for a DUP. */
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (dest) == MATCH_DUP
|
|
|
|
|
|| GET_CODE (dest) == MATCH_OP_DUP
|
|
|
|
|
|| GET_CODE (dest) == MATCH_PAR_DUP)
|
|
|
|
|
dest = find_operand (insn, XINT (dest, 0));
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (src) == MATCH_DUP
|
|
|
|
|
|| GET_CODE (src) == MATCH_OP_DUP
|
|
|
|
|
|| GET_CODE (src) == MATCH_PAR_DUP)
|
|
|
|
|
src = find_operand (insn, XINT (src, 0));
|
|
|
|
|
|
|
|
|
|
/* STRICT_LOW_PART is a wrapper. Its argument is the real
|
|
|
|
|
destination, and it's mode should match the source. */
|
|
|
|
|
if (GET_CODE (dest) == STRICT_LOW_PART)
|
|
|
|
|
dest = XEXP (dest, 0);
|
|
|
|
|
|
|
|
|
|
dmode = GET_MODE (dest);
|
|
|
|
|
smode = GET_MODE (src);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
1999-10-14 11:37:31 +02:00
|
|
|
|
/* The mode of an ADDRESS_OPERAND is the mode of the memory
|
|
|
|
|
reference, not the mode of the address. */
|
|
|
|
|
if (GET_CODE (src) == MATCH_OPERAND
|
|
|
|
|
&& ! strcmp (XSTR (src, 1), "address_operand"))
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
/* The operands of a SET must have the same mode unless one
|
|
|
|
|
is VOIDmode. */
|
|
|
|
|
else if (dmode != VOIDmode && smode != VOIDmode && dmode != smode)
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"mode mismatch in set: %smode vs %smode",
|
|
|
|
|
GET_MODE_NAME (dmode), GET_MODE_NAME (smode));
|
|
|
|
|
error_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If only one of the operands is VOIDmode, and PC or CC0 is
|
|
|
|
|
not involved, it's probably a mistake. */
|
|
|
|
|
else if (dmode != smode
|
|
|
|
|
&& GET_CODE (dest) != PC
|
|
|
|
|
&& GET_CODE (dest) != CC0
|
1999-10-15 08:56:28 +02:00
|
|
|
|
&& GET_CODE (src) != PC
|
|
|
|
|
&& GET_CODE (src) != CC0
|
1999-10-14 11:37:31 +02:00
|
|
|
|
&& GET_CODE (src) != CONST_INT)
|
|
|
|
|
{
|
|
|
|
|
const char *which;
|
|
|
|
|
which = (dmode == VOIDmode ? "destination" : "source");
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: %s missing a mode?", which);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dest != SET_DEST (pattern))
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (dest, insn, pattern);
|
|
|
|
|
validate_pattern (SET_DEST (pattern), insn, pattern);
|
|
|
|
|
validate_pattern (SET_SRC (pattern), insn, NULL_RTX);
|
1999-10-14 11:37:31 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case CLOBBER:
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (SET_DEST (pattern), insn, pattern);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
return;
|
1999-10-14 11:37:31 +02:00
|
|
|
|
|
1999-10-14 05:43:49 +02:00
|
|
|
|
case LABEL_REF:
|
|
|
|
|
if (GET_MODE (XEXP (pattern, 0)) != VOIDmode)
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"operand to label_ref %smode not VOIDmode",
|
|
|
|
|
GET_MODE_NAME (GET_MODE (XEXP (pattern, 0))));
|
|
|
|
|
error_count++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (XEXP (pattern, i), insn, NULL_RTX);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
for (j = 0; j < XVECLEN (pattern, i); j++)
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'i': case 'w': case '0': case 's':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Create a chain of nodes to verify that an rtl expression matches
|
|
|
|
|
PATTERN.
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
LAST is a pointer to the listhead in the previous node in the chain (or
|
|
|
|
|
in the calling function, for the first node).
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
POSITION is the string representing the current position in the insn.
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-08-31 22:37:09 +02:00
|
|
|
|
INSN_TYPE is the type of insn for which we are emitting code.
|
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
A pointer to the final node in the chain is returned. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
static struct decision *
|
1999-08-31 22:37:09 +02:00
|
|
|
|
add_to_sequence (pattern, last, position, insn_type, top)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
rtx pattern;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
struct decision_head *last;
|
genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattrtab.c (fatal, attr_printf, attr_string, write_attr_set,
write_unit_name, write_eligible_delay, expand_units,
make_length_attrs, write_attr_case, find_attr,
make_internal_attr): Likewise.
* gencheck.c (tree_codes): Likewise.
* gencodes.c (fatal): Likewise.
* genconfig.c (fatal): Likewise.
* genemit.c (fatal): Likewise.
* genextract.c (fatal, walk_rtx, copystr): Likewise.
* genflags.c (fatal): Likewise.
* genopinit.c (fatal, optabs, gen_insn): Likewise.
* genoutput.c (fatal, error, predicates): Likewise.
* genpeep.c (fatal): Likewise.
* genrecog.c (fatal, decision, pred_table, add_to_sequence,
write_tree_1, write_tree, change_state, copystr, indents): Likewise.
From-SVN: r24377
1998-12-19 08:04:42 +01:00
|
|
|
|
const char *position;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
enum routine_type insn_type;
|
|
|
|
|
int top;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
RTX_CODE code;
|
|
|
|
|
struct decision *this, *sub;
|
|
|
|
|
struct decision_test *test;
|
|
|
|
|
struct decision_test **place;
|
|
|
|
|
char *subpos;
|
1998-03-12 01:02:51 +01:00
|
|
|
|
register size_t i;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
register const char *fmt;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
int depth = strlen (position);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
int len;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
enum machine_mode mode;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
if (depth > max_depth)
|
|
|
|
|
max_depth = depth;
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
subpos = (char *) alloca (depth + 2);
|
|
|
|
|
strcpy (subpos, position);
|
|
|
|
|
subpos[depth + 1] = 0;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
sub = this = new_decision (position, last);
|
|
|
|
|
place = &this->tests;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
restart:
|
1999-10-05 07:18:54 +02:00
|
|
|
|
mode = GET_MODE (pattern);
|
|
|
|
|
code = GET_CODE (pattern);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
1999-08-31 22:37:09 +02:00
|
|
|
|
case PARALLEL:
|
|
|
|
|
/* Toplevel peephole pattern. */
|
|
|
|
|
if (insn_type == PEEPHOLE2 && top)
|
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* We don't need the node we just created -- unlink it. */
|
|
|
|
|
last->first = last->last = NULL;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < (size_t) XVECLEN (pattern, 0); i++)
|
|
|
|
|
{
|
|
|
|
|
/* Which insn we're looking at is represented by A-Z. We don't
|
|
|
|
|
ever use 'A', however; it is always implied. */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
subpos[depth] = (i > 0 ? 'A' + i : 0);
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, 0, i),
|
|
|
|
|
last, subpos, insn_type, 0);
|
|
|
|
|
last = &sub->success;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return sub;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* Else nothing special. */
|
1999-08-31 22:37:09 +02:00
|
|
|
|
break;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
case MATCH_OPERAND:
|
|
|
|
|
case MATCH_SCRATCH:
|
|
|
|
|
case MATCH_OPERATOR:
|
|
|
|
|
case MATCH_PARALLEL:
|
1999-09-15 05:32:55 +02:00
|
|
|
|
case MATCH_INSN:
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
const char *pred_name;
|
|
|
|
|
RTX_CODE was_code = code;
|
1999-10-10 11:43:26 +02:00
|
|
|
|
int allows_const_int = 1;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
if (code == MATCH_SCRATCH)
|
|
|
|
|
{
|
|
|
|
|
pred_name = "scratch_operand";
|
|
|
|
|
code = UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pred_name = XSTR (pattern, 1);
|
|
|
|
|
if (code == MATCH_PARALLEL)
|
|
|
|
|
code = PARALLEL;
|
|
|
|
|
else
|
|
|
|
|
code = UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We know exactly what const_int_operand matches -- any CONST_INT. */
|
|
|
|
|
if (strcmp ("const_int_operand", pred_name) == 0)
|
|
|
|
|
{
|
|
|
|
|
code = CONST_INT;
|
|
|
|
|
mode = VOIDmode;
|
|
|
|
|
}
|
|
|
|
|
else if (pred_name[0] != 0)
|
|
|
|
|
{
|
|
|
|
|
test = new_decision_test (DT_pred, &place);
|
|
|
|
|
test->u.pred.name = pred_name;
|
|
|
|
|
test->u.pred.mode = mode;
|
|
|
|
|
|
|
|
|
|
/* See if we know about this predicate and save its number. If
|
|
|
|
|
we do, and it only accepts one code, note that fact. The
|
|
|
|
|
predicate `const_int_operand' only tests for a CONST_INT, so
|
|
|
|
|
if we do so we can avoid calling it at all.
|
|
|
|
|
|
|
|
|
|
Finally, if we know that the predicate does not allow
|
|
|
|
|
CONST_INT, we know that the only way the predicate can match
|
|
|
|
|
is if the modes match (here we use the kludge of relying on
|
|
|
|
|
the fact that "address_operand" accepts CONST_INT; otherwise,
|
|
|
|
|
it would have to be a special case), so we can test the mode
|
|
|
|
|
(but we need not). This fact should considerably simplify the
|
|
|
|
|
generated code. */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_KNOWN_PREDS; i++)
|
|
|
|
|
if (! strcmp (preds[i].name, pred_name))
|
|
|
|
|
break;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (i < NUM_KNOWN_PREDS)
|
1993-09-19 18:50:43 +02:00
|
|
|
|
{
|
1999-10-10 21:04:29 +02:00
|
|
|
|
int j;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
test->u.pred.index = i;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (preds[i].codes[1] == 0 && code == UNKNOWN)
|
|
|
|
|
code = preds[i].codes[0];
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
allows_const_int = 0;
|
|
|
|
|
for (j = 0; preds[i].codes[j] != 0; j++)
|
1993-09-19 18:50:43 +02:00
|
|
|
|
if (preds[i].codes[j] == CONST_INT)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
allows_const_int = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
1993-09-19 18:50:43 +02:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
else
|
1999-10-14 05:43:49 +02:00
|
|
|
|
test->u.pred.index = -1;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1999-10-10 11:43:26 +02:00
|
|
|
|
|
|
|
|
|
/* Can't enforce a mode if we allow const_int. */
|
|
|
|
|
if (allows_const_int)
|
|
|
|
|
mode = VOIDmode;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Accept the operand, ie. record it in `operands'. */
|
|
|
|
|
test = new_decision_test (DT_accept_op, &place);
|
|
|
|
|
test->u.opno = XINT (pattern, 0);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (was_code == MATCH_OPERATOR || was_code == MATCH_PARALLEL)
|
|
|
|
|
{
|
|
|
|
|
char base = (was_code == MATCH_OPERATOR ? '0' : 'a');
|
|
|
|
|
for (i = 0; i < (size_t) XVECLEN (pattern, 2); i++)
|
|
|
|
|
{
|
|
|
|
|
subpos[depth] = i + base;
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, 2, i),
|
|
|
|
|
&sub->success, subpos, insn_type, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
goto fini;
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
case MATCH_OP_DUP:
|
1999-10-05 07:18:54 +02:00
|
|
|
|
code = UNKNOWN;
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_dup, &place);
|
|
|
|
|
test->u.dup = XINT (pattern, 0);
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_accept_op, &place);
|
|
|
|
|
test->u.opno = XINT (pattern, 0);
|
|
|
|
|
|
1998-10-14 11:02:55 +02:00
|
|
|
|
for (i = 0; i < (size_t) XVECLEN (pattern, 1); i++)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
subpos[depth] = i + '0';
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, 1, i),
|
|
|
|
|
&sub->success, subpos, insn_type, 0);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
goto fini;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
case MATCH_DUP:
|
1992-08-14 20:44:28 +02:00
|
|
|
|
case MATCH_PAR_DUP:
|
1999-10-05 07:18:54 +02:00
|
|
|
|
code = UNKNOWN;
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_dup, &place);
|
|
|
|
|
test->u.dup = XINT (pattern, 0);
|
|
|
|
|
goto fini;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
case ADDRESS:
|
|
|
|
|
pattern = XEXP (pattern, 0);
|
|
|
|
|
goto restart;
|
|
|
|
|
|
1998-01-29 00:25:15 +01:00
|
|
|
|
default:
|
|
|
|
|
break;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* Do tests against the current node first. */
|
1998-10-14 11:02:55 +02:00
|
|
|
|
for (i = 0; i < (size_t) len; i++)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (fmt[i] == 'i')
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
test = new_decision_test (DT_elt_zero_int, &place);
|
|
|
|
|
test->u.intval = XINT (pattern, i);
|
|
|
|
|
}
|
|
|
|
|
else if (i == 1)
|
|
|
|
|
{
|
|
|
|
|
test = new_decision_test (DT_elt_one_int, &place);
|
|
|
|
|
test->u.intval = XINT (pattern, i);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
abort ();
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
else if (fmt[i] == 'w')
|
1992-07-06 22:02:55 +02:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (i != 0)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_elt_zero_wide, &place);
|
|
|
|
|
test->u.intval = XWINT (pattern, i);
|
1992-07-06 22:02:55 +02:00
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
else if (fmt[i] == 'E')
|
|
|
|
|
{
|
|
|
|
|
if (i != 0)
|
|
|
|
|
abort ();
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_veclen, &place);
|
|
|
|
|
test->u.veclen = XVECLEN (pattern, i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now test our sub-patterns. */
|
|
|
|
|
for (i = 0; i < (size_t) len; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
|
|
|
|
subpos[depth] = '0' + i;
|
|
|
|
|
sub = add_to_sequence (XEXP (pattern, i), &sub->success,
|
|
|
|
|
subpos, insn_type, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
{
|
|
|
|
|
register int j;
|
|
|
|
|
for (j = 0; j < XVECLEN (pattern, i); j++)
|
|
|
|
|
{
|
|
|
|
|
subpos[depth] = 'a' + j;
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, i, j),
|
|
|
|
|
&sub->success, subpos, insn_type, 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'i': case 'w':
|
|
|
|
|
/* Handled above. */
|
|
|
|
|
break;
|
|
|
|
|
case '0':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fini:
|
|
|
|
|
/* Insert nodes testing mode and code, if they're still relevant,
|
|
|
|
|
before any of the nodes we may have added above. */
|
|
|
|
|
if (code != UNKNOWN)
|
|
|
|
|
{
|
|
|
|
|
place = &this->tests;
|
|
|
|
|
test = new_decision_test (DT_code, &place);
|
|
|
|
|
test->u.code = code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode != VOIDmode)
|
|
|
|
|
{
|
|
|
|
|
place = &this->tests;
|
|
|
|
|
test = new_decision_test (DT_mode, &place);
|
|
|
|
|
test->u.mode = mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we didn't insert any tests or accept nodes, hork. */
|
|
|
|
|
if (this->tests == NULL)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A subroutine of maybe_both_true; examines only one test.
|
|
|
|
|
Returns > 0 for "definitely both true" and < 0 for "maybe both true". */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
maybe_both_true_2 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
if (d1->type == d2->type)
|
|
|
|
|
{
|
|
|
|
|
switch (d1->type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
|
|
|
|
return d1->u.mode == d2->u.mode;
|
|
|
|
|
|
|
|
|
|
case DT_code:
|
|
|
|
|
return d1->u.code == d2->u.code;
|
|
|
|
|
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
return d1->u.veclen == d2->u.veclen;
|
|
|
|
|
|
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
return d1->u.intval == d2->u.intval;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If either has a predicate that we know something about, set
|
|
|
|
|
things up so that D1 is the one that always has a known
|
|
|
|
|
predicate. Then see if they have any codes in common. */
|
|
|
|
|
|
|
|
|
|
if (d1->type == DT_pred || d2->type == DT_pred)
|
|
|
|
|
{
|
|
|
|
|
if (d2->type == DT_pred)
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *tmp;
|
|
|
|
|
tmp = d1, d1 = d2, d2 = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If D2 tests a mode, see if it matches D1. */
|
|
|
|
|
if (d1->u.pred.mode != VOIDmode)
|
|
|
|
|
{
|
|
|
|
|
if (d2->type == DT_mode)
|
|
|
|
|
{
|
2000-01-13 11:19:39 +01:00
|
|
|
|
if (d1->u.pred.mode != d2->u.mode
|
|
|
|
|
/* The mode of an address_operand predicate is the
|
|
|
|
|
mode of the memory, not the operand. It can only
|
|
|
|
|
be used for testing the predicate, so we must
|
|
|
|
|
ignore it here. */
|
|
|
|
|
&& strcmp (d1->u.pred.name, "address_operand") != 0)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
1999-10-14 06:07:03 +02:00
|
|
|
|
/* Don't check two predicate modes here, because if both predicates
|
|
|
|
|
accept CONST_INT, then both can still be true even if the modes
|
|
|
|
|
are different. If they don't accept CONST_INT, there will be a
|
|
|
|
|
separate DT_mode that will make maybe_both_true_1 return 0. */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (d1->u.pred.index >= 0)
|
|
|
|
|
{
|
|
|
|
|
/* If D2 tests a code, see if it is in the list of valid
|
|
|
|
|
codes for D1's predicate. */
|
|
|
|
|
if (d2->type == DT_code)
|
|
|
|
|
{
|
|
|
|
|
const RTX_CODE *c = &preds[d1->u.pred.index].codes[0];
|
|
|
|
|
while (*c != 0)
|
|
|
|
|
{
|
|
|
|
|
if (*c == d2->u.code)
|
|
|
|
|
break;
|
|
|
|
|
++c;
|
|
|
|
|
}
|
|
|
|
|
if (*c == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise see if the predicates have any codes in common. */
|
|
|
|
|
else if (d2->type == DT_pred && d2->u.pred.index >= 0)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
const RTX_CODE *c1 = &preds[d1->u.pred.index].codes[0];
|
|
|
|
|
int common = 0;
|
|
|
|
|
|
|
|
|
|
while (*c1 != 0 && !common)
|
|
|
|
|
{
|
|
|
|
|
const RTX_CODE *c2 = &preds[d2->u.pred.index].codes[0];
|
|
|
|
|
while (*c2 != 0 && !common)
|
|
|
|
|
{
|
|
|
|
|
common = (*c1 == *c2);
|
|
|
|
|
++c2;
|
|
|
|
|
}
|
|
|
|
|
++c1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!common)
|
|
|
|
|
return 0;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
return -1;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* A subroutine of maybe_both_true; examines all the tests for a given node.
|
|
|
|
|
Returns > 0 for "definitely both true" and < 0 for "maybe both true". */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
maybe_both_true_1 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *t1, *t2;
|
|
|
|
|
|
|
|
|
|
/* A match_operand with no predicate can match anything. Recognize
|
|
|
|
|
this by the existance of a lone DT_accept_op test. */
|
|
|
|
|
if (d1->type == DT_accept_op || d2->type == DT_accept_op)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* Eliminate pairs of tests while they can exactly match. */
|
|
|
|
|
while (d1 && d2 && d1->type == d2->type)
|
|
|
|
|
{
|
|
|
|
|
if (maybe_both_true_2 (d1, d2) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
d1 = d1->next, d2 = d2->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* After that, consider all pairs. */
|
|
|
|
|
for (t1 = d1; t1 ; t1 = t1->next)
|
|
|
|
|
for (t2 = d2; t2 ; t2 = t2->next)
|
|
|
|
|
if (maybe_both_true_2 (t1, t2) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 0 if we can prove that there is no RTL that can match both
|
|
|
|
|
D1 and D2. Otherwise, return 1 (it may be that there is an RTL that
|
1991-12-28 12:36:37 +01:00
|
|
|
|
can match both or just that we couldn't prove there wasn't such an RTL).
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
TOPLEVEL is non-zero if we are to only look at the top level and not
|
|
|
|
|
recursively descend. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
static int
|
1999-10-05 07:18:54 +02:00
|
|
|
|
maybe_both_true (d1, d2, toplevel)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
struct decision *d1, *d2;
|
|
|
|
|
int toplevel;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1991-12-28 12:36:37 +01:00
|
|
|
|
struct decision *p1, *p2;
|
1999-09-22 03:40:28 +02:00
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
|
|
/* Don't compare strings on the different positions in insn. Doing so
|
|
|
|
|
is incorrect and results in false matches from constructs like
|
|
|
|
|
|
|
|
|
|
[(set (subreg:HI (match_operand:SI "register_operand" "r") 0)
|
|
|
|
|
(subreg:HI (match_operand:SI "register_operand" "r") 0))]
|
|
|
|
|
vs
|
|
|
|
|
[(set (match_operand:HI "register_operand" "r")
|
|
|
|
|
(match_operand:HI "register_operand" "r"))]
|
|
|
|
|
|
|
|
|
|
If we are presented with such, we are recursing through the remainder
|
|
|
|
|
of a node's success nodes (from the loop at the end of this function).
|
|
|
|
|
Skip forward until we come to a position that matches.
|
|
|
|
|
|
|
|
|
|
Due to the way position strings are constructed, we know that iterating
|
|
|
|
|
forward from the lexically lower position (e.g. "00") will run into
|
|
|
|
|
the lexically higher position (e.g. "1") and not the other way around.
|
|
|
|
|
This saves a bit of effort. */
|
|
|
|
|
|
|
|
|
|
cmp = strcmp (d1->position, d2->position);
|
|
|
|
|
if (cmp != 0)
|
|
|
|
|
{
|
|
|
|
|
if (toplevel)
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
|
|
/* If the d2->position was lexically lower, swap. */
|
|
|
|
|
if (cmp > 0)
|
1999-09-22 08:53:35 +02:00
|
|
|
|
p1 = d1, d1 = d2, d2 = p1;
|
1999-09-22 03:40:28 +02:00
|
|
|
|
|
|
|
|
|
if (d1->success.first == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
for (p1 = d1->success.first; p1; p1 = p1->next)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (maybe_both_true (p1, d2, 0))
|
|
|
|
|
return 1;
|
1999-09-22 03:40:28 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return 0;
|
1999-09-22 03:40:28 +02:00
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Test the current level. */
|
|
|
|
|
cmp = maybe_both_true_1 (d1->tests, d2->tests);
|
|
|
|
|
if (cmp >= 0)
|
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
|
|
/* We can't prove that D1 and D2 cannot both be true. If we are only
|
|
|
|
|
to check the top level, return 1. Otherwise, see if we can prove
|
|
|
|
|
that all choices in both successors are mutually exclusive. If
|
|
|
|
|
either does not have any successors, we can't prove they can't both
|
|
|
|
|
be true. */
|
|
|
|
|
|
|
|
|
|
if (toplevel || d1->success.first == 0 || d2->success.first == 0)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
return 1;
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (p1 = d1->success.first; p1; p1 = p1->next)
|
|
|
|
|
for (p2 = d2->success.first; p2; p2 = p2->next)
|
|
|
|
|
if (maybe_both_true (p1, p2, 0))
|
|
|
|
|
return 1;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* A subroutine of nodes_identical. Examine two tests for equivalence. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int
|
|
|
|
|
nodes_identical_1 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
switch (d1->type)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_mode:
|
|
|
|
|
return d1->u.mode == d2->u.mode;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_code:
|
|
|
|
|
return d1->u.code == d2->u.code;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_pred:
|
|
|
|
|
return (d1->u.pred.mode == d2->u.pred.mode
|
|
|
|
|
&& strcmp (d1->u.pred.name, d2->u.pred.name) == 0);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_c_test:
|
|
|
|
|
return strcmp (d1->u.c_test, d2->u.c_test) == 0;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_veclen:
|
|
|
|
|
return d1->u.veclen == d2->u.veclen;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_dup:
|
|
|
|
|
return d1->u.dup == d2->u.dup;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
return d1->u.intval == d2->u.intval;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_accept_op:
|
|
|
|
|
return d1->u.opno == d2->u.opno;
|
|
|
|
|
|
|
|
|
|
case DT_accept_insn:
|
|
|
|
|
/* Differences will be handled in merge_accept_insn. */
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* True iff the two nodes are identical (on one level only). Due
|
|
|
|
|
to the way these lists are constructed, we shouldn't have to
|
|
|
|
|
consider different orderings on the tests. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static int
|
|
|
|
|
nodes_identical (d1, d2)
|
|
|
|
|
struct decision *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *t1, *t2;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (t1 = d1->tests, t2 = d2->tests; t1 && t2; t1 = t1->next, t2 = t2->next)
|
|
|
|
|
{
|
|
|
|
|
if (t1->type != t2->type)
|
|
|
|
|
return 0;
|
|
|
|
|
if (! nodes_identical_1 (t1, t2))
|
1991-12-28 12:36:37 +01:00
|
|
|
|
return 0;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* For success, they should now both be null. */
|
1999-10-15 08:56:28 +02:00
|
|
|
|
if (t1 != t2)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Check that their subnodes are at the same position, as any one set
|
|
|
|
|
of sibling decisions must be at the same position. */
|
|
|
|
|
if (d1->success.first
|
|
|
|
|
&& d2->success.first
|
|
|
|
|
&& strcmp (d1->success.first->position, d2->success.first->position))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* A subroutine of merge_trees; given two nodes that have been declared
|
|
|
|
|
identical, cope with two insn accept states. If they differ in the
|
|
|
|
|
number of clobbers, then the conflict was created by make_insn_sequence
|
|
|
|
|
and we can drop the with-clobbers version on the floor. If both
|
|
|
|
|
nodes have no additional clobbers, we have found an ambiguity in the
|
|
|
|
|
source machine description. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
merge_accept_insn (oldd, addd)
|
|
|
|
|
struct decision *oldd, *addd;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision_test *old, *add;
|
|
|
|
|
|
|
|
|
|
for (old = oldd->tests; old; old = old->next)
|
|
|
|
|
if (old->type == DT_accept_insn)
|
|
|
|
|
break;
|
|
|
|
|
if (old == NULL)
|
|
|
|
|
return;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (add = addd->tests; add; add = add->next)
|
|
|
|
|
if (add->type == DT_accept_insn)
|
|
|
|
|
break;
|
|
|
|
|
if (add == NULL)
|
|
|
|
|
return;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* If one node is for a normal insn and the second is for the base
|
|
|
|
|
insn with clobbers stripped off, the second node should be ignored. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (old->u.insn.num_clobbers_to_add == 0
|
|
|
|
|
&& add->u.insn.num_clobbers_to_add > 0)
|
|
|
|
|
{
|
|
|
|
|
/* Nothing to do here. */
|
|
|
|
|
}
|
|
|
|
|
else if (old->u.insn.num_clobbers_to_add > 0
|
|
|
|
|
&& add->u.insn.num_clobbers_to_add == 0)
|
|
|
|
|
{
|
|
|
|
|
/* In this case, replace OLD with ADD. */
|
|
|
|
|
old->u.insn = add->u.insn;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-10-14 05:43:49 +02:00
|
|
|
|
message_with_line (add->u.insn.lineno, "`%s' matches `%s'",
|
|
|
|
|
get_insn_name (add->u.insn.code_number),
|
|
|
|
|
get_insn_name (old->u.insn.code_number));
|
|
|
|
|
message_with_line (old->u.insn.lineno, "previous definition of `%s'",
|
|
|
|
|
get_insn_name (old->u.insn.code_number));
|
|
|
|
|
error_count++;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Merge two decision trees OLDH and ADDH, modifying OLDH destructively. */
|
|
|
|
|
|
|
|
|
|
static void
|
1991-12-28 12:36:37 +01:00
|
|
|
|
merge_trees (oldh, addh)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision_head *oldh, *addh;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision *next, *add;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (addh->first == 0)
|
|
|
|
|
return;
|
|
|
|
|
if (oldh->first == 0)
|
|
|
|
|
{
|
|
|
|
|
*oldh = *addh;
|
|
|
|
|
return;
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Trying to merge bits at different positions isn't possible. */
|
|
|
|
|
if (strcmp (oldh->first->position, addh->first->position))
|
1991-12-28 12:36:37 +01:00
|
|
|
|
abort ();
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (add = addh->first; add ; add = next)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision *old, *insert_before = NULL;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
next = add->next;
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* The semantics of pattern matching state that the tests are
|
|
|
|
|
done in the order given in the MD file so that if an insn
|
|
|
|
|
matches two patterns, the first one will be used. However,
|
|
|
|
|
in practice, most, if not all, patterns are unambiguous so
|
|
|
|
|
that their order is independent. In that case, we can merge
|
|
|
|
|
identical tests and group all similar modes and codes together.
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
Scan starting from the end of OLDH until we reach a point
|
1999-10-05 07:18:54 +02:00
|
|
|
|
where we reach the head of the list or where we pass a
|
|
|
|
|
pattern that could also be true if NEW is true. If we find
|
|
|
|
|
an identical pattern, we can merge them. Also, record the
|
|
|
|
|
last node that tests the same code and mode and the last one
|
|
|
|
|
that tests just the same mode.
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
If we have no match, place NEW after the closest match we found. */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
for (old = oldh->last; old; old = old->prev)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (nodes_identical (old, add))
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
merge_accept_insn (old, add);
|
|
|
|
|
merge_trees (&old->success, &add->success);
|
|
|
|
|
goto merged_nodes;
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (maybe_both_true (old, add, 0))
|
|
|
|
|
break;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Insert the nodes in DT test type order, which is roughly
|
|
|
|
|
how expensive/important the test is. Given that the tests
|
|
|
|
|
are also ordered within the list, examining the first is
|
|
|
|
|
sufficient. */
|
|
|
|
|
if (add->tests->type < old->tests->type)
|
|
|
|
|
insert_before = old;
|
|
|
|
|
}
|
1992-01-15 22:29:48 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (insert_before == NULL)
|
|
|
|
|
{
|
|
|
|
|
add->next = NULL;
|
|
|
|
|
add->prev = oldh->last;
|
|
|
|
|
oldh->last->next = add;
|
|
|
|
|
oldh->last = add;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ((add->prev = insert_before->prev) != NULL)
|
|
|
|
|
add->prev->next = add;
|
|
|
|
|
else
|
|
|
|
|
oldh->first = add;
|
|
|
|
|
add->next = insert_before;
|
|
|
|
|
insert_before->prev = add;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
merged_nodes:;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Walk the tree looking for sub-nodes that perform common tests.
|
|
|
|
|
Factor out the common test into a new node. This enables us
|
|
|
|
|
(depending on the test type) to emit switch statements later. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
factor_tests (head)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
{
|
|
|
|
|
struct decision *first, *next;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (first = head->first; first && first->next; first = next)
|
|
|
|
|
{
|
|
|
|
|
enum decision_type type;
|
|
|
|
|
struct decision *new, *old_last;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
type = first->tests->type;
|
|
|
|
|
next = first->next;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Want at least two compatible sequential nodes. */
|
|
|
|
|
if (next->tests->type != type)
|
|
|
|
|
continue;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Don't want all node types, just those we can turn into
|
|
|
|
|
switch statements. */
|
|
|
|
|
if (type != DT_mode
|
|
|
|
|
&& type != DT_code
|
|
|
|
|
&& type != DT_veclen
|
|
|
|
|
&& type != DT_elt_zero_int
|
|
|
|
|
&& type != DT_elt_one_int
|
|
|
|
|
&& type != DT_elt_zero_wide)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
continue;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* If we'd been performing more than one test, create a new node
|
|
|
|
|
below our first test. */
|
|
|
|
|
if (first->tests->next != NULL)
|
|
|
|
|
{
|
|
|
|
|
new = new_decision (first->position, &first->success);
|
|
|
|
|
new->tests = first->tests->next;
|
|
|
|
|
first->tests->next = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Crop the node tree off after our first test. */
|
|
|
|
|
first->next = NULL;
|
|
|
|
|
old_last = head->last;
|
|
|
|
|
head->last = first;
|
|
|
|
|
|
|
|
|
|
/* For each compatible test, adjust to perform only one test in
|
|
|
|
|
the top level node, then merge the node back into the tree. */
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
struct decision_head h;
|
|
|
|
|
|
|
|
|
|
if (next->tests->next != NULL)
|
|
|
|
|
{
|
|
|
|
|
new = new_decision (next->position, &next->success);
|
|
|
|
|
new->tests = next->tests->next;
|
|
|
|
|
next->tests->next = NULL;
|
|
|
|
|
}
|
|
|
|
|
new = next;
|
|
|
|
|
next = next->next;
|
|
|
|
|
new->next = NULL;
|
|
|
|
|
h.first = h.last = new;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
merge_trees (head, &h);
|
|
|
|
|
}
|
|
|
|
|
while (next && next->tests->type == type);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* After we run out of compatible tests, graft the remaining nodes
|
|
|
|
|
back onto the tree. */
|
|
|
|
|
if (next)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
next->prev = head->last;
|
|
|
|
|
head->last->next = next;
|
|
|
|
|
head->last = old_last;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Recurse. */
|
|
|
|
|
for (first = head->first; first; first = first->next)
|
|
|
|
|
factor_tests (&first->success);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* After factoring, try to simplify the tests on any one node.
|
|
|
|
|
Tests that are useful for switch statements are recognizable
|
|
|
|
|
by having only a single test on a node -- we'll be manipulating
|
|
|
|
|
nodes with multiple tests:
|
|
|
|
|
|
|
|
|
|
If we have mode tests or code tests that are redundant with
|
|
|
|
|
predicates, remove them. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simplify_tests (head)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
{
|
|
|
|
|
struct decision *tree;
|
|
|
|
|
|
|
|
|
|
for (tree = head->first; tree; tree = tree->next)
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *a, *b;
|
|
|
|
|
|
|
|
|
|
a = tree->tests;
|
|
|
|
|
b = a->next;
|
|
|
|
|
if (b == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Find a predicate node. */
|
|
|
|
|
while (b && b->type != DT_pred)
|
|
|
|
|
b = b->next;
|
|
|
|
|
if (b)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Due to how these tests are constructed, we don't even need
|
|
|
|
|
to check that the mode and code are compatible -- they were
|
|
|
|
|
generated from the predicate in the first place. */
|
|
|
|
|
while (a->type == DT_mode || a->type == DT_code)
|
|
|
|
|
a = a->next;
|
|
|
|
|
tree->tests = a;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Recurse. */
|
|
|
|
|
for (tree = head->first; tree; tree = tree->next)
|
|
|
|
|
simplify_tests (&tree->success);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
1991-12-28 12:36:37 +01:00
|
|
|
|
/* Count the number of subnodes of HEAD. If the number is high enough,
|
|
|
|
|
make the first node in HEAD start a separate subroutine in the C code
|
1999-10-05 07:18:54 +02:00
|
|
|
|
that is generated. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
static int
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break_out_subroutines (head, initial)
|
|
|
|
|
struct decision_head *head;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
int initial;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
|
|
|
|
int size = 0;
|
1994-03-25 20:13:46 +01:00
|
|
|
|
struct decision *sub;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (sub = head->first; sub; sub = sub->next)
|
|
|
|
|
size += 1 + break_out_subroutines (&sub->success, 0);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
if (size > SUBROUTINE_THRESHOLD && ! initial)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
head->first->subroutine_number = ++next_subroutine_number;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
size = 1;
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* For each node p, find the next alternative that might be true
|
|
|
|
|
when p is true. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
static void
|
1999-10-05 07:18:54 +02:00
|
|
|
|
find_afterward (head, real_afterward)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
struct decision *real_afterward;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision *p, *q, *afterward;
|
1999-08-24 15:34:04 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* We can't propogate alternatives across subroutine boundaries.
|
|
|
|
|
This is not incorrect, merely a minor optimization loss. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
p = head->first;
|
|
|
|
|
afterward = (p->subroutine_number > 0 ? NULL : real_afterward);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for ( ; p ; p = p->next)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Find the next node that might be true if this one fails. */
|
|
|
|
|
for (q = p->next; q ; q = q->next)
|
|
|
|
|
if (maybe_both_true (p, q, 1))
|
|
|
|
|
break;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* If we reached the end of the list without finding one,
|
|
|
|
|
use the incoming afterward position. */
|
|
|
|
|
if (!q)
|
|
|
|
|
q = afterward;
|
|
|
|
|
p->afterward = q;
|
|
|
|
|
if (q)
|
|
|
|
|
q->need_label = 1;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Recurse. */
|
|
|
|
|
for (p = head->first; p ; p = p->next)
|
|
|
|
|
if (p->success.first)
|
|
|
|
|
find_afterward (&p->success, p->afterward);
|
|
|
|
|
|
|
|
|
|
/* When we are generating a subroutine, record the real afterward
|
|
|
|
|
position in the first node where write_tree can find it, and we
|
|
|
|
|
can do the right thing at the subroutine call site. */
|
|
|
|
|
p = head->first;
|
|
|
|
|
if (p->subroutine_number > 0)
|
|
|
|
|
p->afterward = real_afterward;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Assuming that the state of argument is denoted by OLDPOS, take whatever
|
|
|
|
|
actions are necessary to move to NEWPOS. If we fail to move to the
|
|
|
|
|
new state, branch to node AFTERWARD if non-zero, otherwise return.
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
Failure to move to the new state can only occur if we are trying to
|
|
|
|
|
match multiple insns and we try to step past the end of the stream. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
change_state (oldpos, newpos, afterward, indent)
|
|
|
|
|
const char *oldpos;
|
|
|
|
|
const char *newpos;
|
|
|
|
|
struct decision *afterward;
|
|
|
|
|
const char *indent;
|
|
|
|
|
{
|
|
|
|
|
int odepth = strlen (oldpos);
|
|
|
|
|
int ndepth = strlen (newpos);
|
|
|
|
|
int depth;
|
|
|
|
|
int old_has_insn, new_has_insn;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Pop up as many levels as necessary. */
|
|
|
|
|
for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
|
|
|
|
|
continue;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Hunt for the last [A-Z] in both strings. */
|
|
|
|
|
for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
|
|
|
|
|
if (oldpos[old_has_insn] >= 'A' && oldpos[old_has_insn] <= 'Z')
|
|
|
|
|
break;
|
1999-12-30 18:48:52 +01:00
|
|
|
|
for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (newpos[new_has_insn] >= 'A' && newpos[new_has_insn] <= 'Z')
|
|
|
|
|
break;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
2000-04-07 09:16:59 +02:00
|
|
|
|
/* Make sure to reset the last_insn pointer when popping back up. */
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (old_has_insn >= 0 && new_has_insn < 0)
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf ("%slast_insn = insn;\n", indent);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Go down to desired level. */
|
|
|
|
|
while (depth < ndepth)
|
|
|
|
|
{
|
|
|
|
|
/* It's a different insn from the first one. */
|
|
|
|
|
if (newpos[depth] >= 'A' && newpos[depth] <= 'Z')
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* We can only fail if we're moving down the tree. */
|
|
|
|
|
if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf ("%slast_insn = recog_next_insn (insn, %d);\n",
|
1999-10-05 07:18:54 +02:00
|
|
|
|
indent, newpos[depth] - 'A');
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf ("%stem = recog_next_insn (insn, %d);\n",
|
|
|
|
|
indent, newpos[depth] - 'A');
|
|
|
|
|
printf ("%sif (tem == NULL_RTX)\n", indent);
|
|
|
|
|
if (afterward)
|
|
|
|
|
printf ("%s goto L%d;\n", indent, afterward->number);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf ("%s goto ret0;\n", indent);
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf ("%slast_insn = tem;\n", indent);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf ("%sx%d = PATTERN (last_insn);\n", indent, depth + 1);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
else if (newpos[depth] >= 'a' && newpos[depth] <= 'z')
|
|
|
|
|
printf ("%sx%d = XVECEXP (x%d, 0, %d);\n",
|
|
|
|
|
indent, depth + 1, depth, newpos[depth] - 'a');
|
|
|
|
|
else
|
|
|
|
|
printf ("%sx%d = XEXP (x%d, %c);\n",
|
|
|
|
|
indent, depth + 1, depth, newpos[depth]);
|
|
|
|
|
++depth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print the enumerator constant for CODE -- the upcase version of
|
|
|
|
|
the name. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_code (code)
|
|
|
|
|
enum rtx_code code;
|
|
|
|
|
{
|
|
|
|
|
register const char *p;
|
|
|
|
|
for (p = GET_RTX_NAME (code); *p; p++)
|
|
|
|
|
putchar (TOUPPER (*p));
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit code to cross an afterward link -- change state and branch. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
write_afterward (start, afterward, indent)
|
|
|
|
|
struct decision *start;
|
|
|
|
|
struct decision *afterward;
|
|
|
|
|
const char *indent;
|
|
|
|
|
{
|
|
|
|
|
if (!afterward || start->subroutine_number > 0)
|
|
|
|
|
printf("%sgoto ret0;\n", indent);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
change_state (start->position, afterward->position, NULL, indent);
|
|
|
|
|
printf ("%sgoto L%d;\n", indent, afterward->number);
|
|
|
|
|
}
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit a switch statement, if possible, for an initial sequence of
|
|
|
|
|
nodes at START. Return the first node yet untested. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static struct decision *
|
|
|
|
|
write_switch (start, depth)
|
|
|
|
|
struct decision *start;
|
|
|
|
|
int depth;
|
|
|
|
|
{
|
|
|
|
|
struct decision *p = start;
|
|
|
|
|
enum decision_type type = p->tests->type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* If we have two or more nodes in sequence that test the same one
|
|
|
|
|
thing, we may be able to use a switch statement. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (!p->next
|
|
|
|
|
|| p->tests->next
|
|
|
|
|
|| p->next->tests->type != type
|
|
|
|
|
|| p->next->tests->next)
|
|
|
|
|
return p;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* DT_code is special in that we can do interesting things with
|
|
|
|
|
known predicates at the same time. */
|
|
|
|
|
if (type == DT_code)
|
|
|
|
|
{
|
|
|
|
|
char codemap[NUM_RTX_CODE];
|
|
|
|
|
struct decision *ret;
|
1999-10-30 23:40:05 +02:00
|
|
|
|
RTX_CODE code;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
memset (codemap, 0, sizeof(codemap));
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" switch (GET_CODE (x%d))\n {\n", depth);
|
1999-10-30 23:40:05 +02:00
|
|
|
|
code = p->tests->u.code;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
do
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" case ");
|
|
|
|
|
print_code (code);
|
|
|
|
|
printf (":\n goto L%d;\n", p->success.first->number);
|
|
|
|
|
p->success.first->need_label = 1;
|
|
|
|
|
|
|
|
|
|
codemap[code] = 1;
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
1999-10-30 23:40:05 +02:00
|
|
|
|
while (p
|
|
|
|
|
&& ! p->tests->next
|
|
|
|
|
&& p->tests->type == DT_code
|
|
|
|
|
&& ! codemap[code = p->tests->u.code]);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* If P is testing a predicate that we know about and we haven't
|
|
|
|
|
seen any of the codes that are valid for the predicate, we can
|
|
|
|
|
write a series of "case" statement, one for each possible code.
|
|
|
|
|
Since we are already in a switch, these redundant tests are very
|
|
|
|
|
cheap and will reduce the number of predicates called. */
|
|
|
|
|
|
|
|
|
|
/* Note that while we write out cases for these predicates here,
|
|
|
|
|
we don't actually write the test here, as it gets kinda messy.
|
|
|
|
|
It is trivial to leave this to later by telling our caller that
|
|
|
|
|
we only processed the CODE tests. */
|
|
|
|
|
ret = p;
|
|
|
|
|
|
|
|
|
|
while (p && p->tests->type == DT_pred
|
|
|
|
|
&& p->tests->u.pred.index >= 0)
|
|
|
|
|
{
|
|
|
|
|
const RTX_CODE *c;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
|
|
|
|
|
if (codemap[(int) *c] != 0)
|
|
|
|
|
goto pred_done;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" case ");
|
|
|
|
|
print_code (*c);
|
|
|
|
|
printf (":\n");
|
|
|
|
|
codemap[(int) *c] = 1;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" goto L%d;\n", p->number);
|
|
|
|
|
p->need_label = 1;
|
|
|
|
|
p = p->next;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
pred_done:
|
|
|
|
|
/* Make the default case skip the predicates we managed to match. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" default:\n");
|
|
|
|
|
if (p != ret)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (p)
|
1995-10-27 00:13:47 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" goto L%d;\n", p->number);
|
|
|
|
|
p->need_label = 1;
|
1995-10-27 00:13:47 +01:00
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_afterward (start, start->afterward, " ");
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" break;\n");
|
|
|
|
|
printf (" }\n");
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
else if (type == DT_mode
|
|
|
|
|
|| type == DT_veclen
|
|
|
|
|
|| type == DT_elt_zero_int
|
|
|
|
|
|| type == DT_elt_one_int
|
|
|
|
|
|| type == DT_elt_zero_wide)
|
|
|
|
|
{
|
|
|
|
|
printf (" switch (");
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
2000-02-19 02:27:00 +01:00
|
|
|
|
printf ("GET_MODE (x%d)", depth);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
case DT_veclen:
|
2000-02-19 02:27:00 +01:00
|
|
|
|
printf ("XVECLEN (x%d, 0)", depth);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_int:
|
2000-02-19 02:27:00 +01:00
|
|
|
|
printf ("XINT (x%d, 0)", depth);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
case DT_elt_one_int:
|
2000-02-19 02:27:00 +01:00
|
|
|
|
printf ("XINT (x%d, 1)", depth);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_wide:
|
2000-02-19 02:27:00 +01:00
|
|
|
|
/* Convert result of XWINT to int for portability since some C
|
|
|
|
|
compilers won't do it and some will. */
|
|
|
|
|
printf ("(int) XWINT (x%d, 0)", depth);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
printf (")\n {\n");
|
1992-01-07 01:30:06 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
do
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" case ");
|
|
|
|
|
switch (type)
|
1992-01-07 01:30:06 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_mode:
|
|
|
|
|
printf ("%smode", GET_MODE_NAME (p->tests->u.mode));
|
|
|
|
|
break;
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
printf ("%d", p->tests->u.veclen);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
printf (HOST_WIDE_INT_PRINT_DEC, p->tests->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
1992-01-07 01:30:06 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (":\n goto L%d;\n", p->success.first->number);
|
|
|
|
|
p->success.first->need_label = 1;
|
1992-01-07 01:30:06 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
p = p->next;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
while (p && p->tests->type == type && !p->tests->next);
|
|
|
|
|
|
|
|
|
|
printf (" default:\n break;\n }\n");
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* None of the other tests are ameanable. */
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit code for one test. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
write_cond (p, depth, subroutine_type)
|
|
|
|
|
struct decision_test *p;
|
|
|
|
|
int depth;
|
|
|
|
|
enum routine_type subroutine_type;
|
|
|
|
|
{
|
|
|
|
|
switch (p->type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
|
|
|
|
printf ("GET_MODE (x%d) == %smode", depth, GET_MODE_NAME (p->u.mode));
|
|
|
|
|
break;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
case DT_code:
|
|
|
|
|
printf ("GET_CODE (x%d) == ", depth);
|
|
|
|
|
print_code (p->u.code);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
printf ("XVECLEN (x%d, 0) == %d", depth, p->u.veclen);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
printf ("XINT (x%d, 0) == %d", depth, (int) p->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
printf ("XINT (x%d, 1) == %d", depth, (int) p->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
printf ("XWINT (x%d, 0) == ", depth);
|
|
|
|
|
printf (HOST_WIDE_INT_PRINT_DEC, p->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_dup:
|
|
|
|
|
printf ("rtx_equal_p (x%d, operands[%d])", depth, p->u.dup);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_pred:
|
|
|
|
|
printf ("%s (x%d, %smode)", p->u.pred.name, depth,
|
|
|
|
|
GET_MODE_NAME (p->u.pred.mode));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_c_test:
|
|
|
|
|
printf ("(%s)", p->u.c_test);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_accept_insn:
|
|
|
|
|
switch (subroutine_type)
|
|
|
|
|
{
|
|
|
|
|
case RECOG:
|
|
|
|
|
if (p->u.insn.num_clobbers_to_add == 0)
|
|
|
|
|
abort ();
|
|
|
|
|
printf ("pnum_clobbers != NULL");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit code for one action. The previous tests have succeeded;
|
|
|
|
|
TEST is the last of the chain. In the normal case we simply
|
|
|
|
|
perform a state change. For the `accept' tests we must do more work. */
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
write_action (test, depth, uncond, success, subroutine_type)
|
|
|
|
|
struct decision_test *test;
|
|
|
|
|
int depth, uncond;
|
|
|
|
|
struct decision *success;
|
|
|
|
|
enum routine_type subroutine_type;
|
|
|
|
|
{
|
|
|
|
|
const char *indent;
|
|
|
|
|
int want_close = 0;
|
|
|
|
|
|
|
|
|
|
if (uncond)
|
|
|
|
|
indent = " ";
|
|
|
|
|
else if (test->type == DT_accept_op || test->type == DT_accept_insn)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
fputs (" {\n", stdout);
|
|
|
|
|
indent = " ";
|
|
|
|
|
want_close = 1;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
else
|
|
|
|
|
indent = " ";
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (test->type == DT_accept_op)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf("%soperands[%d] = x%d;\n", indent, test->u.opno, depth);
|
|
|
|
|
|
|
|
|
|
/* Only allow DT_accept_insn to follow. */
|
|
|
|
|
if (test->next)
|
|
|
|
|
{
|
|
|
|
|
test = test->next;
|
|
|
|
|
if (test->type != DT_accept_insn)
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Sanity check that we're now at the end of the list of tests. */
|
|
|
|
|
if (test->next)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
abort ();
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (test->type == DT_accept_insn)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
switch (subroutine_type)
|
|
|
|
|
{
|
|
|
|
|
case RECOG:
|
|
|
|
|
if (test->u.insn.num_clobbers_to_add != 0)
|
|
|
|
|
printf ("%s*pnum_clobbers = %d;\n",
|
|
|
|
|
indent, test->u.insn.num_clobbers_to_add);
|
|
|
|
|
printf ("%sreturn %d;\n", indent, test->u.insn.code_number);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SPLIT:
|
|
|
|
|
printf ("%sreturn gen_split_%d (operands);\n",
|
|
|
|
|
indent, test->u.insn.code_number);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PEEPHOLE2:
|
|
|
|
|
printf ("%stem = gen_peephole2_%d (insn, operands);\n",
|
|
|
|
|
indent, test->u.insn.code_number);
|
|
|
|
|
printf ("%sif (tem != 0)\n%s goto ret1;\n", indent, indent);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
printf("%sgoto L%d;\n", indent, success->number);
|
|
|
|
|
success->need_label = 1;
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (want_close)
|
|
|
|
|
fputs (" }\n", stdout);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Return 1 if the test is always true and has no fallthru path. Return -1
|
|
|
|
|
if the test does have a fallthru path, but requires that the condition be
|
|
|
|
|
terminated. Otherwise return 0 for a normal test. */
|
|
|
|
|
/* ??? is_unconditional is a stupid name for a tri-state function. */
|
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
static int
|
1999-10-05 07:18:54 +02:00
|
|
|
|
is_unconditional (t, subroutine_type)
|
|
|
|
|
struct decision_test *t;
|
|
|
|
|
enum routine_type subroutine_type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (t->type == DT_accept_op)
|
|
|
|
|
return 1;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (t->type == DT_accept_insn)
|
|
|
|
|
{
|
|
|
|
|
switch (subroutine_type)
|
|
|
|
|
{
|
|
|
|
|
case RECOG:
|
|
|
|
|
return (t->u.insn.num_clobbers_to_add == 0);
|
|
|
|
|
case SPLIT:
|
|
|
|
|
return 1;
|
|
|
|
|
case PEEPHOLE2:
|
|
|
|
|
return -1;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return 0;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit code for one node -- the conditional and the accompanying action.
|
|
|
|
|
Return true if there is no fallthru path. */
|
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
static int
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_node (p, depth, subroutine_type)
|
|
|
|
|
struct decision *p;
|
|
|
|
|
int depth;
|
|
|
|
|
enum routine_type subroutine_type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision_test *test, *last_test;
|
|
|
|
|
int uncond;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
last_test = test = p->tests;
|
|
|
|
|
uncond = is_unconditional (test, subroutine_type);
|
|
|
|
|
if (uncond == 0)
|
|
|
|
|
{
|
|
|
|
|
printf (" if (");
|
|
|
|
|
write_cond (test, depth, subroutine_type);
|
|
|
|
|
|
|
|
|
|
while ((test = test->next) != NULL)
|
|
|
|
|
{
|
|
|
|
|
int uncond2;
|
|
|
|
|
|
|
|
|
|
last_test = test;
|
|
|
|
|
uncond2 = is_unconditional (test, subroutine_type);
|
|
|
|
|
if (uncond2 != 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
printf ("\n && ");
|
|
|
|
|
write_cond (test, depth, subroutine_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf (")\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_action (last_test, depth, uncond, p->success.first, subroutine_type);
|
|
|
|
|
|
|
|
|
|
return uncond > 0;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Emit code for all of the sibling nodes of HEAD. */
|
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
static void
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_tree_1 (head, depth, subroutine_type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
int depth;
|
|
|
|
|
enum routine_type subroutine_type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision *p, *next;
|
|
|
|
|
int uncond = 0;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (p = head->first; p ; p = next)
|
|
|
|
|
{
|
|
|
|
|
/* The label for the first element was printed in write_tree. */
|
|
|
|
|
if (p != head->first && p->need_label)
|
|
|
|
|
OUTPUT_LABEL (" ", p->number);
|
|
|
|
|
|
|
|
|
|
/* Attempt to write a switch statement for a whole sequence. */
|
|
|
|
|
next = write_switch (p, depth);
|
|
|
|
|
if (p != next)
|
|
|
|
|
uncond = 0;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Failed -- fall back and write one node. */
|
|
|
|
|
uncond = write_node (p, depth, subroutine_type);
|
|
|
|
|
next = p->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Finished with this chain. Close a fallthru path by branching
|
|
|
|
|
to the afterward node. */
|
|
|
|
|
if (! uncond)
|
|
|
|
|
write_afterward (head->last, head->last->afterward, " ");
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Write out the decision tree starting at HEAD. PREVPOS is the
|
|
|
|
|
position at the node that branched to this node. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
|
|
|
|
static void
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_tree (head, prevpos, type, initial)
|
|
|
|
|
struct decision_head *head;
|
genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattrtab.c (fatal, attr_printf, attr_string, write_attr_set,
write_unit_name, write_eligible_delay, expand_units,
make_length_attrs, write_attr_case, find_attr,
make_internal_attr): Likewise.
* gencheck.c (tree_codes): Likewise.
* gencodes.c (fatal): Likewise.
* genconfig.c (fatal): Likewise.
* genemit.c (fatal): Likewise.
* genextract.c (fatal, walk_rtx, copystr): Likewise.
* genflags.c (fatal): Likewise.
* genopinit.c (fatal, optabs, gen_insn): Likewise.
* genoutput.c (fatal, error, predicates): Likewise.
* genpeep.c (fatal): Likewise.
* genrecog.c (fatal, decision, pred_table, add_to_sequence,
write_tree_1, write_tree, change_state, copystr, indents): Likewise.
From-SVN: r24377
1998-12-19 08:04:42 +01:00
|
|
|
|
const char *prevpos;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
enum routine_type type;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
int initial;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
register struct decision *p = head->first;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
putchar ('\n');
|
|
|
|
|
if (p->need_label)
|
|
|
|
|
OUTPUT_LABEL (" ", p->number);
|
|
|
|
|
|
|
|
|
|
if (! initial && p->subroutine_number > 0)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static const char * const name_prefix[] = {
|
|
|
|
|
"recog", "split", "peephole2"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char * const call_suffix[] = {
|
|
|
|
|
", pnum_clobbers", "", ", _plast_insn"
|
|
|
|
|
};
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* This node has been broken out into a separate subroutine.
|
|
|
|
|
Call it, test the result, and branch accordingly. */
|
|
|
|
|
|
|
|
|
|
if (p->afterward)
|
1991-12-28 12:36:37 +01:00
|
|
|
|
{
|
|
|
|
|
printf (" tem = %s_%d (x0, insn%s);\n",
|
1999-10-05 07:18:54 +02:00
|
|
|
|
name_prefix[type], p->subroutine_number, call_suffix[type]);
|
1999-08-31 22:37:09 +02:00
|
|
|
|
if (IS_SPLIT (type))
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" if (tem != 0)\n return tem;\n");
|
1992-10-20 08:51:30 +01:00
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" if (tem >= 0)\n return tem;\n");
|
|
|
|
|
|
|
|
|
|
change_state (p->position, p->afterward->position, NULL, " ");
|
|
|
|
|
printf (" goto L%d;\n", p->afterward->number);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
printf (" return %s_%d (x0, insn%s);\n",
|
|
|
|
|
name_prefix[type], p->subroutine_number, call_suffix[type]);
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int depth = strlen (p->position);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
change_state (prevpos, p->position, head->last->afterward, " ");
|
|
|
|
|
write_tree_1 (head, depth, type);
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (p = head->first; p; p = p->next)
|
|
|
|
|
if (p->success.first)
|
|
|
|
|
write_tree (&p->success, p->position, type, 0);
|
|
|
|
|
}
|
1991-12-28 12:36:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Write out a subroutine of type TYPE to do comparisons starting at
|
|
|
|
|
node TREE. */
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
write_subroutine (head, type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type type;
|
|
|
|
|
{
|
1999-10-13 19:22:40 +02:00
|
|
|
|
int subfunction = head->first ? head->first->subroutine_number : 0;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
const char *s_or_e;
|
|
|
|
|
char extension[32];
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
s_or_e = subfunction ? "static " : "";
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (subfunction)
|
|
|
|
|
sprintf (extension, "_%d", subfunction);
|
|
|
|
|
else if (type == RECOG)
|
|
|
|
|
extension[0] = '\0';
|
|
|
|
|
else
|
|
|
|
|
strcpy (extension, "_insns");
|
|
|
|
|
|
c-common.c (check_format_info): Don't call a variadic function with a non-literal format string.
* c-common.c (check_format_info): Don't call a variadic function
with a non-literal format string.
* c-decl.c (grokdeclarator, start_struct, finish_struct): Likewise.
* c-typeck.c (build_component_ref, build_unary_op, lvalue_or_else,
pedantic_lvalue_warning, error_init, pedwarn_init, warning_init):
Likewise.
* cccp.c (check_macro_name, do_xifdef, vwarning_with_line):
Likewise.
* collect2.c (collect_wait): Likewise.
* dbxout.c (dbxout_type): Likewise.
* gcc.c (do_spec_1): Likewise.
* genemit.c (gen_insn, gen_expand): Likewise.
* genrecog.c (write_switch, write_subroutine): Likewise.
* mips-tfile.c (catch_signal, botch): Likewise.
* print-rtl.c (print_rtx): Likewise.
* toplev.c (default_print_error_function, report_error_function,
_fatal_insn): Likewise.
From-SVN: r30666
1999-11-25 17:58:32 +01:00
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case RECOG:
|
2000-01-17 16:17:38 +01:00
|
|
|
|
printf ("%sint recog%s PARAMS ((rtx, rtx, int *));\n", s_or_e, extension);
|
c-common.c (check_format_info): Don't call a variadic function with a non-literal format string.
* c-common.c (check_format_info): Don't call a variadic function
with a non-literal format string.
* c-decl.c (grokdeclarator, start_struct, finish_struct): Likewise.
* c-typeck.c (build_component_ref, build_unary_op, lvalue_or_else,
pedantic_lvalue_warning, error_init, pedwarn_init, warning_init):
Likewise.
* cccp.c (check_macro_name, do_xifdef, vwarning_with_line):
Likewise.
* collect2.c (collect_wait): Likewise.
* dbxout.c (dbxout_type): Likewise.
* gcc.c (do_spec_1): Likewise.
* genemit.c (gen_insn, gen_expand): Likewise.
* genrecog.c (write_switch, write_subroutine): Likewise.
* mips-tfile.c (catch_signal, botch): Likewise.
* print-rtl.c (print_rtx): Likewise.
* toplev.c (default_print_error_function, report_error_function,
_fatal_insn): Likewise.
From-SVN: r30666
1999-11-25 17:58:32 +01:00
|
|
|
|
printf ("%sint\n\
|
|
|
|
|
recog%s (x0, insn, pnum_clobbers)\n\
|
|
|
|
|
register rtx x0;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
int *pnum_clobbers ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
|
|
|
|
case SPLIT:
|
2000-01-17 16:17:38 +01:00
|
|
|
|
printf ("%srtx split%s PARAMS ((rtx, rtx));\n", s_or_e, extension);
|
c-common.c (check_format_info): Don't call a variadic function with a non-literal format string.
* c-common.c (check_format_info): Don't call a variadic function
with a non-literal format string.
* c-decl.c (grokdeclarator, start_struct, finish_struct): Likewise.
* c-typeck.c (build_component_ref, build_unary_op, lvalue_or_else,
pedantic_lvalue_warning, error_init, pedwarn_init, warning_init):
Likewise.
* cccp.c (check_macro_name, do_xifdef, vwarning_with_line):
Likewise.
* collect2.c (collect_wait): Likewise.
* dbxout.c (dbxout_type): Likewise.
* gcc.c (do_spec_1): Likewise.
* genemit.c (gen_insn, gen_expand): Likewise.
* genrecog.c (write_switch, write_subroutine): Likewise.
* mips-tfile.c (catch_signal, botch): Likewise.
* print-rtl.c (print_rtx): Likewise.
* toplev.c (default_print_error_function, report_error_function,
_fatal_insn): Likewise.
From-SVN: r30666
1999-11-25 17:58:32 +01:00
|
|
|
|
printf ("%srtx\n\
|
|
|
|
|
split%s (x0, insn)\n\
|
|
|
|
|
register rtx x0;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
|
|
|
|
case PEEPHOLE2:
|
2000-01-17 16:17:38 +01:00
|
|
|
|
printf ("%srtx peephole2%s PARAMS ((rtx, rtx, rtx *));\n", s_or_e, extension);
|
c-common.c (check_format_info): Don't call a variadic function with a non-literal format string.
* c-common.c (check_format_info): Don't call a variadic function
with a non-literal format string.
* c-decl.c (grokdeclarator, start_struct, finish_struct): Likewise.
* c-typeck.c (build_component_ref, build_unary_op, lvalue_or_else,
pedantic_lvalue_warning, error_init, pedwarn_init, warning_init):
Likewise.
* cccp.c (check_macro_name, do_xifdef, vwarning_with_line):
Likewise.
* collect2.c (collect_wait): Likewise.
* dbxout.c (dbxout_type): Likewise.
* gcc.c (do_spec_1): Likewise.
* genemit.c (gen_insn, gen_expand): Likewise.
* genrecog.c (write_switch, write_subroutine): Likewise.
* mips-tfile.c (catch_signal, botch): Likewise.
* print-rtl.c (print_rtx): Likewise.
* toplev.c (default_print_error_function, report_error_function,
_fatal_insn): Likewise.
From-SVN: r30666
1999-11-25 17:58:32 +01:00
|
|
|
|
printf ("%srtx\n\
|
|
|
|
|
peephole2%s (x0, insn, _plast_insn)\n\
|
|
|
|
|
register rtx x0;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
rtx *_plast_insn ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
2000-01-09 15:23:35 +01:00
|
|
|
|
printf ("{\n register rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];\n");
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (i = 1; i <= max_depth; i++)
|
|
|
|
|
printf (" register rtx x%d ATTRIBUTE_UNUSED;\n", i);
|
|
|
|
|
|
|
|
|
|
if (type == PEEPHOLE2)
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf (" register rtx last_insn = insn;\n");
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" %s tem ATTRIBUTE_UNUSED;\n", IS_SPLIT (type) ? "rtx" : "int");
|
|
|
|
|
|
1999-10-13 19:22:40 +02:00
|
|
|
|
if (head->first)
|
|
|
|
|
write_tree (head, "", type, 1);
|
|
|
|
|
else
|
|
|
|
|
printf (" goto ret0;\n");
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
if (type == PEEPHOLE2)
|
2000-04-07 09:16:59 +02:00
|
|
|
|
printf (" ret1:\n *_plast_insn = last_insn;\n return tem;\n");
|
1999-10-05 07:18:54 +02:00
|
|
|
|
printf (" ret0:\n return %d;\n}\n\n", IS_SPLIT (type) ? 0 : -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In break_out_subroutines, we discovered the boundaries for the
|
|
|
|
|
subroutines, but did not write them out. Do so now. */
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
static void
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_subroutines (head, type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision *p;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
for (p = head->first; p ; p = p->next)
|
|
|
|
|
if (p->success.first)
|
|
|
|
|
write_subroutines (&p->success, type);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
if (head->first->subroutine_number > 0)
|
|
|
|
|
write_subroutine (head, type);
|
|
|
|
|
}
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Begin the output file. */
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
write_header ()
|
|
|
|
|
{
|
|
|
|
|
puts ("\
|
|
|
|
|
/* Generated automatically by the program `genrecog' from the target\n\
|
|
|
|
|
machine description file. */\n\
|
|
|
|
|
\n\
|
|
|
|
|
#include \"config.h\"\n\
|
|
|
|
|
#include \"system.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\
|
1999-10-08 19:10:19 +02:00
|
|
|
|
#include \"hard-reg-set.h\"\n\
|
|
|
|
|
#include \"resource.h\"\n\
|
1999-10-05 07:18:54 +02:00
|
|
|
|
\n");
|
|
|
|
|
|
|
|
|
|
puts ("\n\
|
|
|
|
|
/* `recog' contains a decision tree that recognizes whether the rtx\n\
|
|
|
|
|
X0 is a valid instruction.\n\
|
|
|
|
|
\n\
|
|
|
|
|
recog returns -1 if the rtx is not valid. If the rtx is valid, recog\n\
|
|
|
|
|
returns a nonnegative number which is the insn code number for the\n\
|
|
|
|
|
pattern that matched. This is the same as the order in the machine\n\
|
|
|
|
|
description of the entry that matched. This number can be used as an\n\
|
|
|
|
|
index into `insn_data' and other tables.\n\
|
|
|
|
|
\n\
|
|
|
|
|
The third argument to recog is an optional pointer to an int. If\n\
|
|
|
|
|
present, recog will accept a pattern if it matches except for missing\n\
|
|
|
|
|
CLOBBER expressions at the end. In that case, the value pointed to by\n\
|
|
|
|
|
the optional pointer will be set to the number of CLOBBERs that need\n\
|
|
|
|
|
to be added (it should be initialized to zero by the caller). If it\n\
|
|
|
|
|
is set nonzero, the caller should allocate a PARALLEL of the\n\
|
|
|
|
|
appropriate size, copy the initial entries, and call add_clobbers\n\
|
|
|
|
|
(found in insn-emit.c) to fill in the CLOBBERs.\n\
|
|
|
|
|
");
|
|
|
|
|
|
|
|
|
|
puts ("\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.\n\
|
|
|
|
|
\n\
|
|
|
|
|
The function peephole2_insns returns 0 if the rtl could not\n\
|
|
|
|
|
be matched. If there was a match, the new rtl is returned in a SEQUENCE,\n\
|
|
|
|
|
and LAST_INSN will point to the last recognized insn in the old sequence.\n\
|
|
|
|
|
*/\n\n");
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
/* Construct and return a sequence of decisions
|
|
|
|
|
that will recognize INSN.
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
TYPE says what type of routine we are recognizing (RECOG or SPLIT). */
|
|
|
|
|
|
|
|
|
|
static struct decision_head
|
|
|
|
|
make_insn_sequence (insn, type)
|
|
|
|
|
rtx insn;
|
|
|
|
|
enum routine_type type;
|
|
|
|
|
{
|
|
|
|
|
rtx x;
|
|
|
|
|
const char *c_test = XSTR (insn, type == RECOG ? 2 : 1);
|
|
|
|
|
struct decision *last;
|
|
|
|
|
struct decision_test *test, **place;
|
|
|
|
|
struct decision_head head;
|
2000-04-07 09:16:59 +02:00
|
|
|
|
char *c_test_pos = "";
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
record_insn_name (next_insn_code, (type == RECOG ? XSTR (insn, 0) : NULL));
|
|
|
|
|
|
|
|
|
|
if (type == PEEPHOLE2)
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
|
|
/* peephole2 gets special treatment:
|
|
|
|
|
- X always gets an outer parallel even if it's only one entry
|
|
|
|
|
- we remove all traces of outer-level match_scratch and match_dup
|
|
|
|
|
expressions here. */
|
|
|
|
|
x = rtx_alloc (PARALLEL);
|
|
|
|
|
PUT_MODE (x, VOIDmode);
|
|
|
|
|
XVEC (x, 0) = rtvec_alloc (XVECLEN (insn, 0));
|
|
|
|
|
for (i = j = 0; i < XVECLEN (insn, 0); i++)
|
1999-08-31 22:37:09 +02:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
rtx tmp = XVECEXP (insn, 0, i);
|
|
|
|
|
if (GET_CODE (tmp) != MATCH_SCRATCH && GET_CODE (tmp) != MATCH_DUP)
|
|
|
|
|
{
|
|
|
|
|
XVECEXP (x, 0, j) = tmp;
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
XVECLEN (x, 0) = j;
|
2000-04-07 09:16:59 +02:00
|
|
|
|
|
|
|
|
|
c_test_pos = alloca (2);
|
|
|
|
|
c_test_pos[0] = 'A' + j - 1;
|
|
|
|
|
c_test_pos[1] = '\0';
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
|
|
|
|
else if (XVECLEN (insn, type == RECOG) == 1)
|
|
|
|
|
x = XVECEXP (insn, type == RECOG, 0);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
x = rtx_alloc (PARALLEL);
|
|
|
|
|
XVEC (x, 0) = XVEC (insn, type == RECOG);
|
|
|
|
|
PUT_MODE (x, VOIDmode);
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-15 08:56:28 +02:00
|
|
|
|
validate_pattern (x, insn, NULL_RTX);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
memset(&head, 0, sizeof(head));
|
|
|
|
|
last = add_to_sequence (x, &head, "", type, 1);
|
|
|
|
|
|
|
|
|
|
/* Find the end of the test chain on the last node. */
|
|
|
|
|
for (test = last->tests; test->next; test = test->next)
|
|
|
|
|
continue;
|
|
|
|
|
place = &test->next;
|
|
|
|
|
|
|
|
|
|
if (c_test[0])
|
|
|
|
|
{
|
|
|
|
|
/* Need a new node if we have another test to add. */
|
|
|
|
|
if (test->type == DT_accept_op)
|
|
|
|
|
{
|
2000-04-07 09:16:59 +02:00
|
|
|
|
last = new_decision (c_test_pos, &last->success);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
place = &last->tests;
|
|
|
|
|
}
|
|
|
|
|
test = new_decision_test (DT_c_test, &place);
|
|
|
|
|
test->u.c_test = c_test;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_accept_insn, &place);
|
|
|
|
|
test->u.insn.code_number = next_insn_code;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
test->u.insn.lineno = pattern_lineno;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
test->u.insn.num_clobbers_to_add = 0;
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case RECOG:
|
|
|
|
|
/* If this is an DEFINE_INSN and 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;
|
|
|
|
|
|
|
|
|
|
/* Find the last non-clobber in the parallel. */
|
|
|
|
|
for (i = XVECLEN (x, 0); i > 0; i--)
|
1999-08-31 22:37:09 +02:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
rtx y = XVECEXP (x, 0, i - 1);
|
|
|
|
|
if (GET_CODE (y) != CLOBBER
|
|
|
|
|
|| (GET_CODE (XEXP (y, 0)) != REG
|
|
|
|
|
&& GET_CODE (XEXP (y, 0)) != MATCH_SCRATCH))
|
|
|
|
|
break;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
if (i != XVECLEN (x, 0))
|
1999-08-31 22:37:09 +02:00
|
|
|
|
{
|
1999-10-05 07:18:54 +02:00
|
|
|
|
rtx new;
|
|
|
|
|
struct decision_head clobber_head;
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Build a similar insn without the clobbers. */
|
|
|
|
|
if (i == 1)
|
|
|
|
|
new = XVECEXP (x, 0, 0);
|
1999-08-31 22:37:09 +02:00
|
|
|
|
else
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Recognize it. */
|
|
|
|
|
memset (&clobber_head, 0, sizeof(clobber_head));
|
|
|
|
|
last = add_to_sequence (new, &clobber_head, "", type, 1);
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
/* Find the end of the test chain on the last node. */
|
|
|
|
|
for (test = last->tests; test->next; test = test->next)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* We definitely have a new test to add -- create a new
|
|
|
|
|
node if needed. */
|
|
|
|
|
place = &test->next;
|
|
|
|
|
if (test->type == DT_accept_op)
|
|
|
|
|
{
|
|
|
|
|
last = new_decision ("", &last->success);
|
|
|
|
|
place = &last->tests;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c_test[0])
|
|
|
|
|
{
|
|
|
|
|
test = new_decision_test (DT_c_test, &place);
|
|
|
|
|
test->u.c_test = c_test;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_accept_insn, &place);
|
|
|
|
|
test->u.insn.code_number = next_insn_code;
|
1999-10-14 05:43:49 +02:00
|
|
|
|
test->u.insn.lineno = pattern_lineno;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
test->u.insn.num_clobbers_to_add = XVECLEN (x, 0) - i;
|
|
|
|
|
|
|
|
|
|
merge_trees (&head, &clobber_head);
|
1999-08-31 22:37:09 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SPLIT:
|
|
|
|
|
/* Define the subroutine we will call below and emit in genemit. */
|
2000-01-17 16:17:38 +01:00
|
|
|
|
printf ("extern rtx gen_split_%d PARAMS ((rtx *));\n", next_insn_code);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PEEPHOLE2:
|
|
|
|
|
/* Define the subroutine we will call below and emit in genemit. */
|
2000-01-17 16:17:38 +01:00
|
|
|
|
printf ("extern rtx gen_peephole2_%d PARAMS ((rtx, rtx *));\n",
|
1999-10-05 07:18:54 +02:00
|
|
|
|
next_insn_code);
|
|
|
|
|
break;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
next_insn_code++;
|
1991-12-28 12:36:37 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return head;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
static void
|
|
|
|
|
process_tree (head, subroutine_type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type subroutine_type;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
{
|
1999-10-14 06:07:03 +02:00
|
|
|
|
if (head->first == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* We can elide peephole2_insns, but not recog or split_insns. */
|
|
|
|
|
if (subroutine_type == PEEPHOLE2)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
1999-10-13 19:22:40 +02:00
|
|
|
|
{
|
|
|
|
|
factor_tests (head);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-13 19:22:40 +02:00
|
|
|
|
next_subroutine_number = 0;
|
|
|
|
|
break_out_subroutines (head, 1);
|
|
|
|
|
find_afterward (head, NULL);
|
1999-09-15 16:13:50 +02:00
|
|
|
|
|
1999-10-14 06:07:03 +02:00
|
|
|
|
/* We run this after find_afterward, because find_afterward needs
|
|
|
|
|
the redundant DT_mode tests on predicates to determine whether
|
|
|
|
|
two tests can both be true or not. */
|
|
|
|
|
simplify_tests(head);
|
|
|
|
|
|
1999-10-13 19:22:40 +02:00
|
|
|
|
write_subroutines (head, subroutine_type);
|
|
|
|
|
}
|
1999-10-14 06:07:03 +02:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_subroutine (head, subroutine_type);
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-17 16:17:38 +01:00
|
|
|
|
extern int main PARAMS ((int, char **));
|
1999-10-24 21:23:11 +02:00
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
int
|
|
|
|
|
main (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
|
|
|
|
rtx desc;
|
1999-10-05 07:18:54 +02:00
|
|
|
|
struct decision_head recog_tree, split_tree, peephole2_tree, h;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
FILE *infile;
|
|
|
|
|
register int c;
|
|
|
|
|
|
errors.c: New file...
1999-08-27 00:27 -0700 Zack Weinberg <zack@bitmover.com>
* errors.c: New file; defines functions error, warning, and
fatal, variables have_error and progname.
* errors.h: New file; prototypes and decls for stuff in errors.c.
* Makefile: Add rules to build errors.o and
$(HOST_PREFIX)errors.o. Link genconfig, gencodes, genemit,
genopinit, genrecog, genextract, genpeep, genattr, and
genoutput with errors.o. Add errors.h to deps of genconfig.o,
gencodes.o, genemit.o, genopinit.o, genrecog.o, genextract.o,
genpeep.o, genattr.o, and genoutput.o.
* genconfig.c, gencodes.c, genemit.c, genopinit.c, genrecog.c,
genextract.c, genpeep.c, genattr.c: Include errors.h. Don't
define or prototype fatal. Set progname at beginning of main.
* genoutput.c: Likewise, and don't define or prototype error
either.
From-SVN: r28925
1999-08-27 09:47:17 +02:00
|
|
|
|
progname = "genrecog";
|
1991-11-24 20:57:16 +01:00
|
|
|
|
obstack_init (rtl_obstack);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
memset (&recog_tree, 0, sizeof recog_tree);
|
|
|
|
|
memset (&split_tree, 0, sizeof split_tree);
|
|
|
|
|
memset (&peephole2_tree, 0, sizeof peephole2_tree);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
if (argc <= 1)
|
|
|
|
|
fatal ("No input file name.");
|
|
|
|
|
|
|
|
|
|
infile = fopen (argv[1], "r");
|
|
|
|
|
if (infile == 0)
|
|
|
|
|
{
|
|
|
|
|
perror (argv[1]);
|
1999-10-05 07:18:54 +02:00
|
|
|
|
return FATAL_EXIT_CODE;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-14 05:43:49 +02:00
|
|
|
|
read_rtx_filename = argv[1];
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
next_insn_code = 0;
|
|
|
|
|
next_index = 0;
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
write_header ();
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
/* Read the machine description. */
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
c = read_skip_spaces (infile);
|
|
|
|
|
if (c == EOF)
|
|
|
|
|
break;
|
|
|
|
|
ungetc (c, infile);
|
1999-10-14 05:43:49 +02:00
|
|
|
|
pattern_lineno = read_rtx_lineno;
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
|
|
|
|
desc = read_rtx (infile);
|
|
|
|
|
if (GET_CODE (desc) == DEFINE_INSN)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, RECOG);
|
|
|
|
|
merge_trees (&recog_tree, &h);
|
|
|
|
|
}
|
1991-11-24 20:57:16 +01:00
|
|
|
|
else if (GET_CODE (desc) == DEFINE_SPLIT)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, SPLIT);
|
|
|
|
|
merge_trees (&split_tree, &h);
|
|
|
|
|
}
|
1999-08-31 22:37:09 +02:00
|
|
|
|
else if (GET_CODE (desc) == DEFINE_PEEPHOLE2)
|
1999-10-05 07:18:54 +02:00
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, PEEPHOLE2);
|
|
|
|
|
merge_trees (&peephole2_tree, &h);
|
|
|
|
|
}
|
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
if (GET_CODE (desc) == DEFINE_PEEPHOLE
|
|
|
|
|
|| GET_CODE (desc) == DEFINE_EXPAND)
|
|
|
|
|
next_insn_code++;
|
|
|
|
|
next_index++;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-14 05:43:49 +02:00
|
|
|
|
if (error_count)
|
|
|
|
|
return FATAL_EXIT_CODE;
|
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
puts ("\n\n");
|
1991-11-24 20:57:16 +01:00
|
|
|
|
|
1999-10-05 07:18:54 +02:00
|
|
|
|
process_tree (&recog_tree, RECOG);
|
|
|
|
|
process_tree (&split_tree, SPLIT);
|
|
|
|
|
process_tree (&peephole2_tree, PEEPHOLE2);
|
1999-08-31 22:37:09 +02:00
|
|
|
|
|
1991-11-24 20:57:16 +01:00
|
|
|
|
fflush (stdout);
|
1999-09-15 16:13:50 +02:00
|
|
|
|
return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
|
1991-11-24 20:57:16 +01:00
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
1999-09-12 14:46:08 +02:00
|
|
|
|
/* Define this so we can link with print-rtl.o to get debug_rtx function. */
|
|
|
|
|
const char *
|
|
|
|
|
get_insn_name (code)
|
|
|
|
|
int code;
|
|
|
|
|
{
|
|
|
|
|
if (code < insn_name_ptr_size)
|
|
|
|
|
return insn_name_ptr[code];
|
|
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
1999-10-05 07:18:54 +02:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
record_insn_name (code, name)
|
|
|
|
|
int code;
|
|
|
|
|
const char *name;
|
|
|
|
|
{
|
|
|
|
|
static const char *last_real_name = "insn";
|
|
|
|
|
static int last_real_code = 0;
|
|
|
|
|
char *new;
|
|
|
|
|
|
|
|
|
|
if (insn_name_ptr_size <= code)
|
|
|
|
|
{
|
|
|
|
|
int new_size;
|
|
|
|
|
new_size = (insn_name_ptr_size ? insn_name_ptr_size * 2 : 512);
|
|
|
|
|
insn_name_ptr =
|
|
|
|
|
(char **) xrealloc (insn_name_ptr, sizeof(char *) * new_size);
|
|
|
|
|
memset (insn_name_ptr + insn_name_ptr_size, 0,
|
|
|
|
|
sizeof(char *) * (new_size - insn_name_ptr_size));
|
|
|
|
|
insn_name_ptr_size = new_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!name || name[0] == '\0')
|
|
|
|
|
{
|
|
|
|
|
new = xmalloc (strlen (last_real_name) + 10);
|
|
|
|
|
sprintf (new, "%s+%d", last_real_name, code - last_real_code);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
last_real_name = new = xstrdup (name);
|
|
|
|
|
last_real_code = code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insn_name_ptr[code] = new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
xstrdup (input)
|
|
|
|
|
const char *input;
|
|
|
|
|
{
|
|
|
|
|
register size_t len = strlen (input) + 1;
|
|
|
|
|
register char *output = xmalloc (len);
|
|
|
|
|
memcpy (output, input, len);
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PTR
|
|
|
|
|
xrealloc (old, size)
|
|
|
|
|
PTR old;
|
|
|
|
|
size_t size;
|
|
|
|
|
{
|
|
|
|
|
register PTR ptr;
|
|
|
|
|
if (old)
|
|
|
|
|
ptr = (PTR) realloc (old, size);
|
|
|
|
|
else
|
|
|
|
|
ptr = (PTR) malloc (size);
|
|
|
|
|
if (!ptr)
|
|
|
|
|
fatal ("virtual memory exhausted");
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PTR
|
|
|
|
|
xmalloc (size)
|
|
|
|
|
size_t size;
|
|
|
|
|
{
|
|
|
|
|
register PTR val = (PTR) malloc (size);
|
|
|
|
|
|
|
|
|
|
if (val == 0)
|
|
|
|
|
fatal ("virtual memory exhausted");
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
debug_decision_2 (test)
|
|
|
|
|
struct decision_test *test;
|
|
|
|
|
{
|
|
|
|
|
switch (test->type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
|
|
|
|
fprintf (stderr, "mode=%s", GET_MODE_NAME (test->u.mode));
|
|
|
|
|
break;
|
|
|
|
|
case DT_code:
|
|
|
|
|
fprintf (stderr, "code=%s", GET_RTX_NAME (test->u.code));
|
|
|
|
|
break;
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
fprintf (stderr, "veclen=%d", test->u.veclen);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
fprintf (stderr, "elt0_i=%d", (int) test->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
fprintf (stderr, "elt1_i=%d", (int) test->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
fprintf (stderr, "elt0_w=");
|
|
|
|
|
fprintf (stderr, HOST_WIDE_INT_PRINT_DEC, test->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
case DT_dup:
|
|
|
|
|
fprintf (stderr, "dup=%d", test->u.dup);
|
|
|
|
|
break;
|
|
|
|
|
case DT_pred:
|
|
|
|
|
fprintf (stderr, "pred=(%s,%s)",
|
|
|
|
|
test->u.pred.name, GET_MODE_NAME(test->u.pred.mode));
|
|
|
|
|
break;
|
|
|
|
|
case DT_c_test:
|
|
|
|
|
{
|
|
|
|
|
char sub[16+4];
|
|
|
|
|
strncpy (sub, test->u.c_test, sizeof(sub));
|
|
|
|
|
memcpy (sub+16, "...", 4);
|
|
|
|
|
fprintf (stderr, "c_test=\"%s\"", sub);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DT_accept_op:
|
|
|
|
|
fprintf (stderr, "A_op=%d", test->u.opno);
|
|
|
|
|
break;
|
|
|
|
|
case DT_accept_insn:
|
|
|
|
|
fprintf (stderr, "A_insn=(%d,%d)",
|
|
|
|
|
test->u.insn.code_number, test->u.insn.num_clobbers_to_add);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
debug_decision_1 (d, indent)
|
|
|
|
|
struct decision *d;
|
|
|
|
|
int indent;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct decision_test *test;
|
|
|
|
|
|
|
|
|
|
if (d == NULL)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < indent; ++i)
|
|
|
|
|
putc (' ', stderr);
|
|
|
|
|
fputs ("(nil)\n", stderr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < indent; ++i)
|
|
|
|
|
putc (' ', stderr);
|
|
|
|
|
|
|
|
|
|
putc ('{', stderr);
|
|
|
|
|
test = d->tests;
|
|
|
|
|
if (test)
|
|
|
|
|
{
|
|
|
|
|
debug_decision_2 (test);
|
|
|
|
|
while ((test = test->next) != NULL)
|
|
|
|
|
{
|
|
|
|
|
fputs (" + ", stderr);
|
|
|
|
|
debug_decision_2 (test);
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-10-14 06:07:03 +02:00
|
|
|
|
fprintf (stderr, "} %d n %d a %d\n", d->number,
|
|
|
|
|
(d->next ? d->next->number : -1),
|
|
|
|
|
(d->afterward ? d->afterward->number : -1));
|
1999-10-05 07:18:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
debug_decision_0 (d, indent, maxdepth)
|
|
|
|
|
struct decision *d;
|
|
|
|
|
int indent, maxdepth;
|
|
|
|
|
{
|
|
|
|
|
struct decision *n;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (maxdepth < 0)
|
|
|
|
|
return;
|
|
|
|
|
if (d == NULL)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < indent; ++i)
|
|
|
|
|
putc (' ', stderr);
|
|
|
|
|
fputs ("(nil)\n", stderr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug_decision_1 (d, indent);
|
|
|
|
|
for (n = d->success.first; n ; n = n->next)
|
|
|
|
|
debug_decision_0 (n, indent + 2, maxdepth - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
debug_decision (d)
|
|
|
|
|
struct decision *d;
|
|
|
|
|
{
|
|
|
|
|
debug_decision_0 (d, 0, 1000000);
|
|
|
|
|
}
|
1999-10-10 11:43:26 +02:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
debug_decision_list (d)
|
|
|
|
|
struct decision *d;
|
|
|
|
|
{
|
|
|
|
|
while (d)
|
|
|
|
|
{
|
|
|
|
|
debug_decision_0 (d, 0, 0);
|
|
|
|
|
d = d->next;
|
|
|
|
|
}
|
|
|
|
|
}
|