aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/romp
diff options
context:
space:
mode:
authorRichard Kenner <kenner@gcc.gnu.org>1992-01-02 21:35:26 -0500
committerRichard Kenner <kenner@gcc.gnu.org>1992-01-02 21:35:26 -0500
commite6ee5154b7a515f17fdba2042e8b0873aa11c706 (patch)
tree8675f8ae284752d298652a00580ed9dc2e3f1ab1 /gcc/config/romp
parent9f682aa750be6502cf322245d9a0cb05470d302d (diff)
downloadgcc-e6ee5154b7a515f17fdba2042e8b0873aa11c706.zip
gcc-e6ee5154b7a515f17fdba2042e8b0873aa11c706.tar.gz
gcc-e6ee5154b7a515f17fdba2042e8b0873aa11c706.tar.bz2
Initial revision
From-SVN: r154
Diffstat (limited to 'gcc/config/romp')
-rw-r--r--gcc/config/romp/romp.c1898
1 files changed, 1898 insertions, 0 deletions
diff --git a/gcc/config/romp/romp.c b/gcc/config/romp/romp.c
new file mode 100644
index 0000000..8b72b57
--- /dev/null
+++ b/gcc/config/romp/romp.c
@@ -0,0 +1,1898 @@
+/* Subroutines used for code generation on ROMP.
+ Copyright (C) 1987-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"
+
+#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 reference involving a symbolic constant
+ that is not in the constant pool. */
+
+int
+symbolic_memory_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (! memory_operand (op, mode))
+ return 0;
+
+ while (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ if (GET_CODE (op) != MEM)
+ abort ();
+
+ op = XEXP (op, 0);
+ if (constant_pool_address_operand (op, VOIDmode))
+ return 0;
+ else
+ return romp_symbolic_operand (op, Pmode)
+ || (GET_CODE (op) == PLUS && register_operand (XEXP (op, 0), Pmode)
+ && romp_symbolic_operand (XEXP (op, 1), Pmode));
+}
+
+
+/* Returns 1 if OP is a constant pool reference to the current function. */
+
+int
+current_function_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+ || ! CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
+ return 0;
+
+ op = get_pool_constant (XEXP (op, 0));
+ return (GET_CODE (op) == SYMBOL_REF
+ && ! strcmp (current_function_name, XSTR (op, 0)));
+}
+
+/* Return non-zero if this function is known to have a null epilogue. */
+
+int
+null_epilogue ()
+{
+ return (reload_completed
+ && first_reg_to_save () == 16
+ && ! romp_pushes_stack ());
+}
+
+/* Returns 1 if OP is the address of a location in the constant pool. */
+
+int
+constant_pool_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op))
+ || (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (op, 0), 0))));
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
+
+int
+romp_symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return ! op->integrated;
+
+ case CONST:
+ op = XEXP (op, 0);
+ return (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns 1 if OP is a valid constant for the ROMP. */
+
+int
+constant_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (op))
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case PLUS:
+ case CONST:
+ return romp_symbolic_operand (op,mode);
+
+ case CONST_INT:
+ return (unsigned int) (INTVAL (op) + 0x8000) < 0x10000
+ || (INTVAL (op) & 0xffff) == 0 || (INTVAL (op) & 0xffff0000) == 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns 1 if OP is either a constant integer valid for the ROMP or a
+ register. If a register, it must be in the proper mode unless MODE is
+ VOIDmode. */
+
+int
+reg_or_cint_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT)
+ return constant_operand (op, mode);
+
+ return register_operand (op, mode);
+}
+
+/* Return 1 is the operand is either a register or ANY constant integer. */
+
+int
+reg_or_any_cint_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_INT || register_operand (op, mode);
+}
+
+/* Return 1 if the operand is either a register or a valid D-type operand. */
+
+int
+reg_or_D_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT)
+ return (unsigned) (INTVAL (op) + 0x8000) < 0x10000;
+
+ return register_operand (op, mode);
+}
+
+/* Return 1 if the operand is either a register or an item that can be
+ used as the operand of an SI add insn. */
+
+int
+reg_or_add_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return reg_or_D_operand (op, mode) || romp_symbolic_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0);
+}
+
+/* Return 1 if the operand is either a register or an item that can be
+ used as the operand of a ROMP logical AND insn. */
+
+int
+reg_or_and_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (reg_or_cint_operand (op, mode))
+ return 1;
+
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+
+ return (INTVAL (op) & 0xffff) == 0xffff
+ || (INTVAL (op) & 0xffff0000) == 0xffff0000;
+}
+
+/* Return 1 if the operand is a register or memory operand. */
+
+int
+reg_or_mem_operand (op, mode)
+ register rtx op;
+ register enum machine_mode mode;
+{
+ return register_operand (op, mode) || memory_operand (op, mode);
+}
+
+/* Return 1 if the operand is either a register or a memory operand that is
+ not symbolic. */
+
+int
+reg_or_nonsymb_mem_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (register_operand (op, mode))
+ return 1;
+
+ if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
+ return 1;
+
+ return 0;
+}
+
+/* Return 1 if this operand is valid for the ROMP. This is any operand except
+ certain constant integers. */
+
+int
+romp_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT)
+ return constant_operand (op, mode);
+
+ return general_operand (op, mode);
+}
+
+/* Return 1 if the operand is (reg:mode 0). */
+
+int
+reg_0_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || mode == GET_MODE (op))
+ && GET_CODE (op) == REG && REGNO (op) == 0);
+}
+
+/* Return 1 if the operand is (reg:mode 15). */
+
+int
+reg_15_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || mode == GET_MODE (op))
+ && GET_CODE (op) == REG && REGNO (op) == 15);
+}
+
+/* Return 1 if this is a binary floating-point operation. */
+
+int
+float_binary (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_MODE (op) != SFmode && GET_MODE (op) != DFmode)
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case PLUS:
+ case MINUS:
+ case MULT:
+ case DIV:
+ return GET_MODE (XEXP (op, 0)) == GET_MODE (op)
+ && GET_MODE (XEXP (op, 1)) == GET_MODE (op);
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if this is a unary floating-point operation. */
+
+int
+float_unary (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_MODE (op) != SFmode && GET_MODE (op) != DFmode)
+ return 0;
+
+ return (GET_CODE (op) == NEG || GET_CODE (op) == ABS)
+ && GET_MODE (XEXP (op, 0)) == GET_MODE (op);
+}
+
+/* Return 1 if this is a valid floating-point converstion that can be done
+ as part of an operation by the RT floating-point routines. */
+
+int
+float_conversion (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case FLOAT_TRUNCATE:
+ return GET_MODE (op) == SFmode && GET_MODE (XEXP (op, 0)) == DFmode;
+
+ case FLOAT_EXTEND:
+ return GET_MODE (op) == DFmode && GET_MODE (XEXP (op, 0)) == SFmode;
+
+ case FLOAT:
+ return ((GET_MODE (XEXP (op, 0)) == SImode
+ || GET_CODE (XEXP (op, 0)) == CONST_INT)
+ && (GET_MODE (op) == SFmode || GET_MODE (op) == DFmode));
+
+ case FIX:
+ return ((GET_MODE (op) == SImode
+ || GET_CODE (XEXP (op, 0)) == CONST_INT)
+ && (GET_MODE (XEXP (op, 0)) == SFmode
+ || GET_MODE (XEXP (op, 0)) == DFmode));
+
+ default:
+ return 0;
+ }
+}
+
+/* Print an operand. Recognize special options, documented below. */
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ char code;
+{
+ int i;
+
+ switch (code)
+ {
+ case 'B':
+ /* Byte number (const/8) */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%B value");
+
+ fprintf (file, "%d", INTVAL (x) / 8);
+ break;
+
+ case 'L':
+ /* Low order 16 bits of constant. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%L value");
+
+ fprintf (file, "%d", INTVAL (x) & 0xffff);
+ break;
+
+ case 's':
+ /* Null or "16" depending on whether the constant is greater than 16. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%s value");
+
+ if (INTVAL (x) >= 16)
+ fprintf (file, "16");
+
+ break;
+
+ case 'S':
+ /* For shifts: 's' will have given the half. Just give the amount
+ within 16. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%S value");
+
+ fprintf (file, "%d", INTVAL (x) & 15);
+ break;
+
+ case 'b':
+ /* The number of a single bit set or cleared, mod 16. Note that the ROMP
+ numbers bits with the high-order bit 31. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%b value");
+
+ if ((i = exact_log2 (INTVAL (x))) >= 0)
+ fprintf (file, "%d", (31 - i) % 16);
+ else if ((i = exact_log2 (~ INTVAL (x))) >= 0)
+ fprintf (file, "%d", (31 - i) % 16);
+ else
+ output_operand_lossage ("invalid %%b value");
+
+ break;
+
+ case 'h':
+ /* "l" or "u" depending on which half of the constant is zero. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%h value");
+
+ if ((INTVAL (x) & 0xffff0000) == 0)
+ fprintf (file, "l");
+ else if ((INTVAL (x) & 0xffff) == 0)
+ fprintf (file, "u");
+ else
+ output_operand_lossage ("invalid %%h value");
+
+ break;
+
+ case 'H':
+ /* Upper or lower half, depending on which half is zero. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%H value");
+
+ if ((INTVAL (x) & 0xffff0000) == 0)
+ fprintf (file, "%d", INTVAL (x) & 0xffff);
+ else if ((INTVAL (x) & 0xffff) == 0)
+ fprintf (file, "%d", (INTVAL (x) >> 16) & 0xffff);
+ else
+ output_operand_lossage ("invalid %%H value");
+
+ break;
+
+ case 'z':
+ /* Write two characters:
+ 'lo' if the high order part is all ones
+ 'lz' if the high order part is all zeros
+ 'uo' if the low order part is all ones
+ 'uz' if the low order part is all zeros
+ */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%z value");
+
+ if ((INTVAL (x) & 0xffff0000) == 0)
+ fprintf (file, "lz");
+ else if ((INTVAL (x) & 0xffff0000) == 0xffff0000)
+ fprintf (file, "lo");
+ else if ((INTVAL (x) & 0xffff) == 0)
+ fprintf (file, "uz");
+ else if ((INTVAL (x) & 0xffff) == 0xffff)
+ fprintf (file, "uo");
+ else
+ output_operand_lossage ("invalid %%z value");
+
+ break;
+
+ case 'Z':
+ /* Upper or lower half, depending on which is non-zero or not
+ all ones. Must be consistent with 'z' above. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%Z value");
+
+ if ((INTVAL (x) & 0xffff0000) == 0
+ || (INTVAL (x) & 0xffff0000) == 0xffff0000)
+ fprintf (file, "%d", INTVAL (x) & 0xffff);
+ else if ((INTVAL (x) & 0xffff) == 0 || (INTVAL (x) & 0xffff) == 0xffff)
+ fprintf (file, "%d", (INTVAL (x) >> 16) & 0xffff);
+ else
+ output_operand_lossage ("invalid %%Z value");
+
+ break;
+
+ case 'k':
+ /* Same as 'z', except the trailing 'o' or 'z' is not written. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%k value");
+
+ if ((INTVAL (x) & 0xffff0000) == 0
+ || (INTVAL (x) & 0xffff0000) == 0xffff0000)
+ fprintf (file, "l");
+ else if ((INTVAL (x) & 0xffff) == 0
+ || (INTVAL (x) & 0xffff) == 0xffff)
+ fprintf (file, "u");
+ else
+ output_operand_lossage ("invalid %%k value");
+
+ break;
+
+ case 't':
+ /* Similar to 's', except that we write 'h' or 'u'. */
+ if (GET_CODE (x) != CONST_INT)
+ output_operand_lossage ("invalid %%k value");
+
+ if (INTVAL (x) < 16)
+ fprintf (file, "u");
+ else
+ fprintf (file, "l");
+ break;
+
+ case 'M':
+ /* For memory operations, write 's' if the operand is a short
+ memory operand. */
+ if (short_memory_operand (x, VOIDmode))
+ fprintf (file, "s");
+ break;
+
+ case 'N':
+ /* Like 'M', but check for zero memory offset. */
+ if (zero_memory_operand (x, VOIDmode))
+ fprintf (file, "s");
+ break;
+
+ case 'O':
+ /* Write low-order part of DImode or DFmode. Supported for MEM
+ and REG only. */
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ else if (GET_CODE (x) == MEM)
+ print_operand (file, gen_rtx (MEM, GET_MODE (x),
+ plus_constant (XEXP (x, 0), 4)), 0);
+ else
+ abort ();
+ break;
+
+ case 'C':
+ /* Offset in constant pool for constant pool address. */
+ if (! constant_pool_address_operand (x, VOIDmode))
+ abort ();
+ if (GET_CODE (x) == SYMBOL_REF)
+ fprintf (file, "%d", get_pool_offset (x) + 12);
+ else
+ /* Must be (const (plus (symbol_ref) (const_int))) */
+ fprintf (file, "%d",
+ (get_pool_offset (XEXP (XEXP (x, 0), 0)) + 12
+ + INTVAL (XEXP (XEXP (x, 0), 1))));
+ break;
+
+ case 'j':
+ /* Branch opcode. Check for condition in test bit for eq/ne. */
+ switch (GET_CODE (x))
+ {
+ case EQ:
+ if (cc_status.flags & CC_IN_TB)
+ fprintf (file, "ntb");
+ else
+ fprintf (file, "eq");
+ break;
+
+ case NE:
+ if (cc_status.flags & CC_IN_TB)
+ fprintf (file, "tb");
+ else
+ fprintf (file, "ne");
+ break;
+
+ case GT:
+ case GTU:
+ fprintf (file, "h");
+ break;
+
+ case LT:
+ case LTU:
+ fprintf (file, "l");
+ break;
+
+ case GE:
+ case GEU:
+ fprintf (file, "he");
+ break;
+
+ case LE:
+ case LEU:
+ fprintf (file, "le");
+ break;
+
+ default:
+ output_operand_lossage ("invalid %%j value");
+ }
+ break;
+
+ case 'J':
+ /* Reversed branch opcode. */
+ switch (GET_CODE (x))
+ {
+ case EQ:
+ if (cc_status.flags & CC_IN_TB)
+ fprintf (file, "tb");
+ else
+ fprintf (file, "ne");
+ break;
+
+ case NE:
+ if (cc_status.flags & CC_IN_TB)
+ fprintf (file, "ntb");
+ else
+ fprintf (file, "eq");
+ break;
+
+ case GT:
+ case GTU:
+ fprintf (file, "le");
+ break;
+
+ case LT:
+ case LTU:
+ fprintf (file, "he");
+ break;
+
+ case GE:
+ case GEU:
+ fprintf (file, "l");
+ break;
+
+ case LE:
+ case LEU:
+ fprintf (file, "h");
+ break;
+
+ default:
+ output_operand_lossage ("invalid %%j value");
+ }
+ break;
+
+ case '.':
+ /* Output nothing. Used as delimeter in, e.g., "mc%B1%.3 " */
+ break;
+
+ case '#':
+ /* Output 'x' if this insn has a delay slot, else nothing. */
+ if (dbr_sequence_length ())
+ fprintf (file, "x");
+ break;
+
+ case 0:
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ else if (GET_CODE (x) == MEM)
+ {
+ if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && current_function_operand (x, Pmode))
+ fprintf (file, "r14");
+ else
+ output_address (XEXP (x, 0));
+ }
+ else
+ output_addr_const (file, x);
+ break;
+
+ default:
+ output_operand_lossage ("invalid %%xn code");
+ }
+}
+
+/* This page contains routines that are used to determine what the function
+ prologue and epilogue code will do and write them out. */
+
+/* Return the first register that is required to be saved. 16 if none. */
+
+int
+first_reg_to_save()
+{
+ int first_reg;
+
+ /* Find lowest numbered live register. */
+ for (first_reg = 6; first_reg <= 15; first_reg++)
+ if (regs_ever_live[first_reg])
+ break;
+
+ /* If we think that we do not have to save r14, see if it will be used
+ to be sure. */
+ if (first_reg > 14 && romp_using_r14 ())
+ first_reg = 14;
+
+ return first_reg;
+}
+
+/* Compute the size of the save area in the stack, including the space for
+ the first four incoming arguments. */
+
+int
+romp_sa_size ()
+{
+ int size;
+ int i;
+
+ /* We have the 4 words corresponding to the arguments passed in registers,
+ 4 reserved words, space for static chain, general register save area,
+ and floating-point save area. */
+ size = 4 + 4 + 1 + (16 - first_reg_to_save ());
+
+ /* The documentation says we have to leave 18 words in the save area if
+ any floating-point registers at all are saved, not the three words
+ per register you might otherwise expect. */
+ for (i = 2 + (TARGET_FP_REGS != 0); i <= 7; i++)
+ if (regs_ever_live[i + 17])
+ {
+ size += 18;
+ break;
+ }
+
+ return size * 4;
+}
+
+/* Return non-zero if this function makes calls or has fp operations
+ (which are really calls). */
+
+int
+romp_makes_calls ()
+{
+ rtx insn;
+
+ for (insn = get_insns (); insn; insn = next_insn (insn))
+ {
+ if (GET_CODE (insn) == CALL_INSN)
+ return 1;
+ else if (GET_CODE (insn) == INSN)
+ {
+ rtx body = PATTERN (insn);
+
+ if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER
+ && GET_CODE (body) != ADDR_VEC
+ && GET_CODE (body) != ADDR_DIFF_VEC
+ && get_attr_type (insn) == TYPE_FP)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Return non-zero if this function will use r14 as a pointer to its
+ constant pool. */
+
+int
+romp_using_r14 ()
+{
+ /* If we are debugging, profiling, have a non-empty constant pool, or
+ call a function, we need r14. */
+ return (write_symbols != NO_DEBUG || profile_flag || get_pool_size () != 0
+ || romp_makes_calls ());
+}
+
+/* Return non-zero if this function needs to push space on the stack. */
+
+int
+romp_pushes_stack ()
+{
+ /* We need to push the stack if a frame pointer is needed (because the
+ stack might be dynamically adjusted), if we are debugging, if the
+ total required size is more than 100 bytes, or if we make calls. */
+
+ return (frame_pointer_needed || write_symbols != NO_DEBUG
+ || (romp_sa_size () + get_frame_size ()) > 100
+ || romp_makes_calls ());
+}
+
+/* Write function prologue.
+
+ We compute the size of the fixed area required as follows:
+
+ We always allocate 4 words for incoming arguments, 4 word reserved, 1
+ word for static link, as many words as required for general register
+ save area, plus 2 words for each FP reg 2-7 that must be saved. */
+
+void
+output_prolog (file, size)
+ FILE *file;
+ int size;
+{
+ int first_reg;
+ int reg_save_offset;
+ rtx insn;
+ int fp_save = size + current_function_outgoing_args_size;
+
+ init_fpops ();
+
+ /* Add in fixed size plus output argument area. */
+ size += romp_sa_size () + current_function_outgoing_args_size;
+
+ /* Compute first register to save and perform the save operation if anything
+ needs to be saved. */
+ first_reg = first_reg_to_save();
+ reg_save_offset = - (4 + 4 + 1 + (16 - first_reg)) * 4;
+ if (first_reg == 15)
+ fprintf (file, "\tst r15,%d(r1)\n", reg_save_offset);
+ else if (first_reg < 16)
+ fprintf (file, "\tstm r%d,%d(r1)\n", first_reg, reg_save_offset);
+
+ /* Set up pointer to data area if it is needed. */
+ if (romp_using_r14 ())
+ fprintf (file, "\tcas r14,r0,r0\n");
+
+ /* Set up frame pointer if needed. */
+ if (frame_pointer_needed)
+ fprintf (file, "\tcal r13,-%d(r1)\n", romp_sa_size () + 64);
+
+ /* Push stack if neeeded. There are a couple of ways of doing this. */
+ if (romp_pushes_stack ())
+ {
+ if (size >= 32768)
+ {
+ if (size >= 65536)
+ {
+ fprintf (file, "\tcau r0,%d(r0)\n", size >> 16);
+ fprintf (file, "\toil r0,r0,%d\n", size & 0xffff);
+ }
+ else
+ fprintf (file, "\tcal16 r0,%d(r0)\n", size);
+ fprintf (file, "\ts r1,r0\n");
+ }
+ else
+ fprintf (file, "\tcal r1,-%d(r1)\n", size);
+ }
+
+ /* Save floating-point registers. */
+ output_loadsave_fpregs (file, USE,
+ plus_constant (stack_pointer_rtx, fp_save));
+}
+
+/* Write function epilogue. */
+
+void
+output_epilog (file, size)
+ FILE *file;
+ int size;
+{
+ int first_reg = first_reg_to_save();
+ int pushes_stack = romp_pushes_stack ();
+ int reg_save_offset = - ((16 - first_reg) + 1 + 4 + 4) * 4;
+ int total_size = (size + romp_sa_size ()
+ + current_function_outgoing_args_size);
+ int fp_save = size + current_function_outgoing_args_size;
+ int long_frame = total_size >= 32768;
+ rtx insn = get_last_insn ();
+ int write_code = 1;
+
+ int nargs = 0; /* words of arguments */
+ tree argptr;
+
+ for (argptr = DECL_ARGUMENTS (current_function_decl);
+ argptr; argptr = TREE_CHAIN (argptr))
+ nargs += ((TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (argptr)))
+ + BITS_PER_WORD - 1) / BITS_PER_WORD);
+
+ /* If the last insn was a BARRIER, we don't have to write anything except
+ the trace table. */
+ if (GET_CODE (insn) == NOTE)
+ insn = prev_nonnote_insn (insn);
+ if (insn && GET_CODE (insn) == BARRIER)
+ write_code = 0;
+
+ /* Restore floating-point registers. */
+ if (write_code)
+ output_loadsave_fpregs (file, CLOBBER,
+ gen_rtx (PLUS, Pmode, gen_rtx (REG, Pmode, 1),
+ gen_rtx (CONST_INT, VOIDmode, fp_save)));
+
+ /* If we push the stack and do not have size > 32K, adjust the register
+ save location to the current position of sp. Otherwise, if long frame,
+ restore sp from fp. */
+ if (pushes_stack && ! long_frame)
+ reg_save_offset += total_size;
+ else if (long_frame && write_code)
+ fprintf (file, "\tcal r1,%d(r13)\n", romp_sa_size () + 64);
+
+ /* Restore registers. */
+ if (first_reg == 15 && write_code)
+ fprintf (file, "\tl r15,%d(r1)\n", reg_save_offset);
+ else if (first_reg < 16 && write_code)
+ fprintf (file, "\tlm r%d,%d(r1)\n", first_reg, reg_save_offset);
+ if (first_reg == 16) first_reg = 0;
+
+ /* Handle popping stack, if needed and write debug table entry. */
+ if (pushes_stack)
+ {
+ if (write_code)
+ {
+ if (long_frame)
+ fprintf (file, "\tbr r15\n");
+ else
+ fprintf (file, "\tbrx r15\n\tcal r1,%d(r1)\n", total_size);
+ }
+ fprintf (file, "\t.long 0x%x\n", 0xdf07df08 + first_reg * 0x10);
+
+ if (nargs > 15) nargs = 15;
+ if (frame_pointer_needed)
+ fprintf (file, "\t.byte 0x%xd, 53\n", nargs);
+ else
+ fprintf (file, "\t.short 0x%x100\n", nargs);
+ }
+ else
+ {
+ if (write_code)
+ fprintf (file, "\tbr r15\n");
+ fprintf (file, "\t.long 0xdf02df00\n");
+ }
+
+ /* Output any pending floating-point operations. */
+ if (write_code)
+ output_fpops (file);
+}
+
+/* For the ROMP we need to make new SYMBOL_REFs for the actual name of a
+ called routine. To keep them unique we maintain a hash table of all
+ that have been created so far. */
+
+struct symref_hashent {
+ rtx symref; /* Created SYMBOL_REF rtx. */
+ struct symref_hashent *next; /* Next with same hash code. */
+};
+
+#define SYMHASHSIZE 151
+#define HASHBITS 65535
+
+/* Define the hash table itself. */
+
+static struct symref_hashent *symref_hash_table[SYMHASHSIZE];
+
+/* Given a name (allocatable in temporary storage), return a SYMBOL_REF
+ for the name. The rtx is allocated from the current rtl_obstack, while
+ the name string is allocated from the permanent obstack. */
+rtx
+get_symref (name)
+ register char *name;
+{
+ extern struct obstack permanent_obstack;
+ register char *sp = name;
+ unsigned int hash = 0;
+ struct symref_hashent *p, **last_p;
+
+ /* Compute the hash code for the string. */
+ while (*sp)
+ hash = (hash << 4) + *sp++;
+
+ /* Search for a matching entry in the hash table, keeping track of the
+ insertion location as we do so. */
+ hash = (hash & HASHBITS) % SYMHASHSIZE;
+ for (last_p = &symref_hash_table[hash], p = *last_p;
+ p; last_p = &p->next, p = *last_p)
+ if (strcmp (name, XSTR (p->symref, 0)) == 0)
+ break;
+
+ /* If couldn't find matching SYMBOL_REF, make a new one. */
+ if (p == 0)
+ {
+ /* Ensure SYMBOL_REF will stay around. */
+ end_temporary_allocation ();
+ p = *last_p = (struct symref_hashent *)
+ permalloc (sizeof (struct symref_hashent));
+ p->symref = gen_rtx (SYMBOL_REF, Pmode,
+ obstack_copy0 (&permanent_obstack,
+ name, strlen (name)));
+ p->next = 0;
+ resume_temporary_allocation ();
+ }
+
+ return p->symref;
+}
+
+/* Validate the precision of a floating-point operation.
+
+ We merge conversions from integers and between floating-point modes into
+ the insn. However, this must not effect the desired precision of the
+ insn. The RT floating-point system uses the widest of the operand modes.
+ If this should be a double-precision insn, ensure that one operand
+ passed to the floating-point processor has double mode.
+
+ Note that since we don't check anything if the mode is single precision,
+ it, strictly speaking, isn't necessary to call this for those insns.
+ However, we do so in case something else needs to be checked in the
+ future.
+
+ This routine returns 1 if the operation is OK. */
+
+int
+check_precision (opmode, op1, op2)
+ enum machine_mode opmode;
+ rtx op1, op2;
+{
+ if (opmode == SFmode)
+ return 1;
+
+ /* If operand is not a conversion from an integer mode or an extension from
+ single-precision, it must be a double-precision value. */
+ if (GET_CODE (op1) != FLOAT && GET_CODE (op1) != FLOAT_EXTEND)
+ return 1;
+
+ if (op2 && GET_CODE (op2) != FLOAT && GET_CODE (op2) != FLOAT_EXTEND)
+ return 1;
+
+ return 0;
+}
+
+/* Floating-point on the RT is done by creating an operation block in the data
+ area that describes the operation. If two floating-point operations are the
+ same in a single function, they can use the same block.
+
+ These routines are responsible for managing these blocks. */
+
+/* Structure to describe a floating-point operation. */
+
+struct fp_op {
+ struct fp_op *next_same_hash; /* Next op with same hash code. */
+ struct fp_op *next_in_mem; /* Next op in memory. */
+ int mem_offset; /* Offset from data area. */
+ short size; /* Size of block in bytes. */
+ short noperands; /* Number of operands in block. */
+ rtx ops[3]; /* RTL for operands. */
+ enum rtx_code opcode; /* Operation being performed. */
+};
+
+/* Size of hash table. */
+#define FP_HASH_SIZE 101
+
+/* Hash table of floating-point operation blocks. */
+static struct fp_op *fp_hash_table[FP_HASH_SIZE];
+
+/* First floating-point block in data area. */
+static struct fp_op *first_fpop;
+
+/* Last block in data area so far. */
+static struct fp_op *last_fpop_in_mem;
+
+/* Subroutine number in file, to get unique "LF" labels. */
+static int subr_number = 0;
+
+/* Current word offset in data area (includes header and any constant pool). */
+int data_offset;
+
+/* Compute hash code for an RTX used in floating-point. */
+
+static unsigned int
+hash_rtx (x)
+ register rtx x;
+{
+ register unsigned int hash = (((int) GET_CODE (x) << 10)
+ + ((int) GET_MODE (x) << 20));
+ register int i;
+ register char *fmt = GET_RTX_FORMAT (GET_CODE (x));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
+ if (fmt[i] == 'e')
+ hash += hash_rtx (XEXP (x, i));
+ else if (fmt[i] == 'u')
+ hash += (int) XEXP (x, i);
+ else if (fmt[i] == 'i')
+ hash += XINT (x, i);
+ else if (fmt[i] == 's')
+ hash += (int) XSTR (x, i);
+
+ return hash;
+}
+
+/* Given an operation code and up to three operands, return a character string
+ corresponding to the code to emit to branch to a floating-point operation
+ block. INSN is provided to see if the delay slot has been filled or not.
+
+ A new floating-point operation block is created if this operation has not
+ been seen before. */
+
+char *
+output_fpop (code, op0, op1, op2, insn)
+ enum rtx_code code;
+ rtx op0, op1, op2;
+ rtx insn;
+{
+ static char outbuf[40];
+ unsigned int hash, hash0, hash1, hash2;
+ int size, i;
+ register struct fp_op *fpop, *last_fpop;
+ int dyadic = (op2 != 0);
+ enum machine_mode opmode;
+ int noperands;
+ rtx tem;
+ unsigned int tem_hash;
+ int fr0_avail = 0;
+
+ /* Compute hash code for each operand. If the operation is commutative,
+ put the one with the smaller hash code first. This will make us see
+ more operations as identical. */
+ hash0 = op0 ? hash_rtx (op0) : 0;
+ hash1 = op1 ? hash_rtx (op1) : 0;
+ hash2 = op2 ? hash_rtx (op2) : 0;
+
+ if (hash0 > hash1 && code == EQ)
+ {
+ tem = op0; op0 = op1; op1 = tem;
+ tem_hash = hash0; hash0 = hash1; hash1 = tem_hash;
+ }
+ else if (hash1 > hash2 && (code == PLUS || code == MULT))
+ {
+ tem = op1; op1 = op2; op2 = tem;
+ tem_hash = hash1; hash1 = hash2; hash2 = tem_hash;
+ }
+
+ /* If operation is commutative and the first and third operands are equal,
+ swap the second and third operands. Note that we must consider two
+ operands equal if they are the same register even if different modes. */
+ if (op2 && (code == PLUS || code == MULT)
+ && (rtx_equal_p (op0, op2)
+ || (GET_CODE (op0) == REG && GET_CODE (op2) == REG
+ && REGNO (op0) == REGNO (op2))))
+ {
+ tem = op1; op1 = op2; op2 = tem;
+ tem_hash = hash1; hash1 = hash2; hash2 = tem_hash;
+ }
+
+ /* If the first and second operands are the same, merge them. Don't do this
+ for SFmode in general registers because this triggers a bug in the RT fp
+ code. */
+ if (op1 && rtx_equal_p (op0, op1)
+ && code != EQ && code != GE && code != SET
+ && (GET_MODE (op1) != SFmode || GET_CODE (op0) != REG
+ || FP_REGNO_P (REGNO (op0))))
+ {
+ op1 = op2;
+ op2 = 0;
+ }
+
+ noperands = 1 + (op1 != 0) + (op2 != 0);
+
+ /* Compute hash code for entire expression and see if operation block
+ already exists. */
+ hash = ((int) code << 13) + (hash0 << 2) + (hash1 << 1) + hash2;
+
+ hash %= FP_HASH_SIZE;
+ for (fpop = fp_hash_table[hash], last_fpop = 0;
+ fpop;
+ last_fpop = fpop, fpop = fpop->next_same_hash)
+ if (fpop->opcode == code && noperands == fpop->noperands
+ && (op0 == 0 || rtx_equal_p (op0, fpop->ops[0]))
+ && (op1 == 0 || rtx_equal_p (op1, fpop->ops[1]))
+ && (op2 == 0 || rtx_equal_p (op2, fpop->ops[2])))
+ goto win;
+
+ /* We have never seen this operation before. */
+ fpop = (struct fp_op *) oballoc (sizeof (struct fp_op));
+ fpop->mem_offset = data_offset;
+ fpop->opcode = code;
+ fpop->noperands = noperands;
+ fpop->ops[0] = op0;
+ fpop->ops[1] = op1;
+ fpop->ops[2] = op2;
+
+ /* Compute the size using the rules in Appendix A of the RT Linkage
+ Convention (4.3/RT-PSD:5) manual. These rules are a bit ambiguous,
+ but if we guess wrong, it will effect only efficiency, not correctness. */
+
+ /* Size = 24 + 32 for each non-fp (or fr7) */
+ size = 24;
+ if (op0 && (GET_CODE (op0) != REG
+ || ! FP_REGNO_P (REGNO (op0)) || REGNO (op0) == 23))
+ size += 32;
+
+ if (op1 && (GET_CODE (op1) != REG
+ || ! FP_REGNO_P (REGNO (op1)) || REGNO (op1) == 23))
+ size += 32;
+
+ if (op2 && (GET_CODE (op2) != REG
+ || ! FP_REGNO_P (REGNO (op2)) || REGNO (op2) == 23))
+ size += 32;
+
+ /* Size + 12 for each conversion. First get operation mode. */
+ if ((op0 && GET_MODE (op0) == DFmode)
+ || (op1 && GET_MODE (op1) == DFmode)
+ || (op2 && GET_MODE (op2) == DFmode))
+ opmode = DFmode;
+ else
+ opmode = SFmode;
+
+ if (op0 && GET_MODE (op0) != opmode)
+ size += 12;
+ if (op1 && GET_MODE (op1) != opmode)
+ size += 12;
+ if (op2 && GET_MODE (op2) != opmode)
+ size += 12;
+
+ /* 12 more if first and third operand types not the same. */
+ if (op2 && GET_MODE (op0) != GET_MODE (op2))
+ size += 12;
+
+ /* CMP and CMPT need additional. Also, compute size of save/restore here. */
+ if (code == EQ)
+ size += 32;
+ else if (code == GE)
+ size += 64;
+ else if (code == USE || code == CLOBBER)
+ {
+ /* 34 + 24 for each additional register plus 8 if fr7 saved. (We
+ call it 36 because we need to keep the block length a multiple
+ of four. */
+ size = 36 - 24;
+ for (i = 0; i <= 7; i++)
+ if (INTVAL (op0) & (1 << (7-i)))
+ size += 24 + 8 * (i == 7);
+ }
+
+ /* We provide no general-purpose scratch registers. */
+ size +=16;
+
+ /* No floating-point scratch registers are provided. Compute extra
+ length due to this. This logic is that shown in the referenced
+ appendix. */
+
+ i = 0;
+ if (op0 && GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0)))
+ i++;
+ if (op1 && GET_CODE (op1) == REG && FP_REGNO_P (REGNO (op1)))
+ i++;
+ if (op2 && GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
+ i++;
+
+ if ((op0 == 0 || GET_CODE (op0) != REG || REGNO(op0) != 17)
+ && (op1 == 0 || GET_CODE (op1) != REG || REGNO(op1) != 17)
+ && (op2 == 0 || GET_CODE (op2) != REG || REGNO(op2) != 17))
+ fr0_avail = 1;
+
+ if (dyadic)
+ {
+ if (i == 0)
+ size += fr0_avail ? 64 : 112;
+ else if (fpop->noperands == 2 && i == 1)
+ size += fr0_avail ? 0 : 64;
+ else if (fpop->noperands == 3)
+ {
+ if (GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0))
+ && GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
+ {
+ if (REGNO (op0) == REGNO (op2))
+#if 1
+ /* This triggers a bug on the RT. */
+ abort ();
+#else
+ size += fr0_avail ? 0 : 64;
+#endif
+ }
+ else
+ {
+ i = 0;
+ if (GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0)))
+ i++;
+ if (GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
+ i++;
+ if (i == 0)
+ size += fr0_avail ? 64 : 112;
+ else if (i == 1)
+ size += fr0_avail ? 0 : 64;
+ }
+ }
+ }
+ else if (code != USE && code != CLOBBER
+ && (GET_CODE (op0) != REG || ! FP_REGNO_P (REGNO (op0))))
+ size += 64;
+
+ if (! TARGET_FULL_FP_BLOCKS)
+ {
+ /* If we are not to pad the blocks, just compute its actual length. */
+ size = 12; /* Header + opcode */
+ if (code == USE || code == CLOBBER)
+ size += 2;
+ else
+ {
+ if (op0) size += 2;
+ if (op1) size += 2;
+ if (op2) size += 2;
+ }
+
+ /* If in the middle of a word, round. */
+ if (size % UNITS_PER_WORD)
+ size += 2;
+
+ /* Handle any immediates. */
+ if (code != USE && code != CLOBBER && op0 && GET_CODE (op0) != REG)
+ size += 4;
+ if (op1 && GET_CODE (op1) != REG)
+ size += 4;
+ if (op2 && GET_CODE (op2) != REG)
+ size += 4;
+
+ if (code != USE && code != CLOBBER &&
+ op0 && GET_CODE (op0) == CONST_DOUBLE && GET_MODE (op0) == DFmode)
+ size += 4;
+ if (op1 && GET_CODE (op1) == CONST_DOUBLE && GET_MODE (op1) == DFmode)
+ size += 4;
+ if (op2 && GET_CODE (op2) == CONST_DOUBLE && GET_MODE (op2) == DFmode)
+ size += 4;
+ }
+
+ /* Done with size computation! Chain this in. */
+ fpop->size = size;
+ data_offset += size / UNITS_PER_WORD;
+ fpop->next_in_mem = 0;
+ fpop->next_same_hash = 0;
+
+ if (last_fpop_in_mem)
+ last_fpop_in_mem->next_in_mem = fpop;
+ else
+ first_fpop = fpop;
+ last_fpop_in_mem = fpop;
+
+ if (last_fpop)
+ last_fpop->next_same_hash = fpop;
+ else
+ fp_hash_table[hash] = fpop;
+
+win:
+ /* FPOP describes the operation to be performed. Return a string to branch
+ to it. */
+ if (fpop->mem_offset < 32768 / UNITS_PER_WORD)
+ sprintf (outbuf, "cal r15,%d(r14)\n\tbalr%s r15,r15",
+ fpop->mem_offset * UNITS_PER_WORD,
+ dbr_sequence_length () ? "x" : "");
+ else
+ sprintf (outbuf, "get r15,$L%dF%d\n\tbalr%s r15,r15",
+ subr_number, fpop->mem_offset * UNITS_PER_WORD,
+ dbr_sequence_length () ? "x" : "");
+ return outbuf;
+}
+
+/* If necessary, output a floating-point operation to save or restore all
+ floating-point registers.
+
+ file is the file to write the operation to, CODE is USE for save, CLOBBER
+ for restore, and ADDR is the address of the same area, as RTL. */
+
+static void
+output_loadsave_fpregs (file, code, addr)
+ FILE *file;
+ enum rtx_code code;
+ rtx addr;
+{
+ register int i;
+ register int mask = 0;
+
+ for (i = 2 + (TARGET_FP_REGS != 0); i <= 7; i++)
+ if (regs_ever_live[i + 17])
+ mask |= 1 << (7 - i);
+
+ if (mask)
+ fprintf (file, "\t%s\n",
+ output_fpop (code, gen_rtx (CONST_INT, VOIDmode, mask),
+ gen_rtx (MEM, Pmode, addr),
+ 0, const0_rtx));
+
+}
+
+/* Output any floating-point operations at the end of the routine. */
+
+static void
+output_fpops (file)
+ FILE *file;
+{
+ register struct fp_op *fpop;
+ register int size_so_far;
+ register int i;
+ rtx immed[3];
+
+ if (first_fpop == 0)
+ return;
+
+ data_section ();
+
+ ASM_OUTPUT_ALIGN (file, 2);
+
+ for (fpop = first_fpop; fpop; fpop = fpop->next_in_mem)
+ {
+ if (fpop->mem_offset < 32768 / UNITS_PER_WORD)
+ fprintf (file, "# data area offset = %d\n",
+ fpop->mem_offset * UNITS_PER_WORD);
+ else
+ fprintf (file, "L%dF%d:\n",
+ subr_number, fpop->mem_offset * UNITS_PER_WORD);
+
+ fprintf (file, "\tcas r0,r15,r0\n");
+ fprintf (file, "\t.long FPGLUE\n");
+ switch (fpop->opcode)
+ {
+ case USE:
+ fprintf (file, "\t.byte 0x1d\t# STOREM\n");
+ break;
+ case CLOBBER:
+ fprintf (file, "\t.byte 0x0f\t# LOADM\n");
+ break;
+ case ABS:
+ fprintf (file, "\t.byte 0x00\t# ABS\n");
+ break;
+ case PLUS:
+ fprintf (file, "\t.byte 0x02\t# ADD\n");
+ break;
+ case EQ:
+ fprintf (file, "\t.byte 0x07\t# CMP\n");
+ break;
+ case GE:
+ fprintf (file, "\t.byte 0x08\t# CMPT\n");
+ break;
+ case DIV:
+ fprintf (file, "\t.byte 0x0c\t# DIV\n");
+ break;
+ case SET:
+ fprintf (file, "\t.byte 0x14\t# MOVE\n");
+ break;
+ case MULT:
+ fprintf (file, "\t.byte 0x15\t# MUL\n");
+ break;
+ case NEG:
+ fprintf (file, "\t.byte 0x16\t# NEG\n");
+ break;
+ case SQRT:
+ fprintf (file, "\t.byte 0x1c\t# SQRT\n");
+ break;
+ case MINUS:
+ fprintf (file, "\t.byte 0x1e\t# SUB\n");
+ break;
+ default:
+ abort ();
+ }
+
+ fprintf (file, "\t.byte %d\n", fpop->noperands);
+ fprintf (file, "\t.short 0x8001\n");
+
+ if ((fpop->ops[0] == 0
+ || GET_CODE (fpop->ops[0]) != REG || REGNO(fpop->ops[0]) != 17)
+ && (fpop->ops[1] == 0 || GET_CODE (fpop->ops[1]) != REG
+ || REGNO(fpop->ops[1]) != 17)
+ && (fpop->ops[2] == 0 || GET_CODE (fpop->ops[2]) != REG
+ || REGNO(fpop->ops[2]) != 17))
+ fprintf (file, "\t.byte %d, 0x80\n", fpop->size);
+ else
+ fprintf (file, "\t.byte %d, 0\n", fpop->size);
+ size_so_far = 12;
+ for (i = 0; i < fpop->noperands; i++)
+ {
+ register int type;
+ register int opbyte;
+ register char *desc0;
+ char desc1[50];
+
+ immed[i] = 0;
+ switch (GET_MODE (fpop->ops[i]))
+ {
+ case SImode:
+ case VOIDmode:
+ desc0 = "int";
+ type = 0;
+ break;
+ case SFmode:
+ desc0 = "float";
+ type = 2;
+ break;
+ case DFmode:
+ desc0 = "double";
+ type = 3;
+ break;
+ default:
+ abort ();
+ }
+
+ switch (GET_CODE (fpop->ops[i]))
+ {
+ case REG:
+ strcpy(desc1, reg_names[REGNO (fpop->ops[i])]);
+ if (FP_REGNO_P (REGNO (fpop->ops[i])))
+ {
+ type += 0x10;
+ opbyte = REGNO (fpop->ops[i]) - 17;
+ }
+ else
+ {
+ type += 0x00;
+ opbyte = REGNO (fpop->ops[i]);
+ if (type == 3)
+ opbyte = (opbyte << 4) + opbyte + 1;
+ }
+ break;
+
+ case MEM:
+ type += 0x30;
+ if (GET_CODE (XEXP (fpop->ops[i], 0)) == PLUS)
+ {
+ immed[i] = XEXP (XEXP (fpop->ops[i], 0), 1);
+ opbyte = REGNO (XEXP (XEXP (fpop->ops[i], 0), 0));
+ if (GET_CODE (immed[i]) == CONST_INT)
+ sprintf (desc1, "%d(%s)", INTVAL (immed[i]),
+ reg_names[opbyte]);
+ else
+ sprintf (desc1, "<memory> (%s)", reg_names[opbyte]);
+ }
+ else if (GET_CODE (XEXP (fpop->ops[i], 0)) == REG)
+ {
+ opbyte = REGNO (XEXP (fpop->ops[i], 0));
+ immed[i] = const0_rtx;
+ sprintf (desc1, "(%s)", reg_names[opbyte]);
+ }
+ else
+ {
+ immed[i] = XEXP (fpop->ops[i], 0);
+ opbyte = 0;
+ sprintf(desc1, "<memory>");
+ }
+ break;
+
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ type += 0x20;
+ opbyte = 0;
+ immed[i] = fpop->ops[i];
+ desc1[0] = '$';
+ desc1[1] = '\0';
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Save/restore is special. */
+ if (i == 0 && (fpop->opcode == USE || fpop->opcode == CLOBBER))
+ type = 0xff, opbyte = INTVAL (fpop->ops[0]), immed[i] = 0;
+
+ fprintf (file, "\t.byte 0x%x,0x%x # (%s) %s\n",
+ type, opbyte, desc0, desc1);
+
+ size_so_far += 2;
+ }
+
+ /* If in the middle of a word, round. */
+ if (size_so_far % UNITS_PER_WORD)
+ {
+ fprintf (file, "\t.space 2\n");
+ size_so_far += 2;
+ }
+
+ for (i = 0; i < fpop->noperands; i++)
+ if (immed[i])
+ switch (GET_MODE (immed[i]))
+ {
+ case SImode:
+ case VOIDmode:
+ size_so_far += 4;
+ fprintf (file, "\t.long ");
+ output_addr_const (file, immed[i]);
+ fprintf (file, "\n");
+ break;
+
+ case DFmode:
+ size_so_far += 4;
+ case SFmode:
+ size_so_far += 4;
+ if (GET_CODE (immed[i]) == CONST_DOUBLE)
+ {
+ union real_extract u;
+
+ bcopy (&CONST_DOUBLE_LOW (immed[i]), &u, sizeof u);
+ if (GET_MODE (immed[i]) == DFmode)
+ ASM_OUTPUT_DOUBLE (file, u.d);
+ else
+ ASM_OUTPUT_FLOAT (file, u.d);
+ }
+ else
+ abort ();
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (size_so_far != fpop->size)
+ {
+ if (TARGET_FULL_FP_BLOCKS)
+ fprintf (file, "\t.space %d\n", fpop->size - size_so_far);
+ else
+ abort ();
+ }
+ }
+
+ /* Update for next subroutine. */
+ subr_number++;
+ text_section ();
+}
+
+ /* Initialize floating-point operation table. */
+
+static void
+init_fpops()
+{
+ register int i;
+
+ first_fpop = last_fpop_in_mem = 0;
+ for (i = 0; i < FP_HASH_SIZE; i++)
+ fp_hash_table[i] = 0;
+}