entered into RCS
From-SVN: r36
This commit is contained in:
parent
573ade84cc
commit
585021dcdb
863
gcc/config/pyr/pyr.c
Normal file
863
gcc/config/pyr/pyr.c
Normal file
@ -0,0 +1,863 @@
|
||||
/* Subroutines for insn-output.c for Pyramid 90x, 9000, and MIServer Series.
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU CC.
|
||||
|
||||
GNU CC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU CC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU CC; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* Some output-actions in pyr.md need these. */
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "rtl.h"
|
||||
#include "regs.h"
|
||||
#include "hard-reg-set.h"
|
||||
#include "real.h"
|
||||
#include "insn-config.h"
|
||||
#include "conditions.h"
|
||||
#include "insn-flags.h"
|
||||
#include "output.h"
|
||||
#include "insn-attr.h"
|
||||
#include "tree.h"
|
||||
|
||||
/*
|
||||
* Do FUNCTION_ARG.
|
||||
* This cannot be defined as a macro on pyramids, because Pyramid Technology's
|
||||
* C compiler dies on (several equivalent definitions of) this macro.
|
||||
* The only way around this cc bug was to make this a function.
|
||||
* While it would be possible to use a macro version for gcc, it seems
|
||||
* more reliable to have a single version of the code.
|
||||
*/
|
||||
void *
|
||||
pyr_function_arg(cum, mode, type, named)
|
||||
CUMULATIVE_ARGS cum;
|
||||
enum machine_mode mode;
|
||||
tree type;
|
||||
{
|
||||
return (void *)(FUNCTION_ARG_HELPER (cum, mode,type,named));
|
||||
}
|
||||
|
||||
/* Do the hard part of PARAM_SAFE_FOR_REG_P.
|
||||
* This cannot be defined as a macro on pyramids, because Pyramid Technology's
|
||||
* C compiler dies on (several equivalent definitions of) this macro.
|
||||
* The only way around this cc bug was to make this a function.
|
||||
*/
|
||||
int
|
||||
inner_param_safe_helper (type)
|
||||
tree type;
|
||||
{
|
||||
return (INNER_PARAM_SAFE_HELPER(type));
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if OP is a non-indexed operand of mode MODE.
|
||||
This is either a register reference, a memory reference,
|
||||
or a constant. In the case of a memory reference, the address
|
||||
is checked to make sure it isn't indexed.
|
||||
|
||||
Register and memory references must have mode MODE in order to be valid,
|
||||
but some constants have no machine mode and are valid for any mode.
|
||||
|
||||
If MODE is VOIDmode, OP is checked for validity for whatever mode
|
||||
it has.
|
||||
|
||||
The main use of this function is as a predicate in match_operand
|
||||
expressions in the machine description.
|
||||
|
||||
It is useful to compare this with general_operand(). They should
|
||||
be identical except for one line.
|
||||
|
||||
This function seems necessary because of the non-orthogonality of
|
||||
Pyramid insns.
|
||||
For any 2-operand insn, and any combination of operand modes,
|
||||
if indexing is valid for the isn's second operand, it is invalid
|
||||
for the first operand to be indexed. */
|
||||
|
||||
extern int volatile_ok;
|
||||
|
||||
int
|
||||
nonindexed_operand (op, mode)
|
||||
register rtx op;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
register RTX_CODE code = GET_CODE (op);
|
||||
int mode_altering_drug = 0;
|
||||
|
||||
if (mode == VOIDmode)
|
||||
mode = GET_MODE (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)
|
||||
return 0;
|
||||
|
||||
if (CONSTANT_P (op))
|
||||
return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode)
|
||||
&& LEGITIMATE_CONSTANT_P (op));
|
||||
|
||||
/* Except for certain constants with VOIDmode, already checked for,
|
||||
OP's mode must match MODE if MODE specifies a mode. */
|
||||
|
||||
if (GET_MODE (op) != mode)
|
||||
return 0;
|
||||
|
||||
while (code == SUBREG)
|
||||
{
|
||||
op = SUBREG_REG (op);
|
||||
code = GET_CODE (op);
|
||||
#if 0
|
||||
/* No longer needed, since (SUBREG (MEM...))
|
||||
will load the MEM into a reload reg in the MEM's own mode. */
|
||||
mode_altering_drug = 1;
|
||||
#endif
|
||||
}
|
||||
if (code == REG)
|
||||
return 1;
|
||||
if (code == CONST_DOUBLE)
|
||||
return LEGITIMATE_CONSTANT_P (op);
|
||||
if (code == MEM)
|
||||
{
|
||||
register rtx y = XEXP (op, 0);
|
||||
if (! volatile_ok && MEM_VOLATILE_P (op))
|
||||
return 0;
|
||||
GO_IF_NONINDEXED_ADDRESS (y, win);
|
||||
}
|
||||
return 0;
|
||||
|
||||
win:
|
||||
if (mode_altering_drug)
|
||||
return ! mode_dependent_address_p (XEXP (op, 0));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return non-zero if the rtx OP has an immediate component. An
|
||||
immediate component or additive term equal to zero is rejected
|
||||
due to assembler problems. */
|
||||
|
||||
int
|
||||
has_direct_base (op)
|
||||
rtx op;
|
||||
{
|
||||
if ((CONSTANT_ADDRESS_P (op)
|
||||
&& op != const0_rtx)
|
||||
|| (GET_CODE (op) == PLUS
|
||||
&& ((CONSTANT_ADDRESS_P (XEXP (op, 1))
|
||||
&& XEXP (op, 1) != const0_rtx)
|
||||
|| (CONSTANT_ADDRESS_P (XEXP (op, 0))
|
||||
&& XEXP (op, 0) != const0_rtx))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return zero if the rtx OP has a (scaled) index. */
|
||||
|
||||
int
|
||||
has_index (op)
|
||||
rtx op;
|
||||
{
|
||||
if (GET_CODE (op) == PLUS
|
||||
&& (GET_CODE (XEXP (op, 0)) == MULT
|
||||
|| (GET_CODE (XEXP (op, 1)) == MULT)))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swap_operands;
|
||||
|
||||
/* weird_memory_memory -- return 1 if OP1 and OP2 can be compared (or
|
||||
exchanged with xchw) with one instruction. If the operands need to
|
||||
be swapped, set the global variable SWAP_OPERANDS. This function
|
||||
silently assumes that both OP0 and OP1 are valid memory references.
|
||||
*/
|
||||
|
||||
int
|
||||
weird_memory_memory (op0, op1)
|
||||
rtx op0, op1;
|
||||
{
|
||||
RTX_CODE code0, code1;
|
||||
|
||||
op0 = XEXP (op0, 0);
|
||||
op1 = XEXP (op1, 0);
|
||||
code0 = GET_CODE (op0);
|
||||
code1 = GET_CODE (op1);
|
||||
|
||||
swap_operands = 0;
|
||||
|
||||
if (code1 == REG || code1 == SUBREG)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (code0 == REG || code0 == SUBREG)
|
||||
{
|
||||
swap_operands = 1;
|
||||
return 1;
|
||||
}
|
||||
if (has_direct_base (op0) && has_direct_base (op1))
|
||||
{
|
||||
if (has_index (op1))
|
||||
{
|
||||
if (has_index (op0))
|
||||
return 0;
|
||||
swap_operands = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
signed_comparison (x, mode)
|
||||
rtx x;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
return ! TRULY_UNSIGNED_COMPARE_P (GET_CODE (x));
|
||||
}
|
||||
|
||||
extern rtx force_reg ();
|
||||
rtx test_op0, test_op1;
|
||||
enum machine_mode test_mode;
|
||||
|
||||
/* Sign-extend or zero-extend constant X from FROM_MODE to TO_MODE. */
|
||||
|
||||
rtx
|
||||
extend_const (x, extop, from_mode, to_mode)
|
||||
rtx x;
|
||||
RTX_CODE extop;
|
||||
enum machine_mode from_mode, to_mode;
|
||||
{
|
||||
int val;
|
||||
int negative;
|
||||
if (from_mode == to_mode)
|
||||
return x;
|
||||
if (GET_CODE (x) != CONST_INT)
|
||||
abort ();
|
||||
val = INTVAL (x);
|
||||
negative = val & (1 << (GET_MODE_BITSIZE (from_mode) - 1));
|
||||
if (GET_MODE_BITSIZE (from_mode) == HOST_BITS_PER_INT)
|
||||
abort ();
|
||||
if (negative && extop == SIGN_EXTEND)
|
||||
val = val | ((-1) << (GET_MODE_BITSIZE (from_mode)));
|
||||
else
|
||||
val = val & ~((-1) << (GET_MODE_BITSIZE (from_mode)));
|
||||
if (GET_MODE_BITSIZE (to_mode) == HOST_BITS_PER_INT)
|
||||
return gen_rtx (CONST_INT, VOIDmode, val);
|
||||
return gen_rtx (CONST_INT, VOIDmode,
|
||||
val & ~((-1) << (GET_MODE_BITSIZE (to_mode))));
|
||||
}
|
||||
|
||||
rtx
|
||||
ensure_extended (op, extop, from_mode)
|
||||
rtx op;
|
||||
RTX_CODE extop;
|
||||
enum machine_mode from_mode;
|
||||
{
|
||||
if (GET_CODE (op) == CONST_INT)
|
||||
return extend_const (op, extop, from_mode, SImode);
|
||||
else
|
||||
return force_reg (SImode, gen_rtx (extop, SImode, op));
|
||||
}
|
||||
|
||||
/* Emit rtl for a branch, as well as any delayed (integer) compare insns.
|
||||
The compare insn to perform is determined by the global variables
|
||||
test_op0 and test_op1. */
|
||||
|
||||
void
|
||||
extend_and_branch (extop)
|
||||
RTX_CODE extop;
|
||||
{
|
||||
rtx op0, op1;
|
||||
RTX_CODE code0, code1;
|
||||
|
||||
op0 = test_op0, op1 = test_op1;
|
||||
if (op0 == 0)
|
||||
return;
|
||||
|
||||
code0 = GET_CODE (op0);
|
||||
if (op1 != 0)
|
||||
code1 = GET_CODE (op1);
|
||||
test_op0 = test_op1 = 0;
|
||||
|
||||
if (op1 == 0)
|
||||
{
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, op0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CONSTANT_P (op0) && CONSTANT_P (op1))
|
||||
{
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
op1 = ensure_extended (op1, extop, test_mode);
|
||||
}
|
||||
else if (extop == ZERO_EXTEND && test_mode == HImode)
|
||||
{
|
||||
/* Pyramids have no unsigned "cmphi" instructions. We need to
|
||||
zero extend unsigned halfwords into temporary registers. */
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
op1 = ensure_extended (op1, extop, test_mode);
|
||||
}
|
||||
else if (CONSTANT_P (op0))
|
||||
{
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
op1 = ensure_extended (op1, extop, test_mode);
|
||||
}
|
||||
else if (CONSTANT_P (op1))
|
||||
{
|
||||
op1 = ensure_extended (op1, extop, test_mode);
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
}
|
||||
else if ((code0 == REG || code0 == SUBREG)
|
||||
&& (code1 == REG || code1 == SUBREG))
|
||||
{
|
||||
/* I could do this case without extension, by using the virtual
|
||||
register address (but that would lose for global regs). */
|
||||
op0 = ensure_extended (op0, extop, test_mode);
|
||||
op1 = ensure_extended (op1, extop, test_mode);
|
||||
}
|
||||
else if (code0 == MEM && code1 == MEM)
|
||||
{
|
||||
/* Load into a reg if the address combination can't be handled
|
||||
directly. */
|
||||
if (! weird_memory_memory (op0, op1))
|
||||
op0 = force_reg (test_mode, op0);
|
||||
}
|
||||
|
||||
emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
|
||||
gen_rtx (COMPARE, VOIDmode, op0, op1)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Return non-zero if the two single-word moves with operands[0]
|
||||
and operands[1] for the first single-word move, and operands[2]
|
||||
and operands[3] for the second single-word move, is possible to
|
||||
combine to a double word move.
|
||||
|
||||
The criterion is whether the operands are in consecutive memory cells,
|
||||
registers, etc. */
|
||||
|
||||
int
|
||||
movdi_possible (operands)
|
||||
rtx operands[];
|
||||
{
|
||||
int cnst_diff0, cnst_diff1;
|
||||
RTX_CODE code0 = GET_CODE (operands[0]);
|
||||
RTX_CODE code1 = GET_CODE (operands[1]);
|
||||
|
||||
/* Don't dare to combine (possibly overlapping) memory -> memory moves. */
|
||||
/* It would be possible to detect the cases where we dare, by using
|
||||
constant_diff (operands[0], operands[1])!!! */
|
||||
if (code0 == MEM && code1 == MEM)
|
||||
return 0;
|
||||
|
||||
cnst_diff0 = consecutive_operands (operands[0], operands[2]);
|
||||
if (cnst_diff0 == 0)
|
||||
return 0;
|
||||
|
||||
cnst_diff1 = consecutive_operands (operands[1], operands[3]);
|
||||
if (cnst_diff1 == 0)
|
||||
return 0;
|
||||
|
||||
if (cnst_diff0 & cnst_diff1)
|
||||
{
|
||||
/* The source and destination operands are consecutive. */
|
||||
|
||||
/* If the first move writes into the source of the second move,
|
||||
we cannot combine. */
|
||||
if ((code0 == REG
|
||||
&& reg_overlap_mentioned_p (operands[0], operands[3]))
|
||||
|| (code0 == SUBREG
|
||||
&& subreg_overlap_mentioned_p (operands[0], operands[3])))
|
||||
return 0;
|
||||
|
||||
if (cnst_diff0 & 1)
|
||||
/* operands[0],[1] has higher addresses than operands[2],[3]. */
|
||||
swap_operands = 0;
|
||||
else
|
||||
/* operands[0],[1] has lower addresses than operands[2],[3]. */
|
||||
swap_operands = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Like reg_overlap_mentioned_p, but accepts a subreg rtx instead
|
||||
of a reg. */
|
||||
|
||||
int
|
||||
subreg_overlap_mentioned_p (subreg, x)
|
||||
rtx subreg, x;
|
||||
{
|
||||
rtx reg = SUBREG_REG (subreg);
|
||||
int regno = REGNO (reg) + SUBREG_WORD (subreg);
|
||||
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (subreg));
|
||||
return refers_to_regno_p (regno, endregno, x, 0);
|
||||
}
|
||||
|
||||
/* Return 1 if OP0 is a consecutive operand to OP1, 2 if OP1 is a
|
||||
consecutive operand to OP0.
|
||||
|
||||
This function is used to determine if addresses are consecutive,
|
||||
and therefore possible to combine to fewer instructions. */
|
||||
|
||||
int
|
||||
consecutive_operands (op0, op1)
|
||||
rtx op0, op1;
|
||||
{
|
||||
RTX_CODE code0, code1;
|
||||
int cnst_diff;
|
||||
int regno_off0, regno_off1;
|
||||
|
||||
code0 = GET_CODE (op0);
|
||||
code1 = GET_CODE (op1);
|
||||
|
||||
regno_off0 = 0;
|
||||
if (code0 == SUBREG)
|
||||
{
|
||||
if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))) <= UNITS_PER_WORD)
|
||||
return 0;
|
||||
regno_off0 = SUBREG_WORD (op0);
|
||||
op0 = SUBREG_REG (op0);
|
||||
code0 = REG;
|
||||
}
|
||||
|
||||
regno_off1 = 0;
|
||||
if (code1 == SUBREG)
|
||||
{
|
||||
if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1))) <= UNITS_PER_WORD)
|
||||
return 0;
|
||||
regno_off1 = SUBREG_WORD (op1);
|
||||
op1 = SUBREG_REG (op1);
|
||||
code1 = REG;
|
||||
}
|
||||
|
||||
if (code0 != code1)
|
||||
return 0;
|
||||
|
||||
switch (code0)
|
||||
{
|
||||
case CONST_INT:
|
||||
/* Cannot permit any symbolic constants, even if the consecutive
|
||||
operand is 0, since a movl really performs sign extension. */
|
||||
if (code1 != CONST_INT)
|
||||
return 0;
|
||||
if ((INTVAL (op0) == 0 && INTVAL (op1) == 0)
|
||||
|| (INTVAL (op0) == -1 && INTVAL (op1) == -1))
|
||||
return 3;
|
||||
if ((INTVAL (op0) == 0 && INTVAL (op1) > 0)
|
||||
|| (INTVAL (op0) == -1 && INTVAL (op1) < 0))
|
||||
return 2;
|
||||
if ((INTVAL (op1) == 0 && INTVAL (op0) > 0)
|
||||
|| (INTVAL (op1) == -1 && INTVAL (op0) < 0))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case REG:
|
||||
regno_off0 = REGNO (op0) + regno_off0;
|
||||
regno_off1 = REGNO (op1) + regno_off1;
|
||||
|
||||
cnst_diff = regno_off0 - regno_off1;
|
||||
if (cnst_diff == 1)
|
||||
{
|
||||
/* movl with the highest numbered parameter (local) register as
|
||||
source or destination, doesn't wrap to the lowest numbered local
|
||||
(temporary) register. */
|
||||
|
||||
if (regno_off0 % 16 != 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else if (cnst_diff == -1)
|
||||
{
|
||||
if (regno_off1 % 16 != 0)
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case MEM:
|
||||
op0 = XEXP (op0, 0);
|
||||
op1 = XEXP (op1, 0);
|
||||
if (GET_CODE (op0) == CONST)
|
||||
op0 = XEXP (op0, 0);
|
||||
if (GET_CODE (op1) == CONST)
|
||||
op1 = XEXP (op1, 0);
|
||||
|
||||
cnst_diff = constant_diff (op0, op1);
|
||||
if (cnst_diff)
|
||||
{
|
||||
if (cnst_diff == 4)
|
||||
return 1;
|
||||
else if (cnst_diff == -4)
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the constant difference of the rtx expressions OP0 and OP1,
|
||||
or 0 if they don't have a constant difference.
|
||||
|
||||
This function is used to determine if addresses are consecutive,
|
||||
and therefore possible to combine to fewer instructions. */
|
||||
|
||||
int
|
||||
constant_diff (op0, op1)
|
||||
rtx op0, op1;
|
||||
{
|
||||
RTX_CODE code0, code1;
|
||||
int cnst_diff;
|
||||
|
||||
code0 = GET_CODE (op0);
|
||||
code1 = GET_CODE (op1);
|
||||
|
||||
if (code0 != code1)
|
||||
{
|
||||
if (code0 == PLUS)
|
||||
{
|
||||
if (GET_CODE (XEXP (op0, 1)) == CONST_INT
|
||||
&& rtx_equal_p (op1, XEXP (op0, 0)))
|
||||
return INTVAL (XEXP (op0, 1));
|
||||
}
|
||||
else if (code1 == PLUS)
|
||||
{
|
||||
if (GET_CODE (XEXP (op1, 1)) == CONST_INT
|
||||
&& rtx_equal_p (op0, XEXP (op1, 0)))
|
||||
return -INTVAL (XEXP (op1, 1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (code0 == CONST_INT)
|
||||
return INTVAL (op0) - INTVAL (op1);
|
||||
|
||||
if (code0 == PLUS)
|
||||
{
|
||||
cnst_diff = constant_diff (XEXP (op0, 0), XEXP (op1, 0));
|
||||
if (cnst_diff)
|
||||
return (rtx_equal_p (XEXP (op0, 1), XEXP (op1, 1)))
|
||||
? cnst_diff : 0;
|
||||
cnst_diff = constant_diff (XEXP (op0, 1), XEXP (op1, 1));
|
||||
if (cnst_diff)
|
||||
return (rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0)))
|
||||
? cnst_diff : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
already_sign_extended (insn, from_mode, op)
|
||||
rtx insn;
|
||||
enum machine_mode from_mode;
|
||||
rtx op;
|
||||
{
|
||||
rtx xinsn, xdest, xsrc;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
insn = PREV_INSN (insn);
|
||||
if (insn == 0)
|
||||
return 0;
|
||||
if (GET_CODE (insn) == NOTE || GET_CODE (insn) == JUMP_INSN)
|
||||
continue;
|
||||
if (GET_CODE (insn) == CALL_INSN && ! call_used_regs[REGNO (op)])
|
||||
continue;
|
||||
if (GET_CODE (insn) != INSN)
|
||||
return 0;
|
||||
xinsn = PATTERN (insn);
|
||||
|
||||
if (GET_CODE (xinsn) != SET)
|
||||
return 0;
|
||||
|
||||
xdest = SET_DEST (xinsn);
|
||||
xsrc = SET_SRC (xinsn);
|
||||
|
||||
if (GET_CODE (xdest) == SUBREG)
|
||||
abort ();
|
||||
|
||||
if ( ! REG_P (xdest))
|
||||
continue;
|
||||
|
||||
if (REGNO (op) == REGNO (xdest)
|
||||
&& ((GET_CODE (xsrc) == SIGN_EXTEND
|
||||
&& GET_MODE (XEXP (xsrc, 0)) == from_mode)
|
||||
|| (GET_CODE (xsrc) == MEM
|
||||
&& GET_MODE (xsrc) == from_mode)))
|
||||
return 1;
|
||||
|
||||
/* The register is modified by another operation. */
|
||||
if (reg_overlap_mentioned_p (xdest, op))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
output_move_double (operands)
|
||||
rtx *operands;
|
||||
{
|
||||
if (GET_CODE (operands[1]) == CONST_DOUBLE)
|
||||
{
|
||||
if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT)
|
||||
{
|
||||
/* In an integer, the low-order word is in CONST_DOUBLE_LOW. */
|
||||
rtx const_op = operands[1];
|
||||
if ((CONST_DOUBLE_HIGH (const_op) == 0
|
||||
&& CONST_DOUBLE_LOW (const_op) >= 0)
|
||||
|| (CONST_DOUBLE_HIGH (const_op) == -1
|
||||
&& CONST_DOUBLE_LOW (const_op) < 0))
|
||||
{
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_LOW (const_op));
|
||||
return "movl %1,%0";
|
||||
}
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_HIGH (const_op));
|
||||
output_asm_insn ("movw %1,%0", operands);
|
||||
operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_LOW (const_op));
|
||||
return "movw %1,%0";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In a real, the low-address word is in CONST_DOUBLE_LOW. */
|
||||
rtx const_op = operands[1];
|
||||
if ((CONST_DOUBLE_LOW (const_op) == 0
|
||||
&& CONST_DOUBLE_HIGH (const_op) >= 0)
|
||||
|| (CONST_DOUBLE_LOW (const_op) == -1
|
||||
&& CONST_DOUBLE_HIGH (const_op) < 0))
|
||||
{
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_HIGH (const_op));
|
||||
return "movl %1,%0";
|
||||
}
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_LOW (const_op));
|
||||
output_asm_insn ("movw %1,%0", operands);
|
||||
operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
|
||||
operands[1] = gen_rtx (CONST_INT, VOIDmode,
|
||||
CONST_DOUBLE_HIGH (const_op));
|
||||
return "movw %1,%0";
|
||||
}
|
||||
}
|
||||
|
||||
return "movl %1,%0";
|
||||
}
|
||||
|
||||
/* Output a shift insns, after having reduced integer arguments to
|
||||
avoid as warnings. */
|
||||
|
||||
char *
|
||||
output_shift (pattern, op2, mod)
|
||||
char *pattern;
|
||||
rtx op2;
|
||||
int mod;
|
||||
{
|
||||
if (GET_CODE (op2) == CONST_INT)
|
||||
{
|
||||
int cnt = INTVAL (op2) % mod;
|
||||
if (cnt == 0)
|
||||
{
|
||||
cc_status = cc_prev_status;
|
||||
return "";
|
||||
}
|
||||
op2 = gen_rtx (CONST_INT, VOIDmode, cnt);
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/* Return non-zero if the code of this rtx pattern is a relop. */
|
||||
|
||||
int
|
||||
relop (op, mode)
|
||||
rtx op;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
switch (GET_CODE (op))
|
||||
{
|
||||
case EQ:
|
||||
case NE:
|
||||
case LT:
|
||||
case LE:
|
||||
case GE:
|
||||
case GT:
|
||||
case LTU:
|
||||
case LEU:
|
||||
case GEU:
|
||||
case GTU:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
notice_update_cc (EXP, INSN)
|
||||
rtx EXP, INSN;
|
||||
{
|
||||
switch (GET_CODE (EXP))
|
||||
{
|
||||
case SET:
|
||||
switch (GET_CODE (SET_DEST (EXP)))
|
||||
{
|
||||
case CC0:
|
||||
cc_status.mdep = 0;
|
||||
cc_status.flags = 0;
|
||||
cc_status.value1 = 0;
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
break;
|
||||
|
||||
case PC:
|
||||
break;
|
||||
|
||||
case REG:
|
||||
switch (GET_CODE (SET_SRC (EXP)))
|
||||
{
|
||||
case CALL:
|
||||
goto call;
|
||||
case MEM:
|
||||
if (GET_MODE (SET_SRC (EXP)) == QImode
|
||||
|| GET_MODE (SET_SRC (EXP)) == HImode)
|
||||
{
|
||||
cc_status.mdep = 0;
|
||||
cc_status.flags = CC_NO_OVERFLOW;
|
||||
cc_status.value1 = SET_DEST (EXP);
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
break;
|
||||
}
|
||||
/* else: Fall through. */
|
||||
case CONST_INT:
|
||||
case SYMBOL_REF:
|
||||
case LABEL_REF:
|
||||
case CONST:
|
||||
case CONST_DOUBLE:
|
||||
case REG:
|
||||
if (cc_status.value1
|
||||
&& reg_overlap_mentioned_p (SET_DEST (EXP),
|
||||
cc_status.value1))
|
||||
cc_status.value1 = 0;
|
||||
if (cc_status.value2
|
||||
&& reg_overlap_mentioned_p (SET_DEST (EXP),
|
||||
cc_status.value2))
|
||||
cc_status.value2 = 0;
|
||||
break;
|
||||
|
||||
case UDIV:
|
||||
case UMOD:
|
||||
cc_status.mdep = CC_VALID_FOR_UNSIGNED;
|
||||
cc_status.flags = CC_NO_OVERFLOW;
|
||||
cc_status.value1 = SET_DEST (EXP);
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
break;
|
||||
default:
|
||||
cc_status.mdep = 0;
|
||||
cc_status.flags = CC_NO_OVERFLOW;
|
||||
cc_status.value1 = SET_DEST (EXP);
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MEM:
|
||||
switch (GET_CODE (SET_SRC (EXP)))
|
||||
{
|
||||
case REG:
|
||||
if (GET_MODE (SET_SRC (EXP)) == QImode
|
||||
|| GET_MODE (SET_SRC (EXP)) == HImode)
|
||||
{
|
||||
cc_status.flags = CC_NO_OVERFLOW;
|
||||
cc_status.value1 = SET_DEST (EXP);
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
cc_status.mdep = 0;
|
||||
break;
|
||||
}
|
||||
/* else: Fall through. */
|
||||
case CONST_INT:
|
||||
case SYMBOL_REF:
|
||||
case LABEL_REF:
|
||||
case CONST:
|
||||
case CONST_DOUBLE:
|
||||
case MEM:
|
||||
/* Need to forget cc_status about memory positions each
|
||||
time a memory store is made, even if the memory store
|
||||
insns in question doesn't modify the condition codes. */
|
||||
if (cc_status.value1 &&
|
||||
GET_CODE (cc_status.value1) == MEM)
|
||||
cc_status.value1 = 0;
|
||||
if (cc_status.value2 &&
|
||||
GET_CODE (cc_status.value2) == MEM)
|
||||
cc_status.value2 = 0;
|
||||
break;
|
||||
case SIGN_EXTEND:
|
||||
case FLOAT_EXTEND:
|
||||
case FLOAT_TRUNCATE:
|
||||
case FLOAT:
|
||||
case FIX:
|
||||
cc_status.flags = CC_NO_OVERFLOW;
|
||||
cc_status.value1 = SET_DEST (EXP);
|
||||
cc_status.value2 = SET_SRC (EXP);
|
||||
cc_status.mdep = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case CALL:
|
||||
call:
|
||||
CC_STATUS_INIT;
|
||||
break;
|
||||
/* Do calls preserve the condition codes? (At least forget
|
||||
cc_status expressions if they refer to registers
|
||||
not preserved across calls. Also forget expressions
|
||||
about memory contents.) */
|
||||
if (cc_status.value1
|
||||
&& (refers_to_regno_p (PYR_TREG (0), PYR_TREG (15),
|
||||
cc_status.value1, 0)
|
||||
|| GET_CODE (cc_status.value1) == MEM))
|
||||
cc_status.value1 = 0;
|
||||
if (cc_status.value2
|
||||
&& (refers_to_regno_p (PYR_TREG (0), PYR_TREG (15),
|
||||
cc_status.value2, 0)
|
||||
|| GET_CODE (cc_status.value2) == MEM))
|
||||
cc_status.value2 = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
CC_STATUS_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
forget_cc_if_dependent (op)
|
||||
rtx op;
|
||||
{
|
||||
cc_status = cc_prev_status;
|
||||
if (cc_status.value1 && reg_overlap_mentioned_p (op, cc_status.value1))
|
||||
cc_status.value1 = 0;
|
||||
if (cc_status.value2 && reg_overlap_mentioned_p (op, cc_status.value2))
|
||||
cc_status.value2 = 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user