/* Subroutines used by or related to instruction recognition.
- Copyright (C) 1987, 1988, 91-6, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 91-97, 1998 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "config.h"
-#include <stdio.h>
+#include "system.h"
#include "rtl.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
+#include "toplev.h"
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
#endif
#endif
-/* Import from final.c: */
-extern rtx alter_subreg ();
-
-static rtx *find_single_use_1 PROTO((rtx, rtx *));
+static void validate_replace_rtx_1 PROTO((rtx *, rtx, rtx, rtx));
+static rtx *find_single_use_1 PROTO((rtx, rtx *));
+static rtx *find_constant_term_loc PROTO((rtx *));
+static int insn_invalid_p PROTO((rtx));
/* Nonzero means allow operands to be volatile.
This should be 0 if you are generating rtl, such as if you are calling
int volatile_ok;
+/* The next variables are set up by extract_insn. The first four of them
+ are also set up during insn_extract. */
+
+/* Indexed by N, gives value of operand N. */
+rtx recog_operand[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives location where operand N was found. */
+rtx *recog_operand_loc[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives location where the Nth duplicate-appearance of
+ an operand was found. This is something that matched MATCH_DUP. */
+rtx *recog_dup_loc[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the operand number that was duplicated in the
+ Nth duplicate-appearance of an operand. */
+char recog_dup_num[MAX_RECOG_OPERANDS];
+
+/* The number of operands of the insn. */
+int recog_n_operands;
+
+/* The number of MATCH_DUPs in the insn. */
+int recog_n_dups;
+
+/* The number of alternatives in the constraints for the insn. */
+int recog_n_alternatives;
+
+/* Indexed by N, gives the mode of operand N. */
+enum machine_mode recog_operand_mode[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the constraint string for operand N. */
+char *recog_constraints[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the type (in, out, inout) for operand N. */
+enum op_type recog_op_type[MAX_RECOG_OPERANDS];
+
+#ifndef REGISTER_CONSTRAINTS
+/* Indexed by N, nonzero if operand N should be an address. */
+char recog_operand_address_p[MAX_RECOG_OPERANDS];
+#endif
+
+/* Contains a vector of operand_alternative structures for every operand.
+ Set up by preprocess_constraints. */
+struct operand_alternative recog_op_alt[MAX_RECOG_OPERANDS][MAX_RECOG_ALTERNATIVES];
+
/* On return from `constrain_operands', indicate which alternative
was satisfied. */
return 1;
}
\f
-/* Static data for the next two routines.
+/* Static data for the next two routines. */
- The maximum number of changes supported is defined as the maximum
- number of operands times 5. This allows for repeated substitutions
- inside complex indexed address, or, alternatively, changes in up
- to 5 insns. */
-
-#define MAX_CHANGE_LOCS (MAX_RECOG_OPERANDS * 5)
+typedef struct change_t
+{
+ rtx object;
+ int old_code;
+ rtx *loc;
+ rtx old;
+} change_t;
-static rtx change_objects[MAX_CHANGE_LOCS];
-static int change_old_codes[MAX_CHANGE_LOCS];
-static rtx *change_locs[MAX_CHANGE_LOCS];
-static rtx change_olds[MAX_CHANGE_LOCS];
+static change_t *changes;
+static int changes_allocated;
static int num_changes = 0;
if (old == new || rtx_equal_p (old, new))
return 1;
- if (num_changes >= MAX_CHANGE_LOCS
- || (in_group == 0 && num_changes != 0))
+ if (in_group == 0 && num_changes != 0)
abort ();
*loc = new;
/* Save the information describing this change. */
- change_objects[num_changes] = object;
- change_locs[num_changes] = loc;
- change_olds[num_changes] = old;
+ if (num_changes >= changes_allocated)
+ {
+ if (changes_allocated == 0)
+ /* This value allows for repeated substitutions inside complex
+ indexed addresses, or changes in up to 5 insns. */
+ changes_allocated = MAX_RECOG_OPERANDS * 5;
+ else
+ changes_allocated *= 2;
+
+ changes =
+ (change_t*) xrealloc (changes,
+ sizeof (change_t) * changes_allocated);
+ }
+
+ changes[num_changes].object = object;
+ changes[num_changes].loc = loc;
+ changes[num_changes].old = old;
if (object && GET_CODE (object) != MEM)
{
/* Set INSN_CODE to force rerecognition of insn. Save old code in
case invalid. */
- change_old_codes[num_changes] = INSN_CODE (object);
+ changes[num_changes].old_code = INSN_CODE (object);
INSN_CODE (object) = -1;
}
return apply_change_group ();
}
+/* This subroutine of apply_change_group verifies whether the changes to INSN
+ were valid; i.e. whether INSN can still be recognized. */
+
+static int
+insn_invalid_p (insn)
+ rtx insn;
+{
+ int icode = recog_memoized (insn);
+ int is_asm = icode < 0 && asm_noperands (PATTERN (insn)) >= 0;
+
+ if (is_asm && ! check_asm_operands (PATTERN (insn)))
+ return 1;
+ if (! is_asm && icode < 0)
+ return 1;
+
+ /* After reload, verify that all constraints are satisfied. */
+ if (reload_completed)
+ {
+ extract_insn (insn);
+
+ if (! constrain_operands (1))
+ return 1;
+ }
+
+ return 0;
+}
+
/* Apply a group of changes previously issued with `validate_change'.
Return 1 if all changes are valid, zero otherwise. */
given a MEM and it still is a valid address, or if this is in insn
and it is recognized. In the latter case, if reload has completed,
we also require that the operands meet the constraints for
- the insn. We do not allow modifying an ASM_OPERANDS after reload
- has completed because verifying the constraints is too difficult. */
+ the insn. */
for (i = 0; i < num_changes; i++)
{
- rtx object = change_objects[i];
+ rtx object = changes[i].object;
if (object == 0)
continue;
if (! memory_address_p (GET_MODE (object), XEXP (object, 0)))
break;
}
- else if ((recog_memoized (object) < 0
- && (asm_noperands (PATTERN (object)) < 0
- || ! check_asm_operands (PATTERN (object))
- || reload_completed))
- || (reload_completed
- && (insn_extract (object),
- ! constrain_operands (INSN_CODE (object), 1))))
+ else if (insn_invalid_p (object))
{
rtx pat = PATTERN (object);
they were made. */
for (i = num_changes - 1; i >= num; i--)
{
- *change_locs[i] = change_olds[i];
- if (change_objects[i] && GET_CODE (change_objects[i]) != MEM)
- INSN_CODE (change_objects[i]) = change_old_codes[i];
+ *changes[i].loc = changes[i].old;
+ if (changes[i].object && GET_CODE (changes[i].object) != MEM)
+ INSN_CODE (changes[i].object) = changes[i].old_code;
}
num_changes = num;
}
switch (code)
{
case PLUS:
- /* If we have have a PLUS whose second operand is now a CONST_INT, use
+ /* If we have a PLUS whose second operand is now a CONST_INT, use
plus_constant to try to simplify it. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to)
validate_change (object, loc, plus_constant (XEXP (x, 0), INTVAL (to)),
{
enum machine_mode wanted_mode = VOIDmode;
enum machine_mode is_mode = GET_MODE (to);
- int width = INTVAL (XEXP (x, 1));
int pos = INTVAL (XEXP (x, 2));
#ifdef HAVE_extzv
if (code == ZERO_EXTRACT)
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+ {
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
+ }
#endif
#ifdef HAVE_extv
if (code == SIGN_EXTRACT)
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+ {
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
+ }
#endif
/* If we have a narrower mode, we can do something. */
validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
return apply_change_group ();
}
+
+/* Try replacing every occurrence of FROM in INSN with TO. After all
+ changes have been made, validate by seeing if INSN is still valid. */
+
+void
+validate_replace_rtx_group (from, to, insn)
+ rtx from, to, insn;
+{
+ validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
+}
+
+/* Try replacing every occurrence of FROM in INSN with TO, avoiding
+ SET_DESTs. After all changes have been made, validate by seeing if
+ INSN is still valid. */
+
+int
+validate_replace_src (from, to, insn)
+ rtx from, to, insn;
+{
+ if ((GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
+ || GET_CODE (PATTERN (insn)) != SET)
+ abort ();
+
+ validate_replace_rtx_1 (&SET_SRC (PATTERN (insn)), from, to, insn);
+ if (GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
+ validate_replace_rtx_1 (&XEXP (SET_DEST (PATTERN (insn)), 0),
+ from, to, insn);
+ return apply_change_group ();
+}
\f
#ifdef HAVE_cc0
/* Return 1 if the insn using CC0 set by INSN does not contain
int
const_int_operand (op, mode)
register rtx op;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
{
return GET_CODE (op) == CONST_INT;
}
abort ();
}
\f
+/* Analyze INSN and compute the variables recog_n_operands, recog_n_dups,
+ recog_n_alternatives, recog_operand, recog_operand_loc, recog_constraints,
+ recog_operand_mode, recog_dup_loc and recog_dup_num.
+ If REGISTER_CONSTRAINTS is not defined, also compute
+ recog_operand_address_p. */
+void
+extract_insn (insn)
+ rtx insn;
+{
+ int i;
+ int icode;
+ int noperands;
+ rtx body = PATTERN (insn);
+
+ recog_n_operands = 0;
+ recog_n_alternatives = 0;
+ recog_n_dups = 0;
+
+ switch (GET_CODE (body))
+ {
+ case USE:
+ case CLOBBER:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return;
+
+ case SET:
+ case PARALLEL:
+ case ASM_OPERANDS:
+ recog_n_operands = noperands = asm_noperands (body);
+ if (noperands >= 0)
+ {
+ /* This insn is an `asm' with operands. */
+
+ /* expand_asm_operands makes sure there aren't too many operands. */
+ if (noperands > MAX_RECOG_OPERANDS)
+ abort ();
+
+ /* Now get the operand values and constraints out of the insn. */
+ decode_asm_operands (body, recog_operand, recog_operand_loc,
+ recog_constraints, recog_operand_mode);
+ if (noperands > 0)
+ {
+ char *p = recog_constraints[0];
+ recog_n_alternatives = 1;
+ while (*p)
+ recog_n_alternatives += (*p++ == ',');
+ }
+#ifndef REGISTER_CONSTRAINTS
+ bzero (recog_operand_address_p, sizeof recog_operand_address_p);
+#endif
+ break;
+ }
+
+ /* FALLTHROUGH */
+
+ default:
+ /* Ordinary insn: recognize it, get the operands via insn_extract
+ and get the constraints. */
+
+ icode = recog_memoized (insn);
+ if (icode < 0)
+ fatal_insn_not_found (insn);
+
+ recog_n_operands = noperands = insn_n_operands[icode];
+ recog_n_alternatives = insn_n_alternatives[icode];
+ recog_n_dups = insn_n_dups[icode];
+
+ insn_extract (insn);
+
+ for (i = 0; i < noperands; i++)
+ {
#ifdef REGISTER_CONSTRAINTS
+ recog_constraints[i] = insn_operand_constraint[icode][i];
+#else
+ recog_operand_address_p[i] = insn_operand_address_p[icode][i];
+#endif
+ recog_operand_mode[i] = insn_operand_mode[icode][i];
+ }
+ }
+ for (i = 0; i < noperands; i++)
+ recog_op_type[i] = (recog_constraints[i][0] == '=' ? OP_OUT
+ : recog_constraints[i][0] == '+' ? OP_INOUT
+ : OP_IN);
+
+ if (recog_n_alternatives > MAX_RECOG_ALTERNATIVES)
+ abort ();
+}
+
+/* After calling extract_insn, you can use this function to extract some
+ information from the constraint strings into a more usable form.
+ The collected data is stored in recog_op_alt. */
+void
+preprocess_constraints ()
+{
+ int i;
+
+ for (i = 0; i < recog_n_operands; i++)
+ {
+ int j;
+ struct operand_alternative *op_alt;
+ char *p = recog_constraints[i];
+
+ op_alt = recog_op_alt[i];
+
+ for (j = 0; j < recog_n_alternatives; j++)
+ {
+ op_alt[j].class = NO_REGS;
+ op_alt[j].constraint = p;
+ op_alt[j].matches = -1;
+ op_alt[j].matched = -1;
+
+ if (*p == '\0' || *p == ',')
+ {
+ op_alt[j].anything_ok = 1;
+ continue;
+ }
+
+ for (;;)
+ {
+ char c = *p++;
+ if (c == '#')
+ do
+ c = *p++;
+ while (c != ',' && c != '\0');
+ if (c == ',' || c == '\0')
+ break;
-/* Check the operands of an insn (found in recog_operands)
- against the insn's operand constraints (found via INSN_CODE_NUM)
+ switch (c)
+ {
+ case '=': case '+': case '*': case '%':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+#ifdef EXTRA_CONSTRAINT
+ case 'Q': case 'R': case 'S': case 'T': case 'U':
+#endif
+ /* These don't say anything we care about. */
+ break;
+
+ case '?':
+ op_alt[j].reject += 6;
+ break;
+ case '!':
+ op_alt[j].reject += 600;
+ break;
+ case '&':
+ op_alt[j].earlyclobber = 1;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ op_alt[j].matches = c - '0';
+ op_alt[op_alt[j].matches].matched = i;
+ break;
+
+ case 'm':
+ op_alt[j].memory_ok = 1;
+ break;
+ case '<':
+ op_alt[j].decmem_ok = 1;
+ break;
+ case '>':
+ op_alt[j].incmem_ok = 1;
+ break;
+ case 'V':
+ op_alt[j].nonoffmem_ok = 1;
+ break;
+ case 'o':
+ op_alt[j].offmem_ok = 1;
+ break;
+ case 'X':
+ op_alt[j].anything_ok = 1;
+ break;
+
+ case 'p':
+ op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) BASE_REG_CLASS];
+ break;
+
+ case 'g': case 'r':
+ op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) GENERAL_REGS];
+ break;
+
+ default:
+ op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) REG_CLASS_FROM_LETTER (c)];
+ break;
+ }
+ }
+ }
+ }
+}
+
+#ifdef REGISTER_CONSTRAINTS
+
+/* Check the operands of an insn against the insn's operand constraints
and return 1 if they are valid.
+ The information about the insn's operands, constraints, operand modes
+ etc. is obtained from the global variables set up by extract_insn.
WHICH_ALTERNATIVE is set to a number which indicates which
alternative of constraints was matched: 0 for the first alternative,
};
int
-constrain_operands (insn_code_num, strict)
- int insn_code_num;
+constrain_operands (strict)
int strict;
{
char *constraints[MAX_RECOG_OPERANDS];
int matching_operands[MAX_RECOG_OPERANDS];
- enum op_type {OP_IN, OP_OUT, OP_INOUT} op_types[MAX_RECOG_OPERANDS];
int earlyclobber[MAX_RECOG_OPERANDS];
register int c;
- int noperands = insn_n_operands[insn_code_num];
struct funny_match funny_match[MAX_RECOG_OPERANDS];
int funny_match_index;
- int nalternatives = insn_n_alternatives[insn_code_num];
- if (noperands == 0 || nalternatives == 0)
+ if (recog_n_operands == 0 || recog_n_alternatives == 0)
return 1;
- for (c = 0; c < noperands; c++)
+ for (c = 0; c < recog_n_operands; c++)
{
- constraints[c] = insn_operand_constraint[insn_code_num][c];
+ constraints[c] = recog_constraints[c];
matching_operands[c] = -1;
- op_types[c] = OP_IN;
}
which_alternative = 0;
- while (which_alternative < nalternatives)
+ while (which_alternative < recog_n_alternatives)
{
register int opno;
int lose = 0;
funny_match_index = 0;
- for (opno = 0; opno < noperands; opno++)
+ for (opno = 0; opno < recog_n_operands; opno++)
{
register rtx op = recog_operand[opno];
enum machine_mode mode = GET_MODE (op);
case '!':
case '*':
case '%':
+ case '=':
+ case '+':
break;
case '#':
p++;
break;
- case '=':
- op_types[opno] = OP_OUT;
- break;
-
- case '+':
- op_types[opno] = OP_INOUT;
- break;
-
case '&':
earlyclobber[opno] = 1;
break;
strictly valid, i.e., that all pseudos requiring hard regs
have gotten them. */
if (strict <= 0
- || (strict_memory_address_p
- (insn_operand_mode[insn_code_num][opno], op)))
+ || (strict_memory_address_p (recog_operand_mode[opno],
+ op)))
win = 1;
break;
operand. */
if (strict > 0)
- for (eopno = 0; eopno < noperands; eopno++)
+ for (eopno = 0; eopno < recog_n_operands; eopno++)
/* Ignore earlyclobber operands now in memory,
because we would often report failure when we have
two memory operands, one of which was formerly a REG. */
if (earlyclobber[eopno]
&& GET_CODE (recog_operand[eopno]) == REG)
- for (opno = 0; opno < noperands; opno++)
+ for (opno = 0; opno < recog_n_operands; opno++)
if ((GET_CODE (recog_operand[opno]) == MEM
- || op_types[opno] != OP_OUT)
+ || recog_op_type[opno] != OP_OUT)
&& opno != eopno
/* Ignore things like match_operator operands. */
- && *insn_operand_constraint[insn_code_num][opno] != 0
+ && *recog_constraints[opno] != 0
&& ! (matching_operands[opno] == eopno
&& operands_match_p (recog_operand[opno],
recog_operand[eopno]))
/* If we are about to reject this, but we are not to test strictly,
try a very loose test. Only return failure if it fails also. */
if (strict == 0)
- return constrain_operands (insn_code_num, -1);
+ return constrain_operands (-1);
else
return 0;
}