OSDN Git Service

Initial revision
authorkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 14 Jan 1992 23:37:42 +0000 (23:37 +0000)
committerkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 14 Jan 1992 23:37:42 +0000 (23:37 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@184 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/config/a29k/a29k.c [new file with mode: 0644]

diff --git a/gcc/config/a29k/a29k.c b/gcc/config/a29k/a29k.c
new file mode 100644 (file)
index 0000000..e9568d5
--- /dev/null
@@ -0,0 +1,1286 @@
+/* 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");
+}