/* Subroutines used by or related to instruction recognition.
- Copyright (C) 1987, 1988, 1991 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 91-97, 1998 Free Software Foundation, Inc.
This file is part of GNU CC.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
#include "config.h"
+#include "system.h"
#include "rtl.h"
-#include <stdio.h>
#include "insn-config.h"
#include "insn-attr.h"
#include "insn-flags.h"
/* Import from final.c: */
extern rtx alter_subreg ();
-int strict_memory_address_p ();
-int memory_address_p ();
+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 *));
/* Nonzero means allow operands to be volatile.
This should be 0 if you are generating rtl, such as if you are calling
/* Try recognizing the instruction INSN,
and return the code number that results.
- Remeber the code so that repeated calls do not
+ Remember the code so that repeated calls do not
need to spend the time for actual rerecognition.
This function is the normal interface to instruction recognition.
rtx insn;
{
if (INSN_CODE (insn) < 0)
- INSN_CODE (insn) = recog (PATTERN (insn), insn, 0);
+ INSN_CODE (insn) = recog (PATTERN (insn), insn, NULL_PTR);
return INSN_CODE (insn);
}
\f
return 1;
operands = (rtx *) alloca (noperands * sizeof (rtx));
- decode_asm_operands (x, operands, 0, 0, 0);
+ decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR);
for (i = 0; i < noperands; i++)
if (!general_operand (operands[i], VOIDmode))
{
int j;
- newpat = gen_rtx (PARALLEL, VOIDmode,
- gen_rtvec (XVECLEN (pat, 0) - 1));
+ newpat = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (XVECLEN (pat, 0) - 1));
for (j = 0; j < XVECLEN (newpat, 0); j++)
XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j);
}
if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0)))
{
validate_change (object, loc,
- gen_rtx (GET_RTX_CLASS (code) == 'c' ? code
- : swap_condition (code),
- GET_MODE (x), XEXP (x, 1), XEXP (x, 0)),
+ gen_rtx_fmt_ee (GET_RTX_CLASS (code) == 'c' ? code
+ : swap_condition (code),
+ GET_MODE (x), XEXP (x, 1),
+ XEXP (x, 0)),
1);
x = *loc;
code = GET_CODE (x);
}
}
+ /* Note that if CODE's RTX_CLASS is "c" or "<" we will have already
+ done the substitution, otherwise we won't. */
+
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 (XEXP (x, 1))), 1);
+ validate_change (object, loc, plus_constant (XEXP (x, 0), INTVAL (to)),
+ 1);
return;
+
+ case MINUS:
+ if (GET_CODE (to) == CONST_INT && XEXP (x, 1) == from)
+ {
+ validate_change (object, loc,
+ plus_constant (XEXP (x, 0), - INTVAL (to)),
+ 1);
+ return;
+ }
+ break;
case ZERO_EXTEND:
case SIGN_EXTEND:
of the operand. If we are replacing the operand with a VOIDmode
constant, we lose the information. So try to simplify the operation
in that case. If it fails, substitute in something that we know
- won't be recogized. */
+ won't be recognized. */
if (GET_MODE (to) == VOIDmode
&& (XEXP (x, 0) == from
|| (GET_CODE (XEXP (x, 0)) == REG && GET_CODE (from) == REG
rtx new = simplify_unary_operation (code, GET_MODE (x), to,
GET_MODE (from));
if (new == 0)
- new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+ new = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
validate_change (object, loc, new, 1);
return;
enum machine_mode mode = GET_MODE (x);
rtx new;
-#if BYTES_BIG_ENDIAN
- offset += (MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
-#endif
+ if (BYTES_BIG_ENDIAN)
+ offset += (MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
- new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset));
+ new = gen_rtx_MEM (mode, plus_constant (XEXP (to, 0), offset));
MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to);
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to);
MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (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
wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
#endif
- /* If we have a narrower mode, we can do someting. */
+ /* If we have a narrower mode, we can do something. */
if (wanted_mode != VOIDmode
&& GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
{
/* If the bytes and bits are counted differently, we
must adjust the offset. */
-#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
- offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
- - offset);
-#endif
+ if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
+ offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
+ - offset);
pos %= GET_MODE_BITSIZE (wanted_mode);
- newmem = gen_rtx (MEM, wanted_mode,
- plus_constant (XEXP (to, 0), offset));
+ newmem = gen_rtx_MEM (wanted_mode,
+ plus_constant (XEXP (to, 0), offset));
RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to);
MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to);
MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to);
- validate_change (object, &XEXP (x, 2),
- gen_rtx (CONST_INT, VOIDmode, pos), 1);
+ validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1);
validate_change (object, &XEXP (x, 0), newmem, 1);
}
}
break;
+
+ default:
+ break;
}
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ /* For commutative or comparison operations we've already performed
+ replacements. Don't try to perform them again. */
+ if (GET_RTX_CLASS (code) != '<' && GET_RTX_CLASS (code) != 'c')
{
- if (fmt[i] == 'e')
- validate_replace_rtx_1 (&XEXP (x, i), from, to, object);
- else if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object);
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ validate_replace_rtx_1 (&XEXP (x, i), from, to, object);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object);
+ }
}
}
case MEM:
case SUBREG:
return find_single_use_1 (dest, &XEXP (x, 0));
+
+ default:
+ break;
}
/* If it wasn't one of the common cases above, check each expression and
The main use of this function is as a predicate in match_operand
expressions in the machine description.
- For an explaination of this function's behavior for registers of
+ For an explanation of this function's behavior for registers of
class NO_REGS, see the comment for `register_operand'. */
int
/* Don't accept CONST_INT or anything similar
if the caller wants something floating. */
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
- && GET_MODE_CLASS (mode) != MODE_INT)
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
if (CONSTANT_P (op))
register rtx y = XEXP (op, 0);
if (! volatile_ok && MEM_VOLATILE_P (op))
return 0;
+ if (GET_CODE (y) == ADDRESSOF)
+ return 1;
/* Use the mem's mode, since it will be reloaded thus. */
mode = GET_MODE (op);
GO_IF_LEGITIMATE_ADDRESS (mode, y, win);
}
+
+ /* Pretend this is an operand for now; we'll run force_operand
+ on its replacement in fixup_var_refs_1. */
+ if (code == ADDRESSOF)
+ return 1;
+
return 0;
win:
reg went on the stack.) */
if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM)
return general_operand (op, mode);
+
+#ifdef CLASS_CANNOT_CHANGE_SIZE
+ if (GET_CODE (SUBREG_REG (op)) == REG
+ && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE],
+ REGNO (SUBREG_REG (op)))
+ && (GET_MODE_SIZE (mode)
+ != GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
+ && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_INT
+ && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_FLOAT)
+ return 0;
+#endif
+
op = SUBREG_REG (op);
}
/* Don't accept CONST_INT or anything similar
if the caller wants something floating. */
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
- && GET_MODE_CLASS (mode) != MODE_INT)
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
return (CONSTANT_P (op)
/* Don't accept CONST_INT or anything similar
if the caller wants something floating. */
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
- && GET_MODE_CLASS (mode) != MODE_INT)
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)
/* Don't accept CONST_INT or anything similar
if the caller wants something floating. */
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
- && GET_MODE_CLASS (mode) != MODE_INT)
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode)
enum machine_mode mode;
register rtx addr;
{
+ if (GET_CODE (addr) == ADDRESSOF)
+ return 1;
+
GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
return 0;
register int offset = SUBREG_WORD (op) * UNITS_PER_WORD;
rtx inner = SUBREG_REG (op);
-#if BYTES_BIG_ENDIAN
- offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
-#endif
+ if (BYTES_BIG_ENDIAN)
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
+
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
/* The only way that we can have a general_operand as the resulting
address is if OFFSET is zero and the address already is an operand
return template;
}
\f
-extern rtx plus_constant_for_output ();
-extern rtx copy_rtx ();
-
/* Given an rtx *P, if it is a sum containing an integer constant term,
return the location (type rtx *) of the pointer to that constant term.
Otherwise, return a null pointer. */
if (CONSTANT_ADDRESS_P (y))
{
- new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+ new = gen_rtx_MEM (GET_MODE (op), plus_constant_for_output (y, offset));
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
return new;
}
}
}
- new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+ new = gen_rtx_MEM (GET_MODE (op), plus_constant_for_output (y, offset));
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
return new;
}
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];
return 1;
for (c = 0; c < noperands; c++)
- constraints[c] = insn_operand_constraint[insn_code_num][c];
+ {
+ constraints[c] = insn_operand_constraint[insn_code_num][c];
+ matching_operands[c] = -1;
+ op_types[c] = OP_IN;
+ }
which_alternative = 0;
int win = 0;
int val;
+ earlyclobber[opno] = 0;
+
+ /* A unary operator may be accepted by the predicate, but it
+ is irrelevant for matching constraints. */
+ if (GET_RTX_CLASS (GET_CODE (op)) == '1')
+ op = XEXP (op, 0);
+
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) == REG
while (*p && (c = *p++) != ',')
switch (c)
{
- case '=':
- case '+':
case '?':
- case '#':
- case '&':
case '!':
case '*':
case '%':
break;
+ case '#':
+ /* Ignore rest of this alternative as far as
+ constraint checking is concerned. */
+ while (*p && *p != ',')
+ p++;
+ break;
+
+ case '=':
+ op_types[opno] = OP_OUT;
+ break;
+
+ case '+':
+ op_types[opno] = OP_INOUT;
+ break;
+
+ case '&':
+ earlyclobber[opno] = 1;
+ break;
+
case '0':
case '1':
case '2':
val = operands_match_p (recog_operand[c - '0'],
recog_operand[opno]);
+ matching_operands[opno] = c - '0';
+ matching_operands[c - '0'] = opno;
+
if (val != 0)
win = 1;
/* If output is *x and input is *--x,
case 'p':
/* p is used for address_operands. When we are called by
- gen_input_reload, no one will have checked that the
- address is strictly valid, i.e., that all pseudos
- requiring hard regs have gotten them. */
+ gen_reload, no one will have checked that the address is
+ 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)))
if (strict < 0
|| GENERAL_REGS == ALL_REGS
|| GET_CODE (op) != REG
+ || (reload_in_progress
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
|| reg_fits_class_p (op, GENERAL_REGS, offset, mode))
win = 1;
break;
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
|| (strict == 0 && GET_CODE (op) == SCRATCH)
|| (GET_CODE (op) == REG
- && (GENERAL_REGS == ALL_REGS
+ && ((GENERAL_REGS == ALL_REGS
+ && REGNO (op) < FIRST_PSEUDO_REGISTER)
|| reg_fits_class_p (op, GENERAL_REGS,
offset, mode))))
win = 1;
break;
case 'X':
- /* This is used for a MATCH_SCRATCH in the cases when we
- don't actually need anything. So anything goes any time. */
+ /* This is used for a MATCH_SCRATCH in the cases when
+ we don't actually need anything. So anything goes
+ any time. */
win = 1;
break;
case 'm':
if (GET_CODE (op) == MEM
/* Before reload, accept what reload can turn into mem. */
- || (strict < 0 && CONSTANT_P (op)))
+ || (strict < 0 && CONSTANT_P (op))
+ /* During reload, accept a pseudo */
+ || (reload_in_progress && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER))
win = 1;
break;
break;
case 'E':
+#ifndef REAL_ARITHMETIC
/* Match any CONST_DOUBLE, but only if
we can examine the bits of it reliably. */
if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
- || HOST_BITS_PER_INT != BITS_PER_WORD)
- && GET_CODE (op) != VOIDmode && ! flag_pretend_float)
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
break;
+#endif
if (GET_CODE (op) == CONST_DOUBLE)
win = 1;
break;
case 'V':
if (GET_CODE (op) == MEM
- && ! offsettable_memref_p (op))
+ && ((strict > 0 && ! offsettable_memref_p (op))
+ || (strict < 0
+ && !(CONSTANT_P (op) || GET_CODE (op) == MEM))
+ || (reload_in_progress
+ && !(GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER))))
win = 1;
break;
|| (strict == 0 && offsettable_nonstrict_memref_p (op))
/* Before reload, accept what reload can handle. */
|| (strict < 0
- && (CONSTANT_P (op) || GET_CODE (op) == MEM)))
+ && (CONSTANT_P (op) || GET_CODE (op) == MEM))
+ /* During reload, accept a pseudo */
+ || (reload_in_progress && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER))
win = 1;
break;
Change whichever operands this alternative says to change. */
if (! lose)
{
- while (--funny_match_index >= 0)
+ int opno, eopno;
+
+ /* See if any earlyclobber operand conflicts with some other
+ operand. */
+
+ if (strict > 0)
+ for (eopno = 0; eopno < noperands; 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++)
+ if ((GET_CODE (recog_operand[opno]) == MEM
+ || op_types[opno] != OP_OUT)
+ && opno != eopno
+ /* Ignore things like match_operator operands. */
+ && *insn_operand_constraint[insn_code_num][opno] != 0
+ && ! (matching_operands[opno] == eopno
+ && operands_match_p (recog_operand[opno],
+ recog_operand[eopno]))
+ && ! safe_from_earlyclobber (recog_operand[opno],
+ recog_operand[eopno]))
+ lose = 1;
+
+ if (! lose)
{
- recog_operand[funny_match[funny_match_index].other]
- = recog_operand[funny_match[funny_match_index].this];
+ while (--funny_match_index >= 0)
+ {
+ recog_operand[funny_match[funny_match_index].other]
+ = recog_operand[funny_match[funny_match_index].this];
+ }
+
+ return 1;
}
- return 1;
}
which_alternative++;
}
/* Return 1 iff OPERAND (assumed to be a REG rtx)
- is a hard reg in class CLASS when its regno is offsetted by OFFSET
+ is a hard reg in class CLASS when its regno is offset by OFFSET
and changed to mode MODE.
If REG occupies multiple hard regs, all of them must be in CLASS. */