aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/v850/v850.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/v850/v850.c')
-rw-r--r--gcc/config/v850/v850.c1942
1 files changed, 1942 insertions, 0 deletions
diff --git a/gcc/config/v850/v850.c b/gcc/config/v850/v850.c
new file mode 100644
index 00000000000..6435d409336
--- /dev/null
+++ b/gcc/config/v850/v850.c
@@ -0,0 +1,1942 @@
+/* Subroutines for insn-output.c for NEC V850 series
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Contributed by Jeff Law (law@cygnus.com).
+
+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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.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 "tree.h"
+#include "obstack.h"
+
+/* True if the current function has anonymous arguments. */
+int current_function_anonymous_args;
+
+/* Information about the various small memory areas. */
+struct small_memory_info small_memory[ (int)SMALL_MEMORY_max ] =
+{
+ /* name value max physical max */
+ { "tda", (char *)0, 0, 256 },
+ { "sda", (char *)0, 0, 65536 },
+ { "zda", (char *)0, 0, 32768 },
+};
+
+/* True if we don't need to check any more if the current
+ function is an interrupt handler */
+static int v850_interrupt_cache_p = FALSE;
+
+/* Whether current function is an interrupt handler. */
+static int v850_interrupt_p = FALSE;
+
+
+/* Sometimes certain combinations of command options do not make
+ sense on a particular target machine. You can define a macro
+ `OVERRIDE_OPTIONS' to take account of this. This macro, if
+ defined, is executed once just after all the command options have
+ been parsed.
+
+ Don't use this macro to turn on various extra optimizations for
+ `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+
+void
+override_options ()
+{
+ int i;
+ extern int atoi ();
+
+ /* Parse -m{s,t,z}da=nnn switches */
+ for (i = 0; i < (int)SMALL_MEMORY_max; i++)
+ {
+ if (small_memory[i].value)
+ {
+ if (!isdigit (*small_memory[i].value))
+ error ("%s=%s is not numeric.",
+ small_memory[i].name,
+ small_memory[i].value);
+ else
+ {
+ small_memory[i].max = atoi (small_memory[i].value);
+ if (small_memory[i].max > small_memory[i].physical_max)
+ error ("%s=%s is too large.",
+ small_memory[i].name,
+ small_memory[i].value);
+ }
+ }
+ }
+}
+
+
+/* Output assembly code for the start of the file. */
+
+void
+asm_file_start (file)
+ FILE *file;
+{
+ output_file_directive (file, main_input_filename);
+}
+
+
+/* Return an RTX to represent where a value with mode MODE will be returned
+ from a function. If the result is 0, the argument is pushed. */
+
+rtx
+function_arg (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ rtx result = 0;
+ int size, align;
+
+ if (TARGET_GHS && !named)
+ return NULL_RTX;
+
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (type)
+ align = TYPE_ALIGN (type) / BITS_PER_UNIT;
+ else
+ align = size;
+
+ cum->nbytes = (cum->nbytes + align - 1) &~(align - 1);
+
+ if (cum->nbytes > 4 * UNITS_PER_WORD)
+ return 0;
+
+ if (type == NULL_TREE
+ && cum->nbytes + size > 4 * UNITS_PER_WORD)
+ return 0;
+
+ switch (cum->nbytes / UNITS_PER_WORD)
+ {
+ case 0:
+ result = gen_rtx (REG, mode, 6);
+ break;
+ case 1:
+ result = gen_rtx (REG, mode, 7);
+ break;
+ case 2:
+ result = gen_rtx (REG, mode, 8);
+ break;
+ case 3:
+ result = gen_rtx (REG, mode, 9);
+ break;
+ default:
+ result = 0;
+ }
+
+ return result;
+}
+
+
+/* Return the number of words which must be put into registers
+ for values which are part in registers and part in memory. */
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ int size, align;
+
+ if (TARGET_GHS && !named)
+ return 0;
+
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (type)
+ align = TYPE_ALIGN (type) / BITS_PER_UNIT;
+ else
+ align = size;
+
+ cum->nbytes = (cum->nbytes + align - 1) &~(align - 1);
+
+ if (cum->nbytes > 4 * UNITS_PER_WORD)
+ return 0;
+
+ if (cum->nbytes + size <= 4 * UNITS_PER_WORD)
+ return 0;
+
+ if (type == NULL_TREE
+ && cum->nbytes + size > 4 * UNITS_PER_WORD)
+ return 0;
+
+ return (4 * UNITS_PER_WORD - cum->nbytes) / UNITS_PER_WORD;
+}
+
+
+/* Return the high and low words of a CONST_DOUBLE */
+
+static void
+const_double_split (x, p_high, p_low)
+ rtx x;
+ HOST_WIDE_INT *p_high;
+ HOST_WIDE_INT *p_low;
+{
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ long t[2];
+ REAL_VALUE_TYPE rv;
+
+ switch (GET_MODE (x))
+ {
+ case DFmode:
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, t);
+ *p_high = t[1]; /* since v850 is little endian */
+ *p_low = t[0]; /* high is second word */
+ return;
+
+ case SFmode:
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, *p_high);
+ *p_low = 0;
+ return;
+
+ case VOIDmode:
+ case DImode:
+ *p_high = CONST_DOUBLE_HIGH (x);
+ *p_low = CONST_DOUBLE_LOW (x);
+ return;
+ }
+ }
+
+ fatal_insn ("const_double_split got a bad insn:", x);
+}
+
+
+/* Return the cost of the rtx R with code CODE. */
+
+static int
+const_costs_int (value, zero_cost)
+ HOST_WIDE_INT value;
+ int zero_cost;
+{
+ if (CONST_OK_FOR_I (value))
+ return zero_cost;
+ else if (CONST_OK_FOR_J (value))
+ return 1;
+ else if (CONST_OK_FOR_K (value))
+ return 2;
+ else
+ return 4;
+}
+
+int
+const_costs (r, c)
+ rtx r;
+ enum rtx_code c;
+{
+ HOST_WIDE_INT high, low;
+
+ switch (c)
+ {
+ case CONST_INT:
+ return const_costs_int (INTVAL (r), 0);
+
+ case CONST_DOUBLE:
+ const_double_split (r, &high, &low);
+ if (GET_MODE (r) == SFmode)
+ return const_costs_int (high, 1);
+ else
+ return const_costs_int (high, 1) + const_costs_int (low, 1);
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ return 2;
+
+ case HIGH:
+ return 1;
+
+ default:
+ return 4;
+ }
+}
+
+
+/* Print operand X using operand code CODE to assembly language output file
+ FILE. */
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ int code;
+{
+ HOST_WIDE_INT high, low;
+
+ switch (code)
+ {
+ case 'b':
+ case 'B':
+ switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
+ {
+ case NE:
+ fprintf (file, "bne");
+ break;
+ case EQ:
+ fprintf (file, "be");
+ break;
+ case GE:
+ fprintf (file, "bge");
+ break;
+ case GT:
+ fprintf (file, "bgt");
+ break;
+ case LE:
+ fprintf (file, "ble");
+ break;
+ case LT:
+ fprintf (file, "blt");
+ break;
+ case GEU:
+ fprintf (file, "bnl");
+ break;
+ case GTU:
+ fprintf (file, "bh");
+ break;
+ case LEU:
+ fprintf (file, "bnh");
+ break;
+ case LTU:
+ fprintf (file, "bl");
+ break;
+ default:
+ abort ();
+ }
+ break;
+ switch (GET_CODE (x))
+ {
+ case NE:
+ fprintf (file, "be");
+ break;
+ case EQ:
+ fprintf (file, "bne");
+ break;
+ case GE:
+ fprintf (file, "blt");
+ break;
+ case GT:
+ fprintf (file, "bgt");
+ break;
+ case LE:
+ fprintf (file, "ble");
+ break;
+ case LT:
+ fprintf (file, "blt");
+ break;
+ case GEU:
+ fprintf (file, "bnl");
+ break;
+ case GTU:
+ fprintf (file, "bh");
+ break;
+ case LEU:
+ fprintf (file, "bnh");
+ break;
+ case LTU:
+ fprintf (file, "bl");
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case 'F': /* high word of CONST_DOUBLE */
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%d", (INTVAL (x) >= 0) ? 0 : -1);
+ else if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ const_double_split (x, &high, &low);
+ fprintf (file, "%ld", (long) high);
+ }
+ else
+ abort ();
+ break;
+ case 'G': /* low word of CONST_DOUBLE */
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", (long) INTVAL (x));
+ else if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ const_double_split (x, &high, &low);
+ fprintf (file, "%ld", (long) low);
+ }
+ else
+ abort ();
+ break;
+ case 'L':
+ fprintf (file, "%d\n", INTVAL (x) & 0xffff);
+ break;
+ case 'M':
+ fprintf (file, "%d", exact_log2 (INTVAL (x)));
+ break;
+ case 'O':
+ if (special_symbolref_operand (x, VOIDmode))
+ {
+ char* name;
+
+ if (GET_CODE (x) == SYMBOL_REF)
+ name = XSTR (x, 0);
+ else if (GET_CODE (x) == CONST)
+ name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+ else
+ abort ();
+
+ if (ZDA_NAME_P (name))
+ fprintf (file, "zdaoff");
+ else if (SDA_NAME_P (name))
+ fprintf (file, "sdaoff");
+ else if (TDA_NAME_P (name))
+ fprintf (file, "tdaoff");
+ else
+ abort();
+ }
+ else
+ abort();
+ break;
+ case 'P':
+ if (special_symbolref_operand (x, VOIDmode))
+ output_addr_const (file, x);
+ else
+ abort();
+ break;
+ case 'Q':
+ if (special_symbolref_operand (x, VOIDmode))
+ {
+ char* name;
+
+ if (GET_CODE (x) == SYMBOL_REF)
+ name = XSTR (x, 0);
+ else if (GET_CODE (x) == CONST)
+ name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+ else
+ abort ();
+
+ if (ZDA_NAME_P (name))
+ fprintf (file, "r0");
+ else if (SDA_NAME_P (name))
+ fprintf (file, "gp");
+ else if (TDA_NAME_P (name))
+ fprintf (file, "ep");
+ else
+ abort();
+ }
+ else
+ abort();
+ break;
+ case 'R': /* 2nd word of a double. */
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fprintf (file, reg_names[REGNO (x) + 1]);
+ break;
+ case MEM:
+ print_operand_address (file,
+ XEXP (adj_offsettable_operand (x, 4), 0));
+ break;
+ }
+ break;
+ case 'S':
+ {
+ /* if it's a referance to a TDA variable, use sst/sld vs. st/ld */
+ if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x)))
+ fputs ("s", file);
+
+ break;
+ }
+ case 'W': /* print the instruction suffix */
+ switch (GET_MODE (x))
+ {
+ default:
+ abort ();
+
+ case QImode: fputs (".b", file); break;
+ case HImode: fputs (".h", file); break;
+ case SImode: fputs (".w", file); break;
+ case SFmode: fputs (".w", file); break;
+ }
+ break;
+ case '.': /* register r0 */
+ fputs (reg_names[0], file);
+ break;
+ default:
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+ output_address (gen_rtx (PLUS, SImode,
+ gen_rtx (REG, SImode, 0),
+ XEXP (x, 0)));
+ else
+ output_address (XEXP (x, 0));
+ break;
+
+ case REG:
+ fputs (reg_names[REGNO (x)], file);
+ break;
+ case SUBREG:
+ fputs (reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)], file);
+ break;
+ case CONST_INT:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
+ case CODE_LABEL:
+ print_operand_address (file, x);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ }
+}
+
+
+/* Output assembly language output for the address ADDR to FILE. */
+
+void
+print_operand_address (file, addr)
+ FILE *file;
+ rtx addr;
+{
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ fprintf (file, "0[");
+ print_operand (file, addr, 0);
+ fprintf (file, "]");
+ break;
+ case LO_SUM:
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ /* reg,foo */
+ fprintf (file, "lo(");
+ print_operand (file, XEXP (addr, 1), 0);
+ fprintf (file, ")[");
+ print_operand (file, XEXP (addr, 0), 0);
+ fprintf (file, "]");
+ }
+ break;
+ case PLUS:
+ if (GET_CODE (XEXP (addr, 0)) == REG
+ || GET_CODE (XEXP (addr, 0)) == SUBREG)
+ {
+ /* reg,foo */
+ print_operand (file, XEXP (addr, 1), 0);
+ fprintf (file, "[");
+ print_operand (file, XEXP (addr, 0), 0);
+ fprintf (file, "]");
+ }
+ else
+ {
+ print_operand (file, XEXP (addr, 0), 0);
+ fprintf (file, "+");
+ print_operand (file, XEXP (addr, 1), 0);
+ }
+ break;
+ case SYMBOL_REF:
+ if (ENCODED_NAME_P (XSTR (addr, 0)))
+ {
+ char* name = XSTR (addr, 0);
+ char* off_name;
+ char* reg_name;
+
+ if (ZDA_NAME_P (name))
+ {
+ off_name = "zdaoff";
+ reg_name = "r0";
+ }
+ else if (SDA_NAME_P (name))
+ {
+ off_name = "sdaoff";
+ reg_name = "gp";
+ }
+ else if (TDA_NAME_P (name))
+ {
+ off_name = "tdaoff";
+ reg_name = "ep";
+ }
+ else
+ abort();
+
+ fprintf (file, "%s(", off_name);
+ output_addr_const (file, addr);
+ fprintf (file, ")[%s]", reg_name);
+ }
+ else
+ output_addr_const (file, addr);
+ break;
+ case CONST:
+ if (special_symbolref_operand (addr, VOIDmode))
+ {
+ char* name = XSTR (XEXP (XEXP (addr, 0), 0), 0);
+ char* off_name;
+ char* reg_name;
+
+ if (ZDA_NAME_P (name))
+ {
+ off_name = "zdaoff";
+ reg_name = "r0";
+ }
+ else if (SDA_NAME_P (name))
+ {
+ off_name = "sdaoff";
+ reg_name = "gp";
+ }
+ else if (TDA_NAME_P (name))
+ {
+ off_name = "tdaoff";
+ reg_name = "ep";
+ }
+ else
+ abort();
+
+ fprintf (file, "%s(", off_name);
+ output_addr_const (file, addr);
+ fprintf (file, ")[%s]", reg_name);
+ }
+ else
+ output_addr_const (file, addr);
+ break;
+ default:
+ output_addr_const (file, addr);
+ break;
+ }
+}
+
+
+/* Return appropriate code to load up a 1, 2, or 4 integer/floating
+ point value. */
+
+char *
+output_move_single (operands)
+ rtx *operands;
+{
+ rtx dst = operands[0];
+ rtx src = operands[1];
+
+ if (REG_P (dst))
+ {
+ if (REG_P (src))
+ return "mov %1,%0";
+
+ else if (GET_CODE (src) == CONST_INT)
+ {
+ HOST_WIDE_INT value = INTVAL (src);
+
+ if (CONST_OK_FOR_J (value)) /* signed 5 bit immediate */
+ return "mov %1,%0";
+
+ else if (CONST_OK_FOR_K (value)) /* signed 16 bit immediate */
+ return "movea lo(%1),%.,%0";
+
+ else if (CONST_OK_FOR_L (value)) /* upper 16 bits were set */
+ return "movhi hi(%1),%.,%0";
+
+ else /* random constant */
+ return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
+ }
+
+ else if (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode)
+ {
+ HOST_WIDE_INT high, low;
+
+ const_double_split (src, &high, &low);
+ if (CONST_OK_FOR_J (high)) /* signed 5 bit immediate */
+ return "mov %F1,%0";
+
+ else if (CONST_OK_FOR_K (high)) /* signed 16 bit immediate */
+ return "movea lo(%F1),%.,%0";
+
+ else if (CONST_OK_FOR_L (high)) /* upper 16 bits were set */
+ return "movhi hi(%F1),%.,%0";
+
+ else /* random constant */
+ return "movhi hi(%F1),%.,%0\n\tmovea lo(%F1),%0,%0";
+ }
+
+ else if (GET_CODE (src) == MEM)
+ return "%S1ld%W1 %1,%0";
+
+ else if (special_symbolref_operand (src, VOIDmode))
+ return "movea %O1(%P1),%Q1,%0";
+
+ else if (GET_CODE (src) == LABEL_REF
+ || GET_CODE (src) == SYMBOL_REF
+ || GET_CODE (src) == CONST)
+ return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
+
+ else if (GET_CODE (src) == HIGH)
+ return "movhi hi(%1),%.,%0";
+
+ else if (GET_CODE (src) == LO_SUM)
+ {
+ operands[2] = XEXP (src, 0);
+ operands[3] = XEXP (src, 1);
+ return "movea lo(%3),%2,%0";
+ }
+ }
+
+ else if (GET_CODE (dst) == MEM)
+ {
+ if (REG_P (src))
+ return "%S0st%W0 %1,%0";
+
+ else if (GET_CODE (src) == CONST_INT && INTVAL (src) == 0)
+ return "%S0st%W0 %.,%0";
+
+ else if (GET_CODE (src) == CONST_DOUBLE
+ && CONST0_RTX (GET_MODE (dst)) == src)
+ return "%S0st%W0 %.,%0";
+ }
+
+ fatal_insn ("output_move_single:", gen_rtx (SET, VOIDmode, dst, src));
+ return "";
+}
+
+
+/* Return appropriate code to load up an 8 byte integer or floating point value */
+
+char *
+output_move_double (operands)
+ rtx *operands;
+{
+ enum machine_mode mode = GET_MODE (operands[0]);
+ rtx dst = operands[0];
+ rtx src = operands[1];
+
+ if (register_operand (dst, mode)
+ && register_operand (src, mode))
+ {
+ if (REGNO (src) + 1 == REGNO (dst))
+ return "mov %R1,%R0\n\tmov %1,%0";
+ else
+ return "mov %1,%0\n\tmov %R1,%R0";
+ }
+
+ /* Storing 0 */
+ if (GET_CODE (dst) == MEM
+ && ((GET_CODE (src) == CONST_INT && INTVAL (src) == 0)
+ || (GET_CODE (src) == CONST_DOUBLE && CONST_DOUBLE_OK_FOR_G (src))))
+ return "st.w %.,%0\n\tst.w %.,%R0";
+
+ if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE)
+ {
+ HOST_WIDE_INT high_low[2];
+ int i;
+ rtx xop[10];
+
+ if (GET_CODE (src) == CONST_DOUBLE)
+ const_double_split (src, &high_low[1], &high_low[0]);
+ else
+ {
+ high_low[0] = INTVAL (src);
+ high_low[1] = (INTVAL (src) >= 0) ? 0 : -1;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ xop[0] = gen_rtx (REG, SImode, REGNO (dst)+i);
+ xop[1] = GEN_INT (high_low[i]);
+ output_asm_insn (output_move_single (xop), xop);
+ }
+
+ return "";
+ }
+
+ if (GET_CODE (src) == MEM)
+ {
+ int ptrreg = -1;
+ int dreg = REGNO (dst);
+ rtx inside = XEXP (src, 0);
+
+ if (GET_CODE (inside) == REG)
+ ptrreg = REGNO (inside);
+ else if (GET_CODE (inside) == SUBREG)
+ ptrreg = REGNO (SUBREG_REG (inside)) + SUBREG_WORD (inside);
+ else if (GET_CODE (inside) == PLUS)
+ ptrreg = REGNO (XEXP (inside, 0));
+ else if (GET_CODE (inside) == LO_SUM)
+ ptrreg = REGNO (XEXP (inside, 0));
+
+ if (dreg == ptrreg)
+ return "ld.w %R1,%R0\n\tld.w %1,%0";
+ }
+
+ if (GET_CODE (src) == MEM)
+ return "ld.w %1,%0\n\tld.w %R1,%R0";
+
+ if (GET_CODE (dst) == MEM)
+ return "st.w %1,%0\n\tst.w %R1,%R0";
+
+ return "mov %1,%0\n\tmov %R1,%R0";
+}
+
+
+/* Return true if OP is a valid short EP memory reference */
+
+int
+ep_memory_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ rtx addr, op0, op1;
+ int max_offset;
+ int mask;
+
+ if (GET_CODE (op) != MEM)
+ return FALSE;
+
+ switch (GET_MODE (op))
+ {
+ default:
+ return FALSE;
+
+ case QImode:
+ max_offset = 128;
+ mask = 0;
+ break;
+
+ case HImode:
+ max_offset = 256;
+ mask = 1;
+ break;
+
+ case SImode:
+ case SFmode:
+ max_offset = 256;
+ mask = 3;
+ break;
+ }
+
+ addr = XEXP (op, 0);
+ if (GET_CODE (addr) == CONST)
+ addr = XEXP (addr, 0);
+
+ switch (GET_CODE (addr))
+ {
+ default:
+ break;
+
+ case SYMBOL_REF:
+ return TDA_NAME_P (XSTR (addr, 0));
+
+ case REG:
+ return REGNO (addr) == EP_REGNUM;
+
+ case PLUS:
+ op0 = XEXP (addr, 0);
+ op1 = XEXP (addr, 1);
+ if (GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) < max_offset
+ && (INTVAL (op1) & mask) == 0)
+ {
+ if (GET_CODE (op0) == REG && REGNO (op0) == EP_REGNUM)
+ return TRUE;
+
+ if (GET_CODE (op0) == SYMBOL_REF && TDA_NAME_P (XSTR (op0, 0)))
+ return TRUE;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+/* Return true if OP is either a register or 0 */
+
+int
+reg_or_0_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT)
+ return INTVAL (op) == 0;
+
+ else if (GET_CODE (op) == CONST_DOUBLE)
+ return CONST_DOUBLE_OK_FOR_G (op);
+
+ else if (GET_CODE (op) == REG)
+ return TRUE;
+
+ else if (GET_CODE (op) == SUBREG)
+ {
+ do {
+ op = SUBREG_REG (op);
+ } while (GET_CODE (op) == SUBREG);
+
+ if (GET_CODE (op) == MEM && !reload_completed)
+ return TRUE;
+
+ else if (GET_CODE (op) == REG)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Return true if OP is either a register or a signed five bit integer */
+
+int
+reg_or_int5_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == CONST_INT)
+ return CONST_OK_FOR_J (INTVAL (op));
+
+ else if (GET_CODE (op) == REG)
+ return TRUE;
+
+ else if (GET_CODE (op) == SUBREG)
+ {
+ do {
+ op = SUBREG_REG (op);
+ } while (GET_CODE (op) == SUBREG);
+
+ if (GET_CODE (op) == MEM && !reload_completed)
+ return TRUE;
+
+ else if (GET_CODE (op) == REG)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Return true if OP is a valid call operand. */
+
+int
+call_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* Only registers are valid call operands if TARGET_LONG_CALLS. */
+ if (TARGET_LONG_CALLS)
+ return GET_CODE (op) == REG;
+ return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
+}
+
+int
+special_symbolref_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == SYMBOL_REF)
+ return ENCODED_NAME_P (XSTR (op, 0));
+
+ else if (GET_CODE (op) == CONST)
+ return (GET_CODE (XEXP (op, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
+ && ENCODED_NAME_P (XSTR (XEXP (XEXP (op, 0), 0), 0))
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
+ && CONST_OK_FOR_K (INTVAL (XEXP (XEXP (op, 0), 1))));
+
+ return FALSE;
+}
+
+int
+movsi_source_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* Some constants, as well as symbolic operands
+ must be done with HIGH & LO_SUM patterns. */
+ if (CONSTANT_P (op)
+ && GET_CODE (op) != HIGH
+ && !(GET_CODE (op) == CONST_INT
+ && (CONST_OK_FOR_J (INTVAL (op))
+ || CONST_OK_FOR_K (INTVAL (op))
+ || CONST_OK_FOR_L (INTVAL (op)))))
+ return special_symbolref_operand (op, mode);
+ else
+ return general_operand (op, mode);
+}
+
+int
+power_of_two_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+
+ if (exact_log2 (INTVAL (op)) == -1)
+ return 0;
+ return 1;
+}
+
+int
+not_power_of_two_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ unsigned int mask;
+
+ if (mode == QImode)
+ mask = 0xff;
+ else if (mode == HImode)
+ mask = 0xffff;
+ else if (mode == SImode)
+ mask = 0xffffffff;
+ else
+ return 0;
+
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+
+ if (exact_log2 (~INTVAL (op) & mask) == -1)
+ return 0;
+ return 1;
+}
+
+
+/* Substitute memory references involving a pointer, to use the ep pointer,
+ taking care to save and preserve the ep. */
+
+static void
+substitute_ep_register (first_insn, last_insn, uses, regno, p_r1, p_ep)
+ rtx first_insn;
+ rtx last_insn;
+ int uses;
+ int regno;
+ rtx *p_r1;
+ rtx *p_ep;
+{
+ rtx reg = gen_rtx (REG, Pmode, regno);
+ rtx insn;
+ int i;
+
+ if (!*p_r1)
+ {
+ regs_ever_live[1] = 1;
+ *p_r1 = gen_rtx (REG, Pmode, 1);
+ *p_ep = gen_rtx (REG, Pmode, 30);
+ }
+
+ if (TARGET_DEBUG)
+ fprintf (stderr, "Saved %d bytes (%d uses of register %s) in function %s, starting as insn %d, ending at %d\n",
+ 2 * (uses - 3), uses, reg_names[regno],
+ IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
+ INSN_UID (first_insn), INSN_UID (last_insn));
+
+ if (GET_CODE (first_insn) == NOTE)
+ first_insn = next_nonnote_insn (first_insn);
+
+ last_insn = next_nonnote_insn (last_insn);
+ for (insn = first_insn; insn && insn != last_insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == INSN)
+ {
+ rtx pattern = single_set (insn);
+
+ /* Replace the memory references. */
+ if (pattern)
+ {
+ rtx *p_mem;
+
+ if (GET_CODE (SET_DEST (pattern)) == MEM
+ && GET_CODE (SET_SRC (pattern)) == MEM)
+ p_mem = (rtx *)0;
+
+ else if (GET_CODE (SET_DEST (pattern)) == MEM)
+ p_mem = &SET_DEST (pattern);
+
+ else if (GET_CODE (SET_SRC (pattern)) == MEM)
+ p_mem = &SET_SRC (pattern);
+
+ else
+ p_mem = (rtx *)0;
+
+ if (p_mem)
+ {
+ rtx addr = XEXP (*p_mem, 0);
+
+ if (GET_CODE (addr) == REG && REGNO (addr) == regno)
+ *p_mem = change_address (*p_mem, VOIDmode, *p_ep);
+
+ else if (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && REGNO (XEXP (addr, 0)) == regno
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT
+ && ((unsigned)INTVAL (XEXP (addr, 1))) < 256
+ && (GET_MODE (*p_mem) != QImode
+ || ((unsigned)INTVAL (XEXP (addr, 1))) < 128))
+ *p_mem = change_address (*p_mem, VOIDmode,
+ gen_rtx (PLUS, Pmode,
+ *p_ep, XEXP (addr, 1)));
+ }
+ }
+ }
+ }
+
+ /* Optimize back to back cases of ep <- r1 & r1 <- ep. */
+ insn = prev_nonnote_insn (first_insn);
+ if (insn && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == *p_ep
+ && SET_SRC (PATTERN (insn)) == *p_r1)
+ delete_insn (insn);
+ else
+ emit_insn_before (gen_rtx (SET, Pmode, *p_r1, *p_ep), first_insn);
+
+ emit_insn_before (gen_rtx (SET, Pmode, *p_ep, reg), first_insn);
+ emit_insn_before (gen_rtx (SET, Pmode, *p_ep, *p_r1), last_insn);
+}
+
+
+/* In rare cases, correct code generation requires extra machine
+ dependent processing between the second jump optimization pass and
+ delayed branch scheduling. On those machines, define this macro
+ as a C statement to act on the code starting at INSN.
+
+ On the 850, we use it to implement the -mep mode to copy heavily used
+ pointers to ep to use the implicit addressing */
+
+void v850_reorg (start_insn)
+ rtx start_insn;
+{
+ struct {
+ int uses;
+ rtx first_insn;
+ rtx last_insn;
+ } regs[FIRST_PSEUDO_REGISTER];
+
+ int i;
+ int use_ep = FALSE;
+ rtx r1 = NULL_RTX;
+ rtx ep = NULL_RTX;
+ rtx insn;
+ rtx pattern;
+
+ /* If not ep mode, just return now */
+ if (!TARGET_EP)
+ return;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ regs[i].uses = 0;
+ regs[i].first_insn = NULL_RTX;
+ regs[i].last_insn = NULL_RTX;
+ }
+
+ for (insn = start_insn; insn != NULL_RTX; insn = NEXT_INSN (insn))
+ {
+ switch (GET_CODE (insn))
+ {
+ /* End of basic block */
+ default:
+ if (!use_ep)
+ {
+ int max_uses = -1;
+ int max_regno = -1;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (max_uses < regs[i].uses)
+ {
+ max_uses = regs[i].uses;
+ max_regno = i;
+ }
+ }
+
+ if (max_uses > 3)
+ substitute_ep_register (regs[max_regno].first_insn,
+ regs[max_regno].last_insn,
+ max_uses, max_regno, &r1, &ep);
+ }
+
+ use_ep = FALSE;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ regs[i].uses = 0;
+ regs[i].first_insn = NULL_RTX;
+ regs[i].last_insn = NULL_RTX;
+ }
+ break;
+
+ case NOTE:
+ break;
+
+ case INSN:
+ pattern = single_set (insn);
+
+ /* See if there are any memory references we can shorten */
+ if (pattern)
+ {
+ rtx src = SET_SRC (pattern);
+ rtx dest = SET_DEST (pattern);
+ rtx mem;
+
+ if (GET_CODE (dest) == MEM && GET_CODE (src) == MEM)
+ mem = NULL_RTX;
+
+ else if (GET_CODE (dest) == MEM)
+ mem = dest;
+
+ else if (GET_CODE (src) == MEM)
+ mem = src;
+
+ else
+ mem = NULL_RTX;
+
+ if (mem && ep_memory_operand (mem, GET_MODE (mem)))
+ use_ep = TRUE;
+
+ else if (!use_ep && mem
+ && GET_MODE_SIZE (GET_MODE (mem)) <= UNITS_PER_WORD)
+ {
+ rtx addr = XEXP (mem, 0);
+ int regno = -1;
+ int short_p;
+
+ if (GET_CODE (addr) == REG)
+ {
+ short_p = TRUE;
+ regno = REGNO (addr);
+ }
+
+ else if (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT
+ && ((unsigned)INTVAL (XEXP (addr, 1))) < 256
+ && (GET_MODE (mem) != QImode
+ || ((unsigned)INTVAL (XEXP (addr, 1))) < 128))
+ {
+ short_p = TRUE;
+ regno = REGNO (XEXP (addr, 0));
+ }
+
+ else
+ short_p = FALSE;
+
+ if (short_p)
+ {
+ regs[regno].uses++;
+ regs[regno].last_insn = insn;
+ if (!regs[regno].first_insn)
+ regs[regno].first_insn = insn;
+ }
+ }
+
+ /* Loading up a register in the basic block zaps any savings
+ for the register */
+ if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
+ {
+ enum machine_mode mode = GET_MODE (dest);
+ int word = 0;
+ int regno;
+ int endregno;
+
+ while (GET_CODE (dest) == SUBREG)
+ {
+ word = SUBREG_WORD (dest);
+ dest = SUBREG_REG (dest);
+ }
+
+ regno = REGNO (dest) + word;
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ if (!use_ep)
+ {
+ /* See if we can use the pointer before this
+ modification. */
+ int max_uses = -1;
+ int max_regno = -1;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (max_uses < regs[i].uses)
+ {
+ max_uses = regs[i].uses;
+ max_regno = i;
+ }
+ }
+
+ if (max_uses > 3
+ && max_regno >= regno
+ && max_regno < endregno)
+ {
+ substitute_ep_register (regs[max_regno].first_insn,
+ regs[max_regno].last_insn,
+ max_uses, max_regno, &r1, &ep);
+
+ /* Since we made a substitution, zap all remembered
+ registers. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ regs[i].uses = 0;
+ regs[i].first_insn = NULL_RTX;
+ regs[i].last_insn = NULL_RTX;
+ }
+ }
+ }
+
+ for (i = regno; i < endregno; i++)
+ {
+ regs[i].uses = 0;
+ regs[i].first_insn = NULL_RTX;
+ regs[i].last_insn = NULL_RTX;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/* # of registers saved by the interrupt handler. */
+#define INTERRUPT_FIXED_NUM 4
+
+/* # of bytes for registers saved by the interrupt handler. */
+#define INTERRUPT_FIXED_SAVE_SIZE (4 * INTERRUPT_FIXED_NUM)
+
+/* # of registers saved in register parameter area. */
+#define INTERRUPT_REGPARM_NUM 4
+/* # of words saved for other registers. */
+#define INTERRUPT_ALL_SAVE_NUM \
+ (30 - INTERRUPT_FIXED_NUM + INTERRUPT_REGPARM_NUM)
+
+#define INTERRUPT_ALL_SAVE_SIZE (4 * INTERRUPT_ALL_SAVE_NUM)
+
+int
+compute_register_save_size (p_reg_saved)
+ long *p_reg_saved;
+{
+ int size = 0;
+ int i;
+ int interrupt_handler = v850_interrupt_function_p (current_function_decl);
+ int call_p = regs_ever_live[31];
+ long reg_saved = 0;
+
+ /* Count the return pointer if we need to save it. */
+ if (profile_flag && !call_p)
+ regs_ever_live[31] = call_p = 1;
+
+ /* Count space for the register saves. */
+ if (interrupt_handler)
+ {
+ for (i = 0; i <= 31; i++)
+ switch (i)
+ {
+ default:
+ if (regs_ever_live[i] || call_p)
+ {
+ size += 4;
+ reg_saved |= 1L << i;
+ }
+ break;
+
+ /* We don't save/restore r0 or the stack pointer */
+ case 0:
+ case STACK_POINTER_REGNUM:
+ break;
+
+ /* For registers with fixed use, we save them, set them to the
+ appropriate value, and then restore them.
+ These registers are handled specially, so don't list them
+ on the list of registers to save in the prologue. */
+ case 1: /* temp used to hold ep */
+ case 5: /* gp */
+ case 10: /* temp used to call interrupt save/restore */
+ case EP_REGNUM: /* ep */
+ size += 4;
+ break;
+ }
+ }
+
+ else
+ for (i = 0; i <= 31; i++)
+ if (regs_ever_live[i] && ((! call_used_regs[i]) || i == 31))
+ {
+ size += 4;
+ reg_saved |= 1L << i;
+ }
+
+ if (p_reg_saved)
+ *p_reg_saved = reg_saved;
+
+ return size;
+}
+
+int
+compute_frame_size (size, p_reg_saved)
+ int size;
+ long *p_reg_saved;
+{
+ extern int current_function_outgoing_args_size;
+
+ return (size
+ + compute_register_save_size (p_reg_saved)
+ + current_function_outgoing_args_size);
+}
+
+
+void
+expand_prologue ()
+{
+ unsigned int i;
+ int offset;
+ unsigned int size = get_frame_size ();
+ unsigned int actual_fsize;
+ unsigned int init_stack_alloc = 0;
+ rtx save_regs[32];
+ rtx save_all;
+ int num_save;
+ int default_stack;
+ int code;
+ int interrupt_handler = v850_interrupt_function_p (current_function_decl);
+ long reg_saved = 0;
+
+ actual_fsize = compute_frame_size (size, &reg_saved);
+
+ /* Save/setup global registers for interrupt functions right now */
+ if (interrupt_handler)
+ {
+ emit_insn (gen_save_interrupt ());
+ actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
+ if (((1L << 31) & reg_saved) != 0)
+ actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
+ }
+
+ /* Save arg registers to the stack if necessary. */
+ else if (current_function_anonymous_args)
+ {
+ if (TARGET_PROLOG_FUNCTION)
+ emit_insn (gen_save_r6_r9 ());
+ else
+ {
+ offset = 0;
+ for (i = 6; i < 10; i++)
+ {
+ emit_move_insn (gen_rtx (MEM, SImode,
+ plus_constant (stack_pointer_rtx,
+ offset)),
+ gen_rtx (REG, SImode, i));
+ offset += 4;
+ }
+ }
+ }
+
+ /* Identify all of the saved registers */
+ num_save = 0;
+ default_stack = 0;
+ for (i = 1; i < 31; i++)
+ {
+ if (((1L << i) & reg_saved) != 0)
+ save_regs[num_save++] = gen_rtx (REG, Pmode, i);
+ }
+
+ /* If the return pointer is saved, the helper functions also allocate
+ 16 bytes of stack for arguments to be saved in. */
+ if (((1L << 31) & reg_saved) != 0)
+ {
+ save_regs[num_save++] = gen_rtx (REG, Pmode, 31);
+ default_stack = 16;
+ }
+
+ /* See if we have an insn that allocates stack space and saves the particular
+ registers we want to. */
+ save_all = NULL_RTX;
+ if (TARGET_PROLOG_FUNCTION && num_save > 0 && actual_fsize >= default_stack)
+ {
+ int alloc_stack = (4 * num_save) + default_stack;
+ int unalloc_stack = actual_fsize - alloc_stack;
+ int save_func_len = 4;
+ int save_normal_len;
+
+ if (unalloc_stack)
+ save_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
+
+ /* see if we would have used ep to save the stack */
+ if (TARGET_EP && num_save > 3 && (unsigned)actual_fsize < 255)
+ save_normal_len = (3 * 2) + (2 * num_save);
+ else
+ save_normal_len = 4 * num_save;
+
+ save_normal_len += CONST_OK_FOR_J (actual_fsize) ? 2 : 4;
+
+ /* Don't bother checking if we don't actually save any space.
+ This happens for instance if one register is saved and additional
+ stack space is allocated. */
+ if (save_func_len < save_normal_len)
+ {
+ save_all = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num_save + 2));
+ XVECEXP (save_all, 0, 0) = gen_rtx (SET, VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ GEN_INT (-alloc_stack)));
+
+ XVECEXP (save_all, 0, num_save+1)
+ = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, Pmode, 10));
+
+ offset = - default_stack;
+ for (i = 0; i < num_save; i++)
+ {
+ XVECEXP (save_all, 0, i+1)
+ = gen_rtx (SET, VOIDmode,
+ gen_rtx (MEM, Pmode,
+ plus_constant (stack_pointer_rtx, offset)),
+ save_regs[i]);
+ offset -= 4;
+ }
+
+ code = recog (save_all, NULL_RTX, NULL_PTR);
+ if (code >= 0)
+ {
+ rtx insn = emit_insn (save_all);
+ INSN_CODE (insn) = code;
+ actual_fsize -= alloc_stack;
+
+ if (TARGET_DEBUG)
+ fprintf (stderr, "Saved %d bytes via prologue function (%d vs. %d) for function %s\n",
+ save_normal_len - save_func_len,
+ save_normal_len, save_func_len,
+ IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
+ }
+ else
+ save_all = NULL_RTX;
+ }
+ }
+
+ /* If no prolog save function is available, store the registers the old fashioned
+ way (one by one). */
+ if (!save_all)
+ {
+ /* Special case interrupt functions that save all registers for a call. */
+ if (interrupt_handler && ((1L << 31) & reg_saved) != 0)
+ emit_insn (gen_save_all_interrupt ());
+
+ else
+ {
+ /* If the stack is too big, allocate it in chunks so we can do the
+ register saves. We use the register save size so we use the ep
+ register. */
+ if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
+ init_stack_alloc = compute_register_save_size (NULL);
+ else
+ init_stack_alloc = actual_fsize;
+
+ /* Save registers at the beginning of the stack frame */
+ offset = init_stack_alloc - 4;
+
+ if (init_stack_alloc)
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-init_stack_alloc)));
+
+ /* Save the return pointer first. */
+ if (num_save > 0 && REGNO (save_regs[num_save-1]) == 31)
+ {
+ emit_move_insn (gen_rtx (MEM, SImode,
+ plus_constant (stack_pointer_rtx,
+ offset)),
+ save_regs[--num_save]);
+ offset -= 4;
+ }
+
+ for (i = 0; i < num_save; i++)
+ {
+ emit_move_insn (gen_rtx (MEM, SImode,
+ plus_constant (stack_pointer_rtx,
+ offset)),
+ save_regs[i]);
+ offset -= 4;
+ }
+ }
+ }
+
+ /* Allocate the rest of the stack that was not allocated above (either it is
+ > 32K or we just called a function to save the registers and needed more
+ stack. */
+ if (actual_fsize > init_stack_alloc)
+ {
+ int diff = actual_fsize - init_stack_alloc;
+ if (CONST_OK_FOR_K (diff))
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-diff)));
+ else
+ {
+ rtx reg = gen_rtx (REG, Pmode, 12);
+ emit_move_insn (reg, GEN_INT (-diff));
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
+ }
+ }
+
+ /* If we need a frame pointer, set it up now. */
+ if (frame_pointer_needed)
+ emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+}
+
+
+void
+expand_epilogue ()
+{
+ unsigned int i;
+ int offset;
+ unsigned int size = get_frame_size ();
+ long reg_saved = 0;
+ unsigned int actual_fsize = compute_frame_size (size, &reg_saved);
+ unsigned int init_stack_free = 0;
+ rtx restore_regs[32];
+ rtx restore_all;
+ int num_restore;
+ int default_stack;
+ int code;
+ int interrupt_handler = v850_interrupt_function_p (current_function_decl);
+
+ /* Eliminate the initial stack stored by interrupt functions. */
+ if (interrupt_handler)
+ {
+ actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
+ if (((1L << 31) & reg_saved) != 0)
+ actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
+ }
+
+ /* Cut off any dynamic stack created. */
+ if (frame_pointer_needed)
+ emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+
+ /* Identify all of the saved registers */
+ num_restore = 0;
+ default_stack = 0;
+ for (i = 1; i < 31; i++)
+ {
+ if (((1L << i) & reg_saved) != 0)
+ restore_regs[num_restore++] = gen_rtx (REG, Pmode, i);
+ }
+
+ /* If the return pointer is saved, the helper functions also allocate
+ 16 bytes of stack for arguments to be saved in. */
+ if (((1L << 31) & reg_saved) != 0)
+ {
+ restore_regs[num_restore++] = gen_rtx (REG, Pmode, 31);
+ default_stack = 16;
+ }
+
+ /* See if we have an insn that restores the particular registers we
+ want to. */
+ restore_all = NULL_RTX;
+ if (TARGET_PROLOG_FUNCTION && num_restore > 0 && actual_fsize >= default_stack
+ && !interrupt_handler)
+ {
+ int alloc_stack = (4 * num_restore) + default_stack;
+ int unalloc_stack = actual_fsize - alloc_stack;
+ int restore_func_len = 4;
+ int restore_normal_len;
+
+ if (unalloc_stack)
+ restore_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
+
+ /* see if we would have used ep to restore the registers */
+ if (TARGET_EP && num_restore > 3 && (unsigned)actual_fsize < 255)
+ restore_normal_len = (3 * 2) + (2 * num_restore);
+ else
+ restore_normal_len = 4 * num_restore;
+
+ restore_normal_len += (CONST_OK_FOR_J (actual_fsize) ? 2 : 4) + 2;
+
+ /* Don't bother checking if we don't actually save any space. */
+ if (restore_func_len < restore_normal_len)
+ {
+ restore_all = gen_rtx (PARALLEL, VOIDmode,
+ rtvec_alloc (num_restore + 2));
+ XVECEXP (restore_all, 0, 0) = gen_rtx (RETURN, VOIDmode);
+ XVECEXP (restore_all, 0, 1)
+ = gen_rtx (SET, VOIDmode, stack_pointer_rtx,
+ gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ GEN_INT (alloc_stack)));
+
+ offset = alloc_stack - 4;
+ for (i = 0; i < num_restore; i++)
+ {
+ XVECEXP (restore_all, 0, i+2)
+ = gen_rtx (SET, VOIDmode,
+ restore_regs[i],
+ gen_rtx (MEM, Pmode,
+ plus_constant (stack_pointer_rtx, offset)));
+ offset -= 4;
+ }
+
+ code = recog (restore_all, NULL_RTX, NULL_PTR);
+ if (code >= 0)
+ {
+ rtx insn;
+
+ actual_fsize -= alloc_stack;
+ if (actual_fsize)
+ {
+ if (CONST_OK_FOR_K (actual_fsize))
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (actual_fsize)));
+ else
+ {
+ rtx reg = gen_rtx (REG, Pmode, 12);
+ emit_move_insn (reg, GEN_INT (actual_fsize));
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ reg));
+ }
+ }
+
+ insn = emit_jump_insn (restore_all);
+ INSN_CODE (insn) = code;
+
+ if (TARGET_DEBUG)
+ fprintf (stderr, "Saved %d bytes via epilogue function (%d vs. %d) in function %s\n",
+ restore_normal_len - restore_func_len,
+ restore_normal_len, restore_func_len,
+ IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
+ }
+ else
+ restore_all = NULL_RTX;
+ }
+ }
+
+ /* If no epilog save function is available, restore the registers the
+ old fashioned way (one by one). */
+ if (!restore_all)
+ {
+ /* If the stack is large, we need to cut it down in 2 pieces. */
+ if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
+ init_stack_free = 4 * num_restore;
+ else
+ init_stack_free = actual_fsize;
+
+ /* Deallocate the rest of the stack if it is > 32K or if extra stack
+ was allocated for an interrupt handler that makes a call. */
+ if (actual_fsize > init_stack_free || (interrupt_handler && actual_fsize))
+ {
+ int diff = actual_fsize - ((interrupt_handler) ? 0 : init_stack_free);
+ if (CONST_OK_FOR_K (diff))
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (diff)));
+ else
+ {
+ rtx reg = gen_rtx (REG, Pmode, 12);
+ emit_move_insn (reg, GEN_INT (diff));
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ reg));
+ }
+ }
+
+ /* Special case interrupt functions that save all registers
+ for a call. */
+ if (interrupt_handler && ((1L << 31) & reg_saved) != 0)
+ emit_insn (gen_restore_all_interrupt ());
+ else
+ {
+ /* Restore registers from the beginning of the stack frame */
+ offset = init_stack_free - 4;
+
+ /* Restore the return pointer first. */
+ if (num_restore > 0 && REGNO (restore_regs[num_restore-1]) == 31)
+ {
+ emit_move_insn (restore_regs[--num_restore],
+ gen_rtx (MEM, SImode,
+ plus_constant (stack_pointer_rtx,
+ offset)));
+ offset -= 4;
+ }
+
+ for (i = 0; i < num_restore; i++)
+ {
+ emit_move_insn (restore_regs[i],
+ gen_rtx (MEM, SImode,
+ plus_constant (stack_pointer_rtx,
+ offset)));
+
+ offset -= 4;
+ }
+
+ /* Cut back the remainder of the stack. */
+ if (init_stack_free)
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (init_stack_free)));
+ }
+
+ /* And return or use reti for interrupt handlers. */
+ if (interrupt_handler)
+ emit_jump_insn (gen_restore_interrupt ());
+ else if (actual_fsize)
+ emit_jump_insn (gen_return_internal ());
+ else
+ emit_jump_insn (gen_return ());
+ }
+
+ current_function_anonymous_args = 0;
+ v850_interrupt_cache_p = FALSE;
+ v850_interrupt_p = FALSE;
+}
+
+
+/* Update the condition code from the insn. */
+
+void
+notice_update_cc (body, insn)
+ rtx body;
+ rtx insn;
+{
+ switch (get_attr_cc (insn))
+ {
+ case CC_NONE:
+ /* Insn does not affect CC at all. */
+ break;
+
+ case CC_NONE_0HIT:
+ /* Insn does not change CC, but the 0'th operand has been changed. */
+ if (cc_status.value1 != 0
+ && reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
+ cc_status.value1 = 0;
+ break;
+
+ case CC_SET_ZN:
+ /* Insn sets the Z,N flags of CC to recog_operand[0].
+ V,C is in an unusable state. */
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
+ cc_status.value1 = recog_operand[0];
+ break;
+
+ case CC_SET_ZNV:
+ /* Insn sets the Z,N,V flags of CC to recog_operand[0].
+ C is in an unusable state. */
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_NO_CARRY;
+ cc_status.value1 = recog_operand[0];
+ break;
+
+ case CC_COMPARE:
+ /* The insn is a compare instruction. */
+ CC_STATUS_INIT;
+ cc_status.value1 = SET_SRC (body);
+ break;
+
+ case CC_CLOBBER:
+ /* Insn doesn't leave CC in a usable state. */
+ CC_STATUS_INIT;
+ break;
+ }
+}
+
+
+/* Return nonzero if ATTR is a valid attribute for DECL.
+ ATTRIBUTES are any existing attributes and ARGS are the arguments
+ supplied with ATTR.
+
+ Supported attributes:
+
+ interrupt_handler or interrupt: output a prologue and epilogue suitable
+ for an interrupt handler. */
+
+int
+v850_valid_machine_decl_attribute (decl, attributes, attr, args)
+ tree decl;
+ tree attributes;
+ tree attr;
+ tree args;
+{
+ if (args != NULL_TREE)
+ return 0;
+
+ if (is_attribute_p ("interrupt_handler", attr)
+ || is_attribute_p ("interrupt", attr))
+ return TREE_CODE (decl) == FUNCTION_DECL;
+
+ return 0;
+}
+
+
+/* Return nonzero if FUNC is an interrupt function as specified
+ by the "interrupt" attribute. */
+
+int
+v850_interrupt_function_p (func)
+ tree func;
+{
+ tree a;
+ int ret = 0;
+
+ if (v850_interrupt_cache_p)
+ return v850_interrupt_p;
+
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return 0;
+
+ a = lookup_attribute ("interrupt_handler", DECL_MACHINE_ATTRIBUTES (func));
+ if (a != NULL_TREE)
+ ret = 1;
+
+ else
+ {
+ a = lookup_attribute ("interrupt", DECL_MACHINE_ATTRIBUTES (func));
+ ret = a != NULL_TREE;
+ }
+
+ /* Its not safe to trust global variables until after function inlining has
+ been done. */
+ if (reload_completed | reload_in_progress)
+ v850_interrupt_p = ret;
+
+ return ret;
+}
+
+
+extern struct obstack *saveable_obstack;
+
+v850_encode_data_area (decl)
+ tree decl;
+{
+ char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ int len = strlen (str);
+ char *newstr;
+
+ /* In the Cygnus sources we actually do something; this is just
+ here to make merges easier. */
+ return;
+}