summaryrefslogtreecommitdiff
path: root/gcc/config/v850/v850.c
diff options
context:
space:
mode:
authorYamaArashi <shadow962@live.com>2016-01-06 01:47:28 -0800
committerYamaArashi <shadow962@live.com>2016-01-06 01:47:28 -0800
commitbe8b04496302184c6e8f04d6179f9c3afc50aeb6 (patch)
tree726e2468c0c07add773c0dbd86ab6386844259ae /gcc/config/v850/v850.c
initial commit
Diffstat (limited to 'gcc/config/v850/v850.c')
-rwxr-xr-xgcc/config/v850/v850.c3673
1 files changed, 3673 insertions, 0 deletions
diff --git a/gcc/config/v850/v850.c b/gcc/config/v850/v850.c
new file mode 100755
index 0000000..60cb9e1
--- /dev/null
+++ b/gcc/config/v850/v850.c
@@ -0,0 +1,3673 @@
+/* Subroutines for insn-output.c for NEC V850 series
+ Copyright (C) 1996, 1997, 1998, 1999 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 "tree.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 "toplev.h"
+
+#ifndef streq
+#define streq(a,b) (strcmp (a, b) == 0)
+#endif
+
+/* Function prototypes that cannot exist in v850.h due to dependency
+ compilcations. */
+extern rtx function_arg
+ PROTO ((CUMULATIVE_ARGS *, enum machine_mode, tree, int));
+extern int function_arg_partial_nregs
+ PROTO ((CUMULATIVE_ARGS *, enum machine_mode, tree, int));
+extern void asm_file_start PROTO ((FILE *));
+extern void print_operand PROTO ((FILE *, rtx, int ));
+extern void print_operand_address PROTO ((FILE *, rtx));
+extern void v850_output_aligned_bss
+ PROTO ((FILE *, tree, char *, int, int));
+extern void v850_output_common
+ PROTO ((FILE *, tree, char *, int, int));
+extern void v850_output_local
+ PROTO ((FILE *, tree, char *, int, int));
+extern int const_costs PROTO ((rtx, enum rtx_code));
+extern char * output_move_double PROTO ((rtx *));
+extern char * output_move_single PROTO ((rtx *));
+extern int ep_memory_operand
+ PROTO ((rtx, enum machine_mode, int));
+extern int reg_or_0_operand PROTO ((rtx, enum machine_mode));
+extern int reg_or_int5_operand PROTO ((rtx, enum machine_mode));
+extern int call_address_operand PROTO ((rtx, enum machine_mode));
+extern int movsi_source_operand PROTO ((rtx, enum machine_mode));
+extern int power_of_two_operand PROTO ((rtx, enum machine_mode));
+extern int not_power_of_two_operand PROTO ((rtx, enum machine_mode));
+extern int special_symbolref_operand PROTO ((rtx, enum machine_mode));
+extern void v850_reorg PROTO ((rtx));
+extern void notice_update_cc PROTO ((rtx, rtx));
+extern int v850_valid_machine_decl_attribute
+ PROTO ((tree, tree, tree));
+extern int v850_interrupt_function_p PROTO ((tree));
+extern int pattern_is_ok_for_prologue PROTO ((rtx, enum machine_mode));
+extern int pattern_is_ok_for_epilogue PROTO ((rtx, enum machine_mode));
+extern int register_is_ok_for_epilogue PROTO ((rtx, enum machine_mode));
+extern char * construct_save_jarl PROTO ((rtx));
+extern char * construct_restore_jr PROTO ((rtx));
+extern void v850_encode_data_area PROTO ((tree));
+extern void v850_set_default_decl_attr PROTO ((tree));
+
+/* Function prototypes for stupid compilers: */
+static void const_double_split
+ PROTO ((rtx, HOST_WIDE_INT *, HOST_WIDE_INT *));
+static int const_costs_int PROTO ((HOST_WIDE_INT, int));
+static void substitute_ep_register PROTO ((rtx, rtx, int, int, rtx *, rtx *));
+static int push_data_area PROTO ((v850_data_area));
+static int pop_data_area PROTO ((v850_data_area));
+static int parse_ghs_pragma_token PROTO ((char *));
+static int ep_memory_offset PROTO ((enum machine_mode, int));
+static int mark_current_function_as_interrupt PROTO ((void));
+static void v850_set_data_area PROTO ((tree, v850_data_area));
+/* CYGNUS LOCAL v850e */
+extern int pattern_is_ok_for_prepare PROTO ((rtx, enum machine_mode));
+extern int pattern_is_ok_for_dispose PROTO ((rtx, enum machine_mode));
+extern char * construct_dispose_instruction PROTO ((rtx));
+extern char * construct_prepare_instruction PROTO ((rtx));
+/* END CYGNUS LOCAL */
+
+/* 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 PROTO ((const char *));
+
+ /* 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);
+ }
+ }
+ }
+/* CYGNUS LOCAL v850e */
+#ifdef MASK_US_BIT_SET
+ /* Make sure that the US_BIT_SET mask has been correctly initialised. */
+ if ((target_flags & MASK_US_MASK_SET) == 0)
+ {
+ if (TARGET_V850EA)
+ target_flags |= (MASK_US_MASK_SET | MASK_US_BIT_SET);
+ else
+ {
+ target_flags |= MASK_US_MASK_SET;
+ target_flags &= ~MASK_US_BIT_SET;
+ }
+ }
+#endif
+/* END CYGNUS LOCAL */
+}
+
+
+/* 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;
+
+ default:
+ break;
+ }
+ }
+
+ 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 'c':
+ /* We use 'c' operands with symbols for .vtinherit */
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ output_addr_const(file, x);
+ break;
+ }
+ /* fall through */
+ case 'b':
+ case 'B':
+ case 'C':
+ switch ((code == 'B' || code == 'C')
+ ? reverse_condition (GET_CODE (x)) : GET_CODE (x))
+ {
+ case NE:
+ if (code == 'c' || code == 'C')
+ fprintf (file, "nz");
+ else
+ fprintf (file, "ne");
+ break;
+ case EQ:
+ if (code == 'c' || code == 'C')
+ fprintf (file, "z");
+ else
+ fprintf (file, "e");
+ break;
+ case GE:
+ fprintf (file, "ge");
+ break;
+ case GT:
+ fprintf (file, "gt");
+ break;
+ case LE:
+ fprintf (file, "le");
+ break;
+ case LT:
+ fprintf (file, "lt");
+ break;
+ case GEU:
+ fprintf (file, "nl");
+ break;
+ case GTU:
+ fprintf (file, "h");
+ break;
+ case LEU:
+ fprintf (file, "nh");
+ break;
+ case LTU:
+ fprintf (file, "l");
+ 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;
+
+ default:
+ break;
+ }
+ break;
+ case 'S':
+ {
+ /* if it's a reference to a TDA variable, use sst/sld vs. st/ld */
+ if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), FALSE))
+ fputs ("s", file);
+
+ break;
+ }
+ case 'T':
+ {
+ /* Like an 'S' operand above, but for unsigned loads only. */
+ if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), TRUE))
+ 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;
+ case 'z': /* reg or zero */
+ if (x == const0_rtx)
+ fputs (reg_names[0], file);
+ else if (GET_CODE (x) == REG)
+ fputs (reg_names[REGNO (x)], file);
+ else
+ abort ();
+ 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 */
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E)
+ return "mov %1,%0";
+ else
+/* END CYGNUS LOCAL */
+ 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 */
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E)
+ return "mov %F1,%0";
+ else
+/* END CYGNUS LOCAL */
+ 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)
+ {
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E)
+ return "mov hilo(%1),%0";
+ else
+/* END CYGNUS LOCAL */
+ 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 maximum offset supported for a short EP memory reference of mode
+ MODE and signedness UNSIGNEDP. */
+
+static int
+ep_memory_offset (mode, unsignedp)
+ enum machine_mode mode;
+ int ATTRIBUTE_UNUSED unsignedp;
+{
+ int max_offset = 0;
+
+ switch (mode)
+ {
+ case QImode:
+/* CYGNUS LOCAL v850e */
+ if (TARGET_SMALL_SLD)
+ {
+ max_offset = (1 << 4);
+ break;
+ }
+/* END CYGNUS LOCAL */
+ max_offset = (1 << 7);
+
+/* CYGNUS LOCAL v850e */
+#ifdef TARGET_US_BIT_SET
+ if (TARGET_V850E
+ && ( ( unsignedp && ! TARGET_US_BIT_SET)
+ || (! unsignedp && TARGET_US_BIT_SET)))
+ max_offset = (1 << 4);
+#else
+ if (TARGET_V850E && unsignedp)
+ max_offset = (1 << 4);
+#endif
+/* END CYGNUS LOCAL */
+ break;
+
+ case HImode:
+/* CYGNUS LOCAL v850e */
+ if (TARGET_SMALL_SLD)
+ {
+ max_offset = (1 << 5);
+ break;
+ }
+/* END CYGNUS LOCAL v850e */
+ max_offset = (1 << 8);
+
+/* CYGNUS LOCAL v850e */
+#ifdef TARGET_US_BIT_SET
+ if (TARGET_V850E
+ && (( unsignedp && ! TARGET_US_BIT_SET)
+ || (! unsignedp && TARGET_US_BIT_SET)))
+ max_offset = (1 << 5);
+#else
+ if (TARGET_V850E && unsignedp)
+ max_offset = (1 << 5);
+#endif
+/* END CYGNUS LOCAL */
+ break;
+
+ case SImode:
+ case SFmode:
+ max_offset = (1 << 8);
+ break;
+
+ default:
+ break;
+ }
+
+ return max_offset;
+}
+
+/* Return true if OP is a valid short EP memory reference */
+
+int
+ep_memory_operand (op, mode, unsigned_load)
+ rtx op;
+ enum machine_mode mode;
+ int unsigned_load;
+{
+ rtx addr, op0, op1;
+ int max_offset;
+ int mask;
+
+ if (GET_CODE (op) != MEM)
+ return FALSE;
+
+ max_offset = ep_memory_offset (mode, unsigned_load);
+
+ mask = GET_MODE_SIZE (mode) - 1;
+
+ 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) >= 0
+ && (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
+ return register_operand (op, mode);
+}
+
+/* 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
+ return register_operand (op, mode);
+}
+
+/* Return true if OP is a valid call operand. */
+
+int
+call_address_operand (op, mode)
+ rtx op;
+ enum machine_mode ATTRIBUTE_UNUSED 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 ATTRIBUTE_UNUSED 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) != CONSTANT_P_RTX
+ && !(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 ATTRIBUTE_UNUSED 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;
+
+ 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;
+ /* Memory operands are signed by default. */
+ int unsignedp = FALSE;
+
+ 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);
+
+ /* CYGNUS LOCAL v850e */
+ else if (GET_CODE (SET_SRC (pattern)) == SIGN_EXTEND
+ && GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
+ p_mem = &XEXP (SET_SRC (pattern), 0);
+
+ else if (GET_CODE (SET_SRC (pattern)) == ZERO_EXTEND
+ && GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
+ {
+ p_mem = &XEXP (SET_SRC (pattern), 0);
+ unsignedp = TRUE;
+ }
+ /* END CYGNUS LOCAL */
+
+ 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
+ && ((INTVAL (XEXP (addr, 1)))
+ < ep_memory_offset (GET_MODE (*p_mem),
+ unsignedp))
+ && ((INTVAL (XEXP (addr, 1))) >= 0))
+ *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;
+ /* Memory operands are signed by default. */
+ int unsignedp = FALSE;
+
+ /* We might have (SUBREG (MEM)) here, so just get rid of the
+ subregs to make this code simpler. It is safe to call
+ alter_subreg any time after reload. */
+ if (GET_CODE (dest) == SUBREG)
+ dest = alter_subreg (dest);
+ if (GET_CODE (src) == SUBREG)
+ src = alter_subreg (src);
+
+ 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;
+
+ /* CYGNUS LOCAL v850e */
+ else if (GET_CODE (src) == SIGN_EXTEND
+ && GET_CODE (XEXP (src, 0)) == MEM)
+ mem = XEXP (src, 0);
+
+ else if (GET_CODE (src) == ZERO_EXTEND
+ && GET_CODE (XEXP (src, 0)) == MEM)
+ {
+ mem = XEXP (src, 0);
+ unsignedp = TRUE;
+ }
+ /* END CYGNUS LOCAL */
+
+ else
+ mem = NULL_RTX;
+
+ if (mem && ep_memory_operand (mem, GET_MODE (mem), unsignedp))
+ 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
+ && ((INTVAL (XEXP (addr, 1)))
+ < ep_memory_offset (GET_MODE (mem), unsignedp))
+ && ((INTVAL (XEXP (addr, 1))) >= 0))
+ {
+ 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)
+ {
+ enum machine_mode mode = GET_MODE (dest);
+ int regno;
+ int endregno;
+
+ regno = REGNO (dest);
+ 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 [LINK_POINTER_REGNUM];
+ long reg_saved = 0;
+
+ /* Count the return pointer if we need to save it. */
+ if (profile_flag && !call_p)
+ regs_ever_live [LINK_POINTER_REGNUM] = 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 4: /* gp */
+ case 10: /* temp used to call interrupt save/restore */
+ case EP_REGNUM: /* ep */
+ size += 4;
+ break;
+ }
+ }
+ else
+ {
+ /* Find the first register that needs to be saved. */
+ for (i = 0; i <= 31; i++)
+ if (regs_ever_live[i] && ((! call_used_regs[i])
+ || i == LINK_POINTER_REGNUM))
+ break;
+
+ /* If it is possible that an out-of-line helper function might be
+ used to generate the prologue for the current function, then we
+ need to cover the possibility that such a helper function will
+ be used, despite the fact that there might be gaps in the list of
+ registers that need to be saved. To detect this we note that the
+ helper functions always push at least register r29 if the link
+ register is not used, and at least registers r27 - r31 if the
+ link register is used (and provided that the function is not an
+ interrupt handler). */
+
+ if (TARGET_PROLOG_FUNCTION
+ && (i == 2 || i >= 20)
+ && regs_ever_live[LINK_POINTER_REGNUM] ? (i < 28) : (i < 30))
+ {
+ if (i == 2)
+ {
+ size += 4;
+ reg_saved |= 1L << i;
+
+ i = 20;
+ }
+
+ /* Helper functions save all registers between the starting
+ register and the last register, regardless of whether they
+ are actually used by the function or not. */
+ for (; i <= 29; i++)
+ {
+ size += 4;
+ reg_saved |= 1L << i;
+ }
+
+ if (regs_ever_live [LINK_POINTER_REGNUM])
+ {
+ size += 4;
+ reg_saved |= 1L << LINK_POINTER_REGNUM;
+ }
+ }
+ else
+ {
+ for (; i <= 31; i++)
+ if (regs_ever_live[i] && ((! call_used_regs[i])
+ || i == LINK_POINTER_REGNUM))
+ {
+ 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;
+ unsigned int num_save;
+ unsigned 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)
+ {
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
+ emit_insn (gen_save_interrupt_v850e ());
+ else
+/* END CYGNUS LOCAL */
+ emit_insn (gen_save_interrupt ());
+
+ actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
+
+ if (((1L << LINK_POINTER_REGNUM) & 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)
+ {
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
+ emit_insn (gen_save_r6_r9_v850e ());
+ else
+/* END CYGNUS LOCAL */
+ 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 << LINK_POINTER_REGNUM) & reg_saved) != 0)
+ {
+ save_regs[num_save++] = gen_rtx (REG, Pmode, LINK_POINTER_REGNUM);
+ 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 + (TARGET_V850 ? 2 : 1)));
+ XVECEXP (save_all, 0, 0) = gen_rtx (SET, VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ GEN_INT (-alloc_stack)));
+
+ if (TARGET_V850)
+ {
+ 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 << LINK_POINTER_REGNUM) & reg_saved) != 0)
+ {
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
+ emit_insn (gen_save_all_interrupt_v850e ());
+ else
+/* END CYGNUS LOCAL */
+ 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]) == LINK_POINTER_REGNUM)
+ {
+ 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;
+ unsigned int num_restore;
+ unsigned 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 << LINK_POINTER_REGNUM) & 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 << LINK_POINTER_REGNUM) & reg_saved) != 0)
+ {
+ restore_regs[num_restore++] = gen_rtx (REG, Pmode, LINK_POINTER_REGNUM);
+ 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;
+
+ 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 << LINK_POINTER_REGNUM) & reg_saved) != 0)
+ {
+/* CYGNUS LOCAL v850e */
+ if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
+ emit_insn (gen_restore_all_interrupt_v850e ());
+ else
+/* END CYGNUS LOCAL */
+ 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]) == LINK_POINTER_REGNUM)
+ {
+ 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;
+ }
+}
+
+/* Retrieve the data area that has been chosen for the given decl. */
+
+v850_data_area
+v850_get_data_area (decl)
+ tree decl;
+{
+ if (lookup_attribute ("sda", DECL_MACHINE_ATTRIBUTES (decl)) != NULL_TREE)
+ return DATA_AREA_SDA;
+
+ if (lookup_attribute ("tda", DECL_MACHINE_ATTRIBUTES (decl)) != NULL_TREE)
+ return DATA_AREA_TDA;
+
+ if (lookup_attribute ("zda", DECL_MACHINE_ATTRIBUTES (decl)) != NULL_TREE)
+ return DATA_AREA_ZDA;
+
+ return DATA_AREA_NORMAL;
+}
+
+/* Store the indicated data area in the decl's attributes. */
+
+static void
+v850_set_data_area (decl, data_area)
+ tree decl;
+ v850_data_area data_area;
+{
+ tree name;
+
+ switch (data_area)
+ {
+ case DATA_AREA_SDA: name = get_identifier ("sda"); break;
+ case DATA_AREA_TDA: name = get_identifier ("tda"); break;
+ case DATA_AREA_ZDA: name = get_identifier ("zda"); break;
+ default:
+ return;
+ }
+
+ DECL_MACHINE_ATTRIBUTES (decl) = tree_cons
+ (name, NULL, DECL_MACHINE_ATTRIBUTES (decl));
+}
+
+/* Return nonzero if ATTR is a valid attribute for DECL.
+ ARGS are the arguments supplied with ATTR. */
+
+int
+v850_valid_machine_decl_attribute (decl, attr, args)
+ tree decl;
+ tree attr;
+ tree args;
+{
+ v850_data_area data_area;
+ v850_data_area area;
+
+ if (args != NULL_TREE)
+ return 0;
+
+ if (is_attribute_p ("interrupt_handler", attr)
+ || is_attribute_p ("interrupt", attr))
+ return TREE_CODE (decl) == FUNCTION_DECL;
+
+ /* Implement data area attribute. */
+ if (is_attribute_p ("sda", attr))
+ data_area = DATA_AREA_SDA;
+ else if (is_attribute_p ("tda", attr))
+ data_area = DATA_AREA_TDA;
+ else if (is_attribute_p ("zda", attr))
+ data_area = DATA_AREA_ZDA;
+ else
+ return 0;
+
+ switch (TREE_CODE (decl))
+ {
+ case VAR_DECL:
+ if (current_function_decl != NULL_TREE)
+ error_with_decl (decl, "\
+a data area attribute cannot be specified for local variables");
+
+ /* Drop through. */
+
+ case FUNCTION_DECL:
+ area = v850_get_data_area (decl);
+ if (area != DATA_AREA_NORMAL && data_area != area)
+ error_with_decl (decl, "\
+data area of '%s' conflicts with previous declaration");
+
+ return 1;
+
+ default:
+ break;
+ }
+
+ 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;
+
+void
+v850_encode_data_area (decl)
+ tree decl;
+{
+ char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ int len = strlen (str);
+ char * newstr;
+
+ /* Map explict sections into the appropriate attribute */
+ if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
+ {
+ if (DECL_SECTION_NAME (decl))
+ {
+ char * name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+
+ if (streq (name, ".zdata") || streq (name, ".zbss"))
+ v850_set_data_area (decl, DATA_AREA_ZDA);
+
+ else if (streq (name, ".sdata") || streq (name, ".sbss"))
+ v850_set_data_area (decl, DATA_AREA_SDA);
+
+ else if (streq (name, ".tdata"))
+ v850_set_data_area (decl, DATA_AREA_TDA);
+ }
+
+ /* If no attribute, support -m{zda,sda,tda}=n */
+ else
+ {
+ int size = int_size_in_bytes (TREE_TYPE (decl));
+ if (size <= 0)
+ ;
+
+ else if (size <= small_memory [(int) SMALL_MEMORY_TDA].max)
+ v850_set_data_area (decl, DATA_AREA_TDA);
+
+ else if (size <= small_memory [(int) SMALL_MEMORY_SDA].max)
+ v850_set_data_area (decl, DATA_AREA_SDA);
+
+ else if (size <= small_memory [(int) SMALL_MEMORY_ZDA].max)
+ v850_set_data_area (decl, DATA_AREA_ZDA);
+ }
+
+ if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
+ return;
+ }
+
+ newstr = obstack_alloc (saveable_obstack, len + 2);
+
+ strcpy (newstr + 1, str);
+
+ switch (v850_get_data_area (decl))
+ {
+ case DATA_AREA_ZDA: *newstr = ZDA_NAME_FLAG_CHAR; break;
+ case DATA_AREA_TDA: *newstr = TDA_NAME_FLAG_CHAR; break;
+ case DATA_AREA_SDA: *newstr = SDA_NAME_FLAG_CHAR; break;
+ default: abort ();
+ }
+
+ XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
+}
+
+/* Return true if the given RTX is a register which can be restored
+ by a function epilogue. */
+int
+register_is_ok_for_epilogue (op, mode)
+ rtx op;
+ enum machine_mode ATTRIBUTE_UNUSED mode;
+{
+ /* The save/restore routines can only cope with registers 2, and 20 - 31 */
+ return (GET_CODE (op) == REG)
+ && (((REGNO (op) >= 20) && REGNO (op) <= 31)
+ || REGNO (op) == 2);
+}
+
+/* Return non-zero if the given RTX is suitable for collapsing into
+ jump to a function epilogue. */
+int
+pattern_is_ok_for_epilogue (op, mode)
+ rtx op;
+ enum machine_mode ATTRIBUTE_UNUSED mode;
+{
+ int count = XVECLEN (op, 0);
+ int i;
+
+ /* If there are no registers to restore then the function epilogue
+ is not suitable. */
+ if (count <= 2)
+ return 0;
+
+ /* The pattern matching has already established that we are performing a
+ function epilogue and that we are popping at least one register. We must
+ now check the remaining entries in the vector to make sure that they are
+ also register pops. There is no good reason why there should ever be
+ anything else in this vector, but being paranoid always helps...
+
+ The test below performs the C equivalent of this machine description
+ pattern match:
+
+ (set (match_operand:SI n "register_is_ok_for_epilogue" "r")
+ (mem:SI (plus:SI (reg:SI 3) (match_operand:SI n "immediate_operand" "i"))))
+ */
+
+ for (i = 3; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+ rtx dest;
+ rtx src;
+ rtx plus;
+
+ if (GET_CODE (vector_element) != SET)
+ return 0;
+
+ dest = SET_DEST (vector_element);
+ src = SET_SRC (vector_element);
+
+ if (GET_CODE (dest) != REG
+ || GET_MODE (dest) != SImode
+ || ! register_is_ok_for_epilogue (dest, SImode)
+ || GET_CODE (src) != MEM
+ || GET_MODE (src) != SImode)
+ return 0;
+
+ plus = XEXP (src, 0);
+
+ if (GET_CODE (plus) != PLUS
+ || GET_CODE (XEXP (plus, 0)) != REG
+ || GET_MODE (XEXP (plus, 0)) != SImode
+ || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (plus, 1)) != CONST_INT)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Construct a JR instruction to a routine that will perform the equivalent of
+ the RTL passed in as an argument. This RTL is a function epilogue that
+ pops registers off the stack and possibly releases some extra stack space
+ as well. The code has already verified that the RTL matches these
+ requirements. */
+char *
+construct_restore_jr (op)
+ rtx op;
+{
+ int count = XVECLEN (op, 0);
+ int stack_bytes;
+ unsigned long int mask;
+ unsigned long int first;
+ unsigned long int last;
+ int i;
+ static char buff [100]; /* XXX */
+
+ if (count <= 2)
+ {
+ error ("Bogus JR construction: %d\n", count);
+ return NULL;
+ }
+
+ /* Work out how many bytes to pop off the stack before retrieving
+ registers. */
+ if (GET_CODE (XVECEXP (op, 0, 1)) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != PLUS)
+ abort ();
+ if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) != CONST_INT)
+ abort ();
+
+ stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
+
+ /* Each pop will remove 4 bytes from the stack... */
+ stack_bytes -= (count - 2) * 4;
+
+ /* Make sure that the amount we are popping either 0 or 16 bytes. */
+ if (stack_bytes != 0 && stack_bytes != 16)
+ {
+ error ("Bad amount of stack space removal: %d", stack_bytes);
+ return NULL;
+ }
+
+ /* Now compute the bit mask of registers to push. */
+ mask = 0;
+ for (i = 2; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != SET)
+ abort ();
+ if (GET_CODE (SET_DEST (vector_element)) != REG)
+ abort ();
+ if (! register_is_ok_for_epilogue (SET_DEST (vector_element), SImode))
+ abort ();
+
+ mask |= 1 << REGNO (SET_DEST (vector_element));
+ }
+
+ /* Scan for the first register to pop. */
+ for (first = 0; first < 32; first++)
+ {
+ if (mask & (1 << first))
+ break;
+ }
+
+ if (first >= 32)
+ abort ();
+
+ /* Discover the last register to pop. */
+ if (mask & (1 << LINK_POINTER_REGNUM))
+ {
+ if (stack_bytes != 16)
+ abort ();
+
+ last = LINK_POINTER_REGNUM;
+ }
+ else
+ {
+ if (stack_bytes != 0)
+ abort ();
+
+ if ((mask & (1 << 29)) == 0)
+ abort ();
+
+ last = 29;
+ }
+
+ /* Note, it is possible to have gaps in the register mask.
+ We ignore this here, and generate a JR anyway. We will
+ be popping more registers than is strictly necessary, but
+ it does save code space. */
+
+ if (TARGET_LONG_CALLS)
+ {
+ char name[40];
+
+ if (first == last)
+ sprintf (name, "__return_%s", reg_names [first]);
+ else
+ sprintf (name, "__return_%s_%s", reg_names [first], reg_names [last]);
+
+ sprintf (buff, "movhi hi(%s), r0, r6\n\tmovea lo(%s), r6, r6\n\tjmp r6",
+ name, name);
+ }
+ else
+ {
+ if (first == last)
+ sprintf (buff, "jr __return_%s", reg_names [first]);
+ else
+ sprintf (buff, "jr __return_%s_%s", reg_names [first], reg_names [last]);
+ }
+
+ return buff;
+}
+
+
+/* Return non-zero if the given RTX is suitable for collapsing into
+ a jump to a function prologue. */
+int
+pattern_is_ok_for_prologue (op, mode)
+ rtx op;
+ enum machine_mode ATTRIBUTE_UNUSED mode;
+{
+ int count = XVECLEN (op, 0);
+ int i;
+ rtx vector_element;
+
+ /* If there are no registers to save then the function prologue
+ is not suitable. */
+ if (count <= 2)
+ return 0;
+
+ /* The pattern matching has already established that we are adjusting the
+ stack and pushing at least one register. We must now check that the
+ remaining entries in the vector to make sure that they are also register
+ pushes, except for the last entry which should be a CLOBBER of r10.
+
+ The test below performs the C equivalent of this machine description
+ pattern match:
+
+ (set (mem:SI (plus:SI (reg:SI 3)
+ (match_operand:SI 2 "immediate_operand" "i")))
+ (match_operand:SI 3 "register_is_ok_for_epilogue" "r"))
+
+ */
+
+ for (i = 2; i < count - 1; i++)
+ {
+ rtx dest;
+ rtx src;
+ rtx plus;
+
+ vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != SET)
+ return 0;
+
+ dest = SET_DEST (vector_element);
+ src = SET_SRC (vector_element);
+
+ if (GET_CODE (dest) != MEM
+ || GET_MODE (dest) != SImode
+ || GET_CODE (src) != REG
+ || GET_MODE (src) != SImode
+ || ! register_is_ok_for_epilogue (src, SImode))
+ return 0;
+
+ plus = XEXP (dest, 0);
+
+ if ( GET_CODE (plus) != PLUS
+ || GET_CODE (XEXP (plus, 0)) != REG
+ || GET_MODE (XEXP (plus, 0)) != SImode
+ || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (plus, 1)) != CONST_INT)
+ return 0;
+
+ /* If the register is being pushed somewhere other than the stack
+ space just acquired by the first operand then abandon this quest.
+ Note: the test is <= because both values are negative. */
+ if (INTVAL (XEXP (plus, 1))
+ <= INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)))
+ {
+ return 0;
+ }
+ }
+
+ /* Make sure that the last entry in the vector is a clobber. */
+ vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != CLOBBER
+ || GET_CODE (XEXP (vector_element, 0)) != REG
+ || REGNO (XEXP (vector_element, 0)) != 10)
+ return 0;
+
+ return 1;
+}
+
+/* Construct a JARL instruction to a routine that will perform the equivalent
+ of the RTL passed as a parameter. This RTL is a function prologue that
+ saves some of the registers r20 - r31 onto the stack, and possibly acquires
+ some stack space as well. The code has already verified that the RTL
+ matches these requirements. */
+char *
+construct_save_jarl (op)
+ rtx op;
+{
+ int count = XVECLEN (op, 0);
+ int stack_bytes;
+ unsigned long int mask;
+ unsigned long int first;
+ unsigned long int last;
+ int i;
+ static char buff [100]; /* XXX */
+
+ if (count <= 2)
+ {
+ error ("Bogus JARL construction: %d\n", count);
+ return NULL;
+ }
+
+ /* Paranoia. */
+ if (GET_CODE (XVECEXP (op, 0, 0)) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != PLUS)
+ abort ();
+ if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0)) != REG)
+ abort ();
+ if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) != CONST_INT)
+ abort ();
+
+ /* Work out how many bytes to push onto the stack after storing the
+ registers. */
+ stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
+
+ /* Each push will put 4 bytes from the stack... */
+ stack_bytes += (count - 2) * 4;
+
+ /* Make sure that the amount we are popping either 0 or 16 bytes. */
+ if (stack_bytes != 0 && stack_bytes != -16)
+ {
+ error ("Bad amount of stack space removal: %d", stack_bytes);
+ return NULL;
+ }
+
+ /* Now compute the bit mask of registers to push. */
+ mask = 0;
+ for (i = 1; i < count - 1; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (vector_element)) != REG)
+ abort ();
+ if (! register_is_ok_for_epilogue (SET_SRC (vector_element), SImode))
+ abort ();
+
+ mask |= 1 << REGNO (SET_SRC (vector_element));
+ }
+
+ /* Scan for the first register to push. */
+ for (first = 0; first < 32; first++)
+ {
+ if (mask & (1 << first))
+ break;
+ }
+
+ if (first >= 32)
+ abort ();
+
+ /* Discover the last register to push. */
+ if (mask & (1 << LINK_POINTER_REGNUM))
+ {
+ if (stack_bytes != -16)
+ abort ();
+
+ last = LINK_POINTER_REGNUM;
+ }
+ else
+ {
+ if (stack_bytes != 0)
+ abort ();
+ if ((mask & (1 << 29)) == 0)
+ abort ();
+
+ last = 29;
+ }
+
+ /* Note, it is possible to have gaps in the register mask.
+ We ignore this here, and generate a JARL anyway. We will
+ be pushing more registers than is strictly necessary, but
+ it does save code space. */
+
+ if (TARGET_LONG_CALLS)
+ {
+ char name[40];
+
+ if (first == last)
+ sprintf (name, "__save_%s", reg_names [first]);
+ else
+ sprintf (name, "__save_%s_%s", reg_names [first], reg_names [last]);
+
+ sprintf (buff, "movhi hi(%s), r0, r11\n\tmovea lo(%s), r11, r11\n\tjarl .+4, r10\n\tadd 4, r10\n\tjmp r11",
+ name, name);
+ }
+ else
+ {
+ if (first == last)
+ sprintf (buff, "jarl __save_%s, r10", reg_names [first]);
+ else
+ sprintf (buff, "jarl __save_%s_%s, r10", reg_names [first],
+ reg_names [last]);
+ }
+
+ return buff;
+}
+
+extern tree last_assemble_variable_decl;
+extern int size_directive_output;
+
+/* A version of asm_output_aligned_bss() that copes with the special
+ data areas of the v850. */
+void
+v850_output_aligned_bss (file, decl, name, size, align)
+ FILE * file;
+ tree decl;
+ char * name;
+ int size;
+ int align;
+{
+ ASM_GLOBALIZE_LABEL (file, name);
+
+ switch (v850_get_data_area (decl))
+ {
+ case DATA_AREA_ZDA:
+ zbss_section ();
+ break;
+
+ case DATA_AREA_SDA:
+ sbss_section ();
+ break;
+
+ case DATA_AREA_TDA:
+ tdata_section ();
+
+ default:
+ bss_section ();
+ break;
+ }
+
+ ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
+#ifdef ASM_DECLARE_OBJECT_NAME
+ last_assemble_variable_decl = decl;
+ ASM_DECLARE_OBJECT_NAME (file, name, decl);
+#else
+ /* Standard thing is just output label for the object. */
+ ASM_OUTPUT_LABEL (file, name);
+#endif /* ASM_DECLARE_OBJECT_NAME */
+ ASM_OUTPUT_SKIP (file, size ? size : 1);
+}
+
+/* Called via the macro ASM_OUTPUT_DECL_COMMON */
+void
+v850_output_common (file, decl, name, size, align)
+ FILE * file;
+ tree decl;
+ char * name;
+ int size;
+ int align;
+{
+ if (decl == NULL_TREE)
+ {
+ fprintf (file, "\t%s\t", COMMON_ASM_OP);
+ }
+ else
+ {
+ switch (v850_get_data_area (decl))
+ {
+ case DATA_AREA_ZDA:
+ fprintf (file, "\t%s\t", ZCOMMON_ASM_OP);
+ break;
+
+ case DATA_AREA_SDA:
+ fprintf (file, "\t%s\t", SCOMMON_ASM_OP);
+ break;
+
+ case DATA_AREA_TDA:
+ fprintf (file, "\t%s\t", TCOMMON_ASM_OP);
+ break;
+
+ default:
+ fprintf (file, "\t%s\t", COMMON_ASM_OP);
+ break;
+ }
+ }
+
+ assemble_name (file, name);
+ fprintf (file, ",%u,%u\n", size, align / BITS_PER_UNIT);
+}
+
+/* Called via the macro ASM_OUTPUT_DECL_LOCAL */
+void
+v850_output_local (file, decl, name, size, align)
+ FILE * file;
+ tree decl;
+ char * name;
+ int size;
+ int align;
+{
+ fprintf (file, "\t%s\t", LOCAL_ASM_OP);
+ assemble_name (file, name);
+ fprintf (file, "\n");
+
+ ASM_OUTPUT_ALIGNED_DECL_COMMON (file, decl, name, size, align);
+}
+
+/* The following code is for handling pragmas supported by the
+ v850 compiler produced by Green Hills Software. This is at
+ the specific request of a customer. */
+
+/* Track the current data area set by the data area pragma (which
+ can be nested). Tested by check_default_data_area. */
+
+typedef struct data_area_stack_element
+{
+ struct data_area_stack_element * prev;
+ v850_data_area data_area; /* current default data area. */
+} data_area_stack_element;
+
+static data_area_stack_element * data_area_stack = NULL;
+
+/* Names of the various data areas used on the v850. */
+static tree GHS_default_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
+static tree GHS_current_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
+
+/* Push a data area onto the stack. */
+static int
+push_data_area (data_area)
+ v850_data_area data_area;
+{
+ data_area_stack_element * elem;
+
+ elem = (data_area_stack_element *) xmalloc (sizeof (* elem));
+
+ if (elem == NULL)
+ return 0;
+
+ elem->prev = data_area_stack;
+ elem->data_area = data_area;
+
+ data_area_stack = elem;
+
+ return 1;
+}
+
+/* Remove a data area from the stack. */
+static int
+pop_data_area (data_area)
+ v850_data_area data_area;
+{
+ if (data_area_stack == NULL)
+ warning ("#pragma GHS endXXXX found without previous startXXX");
+ else if (data_area != data_area_stack->data_area)
+ warning ("#pragma GHS endXXX does not match previous startXXX");
+ else
+ {
+ data_area_stack_element * elem;
+
+ elem = data_area_stack;
+ data_area_stack = data_area_stack->prev;
+
+ free (elem);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Set the machine specific 'interrupt' attribute on the current function. */
+static int
+mark_current_function_as_interrupt ()
+{
+ tree name;
+
+ if (current_function_decl == NULL_TREE)
+ {
+ warning ("Cannot set interrupt attribute: no current function");
+ return 0;
+ }
+
+ name = get_identifier ("interrupt");
+
+ if (name == NULL_TREE || TREE_CODE (name) != IDENTIFIER_NODE)
+ {
+ warning ("Cannot set interrupt attribute: no such identifier");
+ return 0;
+ }
+
+ return valid_machine_attribute
+ (name, NULL_TREE, current_function_decl, NULL_TREE);
+}
+
+/* Parse STRING as part of a GHS pragma.
+ Returns 0 if the pragma has been parsed and there was a problem,
+ non-zero in all other cases. */
+static int
+parse_ghs_pragma_token (string)
+ char * string;
+{
+ static enum v850_pragma_state state = V850_PS_START;
+ static enum v850_pragma_type type = V850_PT_UNKNOWN;
+ static v850_data_area data_area = DATA_AREA_NORMAL;
+ static char * data_area_name;
+ static enum GHS_section_kind GHS_section_kind = GHS_SECTION_KIND_DEFAULT;
+
+ /* If the string is NULL then we have reached the end of the
+ #pragma construct. Make sure that we are in an end state, and
+ then implement the pragma's directive. */
+ if (string == NULL)
+ {
+ int ret_val = 1;
+
+ if (state != V850_PS_SHOULD_BE_DONE
+ && state != V850_PS_MAYBE_COMMA
+ && state != V850_PS_MAYBE_SECTION_NAME)
+ {
+ if (state != V850_PS_BAD)
+ warning ("Incomplete #pragma ghs");
+
+ ret_val = 0;
+ }
+ else switch (type)
+ {
+ case V850_PT_UNKNOWN:
+ warning ("Nothing follows #pragma ghs");
+ ret_val = 0;
+ break;
+
+ case V850_PT_INTERRUPT:
+ ret_val = mark_current_function_as_interrupt ();
+ break;
+
+ case V850_PT_SECTION:
+ /* If a section kind has not been specified, then reset
+ all section names back to their defaults. */
+ if (GHS_section_kind == GHS_SECTION_KIND_DEFAULT)
+ {
+ int i;
+
+ for (i = COUNT_OF_GHS_SECTION_KINDS; i--;)
+ GHS_current_section_names [i] = NULL;
+ }
+ /* If a section has been specified, then this will be handled
+ by check_default_section_name (). */
+ break;
+
+ case V850_PT_START_SECTION:
+ ret_val = push_data_area (data_area);
+ break;
+
+ case V850_PT_END_SECTION:
+ ret_val = pop_data_area (data_area);
+ break;
+ }
+
+ state = V850_PS_START;
+ type = V850_PT_UNKNOWN;
+
+ return ret_val;
+ }
+
+ switch (state)
+ {
+ case V850_PS_START:
+ data_area = DATA_AREA_NORMAL;
+ data_area_name = NULL;
+
+ if (streq (string, "interrupt"))
+ {
+ type = V850_PT_INTERRUPT;
+ state = V850_PS_SHOULD_BE_DONE;
+ }
+ else if (streq (string, "section"))
+ {
+ type = V850_PT_SECTION;
+ state = V850_PS_MAYBE_SECTION_NAME;
+ GHS_section_kind = GHS_SECTION_KIND_DEFAULT;
+ }
+ else if (streq (string, "starttda"))
+ {
+ type = V850_PT_START_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_TDA;
+ }
+ else if (streq (string, "endtda"))
+ {
+ type = V850_PT_END_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_TDA;
+ }
+ else if (streq (string, "startsda"))
+ {
+ type = V850_PT_START_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_SDA;
+ }
+ else if (streq (string, "endsda"))
+ {
+ type = V850_PT_END_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_SDA;
+ }
+ else if (streq (string, "startzda"))
+ {
+ type = V850_PT_START_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_ZDA;
+ }
+ else if (streq (string, "endzda"))
+ {
+ type = V850_PT_END_SECTION;
+ state = V850_PS_SHOULD_BE_DONE;
+ data_area = DATA_AREA_ZDA;
+ }
+ else
+ {
+ warning ("Unrecognised GHS pragma: '%s'\n", string);
+ state = V850_PS_BAD;
+ }
+ break;
+
+ case V850_PS_SHOULD_BE_DONE:
+ warning ("Extra text after valid #pragma: '%s'", string);
+ state = V850_PS_BAD;
+ break;
+
+ case V850_PS_BAD:
+ /* Ignore tokens in a pragma that has been diagnosed as being corrupt. */
+ break;
+
+ case V850_PS_MAYBE_SECTION_NAME:
+ state = V850_PS_EXPECTING_EQUALS;
+
+ if (streq (string, "data")) GHS_section_kind = GHS_SECTION_KIND_DATA;
+ else if (streq (string, "text")) GHS_section_kind = GHS_SECTION_KIND_TEXT;
+ else if (streq (string, "rodata")) GHS_section_kind = GHS_SECTION_KIND_RODATA;
+ else if (streq (string, "const")) GHS_section_kind = GHS_SECTION_KIND_RODATA;
+ else if (streq (string, "rosdata")) GHS_section_kind = GHS_SECTION_KIND_ROSDATA;
+ else if (streq (string, "rozdata")) GHS_section_kind = GHS_SECTION_KIND_ROZDATA;
+ else if (streq (string, "sdata")) GHS_section_kind = GHS_SECTION_KIND_SDATA;
+ else if (streq (string, "tdata")) GHS_section_kind = GHS_SECTION_KIND_TDATA;
+ else if (streq (string, "zdata")) GHS_section_kind = GHS_SECTION_KIND_ZDATA;
+ /* According to GHS beta documentation, the following should not be allowed! */
+ else if (streq (string, "bss")) GHS_section_kind = GHS_SECTION_KIND_BSS;
+ else if (streq (string, "zbss")) GHS_section_kind = GHS_SECTION_KIND_ZDATA;
+ else
+ {
+ warning ("Unrecognised section name '%s' in GHS section pragma",
+ string);
+ state = V850_PS_BAD;
+ }
+ break;
+
+ case V850_PS_EXPECTING_EQUALS:
+ if (streq (string, "="))
+ state = V850_PS_EXPECTING_SECTION_ALIAS;
+ else
+ {
+ warning ("Missing '=' in GHS section pragma");
+ state = V850_PS_BAD;
+ }
+ break;
+
+ case V850_PS_EXPECTING_SECTION_ALIAS:
+ if (streq (string, "default"))
+ GHS_current_section_names [GHS_section_kind] = NULL;
+ else
+ GHS_current_section_names [GHS_section_kind] =
+ build_string (strlen (string) + 1, string);
+
+ state = V850_PS_MAYBE_COMMA;
+ break;
+
+ case V850_PS_MAYBE_COMMA:
+ if (streq (string, ","))
+ state = V850_PS_MAYBE_SECTION_NAME;
+ else
+ {
+ warning
+ ("Malformed GHS section pragma: found '%s' instead of a comma",
+ string);
+ state = V850_PS_BAD;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+/* Handle the parsing of an entire GHS pragma. */
+int
+v850_handle_pragma (p_getc, p_ungetc, name)
+ int (* p_getc) PROTO ((void));
+ void (* p_ungetc) PROTO ((int));
+ char * name;
+{
+ /* Parse characters in the input stream until:
+
+ * end of line
+ * end of file
+ * a complete GHS pragma has been parsed
+ * a corrupted GHS pragma has been parsed
+ * an unknown pragma is encountered.
+
+ If an unknown pragma is encountered, we must return with
+ the input stream in the same state as upon entry to this function.
+
+ The first token in the input stream has already been parsed
+ for us, and is passed as 'name'. */
+
+ if (! streq (name, "ghs"))
+ return 0;
+
+ /* We now know that we are parsing a GHS pragma, so we do
+ not need to preserve the original input stream state. */
+ for (;;)
+ {
+ static char buffer [128];
+ int c;
+ char * buff;
+
+ /* Skip white space. */
+ do
+ c = p_getc ();
+ while (c == ' ' || c == '\t');
+
+ p_ungetc (c);
+
+ if (c == '\n' || c == EOF || c == '\r')
+ return parse_ghs_pragma_token (NULL);
+
+ /* Read next word. We have to do the parsing ourselves, rather
+ than calling yylex() because we can be built with front ends
+ that do not provide such functions. */
+ buff = buffer;
+ * buff ++ = (c = p_getc ());
+
+ switch (c)
+ {
+ case ',':
+ case '=':
+ * buff ++ = (c = p_getc ());
+ break;
+
+ case '"':
+ /* Skip opening double parenthesis. */
+ -- buff;
+
+ /* Read string. */
+ do
+ * buff ++ = (c = p_getc ());
+ while (c != EOF && isascii (c)
+ && (isalnum (c) || c == '_' || c == '.' || c == ' ')
+ && (buff < buffer + 126));
+
+ if (c != '"')
+ warning ("Missing trailing \" in #pragma ghs");
+ else
+ c = p_getc ();
+ break;
+
+ default:
+ while (c != EOF && isascii (c)
+ && (isalnum (c) || c == '_' || c == '.')
+ && (buff < buffer + 126))
+ * buff ++ = (c = p_getc ());
+ break;
+ }
+
+ p_ungetc (c);
+
+ /* If nothing was read then terminate the parsing. */
+ if (buff == buffer + 1)
+ return parse_ghs_pragma_token (NULL);
+
+ /* Parse and continue. */
+ * -- buff = 0;
+
+ parse_ghs_pragma_token (buffer);
+ }
+}
+
+/* Add data area to the given declaration if a ghs data area pragma is
+ currently in effect (#pragma ghs startXXX/endXXX). */
+void
+v850_set_default_decl_attr (decl)
+ tree decl;
+{
+ if (data_area_stack
+ && data_area_stack->data_area
+ && current_function_decl == NULL_TREE
+ && (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == CONST_DECL)
+ && v850_get_data_area (decl) == DATA_AREA_NORMAL)
+ v850_set_data_area (decl, data_area_stack->data_area);
+
+ /* Initialise the default names of the v850 specific sections,
+ if this has not been done before. */
+
+ if (GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA] == NULL)
+ {
+ GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA]
+ = build_string (sizeof (".sdata")-1, ".sdata");
+
+ GHS_default_section_names [(int) GHS_SECTION_KIND_ROSDATA]
+ = build_string (sizeof (".rosdata")-1, ".rosdata");
+
+ GHS_default_section_names [(int) GHS_SECTION_KIND_TDATA]
+ = build_string (sizeof (".tdata")-1, ".tdata");
+
+ GHS_default_section_names [(int) GHS_SECTION_KIND_ZDATA]
+ = build_string (sizeof (".zdata")-1, ".zdata");
+
+ GHS_default_section_names [(int) GHS_SECTION_KIND_ROZDATA]
+ = build_string (sizeof (".rozdata")-1, ".rozdata");
+ }
+
+ if (current_function_decl == NULL_TREE
+ && (TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == CONST_DECL
+ || TREE_CODE (decl) == FUNCTION_DECL)
+ && (!DECL_EXTERNAL (decl) || DECL_INITIAL (decl))
+ && !DECL_SECTION_NAME (decl))
+ {
+ enum GHS_section_kind kind = GHS_SECTION_KIND_DEFAULT;
+ tree chosen_section;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ kind = GHS_SECTION_KIND_TEXT;
+ else
+ {
+ /* First choose a section kind based on the data area of the decl. */
+ switch (v850_get_data_area (decl))
+ {
+ default:
+ abort ();
+
+ case DATA_AREA_SDA:
+ kind = ((TREE_READONLY (decl))
+ ? GHS_SECTION_KIND_ROSDATA
+ : GHS_SECTION_KIND_SDATA);
+ break;
+
+ case DATA_AREA_TDA:
+ kind = GHS_SECTION_KIND_TDATA;
+ break;
+
+ case DATA_AREA_ZDA:
+ kind = ((TREE_READONLY (decl))
+ ? GHS_SECTION_KIND_ROZDATA
+ : GHS_SECTION_KIND_ZDATA);
+ break;
+
+ case DATA_AREA_NORMAL: /* default data area */
+ if (TREE_READONLY (decl))
+ kind = GHS_SECTION_KIND_RODATA;
+ else if (DECL_INITIAL (decl))
+ kind = GHS_SECTION_KIND_DATA;
+ else
+ kind = GHS_SECTION_KIND_BSS;
+ }
+ }
+
+ /* Now, if the section kind has been explicitly renamed,
+ then attach a section attribute. */
+ chosen_section = GHS_current_section_names [(int) kind];
+
+ /* Otherwise, if this kind of section needs an explicit section
+ attribute, then also attach one. */
+ if (chosen_section == NULL)
+ chosen_section = GHS_default_section_names [(int) kind];
+
+ if (chosen_section)
+ {
+ /* Only set the section name if specified by a pragma, because
+ otherwise it will force those variables to get allocated storage
+ in this module, rather than by the linker. */
+ DECL_SECTION_NAME (decl) = chosen_section;
+ }
+ }
+}
+
+/* CYGNUS LOCAL v850e */
+/* Return non-zero if the given RTX is suitable for collapsing into a
+ DISPOSE instruction. */
+int
+pattern_is_ok_for_dispose (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int count = XVECLEN (op, 0);
+ int i;
+
+ /* If there are no registers to restore then the dispose instruction is not
+ suitable. */
+ if (count <= 2)
+ return 0;
+
+ /* The pattern matching has already established that we are performing a
+ function epilogue and that we are popping at least one register. We must
+ now check the remaining entries in the vector to make sure that they are
+ also register pops. There is no good reason why there should ever be
+ anything else in this vector, but being paranoid always helps...
+
+ The test below performs the C equivalent of this machine description
+ pattern match:
+
+ (set (match_operand:SI n "register_is_ok_for_epilogue" "r")
+ (mem:SI (plus:SI (reg:SI 3)
+ (match_operand:SI n "immediate_operand" "i"))))
+ */
+
+ for (i = 3; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+ rtx dest;
+ rtx src;
+ rtx plus;
+
+ if (GET_CODE (vector_element) != SET)
+ return 0;
+
+ dest = SET_DEST (vector_element);
+ src = SET_SRC (vector_element);
+
+ if ( GET_CODE (dest) != REG
+ || GET_MODE (dest) != SImode
+ || ! register_is_ok_for_epilogue (dest, SImode)
+ || GET_CODE (src) != MEM
+ || GET_MODE (src) != SImode)
+ return 0;
+
+ plus = XEXP (src, 0);
+
+ if ( GET_CODE (plus) != PLUS
+ || GET_CODE (XEXP (plus, 0)) != REG
+ || GET_MODE (XEXP (plus, 0)) != SImode
+ || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (plus, 1)) != CONST_INT)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Construct a DISPOSE instruction that is the equivalent of
+ the given RTX. We have already verified that this should
+ be possible. */
+char *
+construct_dispose_instruction (op)
+ rtx op;
+{
+ int count = XVECLEN (op, 0);
+ int stack_bytes;
+ unsigned long int mask;
+ int i;
+ static char buff[ 100 ]; /* XXX */
+ int use_callt = 0;
+
+ if (count <= 2)
+ {
+ error ("Bogus DISPOSE construction: %d\n", count);
+ return NULL;
+ }
+
+ /* Work out how many bytes to pop off the stack before retrieving
+ registers. */
+ if (GET_CODE (XVECEXP (op, 0, 1)) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != PLUS)
+ abort ();
+ if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) != CONST_INT)
+ abort ();
+
+ stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
+
+ /* Each pop will remove 4 bytes from the stack... */
+
+ stack_bytes -= (count - 2) * 4;
+
+ /* Make sure that the amount we are popping will fit into the DISPOSE
+ instruction. */
+ if (stack_bytes > 128)
+ {
+ error ("Too much stack space to dispose of: %d", stack_bytes);
+ return NULL;
+ }
+
+ /* Now compute the bit mask of registers to push. */
+ mask = 0;
+
+ for (i = 2; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != SET)
+ abort ();
+ if (GET_CODE (SET_DEST (vector_element)) != REG)
+ abort ();
+ if (! register_is_ok_for_epilogue (SET_DEST (vector_element), SImode))
+ abort ();
+
+ if (REGNO (SET_DEST (vector_element)) == 2)
+ use_callt = 1;
+ else
+ mask |= 1 << REGNO (SET_DEST (vector_element));
+ }
+
+ if (! TARGET_DISABLE_CALLT
+ && (use_callt || stack_bytes == 0 || stack_bytes == 16))
+ {
+ if (use_callt)
+ {
+ sprintf (buff, "callt ctoff(__callt_return_r2_r%d)", (mask & (1 << 31)) ? 31 : 29);
+ return buff;
+ }
+ else
+ {
+ for (i = 20; i < 32; i++)
+ {
+ if (mask & (1 << i))
+ break;
+ }
+
+ if (i == 31)
+ {
+ sprintf (buff, "callt ctoff(__callt_return_r31c)");
+ }
+ else
+ {
+ sprintf (buff, "callt ctoff(__callt_return_r%d_r%d%s)",
+ i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
+ }
+ }
+ }
+ else
+ {
+ static char regs [100]; /* XXX */
+ int done_one;
+
+ /* Generate the DISPOSE instruction. Note we could just issue the
+ bit mask as a number as the assembler can cope with this, but for
+ the sake of our readers we turn it into a textual description. */
+
+ regs[0] = 0;
+ done_one = 0;
+
+ for (i = 20; i < 32; i++)
+ {
+ if (mask & (1 << i))
+ {
+ int first;
+
+ if (done_one)
+ strcat (regs, ", ");
+ else
+ done_one = 1;
+
+ first = i;
+ strcat (regs, reg_names[ first ]);
+
+ for (i++; i < 32; i++)
+ if ((mask & (1 << i)) == 0)
+ break;
+
+ if (i > first + 1)
+ {
+ strcat (regs, " - ");
+ strcat (regs, reg_names[ i - 1 ] );
+ }
+ }
+ }
+
+ sprintf (buff, "dispose %d {%s}, r31", stack_bytes / 4, regs);
+ }
+
+ return buff;
+}
+
+/* Return non-zero if the given RTX is suitable for collapsing into a PREPARE
+ instruction. */
+int
+pattern_is_ok_for_prepare (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ int count = XVECLEN (op, 0);
+ int i;
+
+ /* If there are no registers to restore then the prepare instruction
+ is not suitable. */
+ if (count <= 1)
+ return 0;
+
+ /* The pattern matching has already established that we are adjusting the
+ stack and pushing at least one register. We must now check that the
+ remaining entries in the vector to make sure that they are also register
+ pushes.
+
+ The test below performs the C equivalent of this machine description
+ pattern match:
+
+ (set (mem:SI (plus:SI (reg:SI 3)
+ (match_operand:SI 2 "immediate_operand" "i")))
+ (match_operand:SI 3 "register_is_ok_for_epilogue" "r"))
+
+ */
+
+ for (i = 2; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+ rtx dest;
+ rtx src;
+ rtx plus;
+
+ if (GET_CODE (vector_element) != SET)
+ return 0;
+
+ dest = SET_DEST (vector_element);
+ src = SET_SRC (vector_element);
+
+ if ( GET_CODE (dest) != MEM
+ || GET_MODE (dest) != SImode
+ || GET_CODE (src) != REG
+ || GET_MODE (src) != SImode
+ || ! register_is_ok_for_epilogue (src, SImode)
+ )
+ return 0;
+
+ plus = XEXP (dest, 0);
+
+ if ( GET_CODE (plus) != PLUS
+ || GET_CODE (XEXP (plus, 0)) != REG
+ || GET_MODE (XEXP (plus, 0)) != SImode
+ || REGNO (XEXP (plus, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (plus, 1)) != CONST_INT)
+ return 0;
+
+ /* If the register is being pushed somewhere other than the stack
+ space just aquired by the first operand then abandon this quest.
+ Note: the test is <= becuase both values are negative. */
+ if (INTVAL (XEXP (plus, 1))
+ <= INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)))
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Construct a PREPARE instruction that is the equivalent of
+ the given RTL. We have already verified that this should
+ be possible. */
+char *
+construct_prepare_instruction (op)
+ rtx op;
+{
+ int count = XVECLEN (op, 0);
+ int stack_bytes;
+ unsigned long int mask;
+ int i;
+ static char buff[ 100 ]; /* XXX */
+ int use_callt = 0;
+
+ if (count <= 1)
+ {
+ error ("Bogus PREPEARE construction: %d\n", count);
+ return NULL;
+ }
+
+ /* Work out how many bytes to push onto the stack after storing the
+ registers. */
+ if (GET_CODE (XVECEXP (op, 0, 0)) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != PLUS)
+ abort ();
+ if (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) != CONST_INT)
+ abort ();
+
+ stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
+
+ /* Each push will put 4 bytes from the stack... */
+ stack_bytes += (count - 1) * 4;
+
+ /* Make sure that the amount we are popping will fit into the DISPOSE
+ instruction. */
+ if (stack_bytes < -128)
+ {
+ error ("Too much stack space to prepare: %d", stack_bytes);
+ return NULL;
+ }
+
+ /* Now compute the bit mask of registers to push. */
+ mask = 0;
+ for (i = 1; i < count; i++)
+ {
+ rtx vector_element = XVECEXP (op, 0, i);
+
+ if (GET_CODE (vector_element) != SET)
+ abort ();
+ if (GET_CODE (SET_SRC (vector_element)) != REG)
+ abort ();
+ if (! register_is_ok_for_epilogue (SET_SRC (vector_element), SImode))
+ abort ();
+
+ if (REGNO (SET_SRC (vector_element)) == 2)
+ use_callt = 1;
+ else
+ mask |= 1 << REGNO (SET_SRC (vector_element));
+ }
+
+ if ((! TARGET_DISABLE_CALLT)
+ && (use_callt || stack_bytes == 0 || stack_bytes == -16))
+ {
+ if (use_callt)
+ {
+ sprintf (buff, "callt ctoff(__callt_save_r2_r%d)", (mask & (1 << 31)) ? 31 : 29 );
+ return buff;
+ }
+
+ for (i = 20; i < 32; i++)
+ {
+ if (mask & (1 << i))
+ break;
+ }
+
+ if (i == 31)
+ {
+ sprintf (buff, "callt ctoff(__callt_save_r31c)");
+ }
+ else
+ {
+ sprintf (buff, "callt ctoff(__callt_save_r%d_r%d%s)",
+ i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
+ }
+ }
+ else
+ {
+ static char regs [100]; /* XXX */
+ int done_one;
+
+
+ /* Generate the PREPARE instruction. Note we could just issue the
+ bit mask as a number as the assembler can cope with this, but for
+ the sake of our readers we turn it into a textual description. */
+
+ regs[0] = 0;
+ done_one = 0;
+
+ for (i = 20; i < 32; i++)
+ {
+ if (mask & (1 << i))
+ {
+ int first;
+
+ if (done_one)
+ strcat (regs, ", ");
+ else
+ done_one = 1;
+
+ first = i;
+ strcat (regs, reg_names[ first ]);
+
+ for (i++; i < 32; i++)
+ if ((mask & (1 << i)) == 0)
+ break;
+
+ if (i > first + 1)
+ {
+ strcat (regs, " - ");
+ strcat (regs, reg_names[ i - 1 ] );
+ }
+ }
+ }
+
+ sprintf (buff, "prepare {%s}, %d", regs, (- stack_bytes) / 4);
+ }
+
+ return buff;
+}
+
+/* END CYGNUS LOCAL */