diff options
Diffstat (limited to 'gcc/config/v850/v850.c')
-rwxr-xr-x | gcc/config/v850/v850.c | 3673 |
1 files changed, 0 insertions, 3673 deletions
diff --git a/gcc/config/v850/v850.c b/gcc/config/v850/v850.c deleted file mode 100755 index 60cb9e1..0000000 --- a/gcc/config/v850/v850.c +++ /dev/null @@ -1,3673 +0,0 @@ -/* 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, ®_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, ®_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 */ |