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
/
romp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
49KB
|
1,900 lines
/* Subroutines used for code generation on ROMP.
Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@nyu.edu)
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 <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 "flags.h"
#include "recog.h"
#include "expr.h"
#include "obstack.h"
#include "tree.h"
#define min(A,B) ((A) < (B) ? (A) : (B))
#define max(A,B) ((A) > (B) ? (A) : (B))
static int unsigned_comparisons_p ();
static void output_loadsave_fpregs ();
static void output_fpops ();
static void init_fpops ();
/* Return 1 if the insn using CC0 set by INSN does not contain
any unsigned tests applied to the condition codes.
Based on `next_insn_tests_no_inequality' in recog.c. */
int
next_insn_tests_no_unsigned (insn)
rtx insn;
{
register rtx next = next_cc0_user (insn);
if (next == 0)
{
if (find_reg_note (insn, REG_UNUSED, cc0_rtx))
return 1;
else
abort ();
}
return ((GET_CODE (next) == JUMP_INSN
|| GET_CODE (next) == INSN
|| GET_CODE (next) == CALL_INSN)
&& ! unsigned_comparisons_p (PATTERN (next)));
}
static int
unsigned_comparisons_p (x)
rtx x;
{
register char *fmt;
register int len, i;
register enum rtx_code code = GET_CODE (x);
switch (code)
{
case REG:
case PC:
case CC0:
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
return 0;
case LTU:
case GTU:
case LEU:
case GEU:
return (XEXP (x, 0) == cc0_rtx || XEXP (x, 1) == cc0_rtx);
}
len = GET_RTX_LENGTH (code);
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < len; i++)
{
if (fmt[i] == 'e')
{
if (unsigned_comparisons_p (XEXP (x, i)))
return 1;
}
else if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (unsigned_comparisons_p (XVECEXP (x, i, j)))
return 1;
}
}
return 0;
}
/* Update the condition code from the insn. Look mostly at the first
byte of the machine-specific insn description information.
cc_state.value[12] refer to two possible values that might correspond
to the CC. We only store register values. */
update_cc (body, insn)
rtx body;
rtx insn;
{
switch (get_attr_cc (insn))
{
case CC_NONE:
/* Insn does not affect the CC at all. */
break;
case CC_CHANGE0:
/* Insn doesn't affect the CC but does modify operand[0], known to be
a register. */
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value2))
cc_status.value2 = 0;
break;
case CC_COPY1TO0:
/* Insn copies operand[1] to operand[0], both registers, but doesn't
affect the CC. */
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_operand[0], cc_status.value2))
cc_status.value2 = 0;
if (cc_status.value1 != 0
&& rtx_equal_p (cc_status.value1, recog_operand[1]))
cc_status.value2 = recog_operand[0];
if (cc_status.value2 != 0
&& rtx_equal_p (cc_status.value2, recog_operand[1]))
cc_status.value1 = recog_operand[0];
break;
case CC_CLOBBER:
/* Insn clobbers CC. */
CC_STATUS_INIT;
break;
case CC_SETS:
/* Insn sets CC to recog_operand[0], but overflow is impossible. */
CC_STATUS_INIT;
cc_status.flags |= CC_NO_OVERFLOW;
cc_status.value1 = recog_operand[0];
break;
case CC_COMPARE:
/* Insn is a compare which sets the CC fully. Update CC_STATUS for this
compare and mark whether the test will be signed or unsigned. */
{
register rtx p = PATTERN (insn);
CC_STATUS_INIT;
if (GET_CODE (p) == PARALLEL)
p = XVECEXP (p, 0, 0);
cc_status.value1 = SET_SRC (p);
if (GET_CODE (SET_SRC (p)) == REG)
cc_status.flags |= CC_NO_OVERFLOW;
if (! next_insn_tests_no_unsigned (insn))
cc_status.flags |= CC_UNSIGNED;
}
break;
case CC_TBIT:
/* Insn sets T bit if result is non-zero. Next insn must be branch. */
CC_STATUS_INIT;
cc_status.flags = CC_IN_TB | CC_NOT_NEGATIVE;
break;
default:
abort ();
}
}
/* Return 1 if a previous compare needs to be re-issued. This will happen
if two compares tested the same objects, but one was signed and the
other unsigned. OP is the comparison operation being performed. */
int
restore_compare_p (op)
rtx op;
{
enum rtx_code code = GET_CODE (op);
return (((code == GEU || code == LEU || code == GTU || code == LTU)
&& ! (cc_status.flags & CC_UNSIGNED))
|| ((code == GE || code == LE || code == GT || code == LT)
&& (cc_status.flags & CC_UNSIGNED)));
}
/* Generate the (long) string corresponding to an inline multiply insn.
Note that `r10' does not refer to the register r10, but rather to the
SCR used as the MQ. */
char *
output_in_line_mul ()
{
static char insns[200];
int i;
strcpy (insns, "s %0,%0\n");
strcat (insns, "\tmts r10,%1\n");
for (i = 0; i < 16; i++)
strcat (insns, "\tm %0,%2\n");
strcat (insns, "\tmfs r10,%0");
return insns;
}
/* Returns 1 if OP is a memory reference with an offset from a register within
the range specified. The offset must also be a multiple of the size of the
mode. */
static int
memory_offset_in_range_p (op, mode, low, high)
register rtx op;
enum machine_mode mode;
int low, high;
{
int offset = 0;
if (! memory_operand (op, mode))
return 0;
while (GET_CODE (op) == SUBREG)
{
offset += SUBREG_WORD (op) * UNITS_PER_WORD;
#if BYTES_BIG_ENDIAN
offset -= (min (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
- min (UNITS_PER_WORD,
GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))));
#endif
op = SUBREG_REG (op);
}
/* We must now have either (mem (reg (x)), (mem (plus (reg (x)) (c))),
or a constant pool address. */
if (GET_CODE (op) != MEM)
abort ();
/* Now use the actual mode and get the address. */
mode = GET_MODE (op);
op = XEXP (op, 0);
if (GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op))
offset = get_pool_offset (op) + 12;
else if (GET_CODE (op) == PLUS)
{
if (GET_CODE (XEXP (op, 1)) != CONST_INT
|| ! register_operand (XEXP (op, 0), Pmode))
return 0;
offset += INTVAL (XEXP (op, 1));
}
else if (! register_operand (op, Pmode))
return 0;
return (offset >= low && offset <= high
&& (offset % GET_MODE_SIZE (mode) == 0));
}
/* Return 1 if OP is a valid operand for a memory reference insn that can
only reference indirect through a register. */
int
zero_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return memory_offset_in_range_p (op, mode, 0, 0);
}
/* Return 1 if OP is a valid operand for a `short' memory reference insn. */
int
short_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode == VOIDmode)
mode = GET_MODE (op);
return memory_offset_in_range_p (op, mode, 0,
15 * min (UNITS_PER_WORD,
GET_MODE_SIZE (mode)));
}
/* Returns 1 if OP is a memory referen