#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tm_p.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "hard-reg-set.h"
#include "recog.h"
#include "regs.h"
#include "expr.h"
#include "function.h"
#include "flags.h"
#include "real.h"
#include "toplev.h"
#include "basic-block.h"
#include "output.h"
#include "reload.h"
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
#define STACK_PUSH_CODE PRE_DEC
#else
#define STACK_PUSH_CODE PRE_INC
#endif
#endif
#ifndef STACK_POP_CODE
#ifdef STACK_GROWS_DOWNWARD
#define STACK_POP_CODE POST_INC
#else
#define STACK_POP_CODE POST_DEC
#endif
#endif
static void validate_replace_rtx_1 PARAMS ((rtx *, rtx, rtx, rtx));
static rtx *find_single_use_1 PARAMS ((rtx, rtx *));
static void validate_replace_src_1 PARAMS ((rtx *, void *));
static rtx split_insn PARAMS ((rtx));
int volatile_ok;
struct recog_data recog_data;
struct operand_alternative recog_op_alt[MAX_RECOG_OPERANDS][MAX_RECOG_ALTERNATIVES];
int which_alternative;
int reload_completed;
void
init_recog_no_volatile ()
{
volatile_ok = 0;
}
void
init_recog ()
{
volatile_ok = 1;
}
int
recog_memoized_1 (insn)
rtx insn;
{
if (INSN_CODE (insn) < 0)
INSN_CODE (insn) = recog (PATTERN (insn), insn, 0);
return INSN_CODE (insn);
}
int
check_asm_operands (x)
rtx x;
{
int noperands;
rtx *operands;
const char **constraints;
int i;
if (reload_completed)
{
extract_insn (make_insn_raw (x));
constrain_operands (1);
return which_alternative >= 0;
}
noperands = asm_noperands (x);
if (noperands < 0)
return 0;
if (noperands == 0)
return 1;
operands = (rtx *) alloca (noperands * sizeof (rtx));
constraints = (const char **) alloca (noperands * sizeof (char *));
decode_asm_operands (x, operands, NULL, constraints, NULL);
for (i = 0; i < noperands; i++)
{
const char *c = constraints[i];
if (c[0] == '%')
c++;
if (ISDIGIT ((unsigned char) c[0]) && c[1] == '\0')
c = constraints[c[0] - '0'];
if (! asm_operand_ok (operands[i], c))
return 0;
}
return 1;
}
typedef struct change_t
{
rtx object;
int old_code;
rtx *loc;
rtx old;
} change_t;
static change_t *changes;
static int changes_allocated;
static int num_changes = 0;
int
validate_change (object, loc, new, in_group)
rtx object;
rtx *loc;
rtx new;
int in_group;
{
rtx old = *loc;
if (old == new || rtx_equal_p (old, new))
return 1;
if (in_group == 0 && num_changes != 0)
abort ();
*loc = new;
if (num_changes >= changes_allocated)
{
if (changes_allocated == 0)
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)
{
changes[num_changes].old_code = INSN_CODE (object);
INSN_CODE (object) = -1;
}
num_changes++;
if (in_group)
return 1;
else
return apply_change_group ();
}
int
insn_invalid_p (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
int num_clobbers = 0;
int icode = recog (pat, insn,
(GET_CODE (pat) == SET
&& ! reload_completed && ! reload_in_progress)
? &num_clobbers : 0);
int is_asm = icode < 0 && asm_noperands (PATTERN (insn)) >= 0;
if ((is_asm && ! check_asm_operands (PATTERN (insn)))
|| (!is_asm && icode < 0))
return 1;
if (num_clobbers > 0)
{
rtx newpat;
if (added_clobbers_hard_reg_p (icode))
return 1;
newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_clobbers + 1));
XVECEXP (newpat, 0, 0) = pat;
add_clobbers (newpat, icode);
PATTERN (insn) = pat = newpat;
}
if (reload_completed)
{
extract_insn (insn);
if (! constrain_operands (1))
return 1;
}
INSN_CODE (insn) = icode;
return 0;
}
int
num_changes_pending ()
{
return num_changes;
}
int
apply_change_group ()
{
int i;
rtx last_validated = NULL_RTX;
for (i = 0; i < num_changes; i++)
{
rtx object = changes[i].object;
if (object == 0 || object == last_validated)
continue;
if (GET_CODE (object) == MEM)
{
if (! memory_address_p (GET_MODE (object), XEXP (object, 0)))
break;
}
else if (insn_invalid_p (object))
{
rtx pat = PATTERN (object);
if (GET_CODE (pat) == PARALLEL
&& GET_CODE (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)) == CLOBBER
&& asm_noperands (PATTERN (object)) < 0)
{
rtx newpat;
if (XVECLEN (pat, 0) == 2)
newpat = XVECEXP (pat, 0, 0);
else
{
int j;
newpat
= gen_rtx_PARALLEL (VOIDmode,
rtvec_alloc (XVECLEN (pat, 0) - 1));
for (j = 0; j < XVECLEN (newpat, 0); j++)
XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j);
}
validate_change (object, &PATTERN (object), newpat, 1);
continue;
}
else if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
continue;
else
break;
}
last_validated = object;
}
if (i == num_changes)
{
basic_block bb;
for (i = 0; i < num_changes; i++)
if (changes[i].object
&& INSN_P (changes[i].object)
&& (bb = BLOCK_FOR_INSN (changes[i].object)))
bb->flags |= BB_DIRTY;
num_changes = 0;
return 1;
}
else
{
cancel_changes (0);
return 0;
}
}
int
num_validated_changes ()
{
return num_changes;
}
void
cancel_changes (num)
int num;
{
int i;
for (i = num_changes - 1; i >= num; 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;
}
static void
validate_replace_rtx_1 (loc, from, to, object)
rtx *loc;
rtx from, to, object;
{
int i, j;
const char *fmt;
rtx x = *loc;
enum rtx_code code;
enum machine_mode op0_mode = VOIDmode;
int prev_changes = num_changes;
rtx new;
if (!x)
return;
code = GET_CODE (x);
fmt = GET_RTX_FORMAT (code);
if (fmt[0] == 'e')
op0_mode = GET_MODE (XEXP (x, 0));
if (x == from
|| (GET_CODE (x) == REG && GET_CODE (from) == REG
&& GET_MODE (x) == GET_MODE (from)
&& REGNO (x) == REGNO (from))
|| (GET_CODE (x) == GET_CODE (from) && GET_MODE (x) == GET_MODE (from)
&& rtx_equal_p (x, from)))
{
validate_change (object, loc, to, 1);
return;
}
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);
}
if (num_changes == prev_changes)
return;
if (fmt[0] == 'e' && GET_MODE (XEXP (x, 0)) != VOIDmode)
op0_mode = GET_MODE (XEXP (x, 0));
if ((GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c')
&& swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
{
validate_change (object, loc,
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);
}
switch (code)
{
case PLUS:
if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to)
validate_change (object, loc,
simplify_gen_binary
(PLUS, GET_MODE (x), XEXP (x, 0), XEXP (x, 1)), 1);
break;
case MINUS:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
|| GET_CODE (XEXP (x, 1)) == CONST_DOUBLE)
validate_change (object, loc,
simplify_gen_binary
(PLUS, GET_MODE (x), XEXP (x, 0),
simplify_gen_unary (NEG,
GET_MODE (x), XEXP (x, 1),
GET_MODE (x))), 1);
break;
case ZERO_EXTEND:
case SIGN_EXTEND:
if (GET_MODE (XEXP (x, 0)) == VOIDmode)
{
new = simplify_gen_unary (code, GET_MODE (x), XEXP (x, 0),
op0_mode);
if (!new)
new = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
validate_change (object, loc, new, 1);
}
break;
case SUBREG:
new = simplify_subreg (GET_MODE (x), SUBREG_REG (x), op0_mode,
SUBREG_BYTE (x));
if (!new && GET_MODE (SUBREG_REG (x)) == VOIDmode)
new = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
if (new)
validate_change (object, loc, new, 1);
break;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
if (GET_CODE (XEXP (x, 0)) == MEM
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_CODE (XEXP (x, 2)) == CONST_INT
&& !mode_dependent_address_p (XEXP (XEXP (x, 0), 0))
&& !MEM_VOLATILE_P (XEXP (x, 0)))
{
enum machine_mode wanted_mode = VOIDmode;
enum machine_mode is_mode = GET_MODE (XEXP (x, 0));
int pos = INTVAL (XEXP (x, 2));
if (GET_CODE (x) == ZERO_EXTRACT)
{
enum machine_mode new_mode
= mode_for_extraction (EP_extzv, 1);
if (new_mode != MAX_MACHINE_MODE)
wanted_mode = new_mode;
}
else if (GET_CODE (x) == SIGN_EXTRACT)
{
enum machine_mode new_mode
= mode_for_extraction (EP_extv, 1);
if (new_mode != MAX_MACHINE_MODE)
wanted_mode = new_mode;
}
if (wanted_mode != VOIDmode
&& GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
{
int offset = pos / BITS_PER_UNIT;
rtx newmem;
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 = adjust_address_nv (XEXP (x, 0), wanted_mode, offset);
validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1);
validate_change (object, &XEXP (x, 0), newmem, 1);
}
}
break;
default:
break;
}
}
int
validate_replace_rtx_subexp (from, to, insn, loc)
rtx from, to, insn, *loc;
{
validate_replace_rtx_1 (loc, from, to, insn);
return apply_change_group ();
}
int
validate_replace_rtx (from, to, insn)
rtx from, to, insn;
{
validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
return apply_change_group ();
}
void
validate_replace_rtx_group (from, to, insn)
rtx from, to, insn;
{
validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
}
struct validate_replace_src_data
{
rtx from;
rtx to;
rtx insn;
};
static void
validate_replace_src_1 (x, data)
rtx *x;
void *data;
{
struct validate_replace_src_data *d
= (struct validate_replace_src_data *) data;
validate_replace_rtx_1 (x, d->from, d->to, d->insn);
}
void
validate_replace_src_group (from, to, insn)
rtx from, to, insn;
{
struct validate_replace_src_data d;
d.from = from;
d.to = to;
d.insn = insn;
note_uses (&PATTERN (insn), validate_replace_src_1, &d);
}
int
validate_replace_src (from, to, insn)
rtx from, to, insn;
{
validate_replace_src_group (from, to, insn);
return apply_change_group ();
}
#ifdef HAVE_cc0
int
next_insn_tests_no_inequality (insn)
rtx insn;
{
rtx next = next_cc0_user (insn);
if (next == 0)
return 0;
return ((GET_CODE (next) == JUMP_INSN
|| GET_CODE (next) == INSN
|| GET_CODE (next) == CALL_INSN)
&& ! inequality_comparisons_p (PATTERN (next)));
}
#if 0
int
next_insns_test_no_inequality (insn)
rtx insn;
{
rtx next = NEXT_INSN (insn);
for (; next != 0; next = NEXT_INSN (next))
{
if (GET_CODE (next) == CODE_LABEL
|| GET_CODE (next) == BARRIER)
return 1;
if (GET_CODE (next) == NOTE)
continue;
if (inequality_comparisons_p (PATTERN (next)))
return 0;
if (sets_cc0_p (PATTERN (next)) == 1)
return 1;
if (! reg_mentioned_p (cc0_rtx, PATTERN (next)))
return 1;
}
return 1;
}
#endif
#endif
static rtx *
find_single_use_1 (dest, loc)
rtx dest;
rtx *loc;
{
rtx x = *loc;
enum rtx_code code = GET_CODE (x);
rtx *result = 0;
rtx *this_result;
int i;
const char *fmt;
switch (code)
{
case CONST_INT:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
case CONST_VECTOR:
case CLOBBER:
return 0;
case SET:
if (GET_CODE (SET_DEST (x)) != CC0
&& GET_CODE (SET_DEST (x)) != PC
&& GET_CODE (SET_DEST (x)) != REG
&& ! (GET_CODE (SET_DEST (x)) == SUBREG
&& GET_CODE (SUBREG_REG (SET_DEST (x))) == REG
&& (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
== ((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))))
break;
return find_single_use_1 (dest, &SET_SRC (x));
case MEM:
case SUBREG:
return find_single_use_1 (dest, &XEXP (x, 0));
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (dest == XEXP (x, i)
|| (GET_CODE (dest) == REG && GET_CODE (XEXP (x, i)) == REG
&& REGNO (dest) == REGNO (XEXP (x, i))))
this_result = loc;
else
this_result = find_single_use_1 (dest, &XEXP (x, i));
if (result == 0)
result = this_result;
else if (this_result)
return 0;
}
else if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
{
if (XVECEXP (x, i, j) == dest
|| (GET_CODE (dest) == REG
&& GET_CODE (XVECEXP (x, i, j)) == REG
&& REGNO (XVECEXP (x, i, j)) == REGNO (dest)))
this_result = loc;
else
this_result = find_single_use_1 (dest, &XVECEXP (x, i, j));
if (result == 0)
result = this_result;
else if (this_result)
return 0;
}
}
}
return result;
}
rtx *
find_single_use (dest, insn, ploc)
rtx dest;
rtx insn;
rtx *ploc;
{
rtx next;
rtx *result;
rtx link;
#ifdef HAVE_cc0
if (dest == cc0_rtx)
{
next = NEXT_INSN (insn);
if (next == 0
|| (GET_CODE (next) != INSN && GET_CODE (next) != JUMP_INSN))
return 0;
result = find_single_use_1 (dest, &PATTERN (next));
if (result && ploc)
*ploc = next;
return result;
}
#endif
if (reload_completed || reload_in_progress || GET_CODE (dest) != REG)
return 0;
for (next = next_nonnote_insn (insn);
next != 0 && GET_CODE (next) != CODE_LABEL;
next = next_nonnote_insn (next))
if (INSN_P (next) && dead_or_set_p (next, dest))
{
for (link = LOG_LINKS (next); link; link = XEXP (link, 1))
if (XEXP (link, 0) == insn)
break;
if (link)
{
result = find_single_use_1 (dest, &PATTERN (next));
if (ploc)
*ploc = next;
return result;
}
}
return 0;
}
int
general_operand (op, mode)
rtx op;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (op);
if (mode == VOIDmode)
mode = GET_MODE (op);
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
&& GET_MODE_CLASS (mode) != MODE_INT
&& GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
if (GET_CODE (op) == CONST_INT
&& mode != VOIDmode
&& trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
return 0;
if (CONSTANT_P (op))
return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
|| mode == VOIDmode)
#ifdef LEGITIMATE_PIC_OPERAND_P
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
#endif
&& LEGITIMATE_CONSTANT_P (op));
if (GET_MODE (op) != mode)
return 0;
if (code == SUBREG)
{
rtx sub = SUBREG_REG (op);
#ifdef INSN_SCHEDULING
if (GET_CODE (sub) == MEM
&& GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (sub)))
return 0;
#endif
if (!reload_completed && SUBREG_BYTE (op) != 0
&& GET_CODE (sub) == MEM)
return 0;
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
return 0;
op = sub;
code = GET_CODE (op);
}
if (code == REG)
return (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS);
if (code == MEM)
{
rtx y = XEXP (op, 0);
if (! volatile_ok && MEM_VOLATILE_P (op))
return 0;
if (GET_CODE (y) == ADDRESSOF)
return 1;
mode = GET_MODE (op);
GO_IF_LEGITIMATE_ADDRESS (mode, y, win);
}
if (code == ADDRESSOF)
return 1;
return 0;
win:
return 1;
}
int
address_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return memory_address_p (mode, op);
}
int
register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
{
rtx sub = SUBREG_REG (op);
if (! reload_completed && GET_CODE (sub) == MEM)
return general_operand (op, mode);
#ifdef CANNOT_CHANGE_MODE_CLASS
if (GET_CODE (sub) == REG
&& REGNO (sub) < FIRST_PSEUDO_REGISTER
&& REG_CANNOT_CHANGE_MODE_P (REGNO (sub), GET_MODE (sub), mode)
&& GET_MODE_CLASS (GET_MODE (sub)) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (GET_MODE (sub)) != MODE_COMPLEX_FLOAT)
return 0;
#endif
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
return 0;
op = sub;
}
if (GET_CODE (op) == ADDRESSOF)
return 1;
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
int
pmode_register_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return register_operand (op, Pmode);
}
int
scratch_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
return (GET_CODE (op) == SCRATCH
|| (GET_CODE (op) == REG
&& REGNO (op) < FIRST_PSEUDO_REGISTER));
}
int
immediate_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
&& GET_MODE_CLASS (mode) != MODE_INT
&& GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
if (GET_CODE (op) == CONST_INT
&& mode != VOIDmode
&& trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
return 0;
if (GET_CODE (op) == CONSTANT_P_RTX)
return 1;
return (CONSTANT_P (op)
&& (GET_MODE (op) == mode || mode == VOIDmode
|| GET_MODE (op) == VOIDmode)
#ifdef LEGITIMATE_PIC_OPERAND_P
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
#endif
&& LEGITIMATE_CONSTANT_P (op));
}
int
const_int_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != CONST_INT)
return 0;
if (mode != VOIDmode
&& trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
return 0;
return 1;
}
int
const_double_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
&& 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)
&& (mode == VOIDmode || GET_MODE (op) == mode
|| GET_MODE (op) == VOIDmode));
}
int
nonimmediate_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (general_operand (op, mode) && ! CONSTANT_P (op));
}
int
nonmemory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (CONSTANT_P (op))
{
if (GET_MODE (op) == VOIDmode && mode != VOIDmode
&& GET_MODE_CLASS (mode) != MODE_INT
&& GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
return 0;
if (GET_CODE (op) == CONST_INT
&& mode != VOIDmode
&& trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
return 0;
return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
|| mode == VOIDmode)
#ifdef LEGITIMATE_PIC_OPERAND_P
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
#endif
&& LEGITIMATE_CONSTANT_P (op));
}
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (op) == SUBREG)
{
if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM)
return general_operand (op, mode);
op = SUBREG_REG (op);
}
return (GET_CODE (op) == REG
&& (REGNO (op) >= FIRST_PSEUDO_REGISTER
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
int
push_operand (op, mode)
rtx op;
enum machine_mode mode;
{
unsigned int rounded_size = GET_MODE_SIZE (mode);
#ifdef PUSH_ROUNDING
rounded_size = PUSH_ROUNDING (rounded_size);
#endif
if (GET_CODE (op) != MEM)
return 0;
if (mode != VOIDmode && GET_MODE (op) != mode)
return 0;
op = XEXP (op, 0);
if (rounded_size == GET_MODE_SIZE (mode))
{
if (GET_CODE (op) != STACK_PUSH_CODE)
return 0;
}
else
{
if (GET_CODE (op) != PRE_MODIFY
|| GET_CODE (XEXP (op, 1)) != PLUS
|| XEXP (XEXP (op, 1), 0) != XEXP (op, 0)
|| GET_CODE (XEXP (XEXP (op, 1), 1)) != CONST_INT
#ifdef STACK_GROWS_DOWNWARD
|| INTVAL (XEXP (XEXP (op, 1), 1)) != - (int) rounded_size
#else
|| INTVAL (XEXP (XEXP (op, 1), 1)) != rounded_size
#endif
)
return 0;
}
return XEXP (op, 0) == stack_pointer_rtx;
}
int
pop_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
return 0;
if (mode != VOIDmode && GET_MODE (op) != mode)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != STACK_POP_CODE)
return 0;
return XEXP (op, 0) == stack_pointer_rtx;
}
int
memory_address_p (mode, addr)
enum machine_mode mode ATTRIBUTE_UNUSED;
rtx addr;
{
if (GET_CODE (addr) == ADDRESSOF)
return 1;
GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
return 0;
win:
return 1;
}
int
memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
rtx inner;
if (! reload_completed)
return GET_CODE (op) == MEM && general_operand (op, mode);
if (mode != VOIDmode && GET_MODE (op) != mode)
return 0;
inner = op;
if (GET_CODE (inner) == SUBREG)
inner = SUBREG_REG (inner);
return (GET_CODE (inner) == MEM && general_operand (op, mode));
}
int
indirect_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (! reload_completed
&& GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == MEM)
{
int offset = SUBREG_BYTE (op);
rtx inner = SUBREG_REG (op);
if (mode != VOIDmode && GET_MODE (op) != mode)
return 0;
return ((offset == 0 && general_operand (XEXP (inner, 0), Pmode))
|| (GET_CODE (XEXP (inner, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (inner, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (inner, 0), 1)) == -offset
&& general_operand (XEXP (XEXP (inner, 0), 0), Pmode)));
}
return (GET_CODE (op) == MEM
&& memory_operand (op, mode)
&& general_operand (XEXP (op, 0), Pmode));
}
int
comparison_operator (op, mode)
rtx op;
enum machine_mode mode;
{
return ((mode == VOIDmode || GET_MODE (op) == mode)
&& GET_RTX_CLASS (GET_CODE (op)) == '<');
}
int
asm_noperands (body)
rtx body;
{
switch (GET_CODE (body))
{
case ASM_OPERANDS:
return ASM_OPERANDS_INPUT_LENGTH (body);
case SET:
if (GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1;
else
return -1;
case PARALLEL:
if (GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
{
int i;
int n_sets;
for (i = XVECLEN (body, 0); i > 0; i--)
{
if (GET_CODE (XVECEXP (body, 0, i - 1)) == SET)
break;
if (GET_CODE (XVECEXP (body, 0, i - 1)) != CLOBBER)
return -1;
}
n_sets = i;
for (i = 0; i < n_sets; i++)
{
rtx elt = XVECEXP (body, 0, i);
if (GET_CODE (elt) != SET)
return -1;
if (GET_CODE (SET_SRC (elt)) != ASM_OPERANDS)
return -1;
if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt))
!= ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0))))
return -1;
}
return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)))
+ n_sets);
}
else if (GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
{
int i;
for (i = XVECLEN (body, 0) - 1; i > 0; i--)
if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
return -1;
return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
}
else
return -1;
default:
return -1;
}
}
const char *
decode_asm_operands (body, operands, operand_locs, constraints, modes)
rtx body;
rtx *operands;
rtx **operand_locs;
const char **constraints;
enum machine_mode *modes;
{
int i;
int noperands;
const char *template = 0;
if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
{
rtx asmop = SET_SRC (body);
noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
for (i = 1; i < noperands; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1);
}
if (operands)
operands[0] = SET_DEST (body);
if (operand_locs)
operand_locs[0] = &SET_DEST (body);
if (constraints)
constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop);
if (modes)
modes[0] = GET_MODE (SET_DEST (body));
template = ASM_OPERANDS_TEMPLATE (asmop);
}
else if (GET_CODE (body) == ASM_OPERANDS)
{
rtx asmop = body;
noperands = ASM_OPERANDS_INPUT_LENGTH (asmop);
for (i = 0; i < noperands; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
template = ASM_OPERANDS_TEMPLATE (asmop);
}
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
{
rtx asmop = SET_SRC (XVECEXP (body, 0, 0));
int nparallel = XVECLEN (body, 0);
int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
int nout = 0;
for (i = 0; i < nparallel; i++)
{
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
break;
if (operands)
operands[i] = SET_DEST (XVECEXP (body, 0, i));
if (operand_locs)
operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
if (constraints)
constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
if (modes)
modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
nout++;
}
for (i = 0; i < nin; i++)
{
if (operand_locs)
operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
template = ASM_OPERANDS_TEMPLATE (asmop);
}
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
{
rtx asmop = XVECEXP (body, 0, 0);
int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
for (i = 0; i < nin; i++)
{
if (operand_locs)
operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
if (operands)
operands[i] = ASM_OPERANDS_INPUT (asmop, i);
if (constraints)
constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
if (modes)
modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
}
template = ASM_OPERANDS_TEMPLATE (asmop);
}
return template;
}
int
asm_operand_ok (op, constraint)
rtx op;
const char *constraint;
{
int result = 0;
if (reload_completed)
abort ();
while (*constraint)
{
char c = *constraint++;
switch (c)
{
case '=':
case '+':
case '*':
case '%':
case '?':
case '!':
case '#':
case '&':
case ',':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
while (ISDIGIT (*constraint))
constraint++;
result = -1;
break;
case 'p':
if (address_operand (op, VOIDmode))
return 1;
break;
case 'm':
case 'V':
if (memory_operand (op, VOIDmode))
return 1;
break;
case 'o':
if (offsettable_nonstrict_memref_p (op))
return 1;
break;
case '<':
if (GET_CODE (op) == MEM
&& (1
|| GET_CODE (XEXP (op, 0)) == PRE_DEC
|| GET_CODE (XEXP (op, 0)) == POST_DEC))
return 1;
break;
case '>':
if (GET_CODE (op) == MEM
&& (1
|| GET_CODE (XEXP (op, 0)) == PRE_INC
|| GET_CODE (XEXP (op, 0)) == POST_INC))
return 1;
break;
case 'E':
case 'F':
if (GET_CODE (op) == CONST_DOUBLE
|| (GET_CODE (op) == CONST_VECTOR
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_FLOAT))
return 1;
break;
case 'G':
if (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_OK_FOR_LETTER_P (op, 'G'))
return 1;
break;
case 'H':
if (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_OK_FOR_LETTER_P (op, 'H'))
return 1;
break;
case 's':
if (GET_CODE (op) == CONST_INT
|| (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode))
break;
case 'i':
if (CONSTANT_P (op)
#ifdef LEGITIMATE_PIC_OPERAND_P
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
#endif
)
return 1;
break;
case 'n':
if (GET_CODE (op) == CONST_INT
|| (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode))
return 1;
break;
case 'I':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'I'))
return 1;
break;
case 'J':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'J'))
return 1;
break;
case 'K':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'K'))
return 1;
break;
case 'L':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'L'))
return 1;
break;
case 'M':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'M'))
return 1;
break;
case 'N':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'N'))
return 1;
break;
case 'O':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'O'))
return 1;
break;
case 'P':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), 'P'))
return 1;
break;
case 'X':
return 1;
case 'g':
if (general_operand (op, VOIDmode))
return 1;
break;
default:
if (REG_CLASS_FROM_LETTER (c) != NO_REGS)
{
case 'r':
if (GET_MODE (op) == BLKmode)
break;
if (register_operand (op, VOIDmode))
return 1;
}
#ifdef EXTRA_CONSTRAINT
if (EXTRA_CONSTRAINT (op, c))
return 1;
if (EXTRA_MEMORY_CONSTRAINT (c))
{
if (memory_operand (op, VOIDmode))
return 1;
}
if (EXTRA_ADDRESS_CONSTRAINT (c))
{
if (address_operand (op, VOIDmode))
return 1;
}
#endif
break;
}
}
return result;
}
rtx *
find_constant_term_loc (p)
rtx *p;
{
rtx *tem;
enum rtx_code code = GET_CODE (*p);
if (code == CONST_INT || code == SYMBOL_REF || code == LABEL_REF
|| code == CONST)
return p;
if (GET_CODE (*p) != PLUS)
return 0;
if (XEXP (*p, 0) && CONSTANT_P (XEXP (*p, 0))
&& XEXP (*p, 1) && CONSTANT_P (XEXP (*p, 1)))
return p;
if (XEXP (*p, 0) != 0)
{
tem = find_constant_term_loc (&XEXP (*p, 0));
if (tem != 0)
return tem;
}
if (XEXP (*p, 1) != 0)
{
tem = find_constant_term_loc (&XEXP (*p, 1));
if (tem != 0)
return tem;
}
return 0;
}
int
offsettable_memref_p (op)
rtx op;
{
return ((GET_CODE (op) == MEM)
&& offsettable_address_p (1, GET_MODE (op), XEXP (op, 0)));
}
int
offsettable_nonstrict_memref_p (op)
rtx op;
{
return ((GET_CODE (op) == MEM)
&& offsettable_address_p (0, GET_MODE (op), XEXP (op, 0)));
}
int
offsettable_address_p (strictp, mode, y)
int strictp;
enum machine_mode mode;
rtx y;
{
enum rtx_code ycode = GET_CODE (y);
rtx z;
rtx y1 = y;
rtx *y2;
int (*addressp) PARAMS ((enum machine_mode, rtx)) =
(strictp ? strict_memory_address_p : memory_address_p);
unsigned int mode_sz = GET_MODE_SIZE (mode);
if (CONSTANT_ADDRESS_P (y))
return 1;
if (mode_dependent_address_p (y))
return 0;
if (mode_sz == 0)
mode_sz = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
if ((ycode == PLUS) && (y2 = find_constant_term_loc (&y1)))
{
int good;
y1 = *y2;
*y2 = plus_constant (*y2, mode_sz - 1);
good = (*addressp) (QImode, y);
*y2 = y1;
return good;
}
if (GET_RTX_CLASS (ycode) == 'a')
return 0;
if (GET_CODE (y) == LO_SUM
&& mode != BLKmode
&& mode_sz <= GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT)
z = gen_rtx_LO_SUM (GET_MODE (y), XEXP (y, 0),
plus_constant (XEXP (y, 1), mode_sz - 1));
else
z = plus_constant (y, mode_sz - 1);
return (*addressp) (QImode, z);
}
int
mode_dependent_address_p (addr)
rtx addr ATTRIBUTE_UNUSED;
{
GO_IF_MODE_DEPENDENT_ADDRESS (addr, win);
return 0;
win: ATTRIBUTE_UNUSED_LABEL
return 1;
}
int
mode_independent_operand (op, mode)
enum machine_mode mode;
rtx op;
{
rtx addr;
if (! general_operand (op, mode))
return 0;
if (GET_CODE (op) != MEM)
return 1;
addr = XEXP (op, 0);
GO_IF_MODE_DEPENDENT_ADDRESS (addr, lose);
return 1;
lose: ATTRIBUTE_UNUSED_LABEL
return 0;
}
void
extract_insn_cached (insn)
rtx insn;
{
if (recog_data.insn == insn && INSN_CODE (insn) >= 0)
return;
extract_insn (insn);
recog_data.insn = insn;
}
void
extract_constrain_insn_cached (insn)
rtx insn;
{
extract_insn_cached (insn);
if (which_alternative == -1
&& !constrain_operands (reload_completed))
fatal_insn_not_found (insn);
}
int
constrain_operands_cached (strict)
int strict;
{
if (which_alternative == -1)
return constrain_operands (strict);
else
return 1;
}
void
extract_insn (insn)
rtx insn;
{
int i;
int icode;
int noperands;
rtx body = PATTERN (insn);
recog_data.insn = NULL;
recog_data.n_operands = 0;
recog_data.n_alternatives = 0;
recog_data.n_dups = 0;
which_alternative = -1;
switch (GET_CODE (body))
{
case USE:
case CLOBBER:
case ASM_INPUT:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return;
case SET:
if (GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
goto asm_insn;
else
goto normal_insn;
case PARALLEL:
if ((GET_CODE (XVECEXP (body, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
|| GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
goto asm_insn;
else
goto normal_insn;
case ASM_OPERANDS:
asm_insn:
recog_data.n_operands = noperands = asm_noperands (body);
if (noperands >= 0)
{
if (noperands > MAX_RECOG_OPERANDS)
abort ();
decode_asm_operands (body, recog_data.operand,
recog_data.operand_loc,
recog_data.constraints,
recog_data.operand_mode);
if (noperands > 0)
{
const char *p = recog_data.constraints[0];
recog_data.n_alternatives = 1;
while (*p)
recog_data.n_alternatives += (*p++ == ',');
}
break;
}
fatal_insn_not_found (insn);
default:
normal_insn:
icode = recog_memoized (insn);
if (icode < 0)
fatal_insn_not_found (insn);
recog_data.n_operands = noperands = insn_data[icode].n_operands;
recog_data.n_alternatives = insn_data[icode].n_alternatives;
recog_data.n_dups = insn_data[icode].n_dups;
insn_extract (insn);
for (i = 0; i < noperands; i++)
{
recog_data.constraints[i] = insn_data[icode].operand[i].constraint;
recog_data.operand_mode[i] = insn_data[icode].operand[i].mode;
if (recog_data.operand_mode[i] == VOIDmode)
recog_data.operand_mode[i] = GET_MODE (recog_data.operand[i]);
}
}
for (i = 0; i < noperands; i++)
recog_data.operand_type[i]
= (recog_data.constraints[i][0] == '=' ? OP_OUT
: recog_data.constraints[i][0] == '+' ? OP_INOUT
: OP_IN);
if (recog_data.n_alternatives > MAX_RECOG_ALTERNATIVES)
abort ();
}
void
preprocess_constraints ()
{
int i;
memset (recog_op_alt, 0, sizeof recog_op_alt);
for (i = 0; i < recog_data.n_operands; i++)
{
int j;
struct operand_alternative *op_alt;
const char *p = recog_data.constraints[i];
op_alt = recog_op_alt[i];
for (j = 0; j < recog_data.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;
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':
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':
{
char *end;
op_alt[j].matches = strtoul (p - 1, &end, 10);
recog_op_alt[op_alt[j].matches][j].matched = i;
p = end;
}
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].is_address = 1;
op_alt[j].class = reg_class_subunion[(int) op_alt[j].class]
[(int) MODE_BASE_REG_CLASS (VOIDmode)];
break;
case 'g': case 'r':
op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) GENERAL_REGS];
break;
default:
if (EXTRA_MEMORY_CONSTRAINT (c))
{
op_alt[j].memory_ok = 1;
break;
}
if (EXTRA_ADDRESS_CONSTRAINT (c))
{
op_alt[j].is_address = 1;
op_alt[j].class = reg_class_subunion[(int) op_alt[j].class]
[(int) MODE_BASE_REG_CLASS (VOIDmode)];
break;
}
op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) REG_CLASS_FROM_LETTER ((unsigned char) c)];
break;
}
}
}
}
}
struct funny_match
{
int this, other;
};
int
constrain_operands (strict)
int strict;
{
const char *constraints[MAX_RECOG_OPERANDS];
int matching_operands[MAX_RECOG_OPERANDS];
int earlyclobber[MAX_RECOG_OPERANDS];
int c;
struct funny_match funny_match[MAX_RECOG_OPERANDS];
int funny_match_index;
which_alternative = 0;
if (recog_data.n_operands == 0 || recog_data.n_alternatives == 0)
return 1;
for (c = 0; c < recog_data.n_operands; c++)
{
constraints[c] = recog_data.constraints[c];
matching_operands[c] = -1;
}
do
{
int opno;
int lose = 0;
funny_match_index = 0;
for (opno = 0; opno < recog_data.n_operands; opno++)
{
rtx op = recog_data.operand[opno];
enum machine_mode mode = GET_MODE (op);
const char *p = constraints[opno];
int offset = 0;
int win = 0;
int val;
earlyclobber[opno] = 0;
if (GET_RTX_CLASS (GET_CODE (op)) == '1')
op = XEXP (op, 0);
if (GET_CODE (op) == SUBREG)
{
if (GET_CODE (SUBREG_REG (op)) == REG
&& REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
offset = subreg_regno_offset (REGNO (SUBREG_REG (op)),
GET_MODE (SUBREG_REG (op)),
SUBREG_BYTE (op),
GET_MODE (op));
op = SUBREG_REG (op);
}
if (*p == 0 || *p == ',')
win = 1;
while (*p && (c = *p++) != ',')
switch (c)
{
case '?': case '!': case '*': case '%':
case '=': case '+':
break;
case '#':
while (*p && *p != ',')
p++;
break;
case '&':
earlyclobber[opno] = 1;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
char *end;
int match;
match = strtoul (p - 1, &end, 10);
p = end;
if (strict < 0)
val = 1;
else
{
rtx op1 = recog_data.operand[match];
rtx op2 = recog_data.operand[opno];
if (GET_RTX_CLASS (GET_CODE (op1)) == '1')
op1 = XEXP (op1, 0);
if (GET_RTX_CLASS (GET_CODE (op2)) == '1')
op2 = XEXP (op2, 0);
val = operands_match_p (op1, op2);
}
matching_operands[opno] = match;
matching_operands[match] = opno;
if (val != 0)
win = 1;
if (val == 2 && strict > 0)
{
funny_match[funny_match_index].this = opno;
funny_match[funny_match_index++].other = match;
}
}
break;
case 'p':
if (strict <= 0
|| (strict_memory_address_p (recog_data.operand_mode[opno],
op)))
win = 1;
break;
case 'g':
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;
case 'X':
win = 1;
break;
case 'm':
if (GET_CODE (op) == MEM
|| (strict < 0 && CONSTANT_P (op))
|| (reload_in_progress && GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER))
win = 1;
break;
case '<':
if (GET_CODE (op) == MEM
&& (GET_CODE (XEXP (op, 0)) == PRE_DEC
|| GET_CODE (XEXP (op, 0)) == POST_DEC))
win = 1;
break;
case '>':
if (GET_CODE (op) == MEM
&& (GET_CODE (XEXP (op, 0)) == PRE_INC
|| GET_CODE (XEXP (op, 0)) == POST_INC))
win = 1;
break;
case 'E':
case 'F':
if (GET_CODE (op) == CONST_DOUBLE
|| (GET_CODE (op) == CONST_VECTOR
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_FLOAT))
win = 1;
break;
case 'G':
case 'H':
if (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
win = 1;
break;
case 's':
if (GET_CODE (op) == CONST_INT
|| (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode))
break;
case 'i':
if (CONSTANT_P (op))
win = 1;
break;
case 'n':
if (GET_CODE (op) == CONST_INT
|| (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == VOIDmode))
win = 1;
break;
#ifdef EXTRA_CONSTANT_CONSTRAINTS
case 'A':
case 'B':
case 'C':
case 'D':
#endif
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
if (GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (op), c))
win = 1;
break;
case 'V':
if (GET_CODE (op) == MEM
&& ((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;
case 'o':
if ((strict > 0 && offsettable_memref_p (op))
|| (strict == 0 && offsettable_nonstrict_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;
default:
{
enum reg_class class;
class = (c == 'r' ? GENERAL_REGS : REG_CLASS_FROM_LETTER (c));
if (class != NO_REGS)
{
if (strict < 0
|| (strict == 0
&& GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
|| (strict == 0 && GET_CODE (op) == SCRATCH)
|| (GET_CODE (op) == REG
&& reg_fits_class_p (op, class, offset, mode)))
win = 1;
}
#ifdef EXTRA_CONSTRAINT
else if (EXTRA_CONSTRAINT (op, c))
win = 1;
if (EXTRA_MEMORY_CONSTRAINT (c))
{
if (strict < 0 && GET_CODE (op) == MEM)
win = 1;
if (strict < 0 && CONSTANT_P (op))
win = 1;
if (reload_in_progress && GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)
win = 1;
}
if (EXTRA_ADDRESS_CONSTRAINT (c))
{
if (strict < 0)
win = 1;
}
#endif
break;
}
}
constraints[opno] = p;
if (! win)
lose = 1;
}
if (! lose)
{
int opno, eopno;
if (strict > 0)
for (eopno = 0; eopno < recog_data.n_operands; eopno++)
if (earlyclobber[eopno]
&& GET_CODE (recog_data.operand[eopno]) == REG)
for (opno = 0; opno < recog_data.n_operands; opno++)
if ((GET_CODE (recog_data.operand[opno]) == MEM
|| recog_data.operand_type[opno] != OP_OUT)
&& opno != eopno
&& *recog_data.constraints[opno] != 0
&& ! (matching_operands[opno] == eopno
&& operands_match_p (recog_data.operand[opno],
recog_data.operand[eopno]))
&& ! safe_from_earlyclobber (recog_data.operand[opno],
recog_data.operand[eopno]))
lose = 1;
if (! lose)
{
while (--funny_match_index >= 0)
{
recog_data.operand[funny_match[funny_match_index].other]
= recog_data.operand[funny_match[funny_match_index].this];
}
return 1;
}
}
which_alternative++;
}
while (which_alternative < recog_data.n_alternatives);
which_alternative = -1;
if (strict == 0)
return constrain_operands (-1);
else
return 0;
}
int
reg_fits_class_p (operand, class, offset, mode)
rtx operand;
enum reg_class class;
int offset;
enum machine_mode mode;
{
int regno = REGNO (operand);
if (regno < FIRST_PSEUDO_REGISTER
&& TEST_HARD_REG_BIT (reg_class_contents[(int) class],
regno + offset))
{
int sr;
regno += offset;
for (sr = HARD_REGNO_NREGS (regno, mode) - 1;
sr > 0; sr--)
if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class],
regno + sr))
break;
return sr == 0;
}
return 0;
}
static rtx
split_insn (insn)
rtx insn;
{
rtx set;
if (!INSN_P (insn))
;
else if ((set = single_set (insn)) != NULL && set_noop_p (set))
{
if (reload_completed)
delete_insn_and_edges (insn);
}
else
{
rtx first = PREV_INSN (insn);
rtx last = try_split (PATTERN (insn), insn, 1);
if (last != insn)
{
PUT_CODE (insn, NOTE);
NOTE_SOURCE_FILE (insn) = 0;
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
if (reload_completed && first != last)
{
first = NEXT_INSN (first);
while (1)
{
if (INSN_P (first))
cleanup_subreg_operands (first);
if (first == last)
break;
first = NEXT_INSN (first);
}
}
return last;
}
}
return NULL_RTX;
}
void
split_all_insns (upd_life)
int upd_life;
{
sbitmap blocks;
int changed;
basic_block bb;
blocks = sbitmap_alloc (last_basic_block);
sbitmap_zero (blocks);
changed = 0;
FOR_EACH_BB_REVERSE (bb)
{
rtx insn, next;
bool finish = false;
for (insn = bb->head; !finish ; insn = next)
{
rtx last;
next = NEXT_INSN (insn);
finish = (insn == bb->end);
last = split_insn (insn);
if (last)
{
while (GET_CODE (last) == BARRIER)
last = PREV_INSN (last);
SET_BIT (blocks, bb->index);
changed = 1;
insn = last;
}
}
}
if (changed)
{
find_many_sub_basic_blocks (blocks);
}
if (changed && upd_life)
{
count_or_remove_death_notes (blocks, 1);
update_life_info (blocks, UPDATE_LIFE_LOCAL, PROP_DEATH_NOTES);
}
#ifdef ENABLE_CHECKING
verify_flow_info ();
#endif
sbitmap_free (blocks);
}
void
split_all_insns_noflow ()
{
rtx next, insn;
for (insn = get_insns (); insn; insn = next)
{
next = NEXT_INSN (insn);
split_insn (insn);
}
return;
}
#ifdef HAVE_peephole2
struct peep2_insn_data
{
rtx insn;
regset live_before;
};
static struct peep2_insn_data peep2_insn_data[MAX_INSNS_PER_PEEP2 + 1];
static int peep2_current;
#define PEEP2_EOB pc_rtx
rtx
peep2_next_insn (n)
int n;
{
if (n >= MAX_INSNS_PER_PEEP2 + 1)
abort ();
n += peep2_current;
if (n >= MAX_INSNS_PER_PEEP2 + 1)
n -= MAX_INSNS_PER_PEEP2 + 1;
if (peep2_insn_data[n].insn == PEEP2_EOB)
return NULL_RTX;
return peep2_insn_data[n].insn;
}
int
peep2_regno_dead_p (ofs, regno)
int ofs;
int regno;
{
if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
abort ();
ofs += peep2_current;
if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
ofs -= MAX_INSNS_PER_PEEP2 + 1;
if (peep2_insn_data[ofs].insn == NULL_RTX)
abort ();
return ! REGNO_REG_SET_P (peep2_insn_data[ofs].live_before, regno);
}
int
peep2_reg_dead_p (ofs, reg)
int ofs;
rtx reg;
{
int regno, n;
if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
abort ();
ofs += peep2_current;
if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
ofs -= MAX_INSNS_PER_PEEP2 + 1;
if (peep2_insn_data[ofs].insn == NULL_RTX)
abort ();
regno = REGNO (reg);
n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--n >= 0)
if (REGNO_REG_SET_P (peep2_insn_data[ofs].live_before, regno + n))
return 0;
return 1;
}
rtx
peep2_find_free_register (from, to, class_str, mode, reg_set)
int from, to;
const char *class_str;
enum machine_mode mode;
HARD_REG_SET *reg_set;
{
static int search_ofs;
enum reg_class class;
HARD_REG_SET live;
int i;
if (from >= MAX_INSNS_PER_PEEP2 + 1 || to >= MAX_INSNS_PER_PEEP2 + 1)
abort ();
from += peep2_current;
if (from >= MAX_INSNS_PER_PEEP2 + 1)
from -= MAX_INSNS_PER_PEEP2 + 1;
to += peep2_current;
if (to >= MAX_INSNS_PER_PEEP2 + 1)
to -= MAX_INSNS_PER_PEEP2 + 1;
if (peep2_insn_data[from].insn == NULL_RTX)
abort ();
REG_SET_TO_HARD_REG_SET (live, peep2_insn_data[from].live_before);
while (from != to)
{
HARD_REG_SET this_live;
if (++from >= MAX_INSNS_PER_PEEP2 + 1)
from = 0;
if (peep2_insn_data[from].insn == NULL_RTX)
abort ();
REG_SET_TO_HARD_REG_SET (this_live, peep2_insn_data[from].live_before);
IOR_HARD_REG_SET (live, this_live);
}
class = (class_str[0] == 'r' ? GENERAL_REGS
: REG_CLASS_FROM_LETTER (class_str[0]));
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
int raw_regno, regno, success, j;
raw_regno = search_ofs + i;
if (raw_regno >= FIRST_PSEUDO_REGISTER)
raw_regno -= FIRST_PSEUDO_REGISTER;
#ifdef REG_ALLOC_ORDER
regno = reg_alloc_order[raw_regno];
#else
regno = raw_regno;
#endif
if (fixed_regs[regno])
continue;
if (! TEST_HARD_REG_BIT (reg_class_contents[class], regno))
continue;
if (! HARD_REGNO_MODE_OK (regno, mode))
continue;
if (! call_used_regs[regno] && ! regs_ever_live[regno])
continue;
if ((regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM)
&& (! reload_completed || frame_pointer_needed))
continue;
success = 1;
for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
{
if (TEST_HARD_REG_BIT (*reg_set, regno + j)
|| TEST_HARD_REG_BIT (live, regno + j))
{
success = 0;
break;
}
}
if (success)
{
for (j = HARD_REGNO_NREGS (regno, mode) - 1; j >= 0; j--)
SET_HARD_REG_BIT (*reg_set, regno + j);
if (++raw_regno >= FIRST_PSEUDO_REGISTER)
raw_regno = 0;
search_ofs = raw_regno;
return gen_rtx_REG (mode, regno);
}
}
search_ofs = 0;
return NULL_RTX;
}
void
peephole2_optimize (dump_file)
FILE *dump_file ATTRIBUTE_UNUSED;
{
regset_head rs_heads[MAX_INSNS_PER_PEEP2 + 2];
rtx insn, prev;
regset live;
int i;
basic_block bb;
#ifdef HAVE_conditional_execution
sbitmap blocks;
bool changed;
#endif
bool do_cleanup_cfg = false;
bool do_rebuild_jump_labels = false;
for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
peep2_insn_data[i].live_before = INITIALIZE_REG_SET (rs_heads[i]);
live = INITIALIZE_REG_SET (rs_heads[i]);
#ifdef HAVE_conditional_execution
blocks = sbitmap_alloc (last_basic_block);
sbitmap_zero (blocks);
changed = false;
#else
count_or_remove_death_notes (NULL, 1);
#endif
FOR_EACH_BB_REVERSE (bb)
{
struct propagate_block_info *pbi;
for (i = 0; i < MAX_INSNS_PER_PEEP2; ++i)
peep2_insn_data[i].insn = NULL_RTX;
peep2_insn_data[MAX_INSNS_PER_PEEP2].insn = PEEP2_EOB;
peep2_current = MAX_INSNS_PER_PEEP2;
COPY_REG_SET (live, bb->global_live_at_end);
COPY_REG_SET (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live);
#ifdef HAVE_conditional_execution
pbi = init_propagate_block_info (bb, live, NULL, NULL, 0);
#else
pbi = init_propagate_block_info (bb, live, NULL, NULL, PROP_DEATH_NOTES);
#endif
for (insn = bb->end; ; insn = prev)
{
prev = PREV_INSN (insn);
if (INSN_P (insn))
{
rtx try, before_try, x;
int match_len;
rtx note;
bool was_call = false;
if (--peep2_current < 0)
peep2_current = MAX_INSNS_PER_PEEP2;
peep2_insn_data[peep2_current].insn = insn;
propagate_one_insn (pbi, insn);
COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
try = peephole2_insns (PATTERN (insn), insn, &match_len);
if (try != NULL)
{
for (i = 0; i <= match_len; ++i)
{
int j;
rtx old_insn, new_insn, note;
j = i + peep2_current;
if (j >= MAX_INSNS_PER_PEEP2 + 1)
j -= MAX_INSNS_PER_PEEP2 + 1;
old_insn = peep2_insn_data[j].insn;
if (GET_CODE (old_insn) != CALL_INSN)
continue;
was_call = true;
new_insn = try;
while (new_insn != NULL_RTX)
{
if (GET_CODE (new_insn) == CALL_INSN)
break;
new_insn = NEXT_INSN (new_insn);
}
if (new_insn == NULL_RTX)
abort ();
CALL_INSN_FUNCTION_USAGE (new_insn)
= CALL_INSN_FUNCTION_USAGE (old_insn);
for (note = REG_NOTES (old_insn);
note;
note = XEXP (note, 1))
switch (REG_NOTE_KIND (note))
{
case REG_NORETURN:
case REG_SETJMP:
case REG_ALWAYS_RETURN:
REG_NOTES (new_insn)
= gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (new_insn));
default:
break;
}
while (++i <= match_len)
{
j = i + peep2_current;
if (j >= MAX_INSNS_PER_PEEP2 + 1)
j -= MAX_INSNS_PER_PEEP2 + 1;
old_insn = peep2_insn_data[j].insn;
if (GET_CODE (old_insn) == CALL_INSN)
abort ();
}
break;
}
i = match_len + peep2_current;
if (i >= MAX_INSNS_PER_PEEP2 + 1)
i -= MAX_INSNS_PER_PEEP2 + 1;
note = find_reg_note (peep2_insn_data[i].insn,
REG_EH_REGION, NULL_RTX);
try = emit_insn_after_scope (try, peep2_insn_data[i].insn,
INSN_SCOPE (peep2_insn_data[i].insn));
before_try = PREV_INSN (insn);
delete_insn_chain (insn, peep2_insn_data[i].insn);
if (note || (was_call && nonlocal_goto_handler_labels))
{
edge eh_edge;
for (eh_edge = bb->succ; eh_edge
; eh_edge = eh_edge->succ_next)
if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
break;
for (x = try ; x != before_try ; x = PREV_INSN (x))
if (GET_CODE (x) == CALL_INSN
|| (flag_non_call_exceptions
&& may_trap_p (PATTERN (x))
&& !find_reg_note (x, REG_EH_REGION, NULL)))
{
if (note)
REG_NOTES (x)
= gen_rtx_EXPR_LIST (REG_EH_REGION,
XEXP (note, 0),
REG_NOTES (x));
if (x != bb->end && eh_edge)
{
edge nfte, nehe;
int flags;
nfte = split_block (bb, x);
flags = (eh_edge->flags
& (EDGE_EH | EDGE_ABNORMAL));
if (GET_CODE (x) == CALL_INSN)
flags |= EDGE_ABNORMAL_CALL;
nehe = make_edge (nfte->src, eh_edge->dest,
flags);
nehe->probability = eh_edge->probability;
nfte->probability
= REG_BR_PROB_BASE - nehe->probability;
do_cleanup_cfg |= purge_dead_edges (nfte->dest);
#ifdef HAVE_conditional_execution
SET_BIT (blocks, nfte->dest->index);
changed = true;
#endif
bb = nfte->src;
eh_edge = nehe;
}
}
do_cleanup_cfg |= purge_dead_edges (bb);
}
#ifdef HAVE_conditional_execution
SET_BIT (blocks, bb->index);
changed = true;
for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
peep2_insn_data[i].insn = NULL_RTX;
peep2_insn_data[peep2_current].insn = PEEP2_EOB;
#else
if (++i >= MAX_INSNS_PER_PEEP2 + 1)
i = 0;
COPY_REG_SET (live, peep2_insn_data[i].live_before);
x = try;
do
{
if (INSN_P (x))
{
if (--i < 0)
i = MAX_INSNS_PER_PEEP2;
peep2_insn_data[i].insn = x;
propagate_one_insn (pbi, x);
COPY_REG_SET (peep2_insn_data[i].live_before, live);
}
x = PREV_INSN (x);
}
while (x != prev);
peep2_current = i;
#endif
for (x = try; x != before_try; x = PREV_INSN (x))
if (GET_CODE (x) == JUMP_INSN)
{
do_rebuild_jump_labels = true;
break;
}
}
}
if (insn == bb->head)
break;
}
free_propagate_block_info (pbi);
}
for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
FREE_REG_SET (peep2_insn_data[i].live_before);
FREE_REG_SET (live);
if (do_rebuild_jump_labels)
rebuild_jump_labels (get_insns ());
if (do_cleanup_cfg)
{
cleanup_cfg (0);
update_life_info (0, UPDATE_LIFE_GLOBAL_RM_NOTES, PROP_DEATH_NOTES);
}
#ifdef HAVE_conditional_execution
else
{
count_or_remove_death_notes (blocks, 1);
update_life_info (blocks, UPDATE_LIFE_LOCAL, PROP_DEATH_NOTES);
}
sbitmap_free (blocks);
#endif
}
#endif
int
store_data_bypass_p (out_insn, in_insn)
rtx out_insn, in_insn;
{
rtx out_set, in_set;
in_set = single_set (in_insn);
if (! in_set)
abort ();
if (GET_CODE (SET_DEST (in_set)) != MEM)
return false;
out_set = single_set (out_insn);
if (out_set)
{
if (reg_mentioned_p (SET_DEST (out_set), SET_DEST (in_set)))
return false;
}
else
{
rtx out_pat;
int i;
out_pat = PATTERN (out_insn);
if (GET_CODE (out_pat) != PARALLEL)
abort ();
for (i = 0; i < XVECLEN (out_pat, 0); i++)
{
rtx exp = XVECEXP (out_pat, 0, i);
if (GET_CODE (exp) == CLOBBER)
continue;
if (GET_CODE (exp) != SET)
abort ();
if (reg_mentioned_p (SET_DEST (exp), SET_DEST (in_set)))
return false;
}
}
return true;
}
int
if_test_bypass_p (out_insn, in_insn)
rtx out_insn, in_insn;
{
rtx out_set, in_set;
in_set = single_set (in_insn);
if (! in_set)
{
if (GET_CODE (in_insn) == JUMP_INSN || GET_CODE (in_insn) == CALL_INSN)
return false;
abort ();
}
if (GET_CODE (SET_SRC (in_set)) != IF_THEN_ELSE)
return false;
in_set = SET_SRC (in_set);
out_set = single_set (out_insn);
if (out_set)
{
if (reg_mentioned_p (SET_DEST (out_set), XEXP (in_set, 1))
|| reg_mentioned_p (SET_DEST (out_set), XEXP (in_set, 2)))
return false;
}
else
{
rtx out_pat;
int i;
out_pat = PATTERN (out_insn);
if (GET_CODE (out_pat) != PARALLEL)
abort ();
for (i = 0; i < XVECLEN (out_pat, 0); i++)
{
rtx exp = XVECEXP (out_pat, 0, i);
if (GET_CODE (exp) == CLOBBER)
continue;
if (GET_CODE (exp) != SET)
abort ();
if (reg_mentioned_p (SET_DEST (out_set), XEXP (in_set, 1))
|| reg_mentioned_p (SET_DEST (out_set), XEXP (in_set, 2)))
return false;
}
}
return true;
}