summaryrefslogtreecommitdiff
path: root/gcc/config/d30v/d30v.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/d30v/d30v.c')
-rwxr-xr-xgcc/config/d30v/d30v.c3514
1 files changed, 0 insertions, 3514 deletions
diff --git a/gcc/config/d30v/d30v.c b/gcc/config/d30v/d30v.c
deleted file mode 100755
index 8e70c4a..0000000
--- a/gcc/config/d30v/d30v.c
+++ /dev/null
@@ -1,3514 +0,0 @@
-/* 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 */