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