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
/
rs6000.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
50KB
|
1,805 lines
/* Subroutines used for code generation on IBM RS/6000.
Copyright (C) 1991 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"
extern char *language_string;
#define min(A,B) ((A) < (B) ? (A) : (B))
#define max(A,B) ((A) > (B) ? (A) : (B))
/* Set to non-zero by "fix" operation to indicate that itrunc and
uitrunc must be defined. */
int rs6000_trunc_used;
/* Set to non-zero once they have been defined. */
static int trunc_defined;
/* Save information from a "cmpxx" operation until the branch or scc is
emitted. */
rtx rs6000_compare_op0, rs6000_compare_op1;
int rs6000_compare_fp_p;
/* Return non-zero if this function is known to have a null epilogue. */
int
direct_return ()
{
return (reload_completed
&& first_reg_to_save () == 32
&& first_fp_reg_to_save () == 64
&& ! regs_ever_live[65]
&& ! rs6000_pushes_stack ());
}
/* Returns 1 always. */
int
any_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return 1;
}
/* Return 1 if OP is a constant that can fit in a D field. */
int
short_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& (unsigned) (INTVAL (op) + 0x8000) < 0x10000);
}
/* Similar for a unsigned D field. */
int
u_short_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0);
}
/* Return 1 if OP is a CONST_INT that cannot fit in a signed D field. */
int
non_short_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& (unsigned) (INTVAL (op) + 0x8000) >= 0x10000);
}
/* Returns 1 if OP is a register that is not special (i.e., not MQ,
ctr, or lr). */
int
gpc_reg_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
&& (GET_CODE (op) != REG || REGNO (op) >= 67 || REGNO (op) < 64));
}
/* Returns 1 if OP is either a pseudo-register or a register denoting a
CR field. */
int
cc_reg_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
&& (GET_CODE (op) != REG
|| REGNO (op) >= FIRST_PSEUDO_REGISTER
|| CR_REGNO_P (REGNO (op))));
}
/* Returns 1 if OP is either a constant integer valid for a D-field or a
non-special register. If a register, it must be in the proper mode unless
MODE is VOIDmode. */
int
reg_or_short_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return short_cint_operand (op, mode);
return gpc_reg_operand (op, mode);
}
/* Similar, except check if the negation of the constant would be valid for
a D-field. */
int
reg_or_neg_short_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P');
return gpc_reg_operand (op, mode);
}
/* Return 1 if the operand is either a register or an integer whose high-order
16 bits are zero. */
int
reg_or_u_short_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT
&& (INTVAL (op) & 0xffff0000) == 0)
return 1;
return gpc_reg_operand (op, mode);
}
/* Return 1 is the operand is either a non-special register or ANY
constant integer. */
int
reg_or_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return GET_CODE (op) == CONST_INT || gpc_reg_operand (op, mode);
}
/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a
register with one instruction per word. For SFmode, this means that
the low 16-bits are zero. For DFmode, it means the low 16-bits of
the first word are zero and the high 16 bits of the second word
are zero (usually all bits in the low-order word will be zero).
We only do this if we can safely read CONST_DOUBLE_{LOW,HIGH}. */
int
easy_fp_constant (op, mode)
register rtx op;
register enum machine_mode mode;
{
rtx low, high;
if (GET_CODE (op) != CONST_DOUBLE
|| GET_MODE (op) != mode
|| GET_MODE_CLASS (mode) != MODE_FLOAT)
return 0;
high = operand_subword (op, 0, 0, mode);
low = operand_subword (op, 1, 0, mode);
if (high == 0 || GET_CODE (high) != CONST_INT || (INTVAL (high) & 0xffff))
return 0;
return (mode == SFmode
|| (low != 0 && GET_CODE (low) == CONST_INT
&& (INTVAL (low) & 0xffff0000) == 0));
}
/* Return 1 if the operand is either a floating-point register, a pseudo
register, or memory. */
int
fp_reg_or_mem_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (memory_operand (op, mode)
|| (register_operand (op, mode)
&& (GET_CODE (op) != REG
|| REGNO (op) >= FIRST_PSEUDO_REGISTER
|| FP_REGNO_P (REGNO (op)))));
}
/* Return 1 if the operand is either an easy FP constant (see above) or
memory. */
int
mem_or_easy_const_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return memory_operand (op, mode) || easy_fp_constant (op, mode);
}
/* Return 1 if the operand is either a non-special register or an item
that can be used as the operand of an SI add insn. */
int
add_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (reg_or_short_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0));
}
/* Return 1 if OP is a constant but not a valid add_operand. */
int
non_add_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& (unsigned) (INTVAL (op) + 0x8000) >= 0x10000
&& (INTVAL (op) & 0xffff) != 0);
}
/* Return 1 if the operand is a non-special register or a constant that
can be used as the operand of an OR or XOR insn on the RS/6000. */
int
logical_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (gpc_reg_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& ((INTVAL (op) & 0xffff0000) == 0
|| (INTVAL (op) & 0xffff) == 0)));
}
/* Return 1 if C is a constant that is not a logical operand (as
above). */
int
non_logical_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& (INTVAL (op) & 0xffff0000) != 0
&& (INTVAL (op) & 0xffff) != 0);
}
/* Return 1 if C is a constant that can be encoded in a mask on the
RS/6000. It is if there are no more than two 1->0 or 0->1 transitions.
Reject all ones and all zeros, since these should have been optimized
away and confuse the making of MB and ME. */
int
mask_constant (c)
register int c;
{
int i;
int last_bit_value;
int transitions = 0;
if (c == 0 || c == ~0)
return 0;
last_bit_value = c & 1;
for (i = 1; i < 32; i++)
if (((c >>= 1) & 1) != last_bit_value)
last_bit_value ^= 1, transitions++;
return transitions <= 2;
}
/* Return 1 if the operand is a constant that is a mask on the RS/6000. */
int
mask_operand (op, mode)
register rtx op;