--- /dev/null
+/* Subroutines used for code generation on AMD Am29000.
+ Copyright (C) 1987, 1988, 1990, 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))
+
+/* This gives the size in words of the register stack for the current
+ procedure. */
+
+static int a29k_regstack_size;
+
+/* This points to the last insn of the insn prologue. It is set when
+ an insn without a filled delay slot is found near the start of the
+ function. */
+
+static char *a29k_last_prologue_insn;
+
+/* This points to the first insn that will be in the epilogue. It is null if
+ no epilogue is required. */
+
+static char *a29k_first_epilogue_insn;
+
+/* This is nonzero if a a29k_first_epilogue_insn was put in a delay slot. It
+ indicates that an intermediate label needs to be written. */
+
+static int a29k_first_epilogue_insn_used;
+
+/* Location to hold the name of the current function. We need this prolog to
+ contain the tag words prior to the declaration. So the name must be stored
+ away. */
+
+char *a29k_function_name;
+
+/* Mapping of registers to debug register numbers. The only change is
+ for the frame pointer and the register numbers used for the incoming
+ arguments. */
+
+int a29k_debug_reg_map[FIRST_PSEUDO_REGISTER];
+
+/* Save information from a "cmpxx" operation until the branch or scc is
+ emitted. */
+
+rtx a29k_compare_op0, a29k_compare_op1;
+int a29k_compare_fp_p;
+
+/* Gives names for registers. */
+extern char *reg_names[];
+\f
+/* Returns 1 if OP is a 8-bit constant. */
+
+int
+cint_8_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffffff00) == 0;
+}
+
+/* Returns 1 if OP is a 16-bit constant. */
+
+int
+cint_16_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0;
+}
+
+/* Returns 1 if OP cannot be moved in a single insn. */
+
+int
+long_const_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (! CONSTANT_P (op))
+ return 0;
+
+ if (TARGET_29050 && GET_CODE (op) == CONST_INT
+ && (INTVAL (op) & 0xffff) == 0)
+ return 0;
+
+ return (GET_CODE (op) != CONST_INT
+ || ((INTVAL (op) & 0xffff0000) != 0
+ && (INTVAL (op) & 0xffff0000) != 0xffff0000
+ && INTVAL (op) != 0x80000000));
+}
+\f
+/* The following four functions detect constants of 0, 8, 16, and 24 used as
+ a position in ZERO_EXTRACT operations. They can either be the appropriate
+ constant integer or a shift (which will be produced by combine). */
+
+static int
+shift_constant_operand (op, mode, val)
+ rtx op;
+ enum machine_mode mode;
+ int val;
+{
+ return ((GET_CODE (op) == CONST_INT && INTVAL (op) == val)
+ || (GET_CODE (op) == ASHIFT
+ && GET_CODE (XEXP (op, 0)) == CONST_INT
+ && INTVAL (XEXP (op, 0)) == val / 8
+ && GET_CODE (XEXP (op, 1)) == CONST_INT
+ && INTVAL (XEXP (op, 1)) == 3));
+}
+
+int
+const_0_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return shift_constant_operand (op, mode, 0);
+}
+
+int
+const_8_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return shift_constant_operand (op, mode, 8);
+}
+
+int
+const_16_operand (op, mode)
+ rtx op;
+ enum machine_mode;
+{
+ return shift_constant_operand (op, mode, 16);
+}
+
+int
+const_24_operand (op, mode)
+ rtx op;
+ enum machine_mode;
+{
+ return shift_constant_operand (op, mode, 24);
+}
+
+/* Returns 1 if OP is a floating-point constant of the proper mode. */
+
+int
+float_const_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == mode;
+}
+
+/* Returns 1 if OP is a floating-point constant of the proper mode or a
+ general-purpose register. */
+
+int
+gen_reg_or_float_constant_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return float_const_operand (op, mode) || gen_reg_operand (op, mode);
+}
+
+/* Returns 1 if OP is an integer constant of the proper mode or a
+ general-purpose register. */
+
+int
+gen_reg_or_integer_constant_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((GET_MODE (op) == VOIDmode
+ && (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE))
+ || gen_reg_operand (op, mode));
+}
+\f
+/* Returns 1 if OP is a special machine register. */
+
+int
+spec_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return GET_MODE (op) == SImode && GET_CODE (op) == REG
+ && REGNO (op) >= R_BP && REGNO (op) <= R_EXO;
+}
+
+/* Returns 1 if OP is an accumulator register. */
+
+int
+accum_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == REG
+ && REGNO (op) >= R_ACC (0) && REGNO (op) <= R_ACC (3));
+}
+
+/* Returns 1 if OP is a normal data register. */
+
+int
+gen_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int regno;
+
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == REG)
+ regno = REGNO (op);
+ else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG)
+ {
+ regno = REGNO (SUBREG_REG (op));
+ if (regno < FIRST_PSEUDO_REGISTER)
+ regno += SUBREG_WORD (op);
+ }
+ else
+ return 0;
+
+ return regno >= FIRST_PSEUDO_REGISTER || regno < R_BP;
+}
+
+/* Returns 1 if OP is either an 8-bit constant integer or a general register.
+ If a register, it must be in the proper mode unless MODE is VOIDmode. */
+
+int
+srcb_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT
+ && (mode == QImode
+ || (INTVAL (op) & 0xffffff00) == 0))
+ return 1;
+
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ return gen_reg_operand (op, mode);
+}
+
+/* Return 1 if OP is either an immediate or a general register. This is used
+ for the input operand of mtsr/mtrsim. */
+
+int
+gen_reg_or_immediate_operand (op, mode)
+ rtx op;
+ enum machine_mode;
+{
+ return gen_reg_operand (op, mode) || immediate_operand (op, mode);
+}
+
+/* Return 1 if OP can be used as the second operand of and AND insn. This
+ includes srcb_operand and a constant whose complement fits in 8 bits. */
+
+int
+and_operand (op, mode)
+ rtx op;
+ enum machine_mode;
+{
+ return (srcb_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT
+ && ((unsigned) ((~ INTVAL (op)) & GET_MODE_MASK (mode)) < 256)));
+}
+
+/* Return 1 if OP can be used as the second operand of an ADD insn.
+ This is the same as above, except we use negative, rather than
+ complement. */
+
+int
+add_operand (op, mode)
+ rtx op;
+ enum machine_mode;
+{
+ return (srcb_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT
+ && ((unsigned) ((- INTVAL (op)) & GET_MODE_MASK (mode)) < 256)));
+}
+\f
+/* Return 1 if OP can be used as the input operand for a move insn. */
+
+int
+in_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ rtx orig_op = op;
+
+ if (! general_operand (op, mode))
+ return 0;
+
+ while (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return 1;
+
+ case MEM:
+ return (GET_MODE_SIZE (mode) >= UNITS_PER_WORD || TARGET_DW_ENABLE);
+
+ case CONST_INT:
+ if (GET_MODE_CLASS (mode) != MODE_INT)
+ return 0;
+
+ return 1;
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return (GET_MODE (op) == mode
+ || mode == SImode || mode == HImode || mode == QImode);
+
+ case CONST_DOUBLE:
+ return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ && mode == GET_MODE (op))
+ || (GET_MODE (op) == VOIDmode
+ && GET_MODE_CLASS (mode) == MODE_INT));
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if OP can be used as the output operand for a move insn. */
+
+int
+out_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ rtx orig_op = op;
+
+ if (! general_operand (op, mode))
+ return 0;
+
+ while (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ if (GET_CODE (op) == REG)
+ return (mode == SImode || gen_reg_operand (orig_op, mode)
+ || (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && accum_reg_operand (orig_op, mode)));
+
+ else if (GET_CODE (op) == MEM)
+ return mode == SImode || mode == SFmode || TARGET_DW_ENABLE;
+
+ else
+ return 0;
+}
+
+/* Return 1 if OP is some extension operator. */
+
+int
+extend_operator (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || GET_MODE (op) == mode)
+ && (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND));
+}
+
+/* Return 1 if OP is a comparison operator that we have in floating-point. */
+
+int
+fp_comparison_operator (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || mode == GET_MODE (op))
+ && (GET_CODE (op) == EQ || GET_CODE (op) == GT ||
+ GET_CODE (op) == GE));
+}
+
+/* Return 1 if OP is a valid branch comparison. */
+
+int
+branch_operator (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || mode == GET_MODE (op))
+ && (GET_CODE (op) == GE || GET_CODE (op) == LT));
+}
+\f
+/* Return 1 if OP is a load multiple operation. It is known to be a
+ PARALLEL and the first three sections will be tested. */
+
+int
+load_multiple_operation (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int count = XVECLEN (op, 0) - 2;
+ int dest_regno;
+ rtx src_addr;
+ int i;
+
+ /* Perform a quick check so we don't blow up below. */
+ if (count <= 1
+ || GET_CODE (XVECEXP (op, 0, 0)) != SET
+ || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
+ || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
+ return 0;
+
+ dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
+ src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+
+ for (i = 1; i < count; i++)
+ {
+ rtx elt = XVECEXP (op, 0, i + 2);
+
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_DEST (elt)) != REG
+ || GET_MODE (SET_DEST (elt)) != SImode
+ || REGNO (SET_DEST (elt)) != dest_regno + i
+ || GET_CODE (SET_SRC (elt)) != MEM
+ || GET_MODE (SET_SRC (elt)) != SImode
+ || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+ || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+ || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+ || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Similar, but tests for store multiple. */
+
+int
+store_multiple_operation (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int num_special = TARGET_NO_STOREM_BUG ? 2 : 1;
+ int count = XVECLEN (op, 0) - num_special;
+ int src_regno;
+ rtx dest_addr;
+ int i;
+
+ /* Perform a quick check so we don't blow up below. */
+ if (count <= 1
+ || GET_CODE (XVECEXP (op, 0, 0)) != SET
+ || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
+ || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
+ return 0;
+
+ src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
+ dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+
+ for (i = 1; i < count; i++)
+ {
+ rtx elt = XVECEXP (op, 0, i + num_special);
+
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_SRC (elt)) != REG
+ || GET_MODE (SET_SRC (elt)) != SImode
+ || REGNO (SET_SRC (elt)) != src_regno + i
+ || GET_CODE (SET_DEST (elt)) != MEM
+ || GET_MODE (SET_DEST (elt)) != SImode
+ || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+ || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+ || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+ || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
+ return 0;
+ }
+
+ return 1;
+}
+\f
+/* Given a special register REG and MASK, a value being masked against a
+ quantity to which the special register is set, return 1 if the masking
+ operation is built-in to the setting of that special register. */
+
+int
+masks_bits_for_special (reg, mask)
+ rtx reg;
+ rtx mask;
+{
+ int needed_mask_value;
+
+ if (GET_CODE (reg) != REG || GET_CODE (mask) != CONST_INT)
+ abort ();
+
+ switch (REGNO (reg))
+ {
+ case R_BP:
+ case R_INT:
+ needed_mask_value = 3;
+ break;
+
+ case R_FC:
+ needed_mask_value = 31;
+ break;
+
+ case R_CR:
+ case R_LRU:
+ needed_mask_value = 255;
+ break;
+
+ case R_FPE:
+ needed_mask_value = 511;
+ break;
+
+ case R_MMU:
+ needed_mask_value = 0x3ff;
+ break;
+
+ case R_OPS:
+ case R_CPS:
+ case R_RBP:
+ case R_FPS:
+ needed_mask_value = 0xffff;
+ break;
+
+ case R_VAB:
+ needed_mask_value = 0xffff0000;
+ break;
+
+ case R_Q:
+ case R_CFG:
+ case R_CHA:
+ case R_CHD:
+ case R_CHC:
+ case R_TMC:
+ case R_TMR:
+ case R_PC0:
+ case R_PC1:
+ case R_PC2:
+ return 0;
+
+ default:
+ abort ();
+ }
+
+ return (INTVAL (mask) & ~ needed_mask_value) == 0;
+}
+\f
+/* Return nonzero if this label is that of the return point, but there is
+ a non-null epilogue. */
+
+int
+epilogue_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return next_active_insn (op) == 0 && a29k_first_epilogue_insn != 0;
+}
+\f
+/* Return the register class of a scratch register needed to copy IN into
+ or out of a register in CLASS in MODE. If it can be done directly,
+ NO_REGS is returned. */
+
+enum reg_class
+secondary_reload_class (class, mode, in)
+ enum reg_class class;
+ enum machine_mode mode;
+ rtx in;
+{
+ int regno = -1;
+
+ if (GET_CODE (in) == REG || GET_CODE (in) == SUBREG)
+ regno = true_regnum (in);
+
+ /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
+ into anything. */
+ if (class == GENERAL_REGS || (regno != -1 && regno < R_BP))
+ return NO_REGS;
+
+ /* We can place 16-bit constants into a special register. */
+ if (GET_CODE (in) == CONST_INT
+ && (GET_MODE_BITSIZE (mode) <= 16
+ || (unsigned) INTVAL (in) <= 65535)
+ && (class == BP_REGS || class == Q_REGS || class == SPECIAL_REGS))
+ return NO_REGS;
+
+ /* Otherwise, we need GENERAL_REGS. */
+ return GENERAL_REGS;
+}
+\f
+/* START is the zero-based incoming argument register index used (0 is 160,
+ i.e., the first incoming argument register) and COUNT is the number used.
+
+ Mark the corresponding incoming registers as neither fixed nor call used.
+ For each register used for incoming arguments, we have one less local
+ register that can be used. So also mark some high-numbered registers as
+ fixed.
+
+ Return the first register number to use for the argument. */
+
+int
+incoming_reg (start, count)
+ int start;
+ int count;
+{
+ int i;
+
+ if (! TARGET_NO_REUSE_ARGS)
+ /* Mark all the used registers as not fixed and saved over calls. */
+ for (i = R_AR (start); i < R_AR (16) && i < R_AR (start + count); i++)
+ {
+ fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 0;
+ CLEAR_HARD_REG_BIT (fixed_reg_set, i);
+ CLEAR_HARD_REG_BIT (call_used_reg_set, i);
+ CLEAR_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+
+ /* Shorten the maximum size of the frame. */
+ for (i = R_AR (0) - start - count; i < R_AR (0) - start; i++)
+ {
+ fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 1;
+ SET_HARD_REG_BIT (fixed_reg_set, i);
+ SET_HARD_REG_BIT (call_used_reg_set, i);
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+
+ return R_AR (start);
+}
+\f
+/* These routines are used in finding insns to fill delay slots in the
+ epilogue. */
+
+/* Return 1 if the current function will adjust the register stack. */
+
+int
+needs_regstack_p ()
+{
+ int i;
+ rtx insn;
+
+ if (frame_pointer_needed)
+ return 1;
+
+ /* If any local register is used, we need to adjust the regstack. */
+ for (i = R_LR (127); i >= R_LR (0); i --)
+ if (regs_ever_live[i])
+ return 1;
+
+ /* We need a register stack if we make any calls. */
+ for (insn = get_insns (); insn; insn = next_insn (insn))
+ if (GET_CODE (insn) == CALL_INSN
+ || (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN))
+ return 1;
+
+ /* Otherwise, we don't. */
+ return 0;
+}
+
+/* Return 1 if X uses a local register. */
+
+int
+uses_local_reg_p (x)
+ rtx x;
+{
+ char *fmt;
+ int i, j;
+
+ switch (GET_CODE (x))
+ {
+ case REG:
+ return REGNO (x) >= R_LR (0) && REGNO (x) <= R_FP;
+
+ case CONST_INT:
+ case CONST:
+ case PC:
+ case CC0:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ return 0;
+ }
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (uses_local_reg_p (XEXP (x, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (uses_local_reg_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Returns 1 if this function is known to have a null epilogue. */
+
+int
+null_epilogue ()
+{
+ return (reload_completed && ! needs_regstack_p ()
+ && get_frame_size () == 0
+ && current_function_pretend_args_size == 0);
+}
+\f
+/* Write out the assembler form of an operand. Recognize the following
+ special options:
+
+ %N means write the low-order 8 bits of the negative of the constant
+ %Q means write a QImode operand (truncate constants to 8 bits)
+ %M means write the low-order 16 bits of the constant
+ %C means write the low-order 8 bits of the complement of the constant
+ %X means write the cntl values for LOAD with operand an extension op
+ %b means write `f' is this is a reversed condition, `t' otherwise
+ %B means write `t' is this is a reversed condition, `f' otherwise
+ %J means write the 29k opcode part for a comparison operation
+ %e means write the label with an extra `X' is this is the epilogue
+ otherwise the normal label name
+ %E means write nothing if this insn has a delay slot,
+ a nop unless this is the epilogue label, in which case
+ write the first epilogue insn
+ %F means write just the normal operand if the insn has a delay slot;
+ otherwise, this is a recursive call so output the
+ symbol + 4 and write the first prologue insn in the
+ delay slot.
+ %L means write the register number plus one ("low order" register)
+ or the low-order part of a multi-word constant
+ %O means write the register number plus two
+ %P means write the register number plus three ("low order" of TImode)
+ %S means write the number of words in the mode of the operand,
+ minus one (for CR)
+ %V means write the number of elements in a PARALLEL minus 1
+ %# means write nothing if we have a delay slot, "\n\tnop" otherwise
+ %* means write the register name for TPC. */
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ char code;
+{
+ char buf[100];
+
+ /* These macros test for integers and extract the low-order bits. */
+#define INT_P(X) \
+((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \
+ && GET_MODE (X) == VOIDmode)
+
+#define INT_LOWPART(X) \
+ (GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X))
+
+ switch (code)
+ {
+ case 'Q':
+ if (GET_CODE (x) == REG)
+ break;
+ else if (! INT_P (x))
+ output_operand_lossage ("invalid %%Q value");
+ fprintf (file, "%d", INT_LOWPART (x) & 0xff);
+ return;
+
+ case 'C':
+ if (! INT_P (x))
+ output_operand_lossage ("invalid %%C value");
+ fprintf (file, "%d", (~ INT_LOWPART (x)) & 0xff);
+ return;
+
+ case 'N':
+ if (! INT_P (x))
+ output_operand_lossage ("invalid %%N value");
+ fprintf (file, "%d", (- INT_LOWPART (x)) & 0xff);
+ return;
+
+ case 'M':
+ if (! INT_P (x))
+ output_operand_lossage ("invalid %%M value");
+ fprintf (file, "%d", INT_LOWPART (x) & 0xffff);
+ return;
+
+ case 'X':
+ fprintf (file, "%d", ((GET_MODE (XEXP (x, 0)) == QImode ? 1 : 2)
+ + (GET_CODE (x) == SIGN_EXTEND ? 16 : 0)));
+ return;
+
+ case 'b':
+ if (GET_CODE (x) == GE)
+ fprintf (file, "f");
+ else
+ fprintf (file, "t");
+ return;
+
+ case 'B':
+ if (GET_CODE (x) == GE)
+ fprintf (file, "t");
+ else
+ fprintf (file, "f");
+ return;
+
+ case 'J':
+ /* It so happens that the RTX names for the conditions are the same as
+ the 29k's insns except for "ne", which requires "neq". */
+ fprintf (file, GET_RTX_NAME (GET_CODE (x)));
+ if (GET_CODE (x) == NE)
+ fprintf (file, "q");
+ return;
+
+ case 'e':
+ if (optimize && flag_delayed_branch
+ && a29k_last_prologue_insn == 0 && epilogue_operand (x, VOIDmode)
+ && dbr_sequence_length () == 0)
+ {
+ /* We need to output the label number of the last label in the
+ function, which is not necessarily X since there might be
+ a USE insn in between. First go forward to the last insn, then
+ back up to a label. */
+ while (NEXT_INSN (x) != 0)
+ x = NEXT_INSN (x);
+
+ while (GET_CODE (x) != CODE_LABEL)
+ x = PREV_INSN (x);
+
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LX", CODE_LABEL_NUMBER (x));
+ assemble_name (file, buf);
+ }
+ else
+ output_asm_label (x);
+ return;
+
+ case 'E':
+ if (dbr_sequence_length ())
+ ;
+ else if (a29k_last_prologue_insn)
+ {
+ fprintf (file, "\n\t%s", a29k_last_prologue_insn);
+ a29k_last_prologue_insn = 0;
+ }
+ else if (optimize && flag_delayed_branch
+ && epilogue_operand (x, VOIDmode))
+ {
+ fprintf (file, "\n\t%s", a29k_first_epilogue_insn);
+ a29k_first_epilogue_insn_used = 1;
+ }
+ else
+ fprintf (file, "\n\tnop");
+ return;
+
+ case 'F':
+ output_addr_const (file, x);
+ if (! strcmp (XSTR (x, 0), current_function_name)
+ && dbr_sequence_length () == 0)
+ fprintf (file, "+4\n\t%s,%d",
+ a29k_regstack_size >= 64 ? "const gr121" : "sub gr1,gr1",
+ a29k_regstack_size * 4);
+ return;
+
+ case 'L':
+ if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode)
+ {
+ union real_extract u;
+
+ bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u);
+ fprintf (file, "$double1(%.20e)", u.d);
+ }
+ else if (GET_CODE (x) == REG)
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ else
+ output_operand_lossage ("invalid %%L value");
+ return;
+
+ case 'O':
+ if (GET_CODE (x) != REG)
+ output_operand_lossage ("invalid %%O value");
+ fprintf (file, "%s", reg_names[REGNO (x) + 2]);
+ return;
+
+ case 'P':
+ if (GET_CODE (x) != REG)
+ output_operand_lossage ("invalid %%P value");
+ fprintf (file, "%s", reg_names[REGNO (x) + 3]);
+ return;
+
+ case 'S':
+ fprintf (file, "%d", (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD)-1);
+ return;
+
+ case 'V':
+ if (GET_CODE (x) != PARALLEL)
+ output_operand_lossage ("invalid %%V value");
+ fprintf (file, "%d", XVECLEN (x, 0) - 2);
+ return;
+
+ case '#':
+ if (dbr_sequence_length () == 0)
+ {
+ if (a29k_last_prologue_insn)
+ {
+ fprintf (file, "\n\t%s", a29k_last_prologue_insn);
+ a29k_last_prologue_insn = 0;
+ }
+ else
+ fprintf (file, "\n\tnop");
+ }
+ return;
+
+ case '*':
+ fprintf (file, "%s", reg_names [R_TPC]);
+ return;
+ }
+
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", reg_names [REGNO (x)]);
+
+ else if (GET_CODE (x) == MEM)
+ output_address (XEXP (x, 0));
+
+ else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == SUBREG
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == CONST_DOUBLE)
+ {
+ union real_extract u;
+
+ if (GET_MODE (SUBREG_REG (XEXP (x, 0))) == SFmode)
+ fprintf (file, "$float");
+ else
+ fprintf (file, "$double%d", SUBREG_WORD (XEXP (x, 0)));
+ bcopy (&CONST_DOUBLE_LOW (SUBREG_REG (XEXP (x, 0))), &u, sizeof u);
+ fprintf (file, "(%.20e)", u.d);
+ }
+
+ else if (GET_CODE (x) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ {
+ union real_extract u;
+
+ bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u);
+ fprintf (file, "$%s(%.20e)",
+ GET_MODE (x) == SFmode ? "float" : "double0", u.d);
+ }
+
+ else
+ output_addr_const (file, x);
+}
+\f
+/* This page contains routines to output function prolog and epilog code. */
+
+/* Output function prolog code to file FILE. Memory stack size is SIZE.
+
+ Also sets register names for incoming arguments and frame pointer. */
+
+void
+output_prolog (file, size)
+ FILE *file;
+ int size;
+{
+ int makes_calls = 0;
+ int arg_count = 0;
+ rtx insn;
+ int i;
+ unsigned int tag_word;
+
+ /* See if we make any calls. We need to set lr1 if so. */
+ for (insn = get_insns (); insn; insn = next_insn (insn))
+ if (GET_CODE (insn) == CALL_INSN
+ || (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN))
+ {
+ makes_calls = 1;
+ break;
+ }
+
+ /* Find the highest local register used. */
+ for (i = R_LR (127); i >= R_LR (0); i--)
+ if (regs_ever_live[i])
+ break;
+
+ a29k_regstack_size = i - (R_LR (0) - 1);
+
+ /* If calling routines, ensure we count lr0 & lr1. */
+ if (makes_calls && a29k_regstack_size < 2)
+ a29k_regstack_size = 2;
+
+ /* Count frame pointer and align to 8 byte boundary (even number of
+ registers). */
+ a29k_regstack_size += frame_pointer_needed;
+ if (a29k_regstack_size & 1) a29k_regstack_size++;
+
+ /* See how many incoming arguments we have in registers. */
+ for (i = R_AR (0); i < R_AR (16); i++)
+ if (! fixed_regs[i])
+ arg_count++;
+
+ /* The argument count includes the caller's lr0 and lr1. */
+ arg_count += 2;
+
+ /* Set the names and numbers of the frame pointer and incoming argument
+ registers. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ a29k_debug_reg_map[i] = i;
+
+ reg_names[FRAME_POINTER_REGNUM] = reg_names[R_LR (a29k_regstack_size - 1)];
+ a29k_debug_reg_map[FRAME_POINTER_REGNUM] = R_LR (a29k_regstack_size - 1);
+
+ for (i = 0; i < 16; i++)
+ {
+ reg_names[R_AR (i)] = reg_names[R_LR (a29k_regstack_size + i + 2)];
+ a29k_debug_reg_map[R_AR (i)] = R_LR (a29k_regstack_size + i + 2);
+ }
+
+ /* Compute memory stack size. Add in number of bytes that the we should
+ push and pretend the caller did and the size of outgoing arguments.
+ Then round to a doubleword boundary. */
+ size += (current_function_pretend_args_size
+ + current_function_outgoing_args_size);
+ size = (size + 7) & ~7;
+
+ /* Write header words. See if one or two word form. */
+ tag_word = (frame_pointer_needed ? 0x400000 : 0) + (arg_count << 16);
+
+ if (size / 8 > 0xff)
+ fprintf (file, "\t.word %d, 0x%0x\n", (size / 8) << 2,
+ 0x800000 + tag_word);
+ else
+ fprintf (file, "\t.word 0x%0x\n", tag_word + ((size / 8) << 3));
+
+ /* Define the function name. */
+ assemble_name (file, a29k_function_name);
+ fprintf (file, ":\n");
+
+ /* Push the register stack by the proper amount. There are two possible
+ ways to do this. */
+ if (a29k_regstack_size >= 256/4)
+ fprintf (file, "\tconst %s,%d\n\tsub gr1,gr1,%s\n",
+ reg_names[R_TAV], a29k_regstack_size * 4, reg_names[R_TAV]);
+ else if (a29k_regstack_size)
+ fprintf (file, "\tsub gr1,gr1,%d\n", a29k_regstack_size * 4);
+
+ /* Test that the registers are available. */
+ if (a29k_regstack_size)
+ fprintf (file, "\tasgeu V_%sSPILL,gr1,%s\n",
+ TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RAB]);
+
+ /* Set up frame pointer, if one is needed. */
+ if (frame_pointer_needed)
+ fprintf (file, "\tsll %s,%s,0\n", reg_names[FRAME_POINTER_REGNUM],
+ reg_names[R_MSP]);
+
+ /* Make room for any frame space. There are three ways to do this. */
+ if (size >= 256)
+ {
+ fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size);
+ if (size >= 65536)
+ fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size);
+ if (TARGET_STACK_CHECK)
+ fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]);
+ fprintf (file, "\tsub %s,%s,%s\n",
+ reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]);
+ }
+ else if (size)
+ {
+ if (TARGET_STACK_CHECK)
+ fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]);
+ fprintf (file, "\tsub %s,%s,%d\n",
+ reg_names[R_MSP], reg_names[R_MSP], size);
+ }
+
+ /* If this routine will make calls, set lr1. If we see an insn that
+ can use a delay slot before a call or jump, save this insn for that
+ slot (this condition is equivalent to seeing if we have an insn that
+ needs delay slots before an insn that has a filled delay slot). */
+ a29k_last_prologue_insn = 0;
+ if (makes_calls)
+ {
+ i = (a29k_regstack_size + arg_count) * 4;
+ if (i >= 256)
+ fprintf (file, "\tconst %s,%d\n\tsub lr1,gr1,%s\n",
+ reg_names[R_TAV], i, reg_names[R_TAV]);
+ else
+ {
+ if (optimize && flag_delayed_branch)
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL
+ || (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE))
+ break;
+
+ if (GET_CODE (insn) == NOTE
+ || (GET_CODE (insn) == INSN
+ && (GET_CODE (PATTERN (insn)) == USE
+ || GET_CODE (PATTERN (insn)) == CLOBBER)))
+ continue;
+
+ if (num_delay_slots (insn) > 0)
+ {
+ a29k_last_prologue_insn = (char *) oballoc (100);
+ sprintf (a29k_last_prologue_insn, "add lr1,gr1,%d", i);
+ break;
+ }
+ }
+
+ if (a29k_last_prologue_insn == 0)
+ fprintf (file, "\tadd lr1,gr1,%d\n", i);
+ }
+ }
+
+ /* Compute the first insn of the epilogue. */
+ a29k_first_epilogue_insn_used = 0;
+
+ if (size == 0 && a29k_regstack_size == 0 && ! frame_pointer_needed)
+ a29k_first_epilogue_insn = 0;
+ else
+ a29k_first_epilogue_insn = (char *) oballoc (100);
+
+ if (frame_pointer_needed)
+ sprintf (a29k_first_epilogue_insn, "sll %s,%s,0",
+ reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]);
+ else if (a29k_regstack_size)
+ {
+ if (a29k_regstack_size >= 256 / 4)
+ sprintf (a29k_first_epilogue_insn, "const %s,%d",
+ reg_names[R_TAV], a29k_regstack_size * 4);
+ else
+ sprintf (a29k_first_epilogue_insn, "add gr1,gr1,%d",
+ a29k_regstack_size * 4);
+ }
+ else if (size)
+ {
+ if (size >= 256)
+ sprintf (a29k_first_epilogue_insn, "const %s,%d",
+ reg_names[R_TAV], size);
+ else
+ sprintf (a29k_first_epilogue_insn, "add %s,%s,%d",
+ reg_names[R_MSP], reg_names[R_MSP], size);
+ }
+}
+\f
+/* Call this after writing what might be the first instruction of the
+ epilogue. If that first insn was used in a delay slot, an intermediate
+ label is written. */
+
+static void
+check_epilogue_internal_label (file)
+ FILE *file;
+{
+ rtx insn;
+
+ if (! a29k_first_epilogue_insn_used)
+ return;
+
+ for (insn = get_last_insn ();
+ GET_CODE (insn) != CODE_LABEL;
+ insn = PREV_INSN (insn))
+ ;
+
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LX", CODE_LABEL_NUMBER (insn));
+ a29k_first_epilogue_insn_used = 0;
+}
+
+/* Output the epilog of the last procedure to file FILE. SIZE is the memory
+ stack size. The register stack size is in the variable
+ A29K_REGSTACK_SIZE. */
+
+void
+output_epilog (file, size)
+ FILE *file;
+ int size;
+{
+ rtx insn;
+ int locals_unavailable = 0; /* True until after first insn
+ after gr1 update. */
+
+ /* If we hit a BARRIER before a real insn or CODE_LABEL, we don't
+ need to do anything because we are never jumped to. */
+ insn = get_last_insn ();
+ if (GET_CODE (insn) == NOTE)
+ insn = prev_nonnote_insn (insn);
+
+ if (insn && GET_CODE (insn) == BARRIER)
+ return;
+
+ /* If a frame pointer was needed we must restore the memory stack pointer
+ before adjusting the register stack. */
+ if (frame_pointer_needed)
+ {
+ fprintf (file, "\tsll %s,%s,0\n",
+ reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]);
+ check_epilogue_internal_label (file);
+ }
+
+ /* Restore the register stack. There are two ways to do this. */
+ if (a29k_regstack_size)
+ {
+ if (a29k_regstack_size >= 256/4)
+ {
+ fprintf (file, "\tconst %s,%d\n",
+ reg_names[R_TAV], a29k_regstack_size * 4);
+ check_epilogue_internal_label (file);
+ fprintf (file, "\tadd gr1,gr1,%s\n", reg_names[R_TAV]);
+ }
+ else
+ {
+ fprintf (file, "\tadd gr1,gr1,%d\n", a29k_regstack_size * 4);
+ check_epilogue_internal_label (file);
+ }
+ locals_unavailable = 1;
+ }
+
+ /* Restore the memory stack pointer if there is no frame pointer.
+ Adjust the size to include any pretend arguments and pushed
+ arguments and round to doubleword boundary. */
+ size += (current_function_pretend_args_size
+ + current_function_outgoing_args_size);
+ size = (size + 7) & ~7;
+
+ if (size && ! frame_pointer_needed)
+ {
+ if (size >= 256)
+ {
+ fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size);
+ check_epilogue_internal_label (file);
+ locals_unavailable = 0;
+ if (size >= 65536)
+ fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size);
+ fprintf (file, "\tadd %s,%s,%s\n",
+ reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]);
+ }
+ else
+ {
+ fprintf (file, "\tadd %s,%s,%d\n",
+ reg_names[R_MSP], reg_names[R_MSP], size);
+ check_epilogue_internal_label (file);
+ locals_unavailable = 0;
+ }
+ }
+
+ if (locals_unavailable)
+ {
+ /* If we have an insn for this delay slot, write it. */
+ if (current_function_epilogue_delay_list)
+ final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
+ file, 1, -2, 1);
+ else
+ fprintf (file, "\tnop\n");
+ }
+
+ fprintf (file, "\tjmpi lr0\n");
+ if (a29k_regstack_size)
+ fprintf (file, "\tasleu V_%sFILL,lr1,%s\n",
+ TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RFB]);
+ else if (current_function_epilogue_delay_list)
+ final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
+ file, 1, -2, 1);
+ else
+ fprintf (file, "\tnop\n");
+}