home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 7
/
FreshFishVol7.bin
/
bbs
/
gnu
/
gcc-2.3.3-src.lha
/
GNU
/
src
/
amiga
/
gcc-2.3.3
/
config
/
i860.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
61KB
|
2,049 lines
/* Subroutines for insn-output.c for Intel 860
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
Derived from sparc.c.
Written by Richard Stallman (rms@ai.mit.edu).
Hacked substantially by Ron Guilmette (rfg@ncd.com) to cater
to the whims of the System V Release 4 assembler.
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. */
#include "config.h"
#include "flags.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 "recog.h"
#include "insn-attr.h"
#include <stdio.h>
static rtx find_addr_reg ();
#ifndef I860_REG_PREFIX
#define I860_REG_PREFIX ""
#endif
char *i860_reg_prefix = I860_REG_PREFIX;
/* Save information from a "cmpxx" operation until the branch is emitted. */
rtx i860_compare_op0, i860_compare_op1;
/* Return non-zero if this pattern, can be evaluated safely, even if it
was not asked for. */
int
safe_insn_src_p (op, mode)
rtx op;
enum machine_mode mode;
{
/* Just experimenting. */
/* No floating point src is safe if it contains an arithmetic
operation, since that operation may trap. */
switch (GET_CODE (op))
{
case CONST_INT:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
return 1;
case REG:
return 1;
case MEM:
return CONSTANT_ADDRESS_P (XEXP (op, 0));
/* We never need to negate or complement constants. */
case NEG:
return (mode != SFmode && mode != DFmode);
case NOT:
case ZERO_EXTEND:
return 1;
case EQ:
case NE:
case LT:
case GT:
case LE:
case GE:
case LTU:
case GTU:
case LEU:
case GEU:
case MINUS:
case PLUS:
return (mode != SFmode && mode != DFmode);
case AND:
case IOR:
case XOR:
case LSHIFT:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0)))
|| (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1))))
return 0;
return 1;
default:
return 0;
}
}
/* Return 1 if REG is clobbered in IN.
Return 2 if REG is used in IN.
Return 3 if REG is both used and clobbered in IN.
Return 0 if neither. */
static int
reg_clobbered_p (reg, in)
rtx reg;
rtx in;
{
register enum rtx_code code;
if (in == 0)
return 0;
code = GET_CODE (in);
if (code == SET || code == CLOBBER)
{
rtx dest = SET_DEST (in);
int set = 0;
int used = 0;
while (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == ZERO_EXTRACT)
dest = XEXP (dest, 0);
if (dest == reg)
set = 1;
else if (GET_CODE (dest) == REG
&& refers_to_regno_p (REGNO (reg),
REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)),
SET_DEST (in), 0))
{
set = 1;
/* Anything that sets just part of the register
is considered using as well as setting it.
But note that a straight SUBREG of a single-word value
clobbers the entire value. */
if (dest != SET_DEST (in)
&& ! (GET_CODE (SET_DEST (in)) == SUBREG
|| UNITS_PER_WORD >= GET_MODE_SIZE (GET_MODE (dest))))
used = 1;
}
if (code == SET)
{
if (set)
used = refers_to_regno_p (REGNO (reg),
REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)),
SET_SRC (in), 0);
else
used = refers_to_regno_p (REGNO (reg),
REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)),
in, 0);
}
return set + used * 2;
}
if (refers_to_regno_p (REGNO (reg),
REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)),
in, 0))
return 2;
return 0;
}
/* Return non-zero if OP can be written to without screwing up
GCC's model of what's going on. It is assumed that this operand
appears in the dest position of a SET insn in a conditional
branch's delay slot. AFTER is the label to start looking from. */
int
operand_clobbered_before_used_after (op, after)
rtx op;
rtx after;
{
/* Just experimenting. */
if (GET_CODE (op) == CC0)
return 1;
if (GET_CODE (op) == REG)
{
rtx insn;
if (op == stack_pointer_rtx)
return 0;
/* Scan forward from the label, to see if the value of OP
is clobbered before the first use. */
for (insn = NEXT_INSN (after); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
continue;
if (GET_CODE (insn) == INSN
|| GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN)
{
switch (reg_clobbered_p (op, PATTERN (insn)))
{
default:
return 0;
case 1:
return 1;
case 0:
break;
}
}
/* If we reach another label without clobbering OP,
then we cannot safely write it here. */
else if (GET_CODE (insn) == CODE_LABEL)
return 0;
if (GET_CODE (insn) == JUMP_INSN)
{
if (condjump_p (insn))
return 0;
/* This is a jump insn which has already
been mangled. We can't tell what it does. */
if (GET_CODE (PATTERN (insn)) == PARALLEL)
return 0;
if (! JUMP_LABEL (insn))
return 0;
/* Keep following jumps. */
insn = JUMP_LABEL (insn);
}
}
return 1;
}
/* In both of these cases, the first insn executed
for this op will be a orh whatever%h,%?r0,%?r31,
which is tolerable. */
if (GET_CODE (op) == MEM)
return (CONSTANT_ADDRESS_P (XEXP (op, 0)));
return 0;
}
/* Return non-zero if this pattern, as a source to a "SET",
is known to yield an instruction of unit size. */
int
single_insn_src_p (op, mode)
rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
case CONST_INT:
/* This is not always a single insn src, technically,
but output_delayed_branch knows how to deal with it. */
return 1;
case SYMBOL_REF:
case CONST:
/* This is not a single insn src, technically,
but output_delayed_branch knows how to deal with it. */
return 1;
case REG:
return 1;
case MEM:
return 1;
/* We never need to negate or complement constants. */
case NEG:
return (mode != DFmode);
case NOT:
case ZERO_EXTEND:
return 1;
case PLUS:
case MINUS:
/* Detect cases that require multiple instructions. */
if (CONSTANT_P (XEXP (op, 1))
&& !(GET_CODE (XEXP (op, 1)) == CONST_INT
&& SMALL_INT (XEXP (op, 1))))
return 0;
case EQ:
case NE:
case LT:
case GT:
case LE:
case GE:
case LTU:
case GTU:
case LEU:
case GEU:
/* Not doing floating point, since they probably
take longer than the branch slot they might fill. */
return (mode != SFmode && mode != DFmode);
case AND:
if (GET_CODE (XEXP (op, 1)) == NOT)
{
rtx arg = XEXP (XEXP (op, 1), 0);
if (CONSTANT_P (arg)
&& !(GET_CODE (arg) == CONST_INT
&& (SMALL_INT (arg)
|| INTVAL (arg) & 0xffff == 0)))
return 0;
}
case IOR:
case XOR:
/* Both small and round numbers take one instruction;
others take two. */
if (CONSTANT_P (XEXP (op, 1))
&& !(GET_CODE (XEXP (op, 1)) == CONST_INT
&& (SMALL_INT (XEXP (op, 1))
|| INTVAL (XEXP (op, 1)) & 0xffff == 0)))
return 0;
case LSHIFT:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
return 1;
case SUBREG:
if (SUBREG_WORD (op) != 0)
return 0;