diff options
Diffstat (limited to 'gcc/config/d30v/d30v.c')
-rwxr-xr-x | gcc/config/d30v/d30v.c | 3514 |
1 files changed, 3514 insertions, 0 deletions
diff --git a/gcc/config/d30v/d30v.c b/gcc/config/d30v/d30v.c new file mode 100755 index 0000000..8e70c4a --- /dev/null +++ b/gcc/config/d30v/d30v.c @@ -0,0 +1,3514 @@ +/* CYGNUS LOCAL -- meissner/d30v */ +/* Definitions of target machine for use as an example. + Hack to fit. + Copyright (C) 1997, 1998 Free Software Foundation, Inc. + Contributed by Cygnus Solutions. + +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 "config.h" +#include "system.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "recog.h" +#include "expr.h" +#include "obstack.h" +#include "tree.h" +#include "except.h" +#include "function.h" + +static void d30v_print_operand_memory_reference PROTO((FILE *, rtx)); +static void d30v_build_long_insn PROTO((HOST_WIDE_INT, HOST_WIDE_INT, rtx, rtx)); + +/* Define the information needed to generate branch and scc insns. This is + stored from the compare operation. */ + +struct rtx_def *d30v_compare_op0; +struct rtx_def *d30v_compare_op1; + +/* Define the information needed to modify the epilogue for EH. */ + +rtx d30v_eh_epilogue_sp_ofs; + +/* Cached value of d30v_stack_info */ +static d30v_stack_t *d30v_stack_cache = (d30v_stack_t *)0; + +/* Cache for __builtin_return_addr */ +static rtx d30v_return_addr_rtx; + +/* Values of the -mbranch-cost=n string. */ +int d30v_branch_cost = D30V_DEFAULT_BRANCH_COST; +char *d30v_branch_cost_string = (char *)0; + +/* Values of the -mcond-exec=n string. */ +int d30v_cond_exec = D30V_DEFAULT_MAX_CONDITIONAL_EXECUTE; +char *d30v_cond_exec_string = (char *)0; + +/* Whether or not a hard register can accept a register */ +unsigned char hard_regno_mode_ok[ (int)MAX_MACHINE_MODE ][FIRST_PSEUDO_REGISTER]; + +/* Whether to try and avoid moves between two different modes */ +unsigned char modes_tieable_p[ (NUM_MACHINE_MODES) * (NUM_MACHINE_MODES) ]; + +/* Map register number to smallest register class. */ +enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER]; + +/* Map class letter into register class */ +enum reg_class reg_class_from_letter[256]; + + +/* 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 regno, i, ok_p; + enum machine_mode mode1, mode2; + + /* Set up the branch cost information */ + if (d30v_branch_cost_string) + d30v_branch_cost = atoi (d30v_branch_cost_string); + + /* Set up max # instructions to use with conditional execution */ + if (d30v_cond_exec_string) + d30v_cond_exec = atoi (d30v_cond_exec_string); + + /* Setup hard_regno_mode_ok/modes_tieable_p */ + for (mode1 = VOIDmode; + (int)mode1 < NUM_MACHINE_MODES; + mode1 = (enum machine_mode)((int)mode1 + 1)) + { + int size = GET_MODE_SIZE (mode1); + int large_p = size > UNITS_PER_WORD; + int int_p = GET_MODE_CLASS (mode1) == MODE_INT; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (mode1 == VOIDmode) + ok_p = FALSE; + + else if (GPR_P (regno)) + { + if (!large_p) + ok_p = TRUE; + else + ok_p = (((regno - GPR_FIRST) & 1) == 0); + } + + else if (FLAG_P (regno)) + ok_p = (mode1 == CCmode); + + else if (CR_P (regno)) + ok_p = int_p && !large_p; + + else if (ACCUM_P (regno)) + ok_p = (mode1 == DImode); + + else if (SPECIAL_REG_P (regno)) + ok_p = (mode1 == SImode); + + else + ok_p = FALSE; + + hard_regno_mode_ok[ (int)mode1 ][ regno ] = ok_p; + } + + /* A C expression that is nonzero if it is desirable to choose + register allocation so as to avoid move instructions between a + value of mode MODE1 and a value of mode MODE2. + + If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, + MODE2)' are ever different for any R, then `MODES_TIEABLE_P (MODE1, + MODE2)' must be zero. */ + for (mode2 = VOIDmode; + (int)mode2 <= NUM_MACHINE_MODES; + mode2 = (enum machine_mode)((int)mode2 + 1)) + { + if (mode1 == mode2) + ok_p = TRUE; + +#if 0 + else if (GET_MODE_CLASS (mode1) == MODE_INT + && GET_MODE_SIZE (mode1) <= UNITS_PER_WORD + && GET_MODE_CLASS (mode2) == MODE_INT + && GET_MODE_SIZE (mode2) <= UNITS_PER_WORD) + ok_p = TRUE; +#endif + + else + ok_p = FALSE; + + modes_tieable_p[ ((int)mode1 * (NUM_MACHINE_MODES)) + (int)mode2 ] = ok_p; + } + } + +#if 0 + for (mode1 = VOIDmode; + (int)mode1 < NUM_MACHINE_MODES; + mode1 = (enum machine_mode)((int)mode1 + 1)) + { + for (mode2 = VOIDmode; + (int)mode2 <= NUM_MACHINE_MODES; + mode2 = (enum machine_mode)((int)mode2 + 1)) + { + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (ok_p + && (hard_regno_mode_ok[(int)mode1][regno] + != hard_regno_mode_ok[(int)mode2][regno])) + error ("Bad modes_tieable_p for register %s, mode1 %s, mode2 %s", + reg_names[regno], GET_MODE_NAME (mode1), + GET_MODE_NAME (mode2)); + } + } +#endif + + /* A C expression whose value is a register class containing hard + register REGNO. In general there is more than one such class; + choose a class which is "minimal", meaning that no smaller class + also contains the register. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + enum reg_class class; + + if (GPR_P (regno)) + class = (IN_RANGE_P (regno, GPR_FIRST+2, GPR_FIRST+62) + && ((regno - GPR_FIRST) & 1) == 0) ? EVEN_REGS : GPR_REGS; + + else if (regno == FLAG_F0) + class = F0_REGS; + + else if (regno == FLAG_F1) + class = F1_REGS; + + else if (FLAG_P (regno)) + class = OTHER_FLAG_REGS; + + else if (ACCUM_P (regno)) + class = ACCUM_REGS; + + else if (regno == CR_RPT_C) + class = REPEAT_REGS; + + else if (CR_P (regno)) + class = CR_REGS; + + else if (SPECIAL_REG_P (regno)) + class = GPR_REGS; + + else + class = NO_REGS; + + regno_reg_class[regno] = class; + +#if 0 + { + static char *names[] = REG_CLASS_NAMES; + fprintf (stderr, "Register %s class is %s, can hold modes", reg_names[regno], names[class]); + for (mode1 = VOIDmode; + (int)mode1 < NUM_MACHINE_MODES; + mode1 = (enum machine_mode)((int)mode1 + 1)) + { + if (hard_regno_mode_ok[ (int)mode1 ][ regno ]) + fprintf (stderr, " %s", GET_MODE_NAME (mode1)); + } + fprintf (stderr, "\n"); + } +#endif + } + + /* A C expression which defines the machine-dependent operand + constraint letters for register classes. If CHAR is such a + letter, the value should be the register class corresponding to + it. Otherwise, the value should be `NO_REGS'. The register + letter `r', corresponding to class `GENERAL_REGS', will not be + passed to this macro; you do not need to handle it. + + The following letters are unavailable, due to being used as + constraints: + '0'..'9' + '<', '>' + 'E', 'F', 'G', 'H' + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P' + 'Q', 'R', 'S', 'T', 'U' + 'V', 'X' + 'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */ + + for (i = 0; i < 256; i++) + reg_class_from_letter[i] = NO_REGS; + + reg_class_from_letter['a'] = ACCUM_REGS; + reg_class_from_letter['b'] = BR_FLAG_REGS; + reg_class_from_letter['c'] = CR_REGS; + reg_class_from_letter['d'] = GPR_REGS; + reg_class_from_letter['e'] = EVEN_REGS; + reg_class_from_letter['f'] = FLAG_REGS; + reg_class_from_letter['l'] = REPEAT_REGS; + reg_class_from_letter['x'] = F0_REGS; + reg_class_from_letter['y'] = F1_REGS; + reg_class_from_letter['z'] = OTHER_FLAG_REGS; +} + + +/* Return true if a memory operand is a short memory operand. */ + +int +short_memory_operand (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return (d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed) + == 1); +} + +/* Return true if a memory operand is a long operand. */ + +int +long_memory_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return (d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed) + == 2); +} + +/* Return true if a memory operand is valid for the D30V. */ + +int +d30v_memory_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return (d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed) + != 0); +} + +/* Return true if a memory operand uses a single register for the + address. */ + +int +single_reg_memory_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx addr; + int regno; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + addr = XEXP (op, 0); + if (! d30v_legitimate_address_p (mode_int, addr, reload_completed)) + return FALSE; + + if (GET_CODE (addr) == SUBREG) + addr = SUBREG_REG (addr); + + return (GET_CODE (addr) == REG); +} + +/* Return true if a memory operand uses a constant address. */ + +int +const_addr_memory_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (! d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed)) + return FALSE; + + switch (GET_CODE (XEXP (op, 0))) + { + default: + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST_INT: + case CONST: + return TRUE; + } + + return FALSE; +} + +/* Return true if operand is a memory reference suitable for a call. */ + +int +call_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) != MEM) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (! d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed)) + return FALSE; + + switch (GET_CODE (XEXP (op, 0))) + { + default: + break; + + case SUBREG: + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + return FALSE; + + /* fall through */ + + case REG: + return (GPR_OR_PSEUDO_P (REGNO (XEXP (op, 0)))); + + case SYMBOL_REF: + case LABEL_REF: + case CONST_INT: + case CONST: + return TRUE; + } + + return FALSE; +} + +/* Return true if operand is a GPR register. */ + +int +gpr_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is an accumulator register. */ + +int +accum_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return ACCUM_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is a GPR or an accumulator register. */ + +int +gpr_or_accum_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + if (ACCUM_P (REGNO (op))) + return TRUE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is a CR register. */ + +int +cr_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return CR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is the repeat count register. */ + +int +repeat_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return (REGNO (op) == CR_RPT_C || REGNO (op) >= FIRST_PSEUDO_REGISTER); +} + +/* Return true if operand is a FLAG register. */ + +int +flag_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return FLAG_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is either F0 or F1. */ + +int +br_flag_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return BR_FLAG_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is either F0/F1 or the constants 0/1. */ + +int +br_flag_or_constant_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == CONST_INT) + return (INTVAL (op) == 0 || INTVAL (op) == 1); + + if (GET_CODE (op) != REG) + return FALSE; + + return BR_FLAG_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is either F0 or F1, or a GPR register. */ + +int +gpr_or_br_flag_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)) || BR_FLAG_P (REGNO (op)); +} + +/* Return true if operand is the F0 register. */ + +int +f0_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return (REGNO (op) == FLAG_F0 || REGNO (op) >= FIRST_PSEUDO_REGISTER); +} + +/* Return true if operand is the F1 register. */ + +int +f1_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return (REGNO (op) == FLAG_F1 || REGNO (op) >= FIRST_PSEUDO_REGISTER); +} + +/* Return true if operand is the F1 register. */ + +int +carry_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) != REG) + return FALSE; + + return (REGNO (op) == FLAG_CARRY || REGNO (op) >= FIRST_PSEUDO_REGISTER); +} + +/* Return true if operand is a register of any flavor or a 0 of the + appropriate type. */ + +int +reg_or_0_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + case REG: + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return register_operand (op, mode); + + case CONST_INT: + return INTVAL (op) == 0; + + case CONST_DOUBLE: + return CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0; + } + + return FALSE; +} + +/* Return true if operand is a GPR register or a signed 6 bit immediate. */ + +int +gpr_or_signed6_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), -32, 31); + + if (GET_CODE (op) != REG) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is a GPR register or an unsigned 5 bit immediate. */ + +int +gpr_or_unsigned5_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), 0, 31); + + if (GET_CODE (op) != REG) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is a GPR register or an unsigned 6 bit immediate. */ + +int +gpr_or_unsigned6_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), 0, 63); + + if (GET_CODE (op) != REG) + return FALSE; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); +} + +/* Return true if operand is a GPR register or a constant of some form. */ + +int +gpr_or_constant_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + default: + break; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + return TRUE; + + case SUBREG: + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + break; + + /* fall through */ + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); + } + + return FALSE; +} + +/* Return true if operand is a GPR register or a constant of some form, + including a CONST_DOUBLE, which gpr_or_constant_operand doesn't recognize. */ + +int +gpr_or_dbl_const_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + default: + break; + + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + return TRUE; + + case SUBREG: + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + break; + + /* fall through */ + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); + } + + return FALSE; +} + +/* Return true if operand is a gpr register or a valid memory operation. */ + +int +gpr_or_memory_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + default: + break; + + case SUBREG: + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + break; + + /* fall through */ + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return GPR_OR_PSEUDO_P (REGNO (op)); + + case MEM: + return d30v_legitimate_address_p (mode_int, XEXP (op, 0), reload_completed); + } + + return FALSE; +} + +/* Return true if operand is something that can be an input for a move + operation. */ + +int +move_input_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + default: + break; + + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + return TRUE; + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + break; + + return TRUE; + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + if (GET_CODE (XEXP (op, 0)) == ADDRESSOF) + return TRUE; + return d30v_legitimate_address_p (mode_int, XEXP (op, 0), + reload_completed); + } + + return FALSE; +} + +/* Return true if operand is something that can be an output for a move + operation. */ + +int +move_output_operand (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + switch (GET_CODE (op)) + { + default: + break; + + case SUBREG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + op = SUBREG_REG (op); + if (GET_CODE (op) != REG) + break; + + return TRUE; + + case REG: + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + return TRUE; + + case MEM: + if (GET_CODE (XEXP (op, 0)) == ADDRESSOF) + return TRUE; + return d30v_legitimate_address_p (mode_int, XEXP (op, 0), + reload_completed); + } + + return FALSE; +} + +/* Return true if operand is a signed 6 bit immediate. */ + +int +signed6_operand (op, mode_int) + rtx op; + int mode_int; +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), -32, 31); + + return FALSE; +} + +/* Return true if operand is an unsigned 5 bit immediate. */ + +int +unsigned5_operand (op, mode_int) + rtx op; + int mode_int; +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), 0, 31); + + return FALSE; +} + +/* Return true if operand is an unsigned 6 bit immediate. */ + +int +unsigned6_operand (op, mode_int) + rtx op; + int mode_int; +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (INTVAL (op), 0, 63); + + return FALSE; +} + +/* Return true if operand is a constant with a single bit set. */ + +int +bitset_operand (op, mode_int) + rtx op; + int mode_int; +{ + if (GET_CODE (op) == CONST_INT) + return IN_RANGE_P (exact_log2 (INTVAL (op)), 0, 31); + + return FALSE; +} + +/* Return true if the operator is a ==/!= test against f0 or f1 that can be + used in conditional execution. */ + +int +condexec_test_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != EQ && GET_CODE (op) != NE) + return FALSE; + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG || !BR_FLAG_OR_PSEUDO_P (REGNO (x0))) + return FALSE; + + x1 = XEXP (op, 1); + if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0) + return FALSE; + + return TRUE; +} + +/* Return true if the operator is a ==/!= test against f0, f1, or a general + register that can be used in a branch instruction. */ + +int +condexec_branch_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != EQ && GET_CODE (op) != NE) + return FALSE; + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG) + return FALSE; + + regno = REGNO (x0); + if (!GPR_OR_PSEUDO_P (regno) && !BR_FLAG_P (regno)) + return FALSE; + + x1 = XEXP (op, 1); + if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0) + return FALSE; + + return TRUE; +} + +/* Return true if the unary operator can be executed with conditional + execution. */ + +int +condexec_unary_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx op0; + + /* Only do this after register allocation, so that we can look at the register # */ + if (!reload_completed) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '1') + return FALSE; + + op0 = XEXP (op, 0); + if (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + + switch (GET_CODE (op)) + { + default: + break; + + case ABS: + case NEG: + case NOT: + if (GET_MODE (op) == SImode && GET_CODE (op0) == REG && GPR_P (REGNO (op0))) + return TRUE; + + break; + } + + return FALSE; +} + +/* Return true if the add or subtraction can be executed with conditional + execution. */ + +int +condexec_addsub_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx op0, op1; + + /* Only do this after register allocation, so that we can look at the register # */ + if (!reload_completed) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c') + return FALSE; + + op0 = XEXP (op, 0); + op1 = XEXP (op, 1); + + if (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + + if (GET_CODE (op1) == SUBREG) + op1 = SUBREG_REG (op1); + + if (GET_CODE (op0) != REG) + return FALSE; + + switch (GET_CODE (op)) + { + default: + break; + + case PLUS: + case MINUS: + return (GET_MODE (op) == SImode && GPR_P (REGNO (op0)) + && gpr_or_constant_operand (op1, SImode)); + } + + return FALSE; +} + +/* Return true if the binary operator can be executed with conditional + execution. We don't include add/sub here, since they have extra + clobbers for the flags registers. */ + +int +condexec_binary_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx op0, op1; + + /* Only do this after register allocation, so that we can look at the register # */ + if (!reload_completed) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c') + return FALSE; + + op0 = XEXP (op, 0); + op1 = XEXP (op, 1); + + if (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + + if (GET_CODE (op1) == SUBREG) + op1 = SUBREG_REG (op1); + + if (GET_CODE (op0) != REG) + return FALSE; + + /* MULT is not included here, because it is an IU only instruction. */ + switch (GET_CODE (op)) + { + case AND: + case IOR: + case XOR: + case ASHIFTRT: + case LSHIFTRT: + case ROTATERT: + return (GET_MODE (op) == SImode && GPR_P (REGNO (op0)) + && gpr_or_constant_operand (op1, SImode)); + + case ASHIFT: + case ROTATE: + return (GET_MODE (op) == SImode && GPR_P (REGNO (op0)) + && GET_CODE (op1) == CONST_INT); + } + + return FALSE; +} + +/* Return true if the shift/rotate left operator can be executed with + conditional execution. */ + +int +condexec_shiftl_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx op0, op1; + + /* Only do this after register allocation, so that we can look at the register # */ + if (!reload_completed) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '2' && GET_RTX_CLASS (GET_CODE (op)) != 'c') + return FALSE; + + op0 = XEXP (op, 0); + op1 = XEXP (op, 1); + + if (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + + if (GET_CODE (op1) == SUBREG) + op1 = SUBREG_REG (op1); + + if (GET_CODE (op0) != REG) + return FALSE; + + switch (GET_CODE (op)) + { + case ASHIFT: + case ROTATE: + return (GET_MODE (op) == SImode && GPR_P (REGNO (op0)) + && GET_CODE (op1) == NEG + && GET_CODE (XEXP (op1, 0)) == REG + && GPR_P (REGNO (XEXP (op1, 0)))); + } + + return FALSE; +} + +/* Return true if the {sign,zero} extend operator from memory can be + conditionally executed. */ + +int +condexec_extend_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + + /* Only do this after register allocation, so that we can look at the register # */ + if (!reload_completed) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '1') + return FALSE; + + switch (GET_CODE (op)) + { + default: + break; + + case SIGN_EXTEND: + case ZERO_EXTEND: + if ((GET_MODE (op) == SImode && GET_MODE (XEXP (op, 0)) == QImode) + || (GET_MODE (op) == SImode && GET_MODE (XEXP (op, 0)) == HImode) + || (GET_MODE (op) == HImode && GET_MODE (XEXP (op, 0)) == QImode)) + return TRUE; + + break; + } + + return FALSE; +} + +/* Return true for comparisons against 0 that can be turned into a + bratnz/bratzr instruction. */ + +int +branch_zero_operator (op, mode_int) + rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_CODE (op) != EQ && GET_CODE (op) != NE) + return FALSE; + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG || !GPR_OR_PSEUDO_P (REGNO (x0))) + return FALSE; + + x1 = XEXP (op, 1); + if (GET_CODE (x1) != CONST_INT || INTVAL (x1) != 0) + return FALSE; + + return TRUE; +} + +/* Return true if an operand is simple, suitable for use in a conditional move */ + +int +cond_move_operand (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx addr; + + if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode) + return FALSE; + + switch (GET_CODE (op)) + { + case REG: + case SUBREG: + return gpr_operand (op, mode); + + case CONST_DOUBLE: + return GET_MODE (op) == SFmode; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + return TRUE; + + /* Don't allow post dec/inc, since we might not get the side effects correct. */ + case MEM: + addr = XEXP (op, 0); + return GET_CODE (addr) != POST_DEC && GET_CODE (addr) != POST_INC; + } + + return FALSE; +} + +/* Return true if an operand is simple, suitable for use in conditional execution. + Unlike cond_move, we can allow auto inc/dec. */ + +int +cond_exec_operand (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx addr; + + if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode) + return FALSE; + + switch (GET_CODE (op)) + { + case REG: + case SUBREG: + return gpr_operand (op, mode); + + case CONST_DOUBLE: + return GET_MODE (op) == SFmode; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + return TRUE; + + case MEM: + return memory_operand (op, mode); + } + + return FALSE; +} + +/* Return true if operand is a SI mode signed relational test. */ + +int +srelational_si_operator (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case EQ: + case NE: + case LT: + case LE: + case GT: + case GE: + break; + } + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG) + return FALSE; + + if (GET_MODE (x0) != SImode) + return FALSE; + + x1 = XEXP (op, 1); + switch (GET_CODE (x1)) + { + default: + return FALSE; + + case REG: + case SUBREG: + case CONST_INT: + case LABEL_REF: + case SYMBOL_REF: + case CONST: + break; + } + + return TRUE; +} + +/* Return true if operand is a SI mode unsigned relational test. */ + +int +urelational_si_operator (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + switch (GET_CODE (op)) + { + default: + return FALSE; + + case LTU: + case LEU: + case GTU: + case GEU: + break; + } + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG) + return FALSE; + + if (GET_MODE (x0) != SImode) + return FALSE; + + x1 = XEXP (op, 1); + switch (GET_CODE (x1)) + { + default: + return FALSE; + + case REG: + case SUBREG: + case CONST_INT: + case LABEL_REF: + case SYMBOL_REF: + case CONST: + break; + } + + return TRUE; +} + +/* Return true if operand is a DI mode relational test. */ + +int +relational_di_operator (op, mode_int) + register rtx op; + int mode_int; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return FALSE; + + if (GET_RTX_CLASS (GET_CODE (op)) != '<') + return FALSE; + + x0 = XEXP (op, 0); + if (GET_CODE (x0) != REG && GET_CODE (x0) != SUBREG) + return FALSE; + + if (GET_MODE (x0) != DImode) + return FALSE; + + x1 = XEXP (op, 1); + if (GET_CODE (x1) != REG && GET_CODE (x1) != SUBREG + && GET_CODE (x1) != CONST_INT && GET_CODE (x1) != CONST_DOUBLE) + return FALSE; + + return TRUE; +} + + +/* Calculate the stack information for the current function. + + D30V stack frames look like: + + high | .... | + +-------------------------------+ + | Argument word #19 | + +-------------------------------+ + | Argument word #18 | + +-------------------------------+ + | Argument word #17 | + +-------------------------------+ + | Argument word #16 | + Prev sp +-------------------------------+ + | | + | Save for arguments 1..16 if | + | the func. uses stdarg/varargs | + | | + +-------------------------------+ + | | + | Save area for GPR registers | + | | + +-------------------------------+ + | | + | Save area for accumulators | + | | + +-------------------------------+ + | | + | Local variables | + | | + +-------------------------------+ + | | + | alloca space if used | + | | + +-------------------------------+ + | | + | Space for outgoing arguments | + | | + low SP----> +-------------------------------+ +*/ + +d30v_stack_t * +d30v_stack_info () +{ + static d30v_stack_t info, zero_info; + d30v_stack_t *info_ptr = &info; + tree fndecl = current_function_decl; + tree fntype = TREE_TYPE (fndecl); + int varargs_p = 0; + tree cur_arg; + tree next_arg; + int saved_gprs; + int saved_accs; + int memrefs_2words; + int memrefs_1word; + unsigned char save_gpr_p[GPR_LAST]; + int i; + + /* If we've already calculated the values and reload is complete, just return now */ + if (d30v_stack_cache) + return d30v_stack_cache; + + /* Zero all fields */ + info = zero_info; + + if (profile_flag) + regs_ever_live[GPR_LINK] = 1; + + /* Determine if this is a stdarg function */ + if (TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) + varargs_p = 1; + else + { + /* Find the last argument, and see if it is __builtin_va_alist. */ + for (cur_arg = DECL_ARGUMENTS (fndecl); cur_arg != (tree)0; cur_arg = next_arg) + { + next_arg = TREE_CHAIN (cur_arg); + if (next_arg == (tree)0) + { + if (DECL_NAME (cur_arg) + && !strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "__builtin_va_alist")) + varargs_p = 1; + + break; + } + } + } + + /* Calculate which registers need to be saved & save area size */ + saved_accs = 0; + memrefs_2words = 0; + memrefs_1word = 0; + for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++) + { + if (regs_ever_live[i] && !call_used_regs[i]) + { + info_ptr->save_p[i] = 2; + saved_accs++; + memrefs_2words++; + } + } + + saved_gprs = 0; + for (i = GPR_FIRST; i <= GPR_LAST; i++) + { + if (regs_ever_live[i] && (!call_used_regs[i] || i == GPR_LINK)) + { + save_gpr_p[i] = 1; + saved_gprs++; + } + else + save_gpr_p[i] = 0; + } + + /* Determine which register pairs can be saved together with ld2w/st2w */ + for (i = GPR_FIRST; i <= GPR_LAST; i++) + { + if (((i - GPR_FIRST) & 1) == 0 && save_gpr_p[i] && save_gpr_p[i+1]) + { + memrefs_2words++; + info_ptr->save_p[i++] = 2; + } + else if (save_gpr_p[i]) + { + memrefs_1word++; + info_ptr->save_p[i] = 1; + } + } + + /* Determine various sizes */ + info_ptr->varargs_p = varargs_p; + info_ptr->varargs_size = ((varargs_p) + ? (GPR_ARG_LAST + 1 - GPR_ARG_FIRST) * UNITS_PER_WORD + : 0); + + info_ptr->accum_size = 2 * UNITS_PER_WORD * saved_accs; + info_ptr->gpr_size = D30V_ALIGN (UNITS_PER_WORD * saved_gprs, + 2 * UNITS_PER_WORD); + info_ptr->vars_size = D30V_ALIGN (get_frame_size (), 2 * UNITS_PER_WORD); + info_ptr->parm_size = D30V_ALIGN (current_function_outgoing_args_size, + 2 * UNITS_PER_WORD); + + info_ptr->total_size = D30V_ALIGN ((info_ptr->gpr_size + + info_ptr->accum_size + + info_ptr->vars_size + + info_ptr->parm_size + + info_ptr->varargs_size), + (STACK_BOUNDARY / BITS_PER_UNIT)); + + info_ptr->save_offset = (info_ptr->total_size + - (info_ptr->varargs_size + + info_ptr->gpr_size + + info_ptr->accum_size)); + + /* The link register is the last GPR saved, but there might be some padding + bytes after it, so account for that. */ + info_ptr->link_offset = (info_ptr->total_size + - (info_ptr->varargs_size + + (info_ptr->gpr_size + - UNITS_PER_WORD * saved_gprs) + + UNITS_PER_WORD)); + + info_ptr->memrefs_varargs = info_ptr->varargs_size / (2 * UNITS_PER_WORD); + info_ptr->memrefs_2words = memrefs_2words; + info_ptr->memrefs_1word = memrefs_1word; + + if (reload_completed) + d30v_stack_cache = info_ptr; + + return info_ptr; +} + + +/* Internal function to print all of the information about the stack */ + +void +debug_stack_info (info) + d30v_stack_t *info; +{ + int i; + + if (!info) + info = d30v_stack_info (); + + fprintf (stderr, "\nStack information for function %s:\n", + ((current_function_decl && DECL_NAME (current_function_decl)) + ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl)) + : "<unknown>")); + + fprintf (stderr, "\tsave_offset = %d\n", info->save_offset); + fprintf (stderr, "\tmemrefs_varargs = %d\n", info->memrefs_varargs); + fprintf (stderr, "\tmemrefs_2words = %d\n", info->memrefs_2words); + fprintf (stderr, "\tmemrefs_1word = %d\n", info->memrefs_1word); + fprintf (stderr, "\tvarargs_p = %d\n", info->varargs_p); + fprintf (stderr, "\tvarargs_size = %d\n", info->varargs_size); + fprintf (stderr, "\tvars_size = %d\n", info->vars_size); + fprintf (stderr, "\tparm_size = %d\n", info->parm_size); + fprintf (stderr, "\tgpr_size = %d\n", info->gpr_size); + fprintf (stderr, "\taccum_size = %d\n", info->accum_size); + fprintf (stderr, "\ttotal_size = %d\n", info->total_size); + fprintf (stderr, "\tsaved registers ="); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (info->save_p[i] == 2) + { + fprintf (stderr, " %s-%s", reg_names[i], reg_names[i+1]); + i++; + } + else if (info->save_p[i]) + fprintf (stderr, " %s", reg_names[i]); + } + + putc ('\n', stderr); + fflush (stderr); +} + + +/* Return non-zero if this function is known to have a null or 1 instruction epilogue. */ + +int +direct_return () +{ + if (reload_completed) + { + d30v_stack_t *info = d30v_stack_info (); + + /* If no epilogue code is needed, can use just a simple jump */ + if (info->total_size == 0) + return 1; + +#if 0 + /* If just a small amount of local stack was allocated and no registers + saved, skip forward branch */ + if (info->total_size == info->vars_size + && IN_RANGE_P (info->total_size, 1, 31)) + return 1; +#endif + } + + return 0; +} + + +/* A C statement (sans semicolon) for initializing the variable CUM for the + state at the beginning of the argument list. The variable has type + `CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type + of the function which will receive the args, or 0 if the args are to a + compiler support library function. The value of INDIRECT is nonzero when + processing an indirect call, for example a call through a function pointer. + The value of INDIRECT is zero for a call to an explicitly named function, a + library function call, or when `INIT_CUMULATIVE_ARGS' is used to find + arguments for the function being compiled. + + When processing a call to a compiler support library function, LIBNAME + identifies which one. It is a `symbol_ref' rtx which contains the name of + the function, as a string. LIBNAME is 0 when an ordinary C function call is + being processed. Thus, each time this macro is called, either LIBNAME or + FNTYPE is nonzero, but never both of them at once. */ + +void +d30v_init_cumulative_args (cum, fntype, libname, indirect, incoming) + CUMULATIVE_ARGS *cum; + tree fntype; + rtx libname; + int indirect; + int incoming; +{ + *cum = GPR_ARG_FIRST; + + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "\ninit_cumulative_args:"); + if (indirect) + fputs (" indirect", stderr); + + if (incoming) + fputs (" incoming", stderr); + + if (fntype) + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, " return=%s,", + tree_code_name[ (int)TREE_CODE (ret_type) ]); + } + + if (libname && GET_CODE (libname) == SYMBOL_REF) + fprintf (stderr, " libname=%s", XSTR (libname, 0)); + + putc ('\n', stderr); + } +} + + +/* If defined, a C expression that gives the alignment boundary, in bits, of an + argument with the specified mode and type. If it is not defined, + `PARM_BOUNDARY' is used for all arguments. */ + +int +d30v_function_arg_boundary (mode_int, type) + int mode_int; + tree type; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + int size = ((mode == BLKmode && type) + ? int_size_in_bytes (type) + : GET_MODE_SIZE (mode)); + + return (size > UNITS_PER_WORD) ? 2*UNITS_PER_WORD : UNITS_PER_WORD; +} + + +/* A C expression that controls whether a function argument is passed in a + register, and which register. + + The arguments are CUM, which summarizes all the previous arguments; MODE, + the machine mode of the argument; TYPE, the data type of the argument as a + tree node or 0 if that is not known (which happens for C support library + functions); and NAMED, which is 1 for an ordinary argument and 0 for + nameless arguments that correspond to `...' in the called function's + prototype. + + The value of the expression should either be a `reg' RTX for the hard + register in which to pass the argument, or zero to pass the argument on the + stack. + + For machines like the Vax and 68000, where normally all arguments are + pushed, zero suffices as a definition. + + The usual way to make the ANSI library `stdarg.h' work on a machine where + some arguments are usually passed in registers, is to cause nameless + arguments to be passed on the stack instead. This is done by making + `FUNCTION_ARG' return 0 whenever NAMED is 0. + + You may use the macro `MUST_PASS_IN_STACK (MODE, TYPE)' in the definition of + this macro to determine if this argument is of a type that must be passed in + the stack. If `REG_PARM_STACK_SPACE' is not defined and `FUNCTION_ARG' + returns non-zero for such an argument, the compiler will abort. If + `REG_PARM_STACK_SPACE' is defined, the argument will be computed in the + stack and then loaded into a register. */ + +Rtx +d30v_function_arg (cum, mode_int, type, named, incoming) + CUMULATIVE_ARGS *cum; + int mode_int; + tree type; + int named; + int incoming; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + int size = ((mode == BLKmode && type) + ? int_size_in_bytes (type) + : GET_MODE_SIZE (mode)); + int adjust = (size > UNITS_PER_WORD && (*cum & 1) != 0); + rtx ret; + + /* Return a marker for use in the call instruction. */ + if (mode == VOIDmode) + ret = const0_rtx; + + else if (*cum + adjust <= GPR_ARG_LAST) + ret = gen_rtx (REG, mode, *cum + adjust); + + else + ret = NULL_RTX; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "function_arg: words = %2d, mode = %4s, named = %d, size = %3d, adjust = %1d, arg = %s\n", + *cum, GET_MODE_NAME (mode), named, size, adjust, + (ret) ? ((ret == const0_rtx) ? "<0>" : reg_names[ REGNO (ret) ]) : "memory"); + + return ret; +} + + +/* A C expression for the number of words, at the beginning of an argument, + must be put in registers. The value must be zero for arguments that are + passed entirely in registers or that are entirely pushed on the stack. + + On some machines, certain arguments must be passed partially in registers + and partially in memory. On these machines, typically the first N words of + arguments are passed in registers, and the rest on the stack. If a + multi-word argument (a `double' or a structure) crosses that boundary, its + first few words must be passed in registers and the rest must be pushed. + This macro tells the compiler when this occurs, and how many of the words + should go in registers. + + `FUNCTION_ARG' for these arguments should return the first register to be + used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for + the called function. */ + +int +d30v_function_arg_partial_nregs (cum, mode_int, type, named) + CUMULATIVE_ARGS *cum; + int mode_int; + tree type; + int named; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + int bytes = ((mode == BLKmode) + ? int_size_in_bytes (type) + : GET_MODE_SIZE (mode)); + int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0); + int arg_num = *cum + adjust; + int ret; + + ret = ((arg_num <= GPR_ARG_LAST && arg_num + words > GPR_ARG_LAST+1) + ? GPR_ARG_LAST - arg_num + 1 + : 0); + + if (TARGET_DEBUG_ARG && ret) + fprintf (stderr, "function_arg_partial_nregs: %d\n", ret); + + return ret; +} + + +/* A C expression that indicates when an argument must be passed by reference. + If nonzero for an argument, a copy of that argument is made in memory and a + pointer to the argument is passed instead of the argument itself. The + pointer is passed in whatever way is appropriate for passing a pointer to + that type. + + On machines where `REG_PARM_STACK_SPACE' is not defined, a suitable + definition of this macro might be + #define FUNCTION_ARG_PASS_BY_REFERENCE\ + (CUM, MODE, TYPE, NAMED) \ + MUST_PASS_IN_STACK (MODE, TYPE) */ + +int +d30v_function_arg_pass_by_reference (cum, mode_int, type, named) + CUMULATIVE_ARGS *cum; + int mode_int; + tree type; + int named; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + int ret = MUST_PASS_IN_STACK (mode, type); + + if (TARGET_DEBUG_ARG && ret) + fprintf (stderr, "function_arg_pass_by_reference: %d\n", ret); + + return ret; +} + + +/* A C statement (sans semicolon) to update the summarizer variable CUM to + advance past an argument in the argument list. The values MODE, TYPE and + NAMED describe that argument. Once this is done, the variable CUM is + suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. + + This macro need not do anything if the argument in question was passed on + the stack. The compiler knows how to track the amount of stack space used + for arguments without any special help. */ + +void +d30v_function_arg_advance (cum, mode_int, type, named) + CUMULATIVE_ARGS *cum; + int mode_int; + tree type; + int named; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + int bytes = ((mode == BLKmode) + ? int_size_in_bytes (type) + : GET_MODE_SIZE (mode)); + int words = D30V_ALIGN (bytes, UNITS_PER_WORD) / UNITS_PER_WORD; + int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0); + + *cum += words + adjust; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "function_adv: words = %2d, mode = %4s, named = %d, size = %3d, adjust = %1d\n", + *cum, GET_MODE_NAME (mode), named, words * UNITS_PER_WORD, adjust); +} + + +/* If defined, is a C expression that produces the machine-specific code for a + call to `__builtin_saveregs'. This code will be moved to the very beginning + of the function, before any parameter access are made. The return value of + this function should be an RTX that contains the value to use as the return + of `__builtin_saveregs'. + + The argument ARGS is a `tree_list' containing the arguments that were passed + to `__builtin_saveregs'. + + If this macro is not defined, the compiler will output an ordinary call to + the library function `__builtin_saveregs'. */ + +Rtx +d30v_expand_builtin_saveregs (args) + tree args; +{ + int offset = UNITS_PER_WORD * (GPR_ARG_LAST + 1 - GPR_ARG_FIRST); + + if (TARGET_DEBUG_ARG) + fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n", + offset); + + return gen_rtx (PLUS, Pmode, virtual_incoming_args_rtx, GEN_INT (- offset)); +} + + +/* This macro offers an alternative to using `__builtin_saveregs' and defining + the macro `EXPAND_BUILTIN_SAVEREGS'. Use it to store the anonymous register + arguments into the stack so that all the arguments appear to have been + passed consecutively on the stack. Once this is done, you can use the + standard implementation of varargs that works for machines that pass all + their arguments on the stack. + + The argument ARGS_SO_FAR is the `CUMULATIVE_ARGS' data structure, containing + the values that obtain after processing of the named arguments. The + arguments MODE and TYPE describe the last named argument--its machine mode + and its data type as a tree node. + + The macro implementation should do two things: first, push onto the stack + all the argument registers *not* used for the named arguments, and second, + store the size of the data thus pushed into the `int'-valued variable whose + name is supplied as the argument PRETEND_ARGS_SIZE. The value that you + store here will serve as additional offset for setting up the stack frame. + + Because you must generate code to push the anonymous arguments at compile + time without knowing their data types, `SETUP_INCOMING_VARARGS' is only + useful on machines that have just a single category of argument register and + use it uniformly for all data types. + + If the argument SECOND_TIME is nonzero, it means that the arguments of the + function are being analyzed for the second time. This happens for an inline + function, which is not actually compiled until the end of the source file. + The macro `SETUP_INCOMING_VARARGS' should not generate any instructions in + this case. */ + +void +d30v_setup_incoming_varargs (cum, mode_int, type, pretend_size, second_time) + CUMULATIVE_ARGS *cum; + int mode_int; + tree type; + int *pretend_size; + int second_time; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "setup_vararg: words = %2d, mode = %4s, second_time = %d\n", + *cum, GET_MODE_NAME (mode), second_time); +} + + + +/* A C compound statement that outputs the assembler code for entry to a + function. The prologue is responsible for setting up the stack frame, + initializing the frame pointer register, saving registers that must be + saved, and allocating SIZE additional bytes of storage for the local + variables. SIZE is an integer. FILE is a stdio stream to which the + assembler code should be output. + + The label for the beginning of the function need not be output by this + macro. That has already been done when the macro is run. + + To determine which registers to save, the macro can refer to the array + `regs_ever_live': element R is nonzero if hard register R is used anywhere + within the function. This implies the function prologue should save + register R, provided it is not one of the call-used registers. + (`FUNCTION_EPILOGUE' must likewise use `regs_ever_live'.) + + On machines that have "register windows", the function entry code does not + save on the stack the registers that are in the windows, even if they are + supposed to be preserved by function calls; instead it takes appropriate + steps to "push" the register stack, if any non-call-used registers are used + in the function. + + On machines where functions may or may not have frame-pointers, the function + entry code must vary accordingly; it must set up the frame pointer if one is + wanted, and not otherwise. To determine whether a frame pointer is in + wanted, the macro can refer to the variable `frame_pointer_needed'. The + variable's value will be 1 at run time in a function that needs a frame + pointer. *Note Elimination::. + + The function entry code is responsible for allocating any stack space + required for the function. This stack space consists of the regions listed + below. In most cases, these regions are allocated in the order listed, with + the last listed region closest to the top of the stack (the lowest address + if `STACK_GROWS_DOWNWARD' is defined, and the highest address if it is not + defined). You can use a different order for a machine if doing so is more + convenient or required for compatibility reasons. Except in cases where + required by standard or by a debugger, there is no reason why the stack + layout used by GCC need agree with that used by other compilers for a + machine. + + * A region of `current_function_pretend_args_size' bytes of + uninitialized space just underneath the first argument + arriving on the stack. (This may not be at the very start of + the allocated stack region if the calling sequence has pushed + anything else since pushing the stack arguments. But + usually, on such machines, nothing else has been pushed yet, + because the function prologue itself does all the pushing.) + This region is used on machines where an argument may be + passed partly in registers and partly in memory, and, in some + cases to support the features in `varargs.h' and `stdargs.h'. + + * An area of memory used to save certain registers used by the + function. The size of this area, which may also include + space for such things as the return address and pointers to + previous stack frames, is machine-specific and usually + depends on which registers have been used in the function. + Machines with register windows often do not require a save + area. + + * A region of at least SIZE bytes, possibly rounded up to an + allocation boundary, to contain the local variables of the + function. On some machines, this region and the save area + may occur in the opposite order, with the save area closer to + the top of the stack. + + * Optionally, when `ACCUMULATE_OUTGOING_ARGS' is defined, a + region of `current_function_outgoing_args_size' bytes to be + used for outgoing argument lists of the function. *Note + Stack Arguments::. + + Normally, it is necessary for the macros `FUNCTION_PROLOGUE' and + `FUNCTION_EPILOGUE' to treat leaf functions specially. The C variable + `leaf_function' is nonzero for such a function. */ + +/* For the d30v, move all of the prologue processing into separate insns. */ +void +d30v_function_prologue (stream, size) + FILE *stream; + int size; +{ +} + + +/* Called after register allocation to add any instructions needed for the + prologue. Using a prologue insn is favored compared to putting all of the + instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler + to intermix instructions with the saves of the caller saved registers. In + some cases, it might be necessary to emit a barrier instruction as the last + insn to prevent such scheduling. */ + +void +d30v_expand_prologue () +{ + rtx sp = stack_pointer_rtx; + d30v_stack_t *info = d30v_stack_info (); + int i; + rtx mem_di = NULL_RTX; + rtx mem_si = NULL_RTX; + int num_memrefs = (info->memrefs_2words + + info->memrefs_1word + + info->memrefs_varargs); + + if (TARGET_DEBUG_STACK) + debug_stack_info (info); + + /* Grow the stack. */ + if (info->total_size) + emit_insn (gen_addsi3 (sp, sp, GEN_INT (- info->total_size))); + + /* If there is more than one save, use post-increment addressing which will + result in smaller code, than would the normal references. If there is + only one save, just do the store as normal. */ + + if (num_memrefs > 1) + { + rtx save_tmp = gen_rtx (REG, Pmode, GPR_STACK_TMP); + rtx post_inc = gen_rtx (POST_INC, Pmode, save_tmp); + mem_di = gen_rtx (MEM, DImode, post_inc); + mem_si = gen_rtx (MEM, SImode, post_inc); + emit_insn (gen_addsi3 (save_tmp, sp, GEN_INT (info->save_offset))); + } + else if (num_memrefs == 1) + { + rtx addr = plus_constant (sp, info->save_offset); + mem_di = gen_rtx (MEM, DImode, addr); + mem_si = gen_rtx (MEM, SImode, addr); + } + + /* Save the accumulators. */ + for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++) + if (info->save_p[i]) + { + rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST); + emit_insn (gen_movdi (acc_tmp, gen_rtx (REG, DImode, i))); + emit_insn (gen_movdi (mem_di, acc_tmp)); + } + + /* Save the GPR registers that are adjacent to each other with st2w. */ + for (i = GPR_FIRST; i <= GPR_LAST; i += 2) + if (info->save_p[i] == 2) + emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i))); + + /* Save the GPR registers that need to be saved with a single word store. */ + for (i = GPR_FIRST; i <= GPR_LAST; i++) + if (info->save_p[i] == 1) + emit_insn (gen_movsi (mem_si, gen_rtx (REG, SImode, i))); + + /* Save the argument registers if this function accepts variable args. */ + if (info->varargs_p) + { + /* Realign r22 if an odd # of GPRs were saved. */ + if ((info->memrefs_1word & 1) != 0) + { + rtx save_tmp = XEXP (XEXP (mem_si, 0), 0); + emit_insn (gen_addsi3 (save_tmp, save_tmp, GEN_INT (UNITS_PER_WORD))); + } + + for (i = GPR_ARG_FIRST; i <= GPR_ARG_LAST; i += 2) + emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i))); + } + + /* Update the frame pointer. */ + if (frame_pointer_needed) + emit_move_insn (frame_pointer_rtx, sp); + + /* Hack for now, to prevent scheduler from being too cleaver */ + emit_insn (gen_blockage ()); +} + + +/* A C compound statement that outputs the assembler code for exit from a + function. The epilogue is responsible for restoring the saved registers and + stack pointer to their values when the function was called, and returning + control to the caller. This macro takes the same arguments as the macro + `FUNCTION_PROLOGUE', and the registers to restore are determined from + `regs_ever_live' and `CALL_USED_REGISTERS' in the same way. + + On some machines, there is a single instruction that does all the work of + returning from the function. On these machines, give that instruction the + name `return' and do not define the macro `FUNCTION_EPILOGUE' at all. + + Do not define a pattern named `return' if you want the `FUNCTION_EPILOGUE' + to be used. If you want the target switches to control whether return + instructions or epilogues are used, define a `return' pattern with a + validity condition that tests the target switches appropriately. If the + `return' pattern's validity condition is false, epilogues will be used. + + On machines where functions may or may not have frame-pointers, the function + exit code must vary accordingly. Sometimes the code for these two cases is + completely different. To determine whether a frame pointer is wanted, the + macro can refer to the variable `frame_pointer_needed'. The variable's + value will be 1 when compiling a function that needs a frame pointer. + + Normally, `FUNCTION_PROLOGUE' and `FUNCTION_EPILOGUE' must treat leaf + functions specially. The C variable `leaf_function' is nonzero for such a + function. *Note Leaf Functions::. + + On some machines, some functions pop their arguments on exit while others + leave that for the caller to do. For example, the 68020 when given `-mrtd' + pops arguments in functions that take a fixed number of arguments. + + Your definition of the macro `RETURN_POPS_ARGS' decides which functions pop + their own arguments. `FUNCTION_EPILOGUE' needs to know what was decided. + The variable that is called `current_function_pops_args' is the number of + bytes of its arguments that a function should pop. *Note Scalar Return::. */ + +/* For the d30v, move all processing to be as insns, but do any cleanup + here, since it is done after handling all of the insns. */ +void +d30v_function_epilogue (stream, size) + FILE *stream; + int size; +{ + d30v_stack_cache = (d30v_stack_t *)0; /* reset stack cache */ +} + + + +/* Called after register allocation to add any instructions needed for the + epilogue. Using a epilogue insn is favored compared to putting all of the + instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler + to intermix instructions with the saves of the caller saved registers. In + some cases, it might be necessary to emit a barrier instruction as the last + insn to prevent such scheduling. */ + +void +d30v_expand_epilogue () +{ + rtx sp = stack_pointer_rtx; + d30v_stack_t *info = d30v_stack_info (); + int i; + rtx mem_di = NULL_RTX; + rtx mem_si = NULL_RTX; + int num_memrefs = info->memrefs_2words + info->memrefs_1word; + rtx post_inc; + int extra_stack; + + /* Hack for now, to prevent scheduler from being too cleaver */ + emit_insn (gen_blockage ()); + + /* Restore sp from fp. */ + if (frame_pointer_needed) + emit_move_insn (sp, frame_pointer_rtx); + + /* For the epilogue, use post-increment addressing all of the time. First + adjust the sp, to eliminate all of the stack, except for the save area. */ + + if (info->save_offset) + emit_insn (gen_addsi3 (sp, sp, GEN_INT (info->save_offset))); + + post_inc = gen_rtx (POST_INC, Pmode, sp); + mem_di = gen_rtx (MEM, DImode, post_inc); + mem_si = gen_rtx (MEM, SImode, post_inc); + + /* Restore the accumulators. */ + for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++) + if (info->save_p[i]) + { + rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST); + emit_insn (gen_movdi (acc_tmp, mem_di)); + emit_insn (gen_movdi (gen_rtx (REG, DImode, i), acc_tmp)); + } + + /* Restore the GPR registers that are adjacent to each other with ld2w. */ + for (i = GPR_FIRST; i <= GPR_LAST; i += 2) + if (info->save_p[i] == 2) + emit_insn (gen_movdi (gen_rtx (REG, DImode, i), mem_di)); + + /* Save the GPR registers that need to be saved with a single word store. */ + for (i = GPR_FIRST; i <= GPR_LAST; i++) + if (info->save_p[i] == 1 + && ! (d30v_eh_epilogue_sp_ofs && i == GPR_LINK)) + emit_insn (gen_movsi (gen_rtx (REG, SImode, i), mem_si)); + + /* Release any remaining stack that was allocated for saving the + varargs registers or because an odd # of registers were stored. */ + extra_stack = info->varargs_size; + if ((info->memrefs_1word & 1) != 0) + extra_stack += UNITS_PER_WORD; + + if (extra_stack) + emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack))); + + /* ??? Should try to combine this with the above add? */ + if (d30v_eh_epilogue_sp_ofs) + emit_insn (gen_addsi3 (sp, sp, d30v_eh_epilogue_sp_ofs)); + + /* Hack for now, to prevent scheduler from being too cleaver */ + emit_insn (gen_blockage ()); + + /* Now emit the return instruction. */ + emit_jump_insn (gen_indirect_jump (gen_rtx (REG, Pmode, GPR_LINK))); +} + + +/* A C statement or compound statement to output to FILE some assembler code to + call the profiling subroutine `mcount'. Before calling, the assembler code + must load the address of a counter variable into a register where `mcount' + expects to find the address. The name of this variable is `LP' followed by + the number LABELNO, so you would generate the name using `LP%d' in a + `fprintf'. + + The details of how the address should be passed to `mcount' are determined + by your operating system environment, not by GNU CC. To figure them out, + compile a small program for profiling using the system's installed C + compiler and look at the assembler code that results. */ + +void +d30v_function_profiler (stream, labelno) + FILE *stream; + int labelno; +{ + fprintf (stream, "# profile\n"); +} + + +/* Split a 64 bit item into an upper and a lower part. We specifically do not + want to call gen_highpart/gen_lowpart on CONST_DOUBLEs since it will give us + the wrong part for floating point in cross compilers, and split_double does + not handle registers. Also abort if the register is not a general purpose + register. */ + +void +d30v_split_double (value, p_high, p_low) + rtx value; + rtx *p_high; + rtx *p_low; +{ + int offset = 0; + int regno; + + if (!reload_completed) + abort (); + + switch (GET_CODE (value)) + { + case SUBREG: + offset = SUBREG_WORD (value); + value = SUBREG_REG (value); + if (GET_CODE (value) != REG) + abort (); + + /* fall through */ + + case REG: + regno = REGNO (value) + offset; + if (!GPR_P (regno)) + abort (); + + *p_high = gen_rtx (REG, SImode, regno); + *p_low = gen_rtx (REG, SImode, regno+1); + break; + + case CONST_INT: + case CONST_DOUBLE: + split_double (value, p_high, p_low); + break; + + default: + abort (); + } +} + + +/* A C compound statement to output to stdio stream STREAM the assembler syntax + for an instruction operand that is a memory reference whose address is X. X + is an RTL expression. + + On some machines, the syntax for a symbolic address depends on the section + that the address refers to. On these machines, define the macro + `ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and + then check for it here. *Note Assembler Format::. */ + +void +d30v_print_operand_address (stream, x) + FILE *stream; + rtx x; +{ + if (GET_CODE (x) == MEM) + x = XEXP (x, 0); + + switch (GET_CODE (x)) + { + default: + break; + + case REG: + fputs (reg_names[ REGNO (x) ], stream); + return; + + case CONST_INT: + fprintf (stream, "%ld", (long) INTVAL (x)); + return; + + /* We wrap simple symbol refs inside a parenthesis, so that a name + like `r2' is not taken for a register name. */ + case SYMBOL_REF: + fputs ("(", stream); + assemble_name (stream, XSTR (x, 0)); + fputs (")", stream); + return; + + case LABEL_REF: + case CONST: + output_addr_const (stream, x); + return; + } + + fatal_insn ("Bad insn to d30v_print_operand_address:", x); +} + + +/* Print a memory reference suitable for the ld/st instructions. */ + +static void +d30v_print_operand_memory_reference (stream, x) + FILE *stream; + rtx x; +{ + rtx x0 = NULL_RTX; + rtx x1 = NULL_RTX; + + switch (GET_CODE (x)) + { + default: + fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x); + break; + + case SUBREG: + case REG: + case POST_DEC: + case POST_INC: + x0 = x; + break; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + x1 = x; + break; + + case PLUS: + x0 = XEXP (x, 0); + x1 = XEXP (x, 1); + if (GET_CODE (x0) == CONST_INT || GET_CODE (x0) == SYMBOL_REF + || GET_CODE (x0) == CONST || GET_CODE (x0) == LABEL_REF) + { + x0 = XEXP (x, 1); + x1 = XEXP (x, 0); + } + break; + } + + fputs ("@(", stream); + if (!x0) + fputs (reg_names[GPR_R0], stream); + + else + { + char *suffix = ""; + int offset0 = 0; + + if (GET_CODE (x0) == SUBREG) + { + offset0 = SUBREG_WORD (x0); + x0 = SUBREG_REG (x0); + } + + if (GET_CODE (x0) == POST_INC) + { + x0 = XEXP (x0, 0); + suffix = "+"; + } + else if (GET_CODE (x0) == POST_DEC) + { + x0 = XEXP (x0, 0); + suffix = "-"; + } + + if (GET_CODE (x0) == REG && GPR_P (REGNO (x0))) + fprintf (stream, "%s%s", reg_names[REGNO (x0) + offset0], suffix); + else + fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x); + } + + fputs (",", stream); + + if (!x1) + fputs (reg_names[GPR_R0], stream); + + else + { + int offset1 = 0; + + switch (GET_CODE (x1)) + { + case SUBREG: + offset1 = SUBREG_WORD (x1); + x1 = SUBREG_REG (x1); + if (GET_CODE (x1) != REG) + fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x); + + /* fall through */ + case REG: + fputs (reg_names[REGNO (x1) + offset1], stream); + break; + + case CONST_INT: + fprintf (stream, "%ld", (long) INTVAL (x1)); + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST: + d30v_print_operand_address (stream, x1); + break; + + default: + fatal_insn ("Bad insn to d30v_print_operand_memory_reference:", x); + } + } + + fputs (")", stream); +} + + +/* A C compound statement to output to stdio stream STREAM the assembler syntax + for an instruction operand X. X is an RTL expression. + + LETTER is a value that can be used to specify one of several ways of + printing the operand. It is used when identical operands must be printed + differently depending on the context. LETTER comes from the `%' + specification that was used to request printing of the operand. If the + specification was just `%DIGIT' then LETTER is 0; if the specification was + `%LTR DIGIT' then LETTER is the ASCII code for LTR. + + If X is a register, this macro should print the register's name. The names + can be found in an array `reg_names' whose type is `char *[]'. `reg_names' + is initialized from `REGISTER_NAMES'. + + When the machine description has a specification `%PUNCT' (a `%' followed by + a punctuation character), this macro is called with a null pointer for X and + the punctuation character for LETTER. + + Standard operand flags that are handled elsewhere: + `=' Output a number unique to each instruction in the compilation. + `a' Substitute an operand as if it were a memory reference. + `c' Omit the syntax that indicates an immediate operand. + `l' Substitute a LABEL_REF into a jump instruction. + `n' Like %cDIGIT, except negate the value before printing. + + The d30v specific operand flags are: + `.' Print r0. + `f' Print a SF constant as an int. + `s' Subtract 32 and negate. + `A' Print accumulator number without an `a' in front of it. + `B' Print bit offset for BSET, etc. instructions. + `E' Print u if this is zero extend, nothing if this is sign extend. + `F' Emit /{f,t,x}{f,t,x} for executing a false condition. + `L' Print the lower half of a 64 bit item. + `M' Print a memory reference for ld/st instructions. + `R' Return appropriate cmp instruction for relational test. + `S' Subtract 32. + `T' Emit /{f,t,x}{f,t,x} for executing a true condition. + `U' Print the upper half of a 64 bit item. */ + +void +d30v_print_operand (stream, x, letter) + FILE *stream; + rtx x; + int letter; +{ + enum rtx_code code = (x) ? GET_CODE (x) : NIL; + rtx split_values[2]; + REAL_VALUE_TYPE rv; + long num; + int log; + + switch (letter) + { + case '.': /* Output r0 */ + fputs (reg_names[GPR_R0], stream); + break; + + case 'f': /* Print a SF floating constant as an int */ + if (GET_CODE (x) != CONST_DOUBLE) + fatal_insn ("Bad insn to d30v_print_operand, 'f' modifier:", x); + + REAL_VALUE_FROM_CONST_DOUBLE (rv, x); + REAL_VALUE_TO_TARGET_SINGLE (rv, num); + fprintf (stream, "%ld", num); + break; + + case 'A': /* Print accumulator number without an `a' in front of it. */ + if (GET_CODE (x) != REG || !ACCUM_P (REGNO (x))) + fatal_insn ("Bad insn to d30v_print_operand, 'A' modifier:", x); + + putc ('0' + REGNO (x) - ACCUM_FIRST, stream); + break; + + case 'M': /* Print a memory reference for ld/st */ + if (GET_CODE (x) != MEM) + fatal_insn ("Bad insn to d30v_print_operand, 'M' modifier:", x); + + d30v_print_operand_memory_reference (stream, XEXP (x, 0)); + break; + + case 'L': /* print lower part of 64 bit item. */ + case 'U': /* print upper part of 64 bit item. */ + d30v_split_double (x, &split_values[0], &split_values[1]); + d30v_print_operand (stream, split_values[ letter == 'L' ], '\0'); + break; + + case 'F': /* Print an appropriate suffix for a false comparision. */ + case 'T': /* Print an appropriate suffix for a true comparision. */ + /* Note that the sense of appropriate suffix is for conditional execution + and opposite of what branches want. Branches just use the inverse + operation. */ + if ((GET_CODE (x) == NE || GET_CODE (x) == EQ) + && GET_MODE (x) == CCmode + && GET_CODE (XEXP (x, 0)) == REG + && (GPR_P (REGNO (XEXP (x, 0))) || BR_FLAG_P (REGNO (XEXP (x, 0)))) + && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0) + { + int true_false = (letter == 'T'); + + if (GET_CODE (x) == EQ) + true_false = !true_false; + + if (REGNO (XEXP (x, 0)) == FLAG_F0) + fprintf (stream, "/%cx", (true_false) ? 'f' : 't'); + + else if (REGNO (XEXP (x, 0)) == FLAG_F1) + fprintf (stream, "/x%c", (true_false) ? 'f' : 't'); + + else + fputs ((true_false) ? "tnz" : "tzr", stream); + } + + else if (GET_CODE (x) == REG && REGNO (x) == FLAG_F0) + fprintf (stream, "/%cx", (letter == 'T') ? 't' : 'f'); + + else if (GET_CODE (x) == REG && REGNO (x) == FLAG_F1) + fprintf (stream, "/x%c", (letter == 'T') ? 't' : 'f'); + + else if (GET_CODE (x) == REG && GPR_P (REGNO (x))) + fputs ((letter == 'T') ? "tnz" : "tzr", stream); + + else + fatal_insn ("Bad insn to print_operand, 'F' or 'T' modifier:", x); + break; + + case 'B': /* emit offset single bit to change */ + if (GET_CODE (x) == CONST_INT && (log = exact_log2 (INTVAL (x))) >= 0) + fprintf (stream, "%d", 31 - log); + + else if (GET_CODE (x) == CONST_INT && (log = exact_log2 (~ INTVAL (x))) >= 0) + fprintf (stream, "%d", 31 - log); + + else + fatal_insn ("Bad insn to print_operand, 'B' modifier:", x); + break; + + case 'E': /* Print u if this is zero extend, nothing if sign extend. */ + if (GET_CODE (x) == ZERO_EXTEND) + putc ('u', stream); + else if (GET_CODE (x) != SIGN_EXTEND) + fatal_insn ("Bad insn to print_operand, 'E' modifier:", x); + break; + + case 'R': /* Return appropriate cmp instruction for relational test. */ + switch (GET_CODE (x)) + { + case EQ: fputs ("cmpeq", stream); break; + case NE: fputs ("cmpne", stream); break; + case LT: fputs ("cmplt", stream); break; + case LE: fputs ("cmple", stream); break; + case GT: fputs ("cmpgt", stream); break; + case GE: fputs ("cmpge", stream); break; + case LTU: fputs ("cmpult", stream); break; + case LEU: fputs ("cmpule", stream); break; + case GTU: fputs ("cmpugt", stream); break; + case GEU: fputs ("cmpuge", stream); break; + + default: + fatal_insn ("Bad insn to print_operand, 'R' modifier:", x); + } + break; + + case 's': /* Subtract 32 and negate (for 64 bit shifts). */ + if (GET_CODE (x) == CONST_INT) + fprintf (stream, "%d", (int) (32 - INTVAL (x))); + + else + fatal_insn ("Bad insn to print_operand, 's' modifier:", x); + break; + + case 'S': /* Subtract 32. */ + if (GET_CODE (x) == CONST_INT) + fprintf (stream, "%d", (int)(INTVAL (x) - 32)); + + else + fatal_insn ("Bad insn to print_operand, 's' modifier:", x); + break; + + + case '\0': + if (code == REG) + fputs (reg_names[ REGNO (x) ], stream); + + else if (code == CONST_INT) + fprintf (stream, "%d", (int)INTVAL (x)); + + else if (code == MEM) + d30v_print_operand_address (stream, XEXP (x, 0)); + + else if (CONSTANT_ADDRESS_P (x)) + d30v_print_operand_address (stream, x); + + else + fatal_insn ("Bad insn in d30v_print_operand, 0 case", x); + + return; + + default: + { + char buf[80]; + + sprintf (buf, "Invalid asm template character '%%%c'", letter); + fatal_insn (buf, x); + } + } +} + + +/* A C expression for the size in bytes of the trampoline, as an integer. */ + +int +d30v_trampoline_size () +{ + return 16; +} + + +/* Create a long instruction for building up a trampoline. */ + +static void +d30v_build_long_insn (high_bits, low_bits, imm, mem) + HOST_WIDE_INT high_bits; + HOST_WIDE_INT low_bits; + rtx imm; + rtx mem; +{ + rtx reg = gen_reg_rtx (DImode); + rtx high_word = gen_highpart (SImode, reg); + rtx low_word = gen_lowpart (SImode, reg); + rtx tmp1 = gen_reg_rtx (SImode); + rtx tmp2 = gen_reg_rtx (SImode); + rtx tmp3 = gen_reg_rtx (SImode); + rtx tmp4 = gen_reg_rtx (SImode); + rtx tmp5 = gen_reg_rtx (SImode); + rtx tmp6 = gen_reg_rtx (SImode); + + imm = force_reg (SImode, imm); + + /* Stuff top 6 bits of immediate value into high word */ + emit_insn (gen_lshrsi3 (tmp1, imm, GEN_INT (26))); + emit_insn (gen_andsi3 (tmp2, tmp1, GEN_INT (0x3F))); + emit_insn (gen_iorsi3 (high_word, tmp2, GEN_INT (high_bits))); + + /* Now get the next 8 bits for building the low word */ + emit_insn (gen_andsi3 (tmp3, imm, GEN_INT (0x03FC0000))); + emit_insn (gen_ashlsi3 (tmp4, tmp3, GEN_INT (2))); + + /* And the bottom 18 bits */ + emit_insn (gen_andsi3 (tmp5, imm, GEN_INT (0x0003FFFF))); + emit_insn (gen_iorsi3 (tmp6, tmp4, tmp5)); + emit_insn (gen_iorsi3 (low_word, tmp6, GEN_INT (low_bits))); + + /* Store the instruction */ + emit_insn (gen_movdi (mem, reg)); +} + + +/* A C statement to initialize the variable parts of a trampoline. ADDR is an + RTX for the address of the trampoline; FNADDR is an RTX for the address of + the nested function; STATIC_CHAIN is an RTX for the static chain value that + should be passed to the function when it is called. */ + +void +d30v_initialize_trampoline (addr, fnaddr, static_chain) + rtx addr; + rtx fnaddr; + rtx static_chain; +{ + /* The instruction space can only be accessed by ld2w/st2w. + Generate on the fly: + or r18,r0,<static-chain> + jmp <fnaddr> */ + d30v_build_long_insn (0x83A80000 | ((STATIC_CHAIN_REGNUM - GPR_FIRST) << 12), + 0x80000000, static_chain, + gen_rtx (MEM, DImode, addr)); + + d30v_build_long_insn (0x80180000, 0x80000000, fnaddr, + gen_rtx (MEM, DImode, plus_constant (addr, 8))); +} + + +/* A C compound statement with a conditional `goto LABEL;' executed if X (an + RTX) is a legitimate memory address on the target machine for a memory + operand of mode MODE. + + It usually pays to define several simpler macros to serve as subroutines for + this one. Otherwise it may be too complicated to understand. + + This macro must exist in two variants: a strict variant and a non-strict + one. The strict variant is used in the reload pass. It must be defined so + that any pseudo-register that has not been allocated a hard register is + considered a memory reference. In contexts where some kind of register is + required, a pseudo-register with no hard register must be rejected. + + The non-strict variant is used in other passes. It must be defined to + accept all pseudo-registers in every context where some kind of register is + required. + + Compiler source files that want to use the strict variant of this macro + define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT' + conditional to define the strict variant in that case and the non-strict + variant otherwise. + + Subroutines to check for acceptable registers for various purposes (one for + base registers, one for index registers, and so on) are typically among the + subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these + subroutine macros need have two variants; the higher levels of macros may be + the same whether strict or not. + + Normally, constant addresses which are the sum of a `symbol_ref' and an + integer are stored inside a `const' RTX to mark them as constant. + Therefore, there is no need to recognize such sums specifically as + legitimate addresses. Normally you would simply recognize any `const' as + legitimate. + + Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that + are not marked with `const'. It assumes that a naked `plus' indicates + indexing. If so, then you *must* reject such naked constant sums as + illegitimate addresses, so that none of them will be given to + `PRINT_OPERAND_ADDRESS'. + + On some machines, whether a symbolic address is legitimate depends on the + section that the address refers to. On these machines, define the macro + `ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and + then check for it here. When you see a `const', you will have to look + inside it to find the `symbol_ref' in order to determine the section. *Note + Assembler Format::. + + The best way to modify the name string is by adding text to the beginning, + with suitable punctuation to prevent any ambiguity. Allocate the new name + in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to + remove and decode the added text and output the name accordingly, and define + `STRIP_NAME_ENCODING' to access the original name string. + + You can check the information stored here into the `symbol_ref' in the + definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and + `PRINT_OPERAND_ADDRESS'. + + Return 0 if the address is not legitimate, 1 if the address would fit + in a short instruction, or 2 if the address would fit in a long + instruction. */ + +#define XREGNO_OK_FOR_BASE_P(REGNO, STRICT_P) \ +((STRICT_P) \ + ? REGNO_OK_FOR_BASE_P (REGNO) \ + : GPR_OR_PSEUDO_P (REGNO)) + +int +d30v_legitimate_address_p (mode_int, x, strict_p) + int mode_int; + rtx x; + int strict_p; +{ + enum machine_mode mode = (enum machine_mode) mode_int; + rtx x0, x1; + int ret = 0; + + switch (GET_CODE (x)) + { + default: + break; + + case SUBREG: + x = SUBREG_REG (x); + if (GET_CODE (x) != REG) + break; + + /* fall through */ + + case REG: + ret = XREGNO_OK_FOR_BASE_P (REGNO (x), strict_p); + break; + + case PLUS: + x0 = XEXP (x, 0); + x1 = XEXP (x, 1); + + if (GET_CODE (x0) == SUBREG) + x0 = SUBREG_REG (x0); + + if (GET_CODE (x0) == POST_INC || GET_CODE (x0) == POST_DEC) + x0 = XEXP (x0, 0); + + if (GET_CODE (x0) != REG || !XREGNO_OK_FOR_BASE_P (REGNO (x0), strict_p)) + break; + + switch (GET_CODE (x1)) + { + default: + break; + + case SUBREG: + x1 = SUBREG_REG (x1); + if (GET_CODE (x1) != REG) + break; + + /* fall through */ + + case REG: + ret = XREGNO_OK_FOR_BASE_P (REGNO (x1), strict_p); + break; + + case CONST_INT: + ret = (IN_RANGE_P (INTVAL (x1), -32, 31)) ? 1 : 2; + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST: + ret = 2; + break; + } + break; + + case CONST_INT: + ret = (IN_RANGE_P (INTVAL (x), -32, 31)) ? 1 : 2; + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST: + ret = 2; + break; + + case POST_INC: + case POST_DEC: + x0 = XEXP (x, 0); + if (GET_CODE (x0) == REG && XREGNO_OK_FOR_BASE_P (REGNO (x0), strict_p)) + ret = 1; + break; + } + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, mode = %s, result = %d, addresses are %sstrict\n", + GET_MODE_NAME (mode), ret, (strict_p) ? "" : "not "); + debug_rtx (x); + } + + return ret; +} + + +/* A C compound statement that attempts to replace X with a valid memory + address for an operand of mode MODE. WIN will be a C statement label + elsewhere in the code; the macro definition may use + + GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN); + + to avoid further processing if the address has become legitimate. + + X will always be the result of a call to `break_out_memory_refs', and OLDX + will be the operand that was given to that function to produce X. + + The code generated by this macro should not alter the substructure of X. If + it transforms X into a more legitimate form, it should assign X (which will + always be a C variable) a new value. + + It is not necessary for this macro to come up with a legitimate address. + The compiler has standard ways of doing so in all cases. In fact, it is + safe for this macro to do nothing. But often a machine-dependent strategy + can generate better code. */ + +Rtx +d30v_legitimize_address (x, oldx, mode_int, strict_p) + rtx x; + rtx oldx; + int mode_int; + int strict_p; +{ + enum machine_mode mode = (enum machine_mode)mode_int; + rtx ret = NULL_RTX; + + if (TARGET_DEBUG_ADDR) + { + if (ret) + { + fprintf (stderr, "\n========== LEGITIMIZE_ADDRESS, transformed:\n"); + debug_rtx (x); + fprintf (stderr, "\ninto:\n"); + debug_rtx (ret); + } + else + { + fprintf (stderr, "\n========== LEGITIMIZE_ADDRESS, did nothing with:\n"); + debug_rtx (x); + } + } + + return ret; +} + + +/* A C statement or compound statement with a conditional `goto LABEL;' + executed if memory address X (an RTX) can have different meanings depending + on the machine mode of the memory reference it is used for or if the address + is valid for some modes but not others. + + Autoincrement and autodecrement addresses typically have mode-dependent + effects because the amount of the increment or decrement is the size of the + operand being addressed. Some machines have other mode-dependent addresses. + Many RISC machines have no mode-dependent addresses. + + You may assume that ADDR is a valid address for the machine. */ + +int +d30v_mode_dependent_address_p (addr) + rtx addr; +{ + switch (GET_CODE (addr)) + { + default: + break; + + case POST_INC: + case POST_DEC: + return TRUE; + } + + return FALSE; +} + + +/* Generate the appropriate comparison code for a test. */ + +rtx +d30v_emit_comparison (test_int, result, arg1, arg2) + int test_int; + rtx result; + rtx arg1; + rtx arg2; +{ + enum rtx_code test = (enum rtx_code) test_int; + enum machine_mode mode = GET_MODE (arg1); + rtx rtx_test = gen_rtx (SET, VOIDmode, result, gen_rtx (test, CCmode, arg1, arg2)); + + if (mode == SImode + || (mode == DImode && (test == EQ || test == NE)) + || (mode == DImode && (test == LT || test == GE) + && GET_CODE (arg2) == CONST_INT && INTVAL (arg2) == 0)) + return rtx_test; + + else if (mode == DImode) + return gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + rtx_test, + gen_rtx (CLOBBER, VOIDmode, + gen_reg_rtx (CCmode)))); + + else + fatal_insn ("d30v_emit_comparison", rtx_test); +} + + +/* Return appropriate code to move 2 words. Since DImode registers must start + on even register numbers, there is no possibility of overlap. */ + +char * +d30v_move_2words (operands, insn) + rtx operands[]; + rtx insn; +{ + if (GET_CODE (operands[0]) == REG && GPR_P (REGNO (operands[0]))) + { + if (GET_CODE (operands[1]) == REG && GPR_P (REGNO (operands[1]))) + return "or %U0,%.,%U1\n\tor %L0,%.,%L1"; + + else if (GET_CODE (operands[1]) == REG && ACCUM_P (REGNO (operands[1]))) + return "mvfacc %L0,%1,%.\n\tmvfacc %U0,%1,32"; + + else if (GET_CODE (operands[1]) == MEM) + return "ld2w %0,%M1"; + + else if (GET_CODE (operands[1]) == CONST_INT + || GET_CODE (operands[1]) == CONST_DOUBLE) + return "or %U0,%.,%U1\n\tor %L0,%.,%L1"; + } + + else if (GET_CODE (operands[0]) == REG && ACCUM_P (REGNO (operands[0]))) + { + if (GET_CODE (operands[1]) == REG + && GPR_P (REGNO (operands[1]))) + return "mvtacc %0,%U1,%L1"; + + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) == 0) + return "mvtacc %0,%.,%."; + } + + else if (GET_CODE (operands[0]) == MEM + && GET_CODE (operands[1]) == REG + && GPR_P (REGNO (operands[1]))) + return "st2w %1,%M0"; + + fatal_insn ("Bad call to d30v_move_2words", insn); +} + + +/* Emit the code to do a conditional move instruction. Return FALSE + if the conditional move could not be executed. */ + +int +d30v_emit_cond_move (dest, test, true_value, false_value) + rtx dest; + rtx test; + rtx true_value; + rtx false_value; +{ + rtx br_reg; + enum machine_mode mode = GET_MODE (dest); + + if (GET_CODE (dest) == MEM) + { + if (!reg_or_0_operand (true_value, mode)) + return FALSE; + + if (!reg_or_0_operand (false_value, mode)) + return FALSE; + } + + br_reg = gen_reg_rtx (CCmode); + + /* Recognize simple sequences better done with mvfsys. */ + if ((true_value == const1_rtx && false_value == const0_rtx) + || (true_value == const0_rtx && false_value == const1_rtx + && GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT)) + { + enum rtx_code code = GET_CODE (test); + + if (true_value == const0_rtx) + code = reverse_condition (code); + + emit_insn (d30v_emit_comparison (code, br_reg, + d30v_compare_op0, d30v_compare_op1)); + + if (mode != SImode) + dest = gen_rtx (SUBREG, SImode, dest, 0); + + emit_insn (gen_rtx (SET, SImode, dest, + gen_rtx (EQ, SImode, br_reg, const1_rtx))); + return TRUE; + } + + emit_insn (d30v_emit_comparison (GET_CODE (test), br_reg, + d30v_compare_op0, d30v_compare_op1)); + + emit_insn (gen_rtx (SET, VOIDmode, + dest, + gen_rtx (IF_THEN_ELSE, mode, + gen_rtx (NE, CCmode, br_reg, const0_rtx), + true_value, + false_value))); + return TRUE; +} + + +/* Output a conditional move instruction + operands[0] is the destination + operands[1] is the NE test + operands[2] is f0 or f1 + operands[3] is the value to move if the test was true + operands[4] is the value to move if the test was false */ + +char * +d30v_cond_move (operands, insn, load, store) + rtx operands[]; + rtx insn; + char *load; + char *store; +{ + rtx dest = operands[0]; + enum machine_mode mode = GET_MODE (dest); + char buffer[80]; + + if (GET_CODE (dest) == REG) + { + /* Move value into register for false condition */ + switch (GET_CODE (operands[4])) + { + case REG: + if (REGNO (operands[4]) != REGNO (operands[0])) + output_asm_insn ("or%T1 %0,%.,%4", operands); + break; + + case MEM: + sprintf (buffer, "%s%%T1 %%0,%%M4", load); + output_asm_insn (buffer, operands); + break; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + output_asm_insn ("or%T1 %0,%.,%4", operands); + break; + + case CONST_DOUBLE: + output_asm_insn ("or%T1 %0,%.,%f4", operands); + break; + + default: + fatal_insn ("d30v_cond_move", insn); + } + + /* Move value into register for true condition */ + switch (GET_CODE (operands[3])) + { + case REG: + if (REGNO (operands[3]) != REGNO (operands[0])) + output_asm_insn ("or%F1 %0,%.,%3", operands); + break; + + case MEM: + sprintf (buffer, "%s%%F1 %%0,%%M3", load); + output_asm_insn (buffer, operands); + break; + + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + output_asm_insn ("or%F1 %0,%.,%3", operands); + break; + + case CONST_DOUBLE: + output_asm_insn ("or%F1 %0,%.,%f3", operands); + break; + + default: + fatal_insn ("d30v_cond_move", insn); + } + } + + else if (GET_CODE (dest) == MEM) + { + sprintf (buffer, "%s%%T1 %s,%M0", store, + (GET_CODE (operands[4]) == CONST_INT ? "%." : "%4")); + output_asm_insn (buffer, operands); + + sprintf (buffer, "%s%%F1 %s,%M0", store, + (GET_CODE (operands[3]) == CONST_INT ? "%." : "%3")); + output_asm_insn (buffer, operands); + } + + else + fatal_insn ("d30v_cond_move", insn); + + return ""; +} + + +/* 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. */ + +void +d30v_machine_dependent_reorg (insn) + rtx insn; +{ +} + + +/* A C statement (sans semicolon) to update the integer variable COST based on + the relationship between INSN that is dependent on DEP_INSN through the + dependence LINK. The default is to make no adjustment to COST. This can be + used for example to specify to the scheduler that an output- or + anti-dependence does not incur the same cost as a data-dependence. */ + +/* For the d30v, try to insure that the source operands for a load/store are + set 2 cycles before the memory reference. */ + +int +d30v_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + rtx set_dep = single_set (dep_insn); + rtx set_insn = single_set (insn); + + if (set_dep != NULL_RTX && set_insn != NULL_RTX + && GET_CODE (SET_DEST (set_dep)) == REG) + { + rtx reg = SET_DEST (set_dep); + rtx mem; + + if ((GET_CODE (mem = SET_SRC (set_insn)) == MEM + && reg_mentioned_p (reg, XEXP (mem, 0))) + || (GET_CODE (mem = SET_DEST (set_insn)) == MEM + && reg_mentioned_p (reg, XEXP (mem, 0)))) + { + return cost + ((HAIFA_P) ? 2 : 4); + } + } + + return cost; +} + + +/* Functions to save and restore d30v_return_addr_rtx. */ + +struct machine_function +{ + rtx ra_rtx; +}; + +static void +d30v_save_machine_status (p) + struct function *p; +{ + struct machine_function *machine = + (struct machine_function *) xmalloc (sizeof (struct machine_function)); + + p->machine = machine; + machine->ra_rtx = d30v_return_addr_rtx; +} + +static void +d30v_restore_machine_status (p) + struct function *p; +{ + struct machine_function *machine = p->machine; + + d30v_return_addr_rtx = machine->ra_rtx; + + free (machine); + p->machine = (struct machine_function *)0; +} + +/* Do anything needed before RTL is emitted for each function. */ + +void +d30v_init_expanders () +{ + d30v_return_addr_rtx = NULL_RTX; + d30v_eh_epilogue_sp_ofs = NULL_RTX; + + /* Arrange to save and restore machine status around nested functions. */ + save_machine_status = d30v_save_machine_status; + restore_machine_status = d30v_restore_machine_status; +} + +/* Find the current function's return address. + + ??? It would be better to arrange things such that if we would ordinarily + have been a leaf function and we didn't spill the hard reg that we + wouldn't have to save the register in the prolog. But it's not clear + how to get the right information at the right time. */ + +rtx +d30v_return_addr () +{ + rtx ret; + + if ((ret = d30v_return_addr_rtx) == NULL) + { + rtx init; + + d30v_return_addr_rtx = ret = gen_reg_rtx (Pmode); + + init = gen_rtx (SET, VOIDmode, ret, gen_rtx (REG, Pmode, GPR_LINK)); + push_topmost_sequence (); + emit_insn_after (init, get_insns ()); + pop_topmost_sequence (); + } + + return ret; +} + +/* END CYGNUS LOCAL -- meissner/d30v */ |