1996-09-18 05:35:50 +00:00
|
|
|
|
/* Generate code from machine description to recognize rtl as insns.
|
2002-02-01 18:16:02 +00:00
|
|
|
|
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1997, 1998,
|
|
|
|
|
1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
This file is part of GCC.
|
|
|
|
|
|
|
|
|
|
GCC 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.
|
|
|
|
|
|
|
|
|
|
GCC 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 GCC; 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
|
1996-09-18 05:35:50 +00: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
|
2002-02-01 18:16:02 +00:00
|
|
|
|
PARALLEL of the appropriate size, copy the initial entries, and
|
|
|
|
|
call add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#include "hconfig.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "system.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "rtl.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "errors.h"
|
|
|
|
|
#include "gensupport.h"
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
#define OUTPUT_LABEL(INDENT_STRING, LABEL_NUMBER) \
|
|
|
|
|
printf("%sL%d: ATTRIBUTE_UNUSED_LABEL\n", (INDENT_STRING), (LABEL_NUMBER))
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Holds an array of names indexed by insn_code_number. */
|
|
|
|
|
static char **insn_name_ptr = 0;
|
|
|
|
|
static int insn_name_ptr_size = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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_elt_zero_wide_safe,
|
|
|
|
|
DT_veclen_ge, 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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
|
|
|
|
int lineno; /* Line number of the insn. */
|
|
|
|
|
int num_clobbers_to_add; /* Number of CLOBBERs to be added. */
|
|
|
|
|
} insn;
|
|
|
|
|
} u;
|
|
|
|
|
};
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Data structure for decision tree for recognizing legitimate insns. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
struct decision
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00: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. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int number; /* Node number, used for labels */
|
|
|
|
|
int subroutine_number; /* Number of subroutine this node starts */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int need_label; /* Label needs to be output. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
};
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#define SUBROUTINE_THRESHOLD 100
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static int next_subroutine_number;
|
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
|
|
|
|
|
|
|
|
|
enum routine_type {
|
|
|
|
|
RECOG, SPLIT, PEEPHOLE2
|
|
|
|
|
};
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#define IS_SPLIT(X) ((X) != RECOG)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Next available node number for tree nodes. */
|
|
|
|
|
|
|
|
|
|
static int next_number;
|
|
|
|
|
|
|
|
|
|
/* Next number to use as an insn_code. */
|
|
|
|
|
|
|
|
|
|
static int next_insn_code;
|
|
|
|
|
|
|
|
|
|
/* Similar, but counts all expressions in the MD file; used for
|
1999-08-26 09:30:50 +00:00
|
|
|
|
error messages. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static int next_index;
|
|
|
|
|
|
|
|
|
|
/* Record the highest depth we ever have so we know how many variables to
|
|
|
|
|
allocate in each subroutine we make. */
|
|
|
|
|
|
|
|
|
|
static int max_depth;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* The line number of the start of the pattern currently being processed. */
|
|
|
|
|
static int pattern_lineno;
|
|
|
|
|
|
|
|
|
|
/* Count of errors. */
|
|
|
|
|
static int error_count;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* This table contains a list of the rtl codes that can possibly match a
|
2002-02-01 18:16:02 +00:00
|
|
|
|
predicate defined in recog.c. The function `maybe_both_true' uses it to
|
1996-09-18 05:35:50 +00: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. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static const struct pred_table
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
const char *const name;
|
|
|
|
|
const RTX_CODE codes[NUM_RTX_CODE];
|
|
|
|
|
} preds[] = {
|
|
|
|
|
{"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM}},
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#ifdef PREDICATE_CODES
|
2002-02-01 18:16:02 +00:00
|
|
|
|
PREDICATE_CODES
|
|
|
|
|
#endif
|
|
|
|
|
{"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM, PLUS, MINUS, MULT}},
|
|
|
|
|
{"register_operand", {SUBREG, REG}},
|
|
|
|
|
{"pmode_register_operand", {SUBREG, REG}},
|
|
|
|
|
{"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}},
|
|
|
|
|
{"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
|
|
|
|
|
UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
|
|
|
|
|
UNLT, LTGT}},
|
|
|
|
|
{"mode_independent_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
|
|
|
|
|
LABEL_REF, SUBREG, REG, MEM}}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define NUM_KNOWN_PREDS ARRAY_SIZE (preds)
|
|
|
|
|
|
|
|
|
|
static const char *const special_mode_pred_table[] = {
|
|
|
|
|
#ifdef SPECIAL_MODE_PREDICATES
|
|
|
|
|
SPECIAL_MODE_PREDICATES
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#endif
|
2002-02-01 18:16:02 +00:00
|
|
|
|
"pmode_register_operand"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define NUM_SPECIAL_MODE_PREDS ARRAY_SIZE (special_mode_pred_table)
|
|
|
|
|
|
|
|
|
|
static struct decision *new_decision
|
|
|
|
|
PARAMS ((const char *, struct decision_head *));
|
|
|
|
|
static struct decision_test *new_decision_test
|
|
|
|
|
PARAMS ((enum decision_type, struct decision_test ***));
|
|
|
|
|
static rtx find_operand
|
|
|
|
|
PARAMS ((rtx, int));
|
|
|
|
|
static rtx find_matching_operand
|
|
|
|
|
PARAMS ((rtx, int));
|
|
|
|
|
static void validate_pattern
|
|
|
|
|
PARAMS ((rtx, rtx, rtx, int));
|
|
|
|
|
static struct decision *add_to_sequence
|
|
|
|
|
PARAMS ((rtx, struct decision_head *, const char *, enum routine_type, int));
|
|
|
|
|
|
|
|
|
|
static int maybe_both_true_2
|
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
|
|
|
|
static int maybe_both_true_1
|
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
|
|
|
|
static int maybe_both_true
|
|
|
|
|
PARAMS ((struct decision *, struct decision *, int));
|
|
|
|
|
|
|
|
|
|
static int nodes_identical_1
|
|
|
|
|
PARAMS ((struct decision_test *, struct decision_test *));
|
|
|
|
|
static int nodes_identical
|
|
|
|
|
PARAMS ((struct decision *, struct decision *));
|
|
|
|
|
static void merge_accept_insn
|
|
|
|
|
PARAMS ((struct decision *, struct decision *));
|
|
|
|
|
static void merge_trees
|
|
|
|
|
PARAMS ((struct decision_head *, struct decision_head *));
|
|
|
|
|
|
|
|
|
|
static void factor_tests
|
|
|
|
|
PARAMS ((struct decision_head *));
|
|
|
|
|
static void simplify_tests
|
|
|
|
|
PARAMS ((struct decision_head *));
|
|
|
|
|
static int break_out_subroutines
|
|
|
|
|
PARAMS ((struct decision_head *, int));
|
|
|
|
|
static void find_afterward
|
|
|
|
|
PARAMS ((struct decision_head *, struct decision *));
|
|
|
|
|
|
|
|
|
|
static void change_state
|
|
|
|
|
PARAMS ((const char *, const char *, struct decision *, const char *));
|
|
|
|
|
static void print_code
|
|
|
|
|
PARAMS ((enum rtx_code));
|
|
|
|
|
static void write_afterward
|
|
|
|
|
PARAMS ((struct decision *, struct decision *, const char *));
|
|
|
|
|
static struct decision *write_switch
|
|
|
|
|
PARAMS ((struct decision *, int));
|
|
|
|
|
static void write_cond
|
|
|
|
|
PARAMS ((struct decision_test *, int, enum routine_type));
|
|
|
|
|
static void write_action
|
|
|
|
|
PARAMS ((struct decision *, struct decision_test *, int, int,
|
|
|
|
|
struct decision *, enum routine_type));
|
|
|
|
|
static int is_unconditional
|
|
|
|
|
PARAMS ((struct decision_test *, enum routine_type));
|
|
|
|
|
static int write_node
|
|
|
|
|
PARAMS ((struct decision *, int, enum routine_type));
|
|
|
|
|
static void write_tree_1
|
|
|
|
|
PARAMS ((struct decision_head *, int, enum routine_type));
|
|
|
|
|
static void write_tree
|
|
|
|
|
PARAMS ((struct decision_head *, const char *, enum routine_type, int));
|
|
|
|
|
static void write_subroutine
|
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
|
|
|
|
static void write_subroutines
|
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
|
|
|
|
static void write_header
|
|
|
|
|
PARAMS ((void));
|
|
|
|
|
|
|
|
|
|
static struct decision_head make_insn_sequence
|
|
|
|
|
PARAMS ((rtx, enum routine_type));
|
|
|
|
|
static void process_tree
|
|
|
|
|
PARAMS ((struct decision_head *, enum routine_type));
|
|
|
|
|
|
|
|
|
|
static void record_insn_name
|
|
|
|
|
PARAMS ((int, const char *));
|
|
|
|
|
|
|
|
|
|
static void debug_decision_0
|
|
|
|
|
PARAMS ((struct decision *, int, int));
|
|
|
|
|
static void debug_decision_1
|
|
|
|
|
PARAMS ((struct decision *, int));
|
|
|
|
|
static void debug_decision_2
|
|
|
|
|
PARAMS ((struct decision_test *));
|
|
|
|
|
extern void debug_decision
|
|
|
|
|
PARAMS ((struct decision *));
|
|
|
|
|
extern void debug_decision_list
|
|
|
|
|
PARAMS ((struct decision *));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Create a new node in sequence after LAST. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static struct decision *
|
|
|
|
|
new_decision (position, last)
|
|
|
|
|
const char *position;
|
|
|
|
|
struct decision_head *last;
|
|
|
|
|
{
|
|
|
|
|
struct decision *new
|
|
|
|
|
= (struct decision *) xmalloc (sizeof (struct decision));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
memset (new, 0, sizeof (*new));
|
|
|
|
|
new->success = *last;
|
|
|
|
|
new->position = xstrdup (position);
|
|
|
|
|
new->number = next_number++;
|
|
|
|
|
|
|
|
|
|
last->first = last->last = new;
|
|
|
|
|
return new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a new test and link it in at PLACE. */
|
|
|
|
|
|
|
|
|
|
static struct decision_test *
|
|
|
|
|
new_decision_test (type, pplace)
|
|
|
|
|
enum decision_type type;
|
|
|
|
|
struct decision_test ***pplace;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_test **place = *pplace;
|
|
|
|
|
struct decision_test *test;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
test = (struct decision_test *) xmalloc (sizeof (*test));
|
|
|
|
|
test->next = *place;
|
|
|
|
|
test->type = type;
|
|
|
|
|
*place = test;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
place = &test->next;
|
|
|
|
|
*pplace = place;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return test;
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
|
|
|
|
if ((r = find_operand (XEXP (pattern, i), n)) != NULL_RTX)
|
|
|
|
|
return r;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'V':
|
|
|
|
|
if (! XVEC (pattern, i))
|
|
|
|
|
break;
|
|
|
|
|
/* FALLTHRU */
|
|
|
|
|
|
|
|
|
|
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 ();
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Search for and return operand M, such that it has a matching
|
|
|
|
|
constraint for operand N. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static rtx
|
|
|
|
|
find_matching_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_OPERAND
|
|
|
|
|
&& (XSTR (pattern, 2)[0] == '0' + n
|
|
|
|
|
|| (XSTR (pattern, 2)[0] == '%'
|
|
|
|
|
&& XSTR (pattern, 2)[1] == '0' + n)))
|
|
|
|
|
return pattern;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
|
|
|
|
for (i = 0; i < len; i++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
|
|
|
|
if ((r = find_matching_operand (XEXP (pattern, i), n)))
|
|
|
|
|
return r;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'V':
|
|
|
|
|
if (! XVEC (pattern, i))
|
|
|
|
|
break;
|
|
|
|
|
/* FALLTHRU */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'E':
|
|
|
|
|
for (j = 0; j < XVECLEN (pattern, i); j++)
|
|
|
|
|
if ((r = find_matching_operand (XVECEXP (pattern, i, j), n)))
|
|
|
|
|
return r;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'i': case 'w': case '0': case 's':
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check for various errors in patterns. SET is nonnull for a destination,
|
|
|
|
|
and is the complete set pattern. SET_CODE is '=' for normal sets, and
|
|
|
|
|
'+' within a context that requires in-out constraints. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
validate_pattern (pattern, insn, set, set_code)
|
|
|
|
|
rtx pattern;
|
|
|
|
|
rtx insn;
|
|
|
|
|
rtx set;
|
|
|
|
|
int set_code;
|
|
|
|
|
{
|
|
|
|
|
const char *fmt;
|
|
|
|
|
RTX_CODE code;
|
|
|
|
|
size_t i, len;
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
code = GET_CODE (pattern);
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case MATCH_SCRATCH:
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case MATCH_INSN:
|
|
|
|
|
case MATCH_OPERAND:
|
|
|
|
|
case MATCH_OPERATOR:
|
|
|
|
|
{
|
|
|
|
|
const char *pred_name = XSTR (pattern, 1);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (pred_name[0] != 0)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < NUM_KNOWN_PREDS; i++)
|
|
|
|
|
if (! strcmp (preds[i].name, pred_name))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (i < NUM_KNOWN_PREDS)
|
|
|
|
|
{
|
|
|
|
|
int j;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
allows_non_lvalue = allows_non_const = 0;
|
|
|
|
|
for (j = 0; preds[i].codes[j] != 0; j++)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#ifdef PREDICATE_CODES
|
|
|
|
|
/* If the port has a list of the predicates it uses but
|
|
|
|
|
omits one, warn. */
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: `%s' not in PREDICATE_CODES",
|
|
|
|
|
pred_name);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
|
|
|
|
|
if (strcmp (pred_name, special_mode_pred_table[i]) == 0)
|
|
|
|
|
{
|
|
|
|
|
special_mode_pred = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (code == MATCH_OPERAND)
|
|
|
|
|
{
|
|
|
|
|
const char constraints0 = XSTR (pattern, 2)[0];
|
|
|
|
|
|
|
|
|
|
/* In DEFINE_EXPAND, DEFINE_SPLIT, and DEFINE_PEEPHOLE2, we
|
|
|
|
|
don't use the MATCH_OPERAND constraint, only the predicate.
|
|
|
|
|
This is confusing to folks doing new ports, so help them
|
|
|
|
|
not make the mistake. */
|
|
|
|
|
if (GET_CODE (insn) == DEFINE_EXPAND
|
|
|
|
|
|| GET_CODE (insn) == DEFINE_SPLIT
|
|
|
|
|
|| GET_CODE (insn) == DEFINE_PEEPHOLE2)
|
|
|
|
|
{
|
|
|
|
|
if (constraints0)
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: constraints not supported in %s",
|
|
|
|
|
rtx_name[GET_CODE (insn)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A MATCH_OPERAND that is a SET should have an output reload. */
|
|
|
|
|
else if (set && constraints0)
|
|
|
|
|
{
|
|
|
|
|
if (set_code == '+')
|
|
|
|
|
{
|
|
|
|
|
if (constraints0 == '+')
|
|
|
|
|
;
|
|
|
|
|
/* If we've only got an output reload for this operand,
|
|
|
|
|
we'd better have a matching input operand. */
|
|
|
|
|
else if (constraints0 == '='
|
|
|
|
|
&& find_matching_operand (insn, XINT (pattern, 0)))
|
|
|
|
|
;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"operand %d missing in-out reload",
|
|
|
|
|
XINT (pattern, 0));
|
|
|
|
|
error_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (constraints0 != '=' && constraints0 != '+')
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"operand %d missing output reload",
|
|
|
|
|
XINT (pattern, 0));
|
|
|
|
|
error_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allowing non-lvalues in destinations -- particularly CONST_INT --
|
|
|
|
|
while not likely to occur at runtime, results in less efficient
|
|
|
|
|
code from insn-recog.c. */
|
|
|
|
|
if (set
|
|
|
|
|
&& pred_name[0] != '\0'
|
|
|
|
|
&& allows_non_lvalue)
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: destination operand %d allows non-lvalue",
|
|
|
|
|
XINT (pattern, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
and PEEP2 can FAIL within the output pattern. Exclude
|
|
|
|
|
address_operand, since its mode is related to the mode of
|
|
|
|
|
the memory not the operand. Exclude the SET_DEST of a call
|
|
|
|
|
instruction, as that is a common idiom. */
|
|
|
|
|
|
|
|
|
|
if (GET_MODE (pattern) == VOIDmode
|
|
|
|
|
&& code == MATCH_OPERAND
|
|
|
|
|
&& GET_CODE (insn) == DEFINE_INSN
|
|
|
|
|
&& allows_non_const
|
|
|
|
|
&& ! special_mode_pred
|
|
|
|
|
&& pred_name[0] != '\0'
|
|
|
|
|
&& strcmp (pred_name, "address_operand") != 0
|
|
|
|
|
&& strstr (c_test, "operands") == NULL
|
|
|
|
|
&& ! (set
|
|
|
|
|
&& GET_CODE (set) == SET
|
|
|
|
|
&& GET_CODE (SET_SRC (set)) == CALL))
|
|
|
|
|
{
|
|
|
|
|
message_with_line (pattern_lineno,
|
|
|
|
|
"warning: operand %d missing mode?",
|
|
|
|
|
XINT (pattern, 0));
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SET:
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode dmode, smode;
|
|
|
|
|
rtx dest, src;
|
|
|
|
|
|
|
|
|
|
dest = SET_DEST (pattern);
|
|
|
|
|
src = SET_SRC (pattern);
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
|
|
|
|
|
dmode = GET_MODE (dest);
|
|
|
|
|
smode = GET_MODE (src);
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
&& GET_CODE (src) != PC
|
|
|
|
|
&& GET_CODE (src) != CC0
|
|
|
|
|
&& 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))
|
|
|
|
|
validate_pattern (dest, insn, pattern, '=');
|
|
|
|
|
validate_pattern (SET_DEST (pattern), insn, pattern, '=');
|
|
|
|
|
validate_pattern (SET_SRC (pattern), insn, NULL_RTX, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case CLOBBER:
|
|
|
|
|
validate_pattern (SET_DEST (pattern), insn, pattern, '=');
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case ZERO_EXTRACT:
|
|
|
|
|
validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
|
|
|
|
|
validate_pattern (XEXP (pattern, 1), insn, NULL_RTX, 0);
|
|
|
|
|
validate_pattern (XEXP (pattern, 2), insn, NULL_RTX, 0);
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case STRICT_LOW_PART:
|
|
|
|
|
validate_pattern (XEXP (pattern, 0), insn, set, set ? '+' : 0);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
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++;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (fmt[i])
|
|
|
|
|
{
|
|
|
|
|
case 'e': case 'u':
|
|
|
|
|
validate_pattern (XEXP (pattern, i), insn, NULL_RTX, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
for (j = 0; j < XVECLEN (pattern, i); j++)
|
|
|
|
|
validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX, 0);
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'i': case 'w': case '0': case 's':
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Create a chain of nodes to verify that an rtl expression matches
|
|
|
|
|
PATTERN.
|
|
|
|
|
|
|
|
|
|
LAST is a pointer to the listhead in the previous node in the chain (or
|
|
|
|
|
in the calling function, for the first node).
|
|
|
|
|
|
|
|
|
|
POSITION is the string representing the current position in the insn.
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
INSN_TYPE is the type of insn for which we are emitting code.
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
A pointer to the final node in the chain is returned. */
|
|
|
|
|
|
|
|
|
|
static struct decision *
|
2002-02-01 18:16:02 +00:00
|
|
|
|
add_to_sequence (pattern, last, position, insn_type, top)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx pattern;
|
|
|
|
|
struct decision_head *last;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
const char *position;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
enum routine_type insn_type;
|
|
|
|
|
int top;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
RTX_CODE code;
|
|
|
|
|
struct decision *this, *sub;
|
|
|
|
|
struct decision_test *test;
|
|
|
|
|
struct decision_test **place;
|
|
|
|
|
char *subpos;
|
|
|
|
|
size_t i;
|
|
|
|
|
const char *fmt;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int depth = strlen (position);
|
|
|
|
|
int len;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
enum machine_mode mode;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (depth > max_depth)
|
|
|
|
|
max_depth = depth;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subpos = (char *) xmalloc (depth + 2);
|
|
|
|
|
strcpy (subpos, position);
|
|
|
|
|
subpos[depth + 1] = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
sub = this = new_decision (position, last);
|
|
|
|
|
place = &this->tests;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
restart:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mode = GET_MODE (pattern);
|
|
|
|
|
code = GET_CODE (pattern);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case PARALLEL:
|
|
|
|
|
/* Toplevel peephole pattern. */
|
|
|
|
|
if (insn_type == PEEPHOLE2 && top)
|
|
|
|
|
{
|
|
|
|
|
/* We don't need the node we just created -- unlink it. */
|
|
|
|
|
last->first = last->last = NULL;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subpos[depth] = (i > 0 ? 'A' + i : 0);
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, 0, i),
|
|
|
|
|
last, subpos, insn_type, 0);
|
|
|
|
|
last = &sub->success;
|
|
|
|
|
}
|
|
|
|
|
goto ret;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Else nothing special. */
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case MATCH_PARALLEL:
|
|
|
|
|
/* The explicit patterns within a match_parallel enforce a minimum
|
|
|
|
|
length on the vector. The match_parallel predicate may allow
|
|
|
|
|
for more elements. We do need to check for this minimum here
|
|
|
|
|
or the code generated to match the internals may reference data
|
|
|
|
|
beyond the end of the vector. */
|
|
|
|
|
test = new_decision_test (DT_veclen_ge, &place);
|
|
|
|
|
test->u.veclen = XVECLEN (pattern, 2);
|
|
|
|
|
/* FALLTHRU */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case MATCH_OPERAND:
|
|
|
|
|
case MATCH_SCRATCH:
|
|
|
|
|
case MATCH_OPERATOR:
|
|
|
|
|
case MATCH_INSN:
|
|
|
|
|
{
|
|
|
|
|
const char *pred_name;
|
|
|
|
|
RTX_CODE was_code = code;
|
|
|
|
|
int allows_const_int = 1;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if (i < NUM_KNOWN_PREDS)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
test->u.pred.index = i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (preds[i].codes[1] == 0 && code == UNKNOWN)
|
|
|
|
|
code = preds[i].codes[0];
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
allows_const_int = 0;
|
|
|
|
|
for (j = 0; preds[i].codes[j] != 0; j++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (preds[i].codes[j] == CONST_INT)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
allows_const_int = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
test->u.pred.index = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Can't enforce a mode if we allow const_int. */
|
|
|
|
|
if (allows_const_int)
|
|
|
|
|
mode = VOIDmode;
|
|
|
|
|
|
|
|
|
|
/* Accept the operand, ie. record it in `operands'. */
|
|
|
|
|
test = new_decision_test (DT_accept_op, &place);
|
|
|
|
|
test->u.opno = XINT (pattern, 0);
|
|
|
|
|
|
|
|
|
|
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);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
goto fini;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case MATCH_OP_DUP:
|
|
|
|
|
code = UNKNOWN;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
test = new_decision_test (DT_dup, &place);
|
|
|
|
|
test->u.dup = XINT (pattern, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
test = new_decision_test (DT_accept_op, &place);
|
|
|
|
|
test->u.opno = XINT (pattern, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
for (i = 0; i < (size_t) XVECLEN (pattern, 1); i++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subpos[depth] = i + '0';
|
|
|
|
|
sub = add_to_sequence (XVECEXP (pattern, 1, i),
|
|
|
|
|
&sub->success, subpos, insn_type, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
goto fini;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case MATCH_DUP:
|
|
|
|
|
case MATCH_PAR_DUP:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
code = UNKNOWN;
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_dup, &place);
|
|
|
|
|
test->u.dup = XINT (pattern, 0);
|
|
|
|
|
goto fini;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case ADDRESS:
|
|
|
|
|
pattern = XEXP (pattern, 0);
|
|
|
|
|
goto restart;
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt = GET_RTX_FORMAT (code);
|
|
|
|
|
len = GET_RTX_LENGTH (code);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Do tests against the current node first. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
for (i = 0; i < (size_t) len; i++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (fmt[i] == 'i')
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00: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 ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (fmt[i] == 'w')
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If this value actually fits in an int, we can use a switch
|
|
|
|
|
statement here, so indicate that. */
|
|
|
|
|
enum decision_type type
|
|
|
|
|
= ((int) XWINT (pattern, i) == XWINT (pattern, i))
|
|
|
|
|
? DT_elt_zero_wide_safe : DT_elt_zero_wide;
|
|
|
|
|
|
|
|
|
|
if (i != 0)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
test = new_decision_test (type, &place);
|
|
|
|
|
test->u.intval = XWINT (pattern, i);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else if (fmt[i] == 'E')
|
|
|
|
|
{
|
|
|
|
|
if (i != 0)
|
|
|
|
|
abort ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
test = new_decision_test (DT_veclen, &place);
|
|
|
|
|
test->u.veclen = XVECLEN (pattern, i);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'E':
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'i': case 'w':
|
|
|
|
|
/* Handled above. */
|
|
|
|
|
break;
|
|
|
|
|
case '0':
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (mode != VOIDmode)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
place = &this->tests;
|
|
|
|
|
test = new_decision_test (DT_mode, &place);
|
|
|
|
|
test->u.mode = mode;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If we didn't insert any tests or accept nodes, hork. */
|
|
|
|
|
if (this->tests == NULL)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
ret:
|
|
|
|
|
free (subpos);
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A subroutine of maybe_both_true; examines only one test.
|
|
|
|
|
Returns > 0 for "definitely both true" and < 0 for "maybe both true". */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
|
|
|
|
maybe_both_true_2 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
if (d1->type == d2->type)
|
|
|
|
|
{
|
|
|
|
|
switch (d1->type)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_mode:
|
2002-05-09 20:02:13 +00:00
|
|
|
|
return d1->u.mode == d2->u.mode;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
case DT_code:
|
|
|
|
|
return d1->u.code == d2->u.code;
|
|
|
|
|
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
return d1->u.veclen == d2->u.veclen;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
case DT_elt_zero_wide_safe:
|
|
|
|
|
return d1->u.intval == d2->u.intval;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (d1->type == DT_pred || d2->type == DT_pred)
|
|
|
|
|
{
|
|
|
|
|
if (d2->type == DT_pred)
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *tmp;
|
|
|
|
|
tmp = d1, d1 = d2, d2 = tmp;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If D2 tests a mode, see if it matches D1. */
|
|
|
|
|
if (d1->u.pred.mode != VOIDmode)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (d2->type == DT_mode)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-05-09 20:02:13 +00:00
|
|
|
|
if (d1->u.pred.mode != d2->u.mode
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* 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. */
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Otherwise see if the predicates have any codes in common. */
|
|
|
|
|
else if (d2->type == DT_pred && d2->u.pred.index >= 0)
|
|
|
|
|
{
|
|
|
|
|
const RTX_CODE *c1 = &preds[d1->u.pred.index].codes[0];
|
|
|
|
|
int common = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
while (*c1 != 0 && !common)
|
|
|
|
|
{
|
|
|
|
|
const RTX_CODE *c2 = &preds[d2->u.pred.index].codes[0];
|
|
|
|
|
while (*c2 != 0 && !common)
|
|
|
|
|
{
|
|
|
|
|
common = (*c1 == *c2);
|
|
|
|
|
++c2;
|
|
|
|
|
}
|
|
|
|
|
++c1;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (!common)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Tests vs veclen may be known when strict equality is involved. */
|
|
|
|
|
if (d1->type == DT_veclen && d2->type == DT_veclen_ge)
|
|
|
|
|
return d1->u.veclen >= d2->u.veclen;
|
|
|
|
|
if (d1->type == DT_veclen_ge && d2->type == DT_veclen)
|
|
|
|
|
return d2->u.veclen >= d1->u.veclen;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return -1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00: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". */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static int
|
2002-02-01 18:16:02 +00:00
|
|
|
|
maybe_both_true_1 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_test *t1, *t2;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* A match_operand with no predicate can match anything. Recognize
|
|
|
|
|
this by the existence of a lone DT_accept_op test. */
|
|
|
|
|
if (d1->type == DT_accept_op || d2->type == DT_accept_op)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
return 1;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return -1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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
|
|
|
|
|
can match both or just that we couldn't prove there wasn't such an RTL).
|
|
|
|
|
|
|
|
|
|
TOPLEVEL is non-zero if we are to only look at the top level and not
|
|
|
|
|
recursively descend. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
maybe_both_true (d1, d2, toplevel)
|
|
|
|
|
struct decision *d1, *d2;
|
|
|
|
|
int toplevel;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *p1, *p2;
|
|
|
|
|
int cmp;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Don't compare strings on the different positions in insn. Doing so
|
|
|
|
|
is incorrect and results in false matches from constructs like
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
[(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"))]
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (toplevel)
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If the d2->position was lexically lower, swap. */
|
|
|
|
|
if (cmp > 0)
|
|
|
|
|
p1 = d1, d1 = d2, d2 = p1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (d1->success.first == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
for (p1 = d1->success.first; p1; p1 = p1->next)
|
|
|
|
|
if (maybe_both_true (p1, d2, 0))
|
|
|
|
|
return 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Test the current level. */
|
|
|
|
|
cmp = maybe_both_true_1 (d1->tests, d2->tests);
|
|
|
|
|
if (cmp >= 0)
|
|
|
|
|
return cmp;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (toplevel || d1->success.first == 0 || d2->success.first == 0)
|
|
|
|
|
return 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* A subroutine of nodes_identical. Examine two tests for equivalence. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
|
|
|
|
nodes_identical_1 (d1, d2)
|
|
|
|
|
struct decision_test *d1, *d2;
|
|
|
|
|
{
|
|
|
|
|
switch (d1->type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
|
|
|
|
return d1->u.mode == d2->u.mode;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_code:
|
|
|
|
|
return d1->u.code == d2->u.code;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_pred:
|
|
|
|
|
return (d1->u.pred.mode == d2->u.pred.mode
|
|
|
|
|
&& strcmp (d1->u.pred.name, d2->u.pred.name) == 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_c_test:
|
|
|
|
|
return strcmp (d1->u.c_test, d2->u.c_test) == 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_veclen:
|
|
|
|
|
case DT_veclen_ge:
|
|
|
|
|
return d1->u.veclen == d2->u.veclen;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_dup:
|
|
|
|
|
return d1->u.dup == d2->u.dup;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
case DT_elt_zero_wide:
|
|
|
|
|
case DT_elt_zero_wide_safe:
|
|
|
|
|
return d1->u.intval == d2->u.intval;
|
|
|
|
|
|
|
|
|
|
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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static int
|
2002-02-01 18:16:02 +00:00
|
|
|
|
nodes_identical (d1, d2)
|
|
|
|
|
struct decision *d1, *d2;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_test *t1, *t2;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (t1 = d1->tests, t2 = d2->tests; t1 && t2; t1 = t1->next, t2 = t2->next)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (t1->type != t2->type)
|
|
|
|
|
return 0;
|
|
|
|
|
if (! nodes_identical_1 (t1, t2))
|
|
|
|
|
return 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* For success, they should now both be null. */
|
|
|
|
|
if (t1 != t2)
|
|
|
|
|
return 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Check that their subnodes are at the same position, as any one set
|
|
|
|
|
of sibling decisions must be at the same position. Allowing this
|
|
|
|
|
requires complications to find_afterward and when change_state is
|
|
|
|
|
invoked. */
|
|
|
|
|
if (d1->success.first
|
|
|
|
|
&& d2->success.first
|
|
|
|
|
&& strcmp (d1->success.first->position, d2->success.first->position))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
merge_accept_insn (oldd, addd)
|
|
|
|
|
struct decision *oldd, *addd;
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *old, *add;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (old = oldd->tests; old; old = old->next)
|
|
|
|
|
if (old->type == DT_accept_insn)
|
|
|
|
|
break;
|
|
|
|
|
if (old == NULL)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (add = addd->tests; add; add = add->next)
|
|
|
|
|
if (add->type == DT_accept_insn)
|
|
|
|
|
break;
|
|
|
|
|
if (add == NULL)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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
|
|
|
|
|
{
|
|
|
|
|
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++;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Merge two decision trees OLDH and ADDH, modifying OLDH destructively. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
merge_trees (oldh, addh)
|
|
|
|
|
struct decision_head *oldh, *addh;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *next, *add;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (addh->first == 0)
|
|
|
|
|
return;
|
|
|
|
|
if (oldh->first == 0)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
*oldh = *addh;
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Trying to merge bits at different positions isn't possible. */
|
|
|
|
|
if (strcmp (oldh->first->position, addh->first->position))
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (add = addh->first; add ; add = next)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *old, *insert_before = NULL;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
next = add->next;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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.
|
|
|
|
|
|
|
|
|
|
Scan starting from the end of OLDH until we reach a point
|
|
|
|
|
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.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
If we have no match, place NEW after the closest match we found. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (old = oldh->last; old; old = old->prev)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (nodes_identical (old, add))
|
|
|
|
|
{
|
|
|
|
|
merge_accept_insn (old, add);
|
|
|
|
|
merge_trees (&old->success, &add->success);
|
|
|
|
|
goto merged_nodes;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (maybe_both_true (old, add, 0))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* 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 ((int) add->tests->type < (int) old->tests->type)
|
|
|
|
|
insert_before = old;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (insert_before == NULL)
|
|
|
|
|
{
|
|
|
|
|
add->next = NULL;
|
|
|
|
|
add->prev = oldh->last;
|
|
|
|
|
oldh->last->next = add;
|
|
|
|
|
oldh->last = add;
|
|
|
|
|
}
|
|
|
|
|
else
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if ((add->prev = insert_before->prev) != NULL)
|
|
|
|
|
add->prev->next = add;
|
|
|
|
|
else
|
|
|
|
|
oldh->first = add;
|
|
|
|
|
add->next = insert_before;
|
|
|
|
|
insert_before->prev = add;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
factor_tests (head)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
{
|
|
|
|
|
struct decision *first, *next;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (first = head->first; first && first->next; first = next)
|
|
|
|
|
{
|
|
|
|
|
enum decision_type type;
|
|
|
|
|
struct decision *new, *old_last;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
type = first->tests->type;
|
|
|
|
|
next = first->next;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Want at least two compatible sequential nodes. */
|
|
|
|
|
if (next->tests->type != type)
|
|
|
|
|
continue;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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_safe)
|
|
|
|
|
continue;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Crop the node tree off after our first test. */
|
|
|
|
|
first->next = NULL;
|
|
|
|
|
old_last = head->last;
|
|
|
|
|
head->last = first;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* For each compatible test, adjust to perform only one test in
|
|
|
|
|
the top level node, then merge the node back into the tree. */
|
|
|
|
|
do
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_head h;
|
|
|
|
|
|
|
|
|
|
if (next->tests->next != NULL)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
new = new_decision (next->position, &next->success);
|
|
|
|
|
new->tests = next->tests->next;
|
|
|
|
|
next->tests->next = NULL;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
new = next;
|
|
|
|
|
next = next->next;
|
|
|
|
|
new->next = NULL;
|
|
|
|
|
h.first = h.last = new;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
merge_trees (head, &h);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
while (next && next->tests->type == type);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* After we run out of compatible tests, graft the remaining nodes
|
|
|
|
|
back onto the tree. */
|
|
|
|
|
if (next)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
next->prev = head->last;
|
|
|
|
|
head->last->next = next;
|
|
|
|
|
head->last = old_last;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Recurse. */
|
|
|
|
|
for (first = head->first; first; first = first->next)
|
|
|
|
|
factor_tests (&first->success);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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:
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
If we have mode tests or code tests that are redundant with
|
|
|
|
|
predicates, remove them. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
simplify_tests (head)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
{
|
|
|
|
|
struct decision *tree;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (tree = head->first; tree; tree = tree->next)
|
|
|
|
|
{
|
|
|
|
|
struct decision_test *a, *b;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Recurse. */
|
|
|
|
|
for (tree = head->first; tree; tree = tree->next)
|
|
|
|
|
simplify_tests (&tree->success);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
that is generated. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
break_out_subroutines (head, initial)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
int initial;
|
|
|
|
|
{
|
|
|
|
|
int size = 0;
|
|
|
|
|
struct decision *sub;
|
|
|
|
|
|
|
|
|
|
for (sub = head->first; sub; sub = sub->next)
|
|
|
|
|
size += 1 + break_out_subroutines (&sub->success, 0);
|
|
|
|
|
|
|
|
|
|
if (size > SUBROUTINE_THRESHOLD && ! initial)
|
|
|
|
|
{
|
|
|
|
|
head->first->subroutine_number = ++next_subroutine_number;
|
|
|
|
|
size = 1;
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For each node p, find the next alternative that might be true
|
|
|
|
|
when p is true. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
find_afterward (head, real_afterward)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
struct decision *real_afterward;
|
|
|
|
|
{
|
|
|
|
|
struct decision *p, *q, *afterward;
|
|
|
|
|
|
|
|
|
|
/* We can't propagate alternatives across subroutine boundaries.
|
|
|
|
|
This is not incorrect, merely a minor optimization loss. */
|
|
|
|
|
|
|
|
|
|
p = head->first;
|
|
|
|
|
afterward = (p->subroutine_number > 0 ? NULL : real_afterward);
|
|
|
|
|
|
|
|
|
|
for ( ; p ; p = p->next)
|
|
|
|
|
{
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* Pop up as many levels as necessary. */
|
|
|
|
|
for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Hunt for the last [A-Z] in both strings. */
|
|
|
|
|
for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
|
|
|
|
|
if (ISUPPER (oldpos[old_has_insn]))
|
|
|
|
|
break;
|
|
|
|
|
for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
|
|
|
|
|
if (ISUPPER (newpos[new_has_insn]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Go down to desired level. */
|
|
|
|
|
while (depth < ndepth)
|
|
|
|
|
{
|
|
|
|
|
/* It's a different insn from the first one. */
|
|
|
|
|
if (ISUPPER (newpos[depth]))
|
|
|
|
|
{
|
|
|
|
|
/* We can only fail if we're moving down the tree. */
|
|
|
|
|
if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
|
|
|
|
|
{
|
|
|
|
|
printf ("%stem = peep2_next_insn (%d);\n",
|
|
|
|
|
indent, newpos[depth] - 'A');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf ("%stem = peep2_next_insn (%d);\n",
|
|
|
|
|
indent, newpos[depth] - 'A');
|
|
|
|
|
printf ("%sif (tem == NULL_RTX)\n", indent);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (afterward)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf ("%s goto L%d;\n", indent, afterward->number);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf ("%s goto ret0;\n", indent);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf ("%sx%d = PATTERN (tem);\n", indent, depth + 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (ISLOWER (newpos[depth]))
|
|
|
|
|
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;
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
for (p = GET_RTX_NAME (code); *p; p++)
|
|
|
|
|
putchar (TOUPPER (*p));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Emit code to cross an afterward link -- change state and branch. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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);
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Emit a switch statement, if possible, for an initial sequence of
|
|
|
|
|
nodes at START. Return the first node yet untested. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static struct decision *
|
|
|
|
|
write_switch (start, depth)
|
|
|
|
|
struct decision *start;
|
|
|
|
|
int depth;
|
|
|
|
|
{
|
|
|
|
|
struct decision *p = start;
|
|
|
|
|
enum decision_type type = p->tests->type;
|
|
|
|
|
struct decision *needs_label = NULL;
|
|
|
|
|
|
|
|
|
|
/* If we have two or more nodes in sequence that test the same one
|
|
|
|
|
thing, we may be able to use a switch statement. */
|
|
|
|
|
|
|
|
|
|
if (!p->next
|
|
|
|
|
|| p->tests->next
|
|
|
|
|
|| p->next->tests->type != type
|
|
|
|
|
|| p->next->tests->next
|
|
|
|
|
|| nodes_identical_1 (p->tests, p->next->tests))
|
|
|
|
|
return p;
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
RTX_CODE code;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
memset (codemap, 0, sizeof(codemap));
|
|
|
|
|
|
|
|
|
|
printf (" switch (GET_CODE (x%d))\n {\n", depth);
|
|
|
|
|
code = p->tests->u.code;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (p != start && p->need_label && needs_label == NULL)
|
|
|
|
|
needs_label = p;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf (" case ");
|
|
|
|
|
print_code (code);
|
|
|
|
|
printf (":\n goto L%d;\n", p->success.first->number);
|
|
|
|
|
p->success.first->need_label = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
codemap[code] = 1;
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
while (p
|
|
|
|
|
&& ! p->tests->next
|
|
|
|
|
&& p->tests->type == DT_code
|
|
|
|
|
&& ! codemap[code = p->tests->u.code]);
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
if (needs_label != NULL)
|
|
|
|
|
ret = needs_label;
|
|
|
|
|
else
|
|
|
|
|
ret = p;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
while (p && p->tests->type == DT_pred
|
|
|
|
|
&& p->tests->u.pred.index >= 0)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
const RTX_CODE *c;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
|
|
|
|
|
if (codemap[(int) *c] != 0)
|
|
|
|
|
goto pred_done;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (c = &preds[p->tests->u.pred.index].codes[0]; *c ; ++c)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf (" case ");
|
|
|
|
|
print_code (*c);
|
|
|
|
|
printf (":\n");
|
|
|
|
|
codemap[(int) *c] = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf (" goto L%d;\n", p->number);
|
|
|
|
|
p->need_label = 1;
|
|
|
|
|
p = p->next;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
pred_done:
|
|
|
|
|
/* Make the default case skip the predicates we managed to match. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf (" default:\n");
|
|
|
|
|
if (p != ret)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (p)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf (" goto L%d;\n", p->number);
|
|
|
|
|
p->need_label = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
write_afterward (start, start->afterward, " ");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
printf (" break;\n");
|
|
|
|
|
printf (" }\n");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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_safe)
|
|
|
|
|
{
|
|
|
|
|
const char *indent = "";
|
|
|
|
|
|
|
|
|
|
/* We cast switch parameter to integer, so we must ensure that the value
|
|
|
|
|
fits. */
|
|
|
|
|
if (type == DT_elt_zero_wide_safe)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
indent = " ";
|
|
|
|
|
printf(" if ((int) XWINT (x%d, 0) == XWINT (x%d, 0))\n", depth, depth);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf ("%s switch (", indent);
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case DT_mode:
|
|
|
|
|
printf ("GET_MODE (x%d)", depth);
|
|
|
|
|
break;
|
|
|
|
|
case DT_veclen:
|
|
|
|
|
printf ("XVECLEN (x%d, 0)", depth);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_int:
|
|
|
|
|
printf ("XINT (x%d, 0)", depth);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_one_int:
|
|
|
|
|
printf ("XINT (x%d, 1)", depth);
|
|
|
|
|
break;
|
|
|
|
|
case DT_elt_zero_wide_safe:
|
|
|
|
|
/* 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);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
printf (")\n%s {\n", indent);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
do
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Merge trees will not unify identical nodes if their
|
|
|
|
|
sub-nodes are at different levels. Thus we must check
|
|
|
|
|
for duplicate cases. */
|
|
|
|
|
struct decision *q;
|
|
|
|
|
for (q = start; q != p; q = q->next)
|
|
|
|
|
if (nodes_identical_1 (p->tests, q->tests))
|
|
|
|
|
goto case_done;
|
|
|
|
|
|
|
|
|
|
if (p != start && p->need_label && needs_label == NULL)
|
|
|
|
|
needs_label = p;
|
|
|
|
|
|
|
|
|
|
printf ("%s case ", indent);
|
|
|
|
|
switch (type)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00: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:
|
|
|
|
|
case DT_elt_zero_wide_safe:
|
|
|
|
|
printf (HOST_WIDE_INT_PRINT_DEC, p->tests->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
printf (":\n%s goto L%d;\n", indent, p->success.first->number);
|
|
|
|
|
p->success.first->need_label = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
while (p && p->tests->type == type && !p->tests->next);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case_done:
|
|
|
|
|
printf ("%s default:\n%s break;\n%s }\n",
|
|
|
|
|
indent, indent, indent);
|
|
|
|
|
|
|
|
|
|
return needs_label != NULL ? needs_label : p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* None of the other tests are ameanable. */
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Emit code for one test. */
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
case DT_elt_zero_wide_safe:
|
|
|
|
|
printf ("XWINT (x%d, 0) == ", depth);
|
|
|
|
|
printf (HOST_WIDE_INT_PRINT_DEC, p->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DT_veclen_ge:
|
|
|
|
|
printf ("XVECLEN (x%d, 0) >= %d", depth, p->u.veclen);
|
|
|
|
|
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 ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
write_action (p, test, depth, uncond, success, subroutine_type)
|
|
|
|
|
struct decision *p;
|
|
|
|
|
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)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fputs (" {\n", stdout);
|
|
|
|
|
indent = " ";
|
|
|
|
|
want_close = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
indent = " ";
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (test->type == DT_accept_op)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00: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 ();
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Sanity check that we're now at the end of the list of tests. */
|
|
|
|
|
if (test->next)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
abort ();
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (test->type == DT_accept_insn)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00: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:
|
|
|
|
|
{
|
|
|
|
|
int match_len = 0, i;
|
|
|
|
|
|
|
|
|
|
for (i = strlen (p->position) - 1; i >= 0; --i)
|
|
|
|
|
if (ISUPPER (p->position[i]))
|
|
|
|
|
{
|
|
|
|
|
match_len = p->position[i] - 'A';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
printf ("%s*_pmatch_len = %d;\n", indent, match_len);
|
|
|
|
|
printf ("%stem = gen_peephole2_%d (insn, operands);\n",
|
|
|
|
|
indent, test->u.insn.code_number);
|
|
|
|
|
printf ("%sif (tem != 0)\n%s return tem;\n", indent, indent);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
printf("%sgoto L%d;\n", indent, success->number);
|
|
|
|
|
success->need_label = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
if (want_close)
|
|
|
|
|
fputs (" }\n", stdout);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00: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. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
static int
|
2002-02-01 18:16:02 +00:00
|
|
|
|
is_unconditional (t, subroutine_type)
|
|
|
|
|
struct decision_test *t;
|
|
|
|
|
enum routine_type subroutine_type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (t->type == DT_accept_op)
|
|
|
|
|
return 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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 ();
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Emit code for one node -- the conditional and the accompanying action.
|
|
|
|
|
Return true if there is no fallthru path. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
static int
|
2002-02-01 18:16:02 +00:00
|
|
|
|
write_node (p, depth, subroutine_type)
|
|
|
|
|
struct decision *p;
|
|
|
|
|
int depth;
|
|
|
|
|
enum routine_type subroutine_type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_test *test, *last_test;
|
|
|
|
|
int uncond;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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 (p, last_test, depth, uncond, p->success.first, subroutine_type);
|
|
|
|
|
|
|
|
|
|
return uncond > 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Emit code for all of the sibling nodes of HEAD. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
write_tree_1 (head, depth, subroutine_type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
int depth;
|
|
|
|
|
enum routine_type subroutine_type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *p, *next;
|
|
|
|
|
int uncond = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00: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;
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Finished with this chain. Close a fallthru path by branching
|
|
|
|
|
to the afterward node. */
|
|
|
|
|
if (! uncond)
|
|
|
|
|
write_afterward (head->last, head->last->afterward, " ");
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Write out the decision tree starting at HEAD. PREVPOS is the
|
|
|
|
|
position at the node that branched to this node. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
write_tree (head, prevpos, type, initial)
|
|
|
|
|
struct decision_head *head;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
const char *prevpos;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
enum routine_type type;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int initial;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *p = head->first;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
putchar ('\n');
|
|
|
|
|
if (p->need_label)
|
|
|
|
|
OUTPUT_LABEL (" ", p->number);
|
|
|
|
|
|
|
|
|
|
if (! initial && p->subroutine_number > 0)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static const char * const name_prefix[] = {
|
|
|
|
|
"recog", "split", "peephole2"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char * const call_suffix[] = {
|
|
|
|
|
", pnum_clobbers", "", ", _pmatch_len"
|
|
|
|
|
};
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* This node has been broken out into a separate subroutine.
|
|
|
|
|
Call it, test the result, and branch accordingly. */
|
|
|
|
|
|
|
|
|
|
if (p->afterward)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
printf (" tem = %s_%d (x0, insn%s);\n",
|
2002-02-01 18:16:02 +00:00
|
|
|
|
name_prefix[type], p->subroutine_number, call_suffix[type]);
|
|
|
|
|
if (IS_SPLIT (type))
|
|
|
|
|
printf (" if (tem != 0)\n return tem;\n");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
2002-02-01 18:16:02 +00: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);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
printf (" return %s_%d (x0, insn%s);\n",
|
|
|
|
|
name_prefix[type], p->subroutine_number, call_suffix[type]);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int depth = strlen (p->position);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
change_state (prevpos, p->position, head->last->afterward, " ");
|
|
|
|
|
write_tree_1 (head, depth, type);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (p = head->first; p; p = p->next)
|
|
|
|
|
if (p->success.first)
|
|
|
|
|
write_tree (&p->success, p->position, type, 0);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Write out a subroutine of type TYPE to do comparisons starting at
|
|
|
|
|
node TREE. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
write_subroutine (head, type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int subfunction = head->first ? head->first->subroutine_number : 0;
|
|
|
|
|
const char *s_or_e;
|
|
|
|
|
char extension[32];
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
s_or_e = subfunction ? "static " : "";
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (subfunction)
|
|
|
|
|
sprintf (extension, "_%d", subfunction);
|
|
|
|
|
else if (type == RECOG)
|
|
|
|
|
extension[0] = '\0';
|
|
|
|
|
else
|
|
|
|
|
strcpy (extension, "_insns");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
switch (type)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case RECOG:
|
|
|
|
|
printf ("%sint recog%s PARAMS ((rtx, rtx, int *));\n", s_or_e, extension);
|
|
|
|
|
printf ("%sint\n\
|
|
|
|
|
recog%s (x0, insn, pnum_clobbers)\n\
|
|
|
|
|
rtx x0 ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
int *pnum_clobbers ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
|
|
|
|
case SPLIT:
|
|
|
|
|
printf ("%srtx split%s PARAMS ((rtx, rtx));\n", s_or_e, extension);
|
|
|
|
|
printf ("%srtx\n\
|
|
|
|
|
split%s (x0, insn)\n\
|
|
|
|
|
rtx x0 ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
|
|
|
|
case PEEPHOLE2:
|
|
|
|
|
printf ("%srtx peephole2%s PARAMS ((rtx, rtx, int *));\n",
|
|
|
|
|
s_or_e, extension);
|
|
|
|
|
printf ("%srtx\n\
|
|
|
|
|
peephole2%s (x0, insn, _pmatch_len)\n\
|
|
|
|
|
rtx x0 ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
rtx insn ATTRIBUTE_UNUSED;\n\
|
|
|
|
|
int *_pmatch_len ATTRIBUTE_UNUSED;\n", s_or_e, extension);
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
printf ("{\n rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];\n");
|
|
|
|
|
for (i = 1; i <= max_depth; i++)
|
|
|
|
|
printf (" rtx x%d ATTRIBUTE_UNUSED;\n", i);
|
|
|
|
|
|
|
|
|
|
printf (" %s tem ATTRIBUTE_UNUSED;\n", IS_SPLIT (type) ? "rtx" : "int");
|
|
|
|
|
|
|
|
|
|
if (!subfunction)
|
|
|
|
|
printf (" recog_data.insn = NULL_RTX;\n");
|
|
|
|
|
|
|
|
|
|
if (head->first)
|
|
|
|
|
write_tree (head, "", type, 1);
|
|
|
|
|
else
|
|
|
|
|
printf (" goto ret0;\n");
|
|
|
|
|
|
|
|
|
|
printf (" ret0:\n return %d;\n}\n\n", IS_SPLIT (type) ? 0 : -1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* In break_out_subroutines, we discovered the boundaries for the
|
|
|
|
|
subroutines, but did not write them out. Do so now. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_subroutines (head, type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision *p;
|
|
|
|
|
|
|
|
|
|
for (p = head->first; p ; p = p->next)
|
|
|
|
|
if (p->success.first)
|
|
|
|
|
write_subroutines (&p->success, type);
|
|
|
|
|
|
|
|
|
|
if (head->first->subroutine_number > 0)
|
|
|
|
|
write_subroutine (head, type);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Begin the output file. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_header ()
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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\
|
|
|
|
|
#include \"hard-reg-set.h\"\n\
|
|
|
|
|
#include \"resource.h\"\n\
|
|
|
|
|
#include \"toplev.h\"\n\
|
|
|
|
|
#include \"reload.h\"\n\
|
|
|
|
|
\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");
|
|
|
|
|
puts ("\
|
|
|
|
|
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");
|
|
|
|
|
puts ("\
|
|
|
|
|
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");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Construct and return a sequence of decisions
|
|
|
|
|
that will recognize INSN.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
TYPE says what type of routine we are recognizing (RECOG or SPLIT). */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static struct decision_head
|
|
|
|
|
make_insn_sequence (insn, type)
|
|
|
|
|
rtx insn;
|
|
|
|
|
enum routine_type type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx x;
|
|
|
|
|
const char *c_test = XSTR (insn, type == RECOG ? 2 : 1);
|
|
|
|
|
struct decision *last;
|
|
|
|
|
struct decision_test *test, **place;
|
|
|
|
|
struct decision_head head;
|
|
|
|
|
char c_test_pos[2];
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
record_insn_name (next_insn_code, (type == RECOG ? XSTR (insn, 0) : NULL));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
c_test_pos[0] = '\0';
|
|
|
|
|
if (type == PEEPHOLE2)
|
|
|
|
|
{
|
|
|
|
|
int i, j;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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++)
|
|
|
|
|
{
|
|
|
|
|
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;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
c_test_pos[0] = 'A' + j - 1;
|
|
|
|
|
c_test_pos[1] = '\0';
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
validate_pattern (x, insn, NULL_RTX, 0);
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
last = new_decision (c_test_pos, &last->success);
|
|
|
|
|
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;
|
|
|
|
|
test->u.insn.lineno = pattern_lineno;
|
|
|
|
|
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--)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i != XVECLEN (x, 0))
|
|
|
|
|
{
|
|
|
|
|
rtx new;
|
|
|
|
|
struct decision_head clobber_head;
|
|
|
|
|
|
|
|
|
|
/* Build a similar insn without the clobbers. */
|
|
|
|
|
if (i == 1)
|
|
|
|
|
new = XVECEXP (x, 0, 0);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
new = rtx_alloc (PARALLEL);
|
|
|
|
|
XVEC (new, 0) = rtvec_alloc (i);
|
|
|
|
|
for (j = i - 1; j >= 0; j--)
|
|
|
|
|
XVECEXP (new, 0, j) = XVECEXP (x, 0, j);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Recognize it. */
|
|
|
|
|
memset (&clobber_head, 0, sizeof(clobber_head));
|
|
|
|
|
last = add_to_sequence (new, &clobber_head, "", type, 1);
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
test->u.insn.lineno = pattern_lineno;
|
|
|
|
|
test->u.insn.num_clobbers_to_add = XVECLEN (x, 0) - i;
|
|
|
|
|
|
|
|
|
|
merge_trees (&head, &clobber_head);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SPLIT:
|
|
|
|
|
/* Define the subroutine we will call below and emit in genemit. */
|
|
|
|
|
printf ("extern rtx gen_split_%d PARAMS ((rtx *));\n", next_insn_code);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PEEPHOLE2:
|
|
|
|
|
/* Define the subroutine we will call below and emit in genemit. */
|
|
|
|
|
printf ("extern rtx gen_peephole2_%d PARAMS ((rtx, rtx *));\n",
|
|
|
|
|
next_insn_code);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return head;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
process_tree (head, subroutine_type)
|
|
|
|
|
struct decision_head *head;
|
|
|
|
|
enum routine_type subroutine_type;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (head->first == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* We can elide peephole2_insns, but not recog or split_insns. */
|
|
|
|
|
if (subroutine_type == PEEPHOLE2)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
factor_tests (head);
|
|
|
|
|
|
|
|
|
|
next_subroutine_number = 0;
|
|
|
|
|
break_out_subroutines (head, 1);
|
|
|
|
|
find_afterward (head, NULL);
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
write_subroutines (head, subroutine_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_subroutine (head, subroutine_type);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
extern int main PARAMS ((int, char **));
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int
|
|
|
|
|
main (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
|
|
|
|
rtx desc;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct decision_head recog_tree, split_tree, peephole2_tree, h;
|
|
|
|
|
|
|
|
|
|
progname = "genrecog";
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
memset (&recog_tree, 0, sizeof recog_tree);
|
|
|
|
|
memset (&split_tree, 0, sizeof split_tree);
|
|
|
|
|
memset (&peephole2_tree, 0, sizeof peephole2_tree);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (argc <= 1)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fatal ("no input file name");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
|
|
|
|
|
return (FATAL_EXIT_CODE);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
next_insn_code = 0;
|
|
|
|
|
next_index = 0;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
write_header ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Read the machine description. */
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
desc = read_md_rtx (&pattern_lineno, &next_insn_code);
|
|
|
|
|
if (desc == NULL)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (desc) == DEFINE_INSN)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, RECOG);
|
|
|
|
|
merge_trees (&recog_tree, &h);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else if (GET_CODE (desc) == DEFINE_SPLIT)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, SPLIT);
|
|
|
|
|
merge_trees (&split_tree, &h);
|
|
|
|
|
}
|
|
|
|
|
else if (GET_CODE (desc) == DEFINE_PEEPHOLE2)
|
|
|
|
|
{
|
|
|
|
|
h = make_insn_sequence (desc, PEEPHOLE2);
|
|
|
|
|
merge_trees (&peephole2_tree, &h);
|
|
|
|
|
}
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
next_index++;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (error_count)
|
|
|
|
|
return FATAL_EXIT_CODE;
|
|
|
|
|
|
|
|
|
|
puts ("\n\n");
|
|
|
|
|
|
|
|
|
|
process_tree (&recog_tree, RECOG);
|
|
|
|
|
process_tree (&split_tree, SPLIT);
|
|
|
|
|
process_tree (&peephole2_tree, PEEPHOLE2);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
fflush (stdout);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_elt_zero_wide_safe:
|
|
|
|
|
fprintf (stderr, "elt0_ws=");
|
|
|
|
|
fprintf (stderr, HOST_WIDE_INT_PRINT_DEC, test->u.intval);
|
|
|
|
|
break;
|
|
|
|
|
case DT_veclen_ge:
|
|
|
|
|
fprintf (stderr, "veclen>=%d", test->u.veclen);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fprintf (stderr, "} %d n %d a %d\n", d->number,
|
|
|
|
|
(d->next ? d->next->number : -1),
|
|
|
|
|
(d->afterward ? d->afterward->number : -1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
debug_decision_list (d)
|
|
|
|
|
struct decision *d;
|
|
|
|
|
{
|
|
|
|
|
while (d)
|
|
|
|
|
{
|
|
|
|
|
debug_decision_0 (d, 0, 0);
|
|
|
|
|
d = d->next;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|