X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=gcc%2Fconfig%2Fd30v%2Fd30v.c;fp=gcc%2Fconfig%2Fd30v%2Fd30v.c;h=0000000000000000000000000000000000000000;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=4bfd4bb9976bb88ae0883d31b7d7f1abdb2af5a1;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/config/d30v/d30v.c b/gcc/config/d30v/d30v.c deleted file mode 100644 index 4bfd4bb9..00000000 --- a/gcc/config/d30v/d30v.c +++ /dev/null @@ -1,3609 +0,0 @@ -/* Definitions of target machine for Mitsubishi D30V. - Copyright (C) 1997, 1998, 1999, 2000, 2001 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 "tree.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" -#include "insn-config.h" -#include "conditions.h" -#include "output.h" -#include "insn-attr.h" -#include "flags.h" -#include "recog.h" -#include "expr.h" -#include "obstack.h" -#include "tm_p.h" -#include "except.h" -#include "function.h" -#include "toplev.h" -#include "integrate.h" -#include "ggc.h" -#include "target.h" -#include "target-def.h" - -static void d30v_print_operand_memory_reference PARAMS ((FILE *, rtx)); -static void d30v_build_long_insn PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT, - rtx, rtx)); -static void d30v_add_gc_roots PARAMS ((void)); -static void d30v_init_machine_status PARAMS ((struct function *)); -static void d30v_mark_machine_status PARAMS ((struct function *)); -static void d30v_free_machine_status PARAMS ((struct function *)); -static void d30v_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT)); -static void d30v_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); -static int d30v_adjust_cost PARAMS ((rtx, rtx, rtx, int)); -static int d30v_issue_rate PARAMS ((void)); - -/* 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; - -/* Cached value of d30v_stack_info */ -static d30v_stack_t *d30v_stack_cache = (d30v_stack_t *)0; - -/* Values of the -mbranch-cost=n string. */ -int d30v_branch_cost = D30V_DEFAULT_BRANCH_COST; -const char *d30v_branch_cost_string = (const char *)0; - -/* Values of the -mcond-exec=n string. */ -int d30v_cond_exec = D30V_DEFAULT_MAX_CONDITIONAL_EXECUTE; -const char *d30v_cond_exec_string = (const 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]; - -/* Initialize the GCC target structure. */ -#undef TARGET_ASM_ALIGNED_HI_OP -#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" -#undef TARGET_ASM_ALIGNED_SI_OP -#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" - -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE d30v_output_function_prologue -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE d30v_output_function_epilogue -#undef TARGET_SCHED_ADJUST_COST -#define TARGET_SCHED_ADJUST_COST d30v_adjust_cost -#undef TARGET_SCHED_ISSUE_RATE -#define TARGET_SCHED_ISSUE_RATE d30v_issue_rate - -struct gcc_target targetm = TARGET_INITIALIZER; - -/* 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 const char *const 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; - - d30v_add_gc_roots (); -} - - -/* Return true if a memory operand is a short memory operand. */ - -int -short_memory_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return FALSE; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - return (d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed) - == 1); -} - -/* Return true if a memory operand is a long operand. */ - -int -long_memory_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return FALSE; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - return (d30v_legitimate_address_p (mode, XEXP (op, 0), reload_completed) - == 2); -} - -/* Return true if a memory operand is valid for the D30V. */ - -int -d30v_memory_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return FALSE; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - return (d30v_legitimate_address_p (mode, 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) - rtx op; - enum machine_mode mode; -{ - rtx addr; - - 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, 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) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return FALSE; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (! d30v_legitimate_address_p (mode, 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) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) != MEM) - return FALSE; - - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (! d30v_legitimate_address_p (mode, 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - switch (GET_CODE (op)) - { - default: - break; - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG) - { - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - 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) - rtx op; - enum machine_mode mode; -{ - switch (GET_CODE (op)) - { - default: - break; - - case CONST_INT: - case SYMBOL_REF: - case LABEL_REF: - case CONST: - return TRUE; - - case SUBREG: - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - op = SUBREG_REG (op); - /* 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) - rtx op; - enum machine_mode mode; -{ - 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_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - op = SUBREG_REG (op); - /* 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) - rtx op; - enum machine_mode mode; -{ - switch (GET_CODE (op)) - { - default: - break; - - case SUBREG: - if (GET_CODE (SUBREG_REG (op)) != REG) - return register_operand (op, mode); - - op = SUBREG_REG (op); - /* 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, 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) - rtx op; - enum machine_mode mode; -{ - rtx subreg; - enum rtx_code code; - - 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; - - subreg = SUBREG_REG (op); - code = GET_CODE (subreg); - if (code == MEM) - return d30v_legitimate_address_p ((int)mode, XEXP (subreg, 0), - reload_completed); - - return (code == REG); - - 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, 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) - rtx op; - enum machine_mode mode; -{ - rtx subreg; - enum rtx_code code; - - switch (GET_CODE (op)) - { - default: - break; - - case SUBREG: - if (GET_MODE (op) != mode && mode != VOIDmode) - return FALSE; - - subreg = SUBREG_REG (op); - code = GET_CODE (subreg); - if (code == MEM) - return d30v_legitimate_address_p ((int)mode, XEXP (subreg, 0), - reload_completed); - - return (code == REG); - - 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, XEXP (op, 0), - reload_completed); - } - - return FALSE; -} - -/* Return true if operand is a signed 6 bit immediate. */ - -int -signed6_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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) - rtx op; - enum machine_mode mode; -{ - 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) - rtx op; - enum machine_mode mode; -{ - 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) - { - int regno = REGNO (x0); - if (!GPR_OR_PSEUDO_P (regno) && !BR_FLAG_P (regno)) - return FALSE; - } - /* Allow the optimizer to generate things like: - (if_then_else (ne (const_int 1) (const_int 0))) */ - else if (GET_CODE (x0) != CONST_INT) - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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)) - { - default: - break; - - 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - 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 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) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - /* 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) - rtx op; - enum machine_mode mode; -{ - 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 as the destination of - a conditional move */ - -int -cond_move_dest_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - rtx addr; - - if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode) - return FALSE; - - switch (GET_CODE (op)) - { - default: - break; - - case REG: - case SUBREG: - return gpr_operand (op, mode); - - /* 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 - && d30v_legitimate_address_p (mode, addr, reload_completed)); - } - - return FALSE; -} - -/* Return true if an operand is simple, suitable for use in a conditional move */ - -int -cond_move_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - rtx addr; - - if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode) - return FALSE; - - switch (GET_CODE (op)) - { - default: - break; - - 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 - && d30v_legitimate_address_p (mode, addr, reload_completed)); - } - - 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) - register rtx op; - enum machine_mode mode; -{ - if (mode != QImode && mode != HImode && mode != SImode && mode != SFmode) - return FALSE; - - switch (GET_CODE (op)) - { - default: - break; - - 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) - register rtx op; - enum machine_mode mode; -{ - 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) - register rtx op; - enum machine_mode mode; -{ - 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) - register rtx op; - enum machine_mode mode; -{ - 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 (current_function_profile) - 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 - + current_function_pretend_args_size), - (STACK_BOUNDARY / BITS_PER_UNIT)); - - info_ptr->save_offset = (info_ptr->total_size - - (current_function_pretend_args_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 - - (current_function_pretend_args_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)) - : "")); - - 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, type) - enum machine_mode mode; - tree type; -{ - int size = ((mode == BLKmode && type) - ? int_size_in_bytes (type) - : (int) 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, type, named, incoming) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; - int incoming ATTRIBUTE_UNUSED; -{ - int size = ((mode == BLKmode && type) - ? int_size_in_bytes (type) - : (int) 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, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named ATTRIBUTE_UNUSED; -{ - int bytes = ((mode == BLKmode) - ? int_size_in_bytes (type) - : (int) 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, type, named) - CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; - enum machine_mode mode; - tree type; - int named ATTRIBUTE_UNUSED; -{ - 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, type, named) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int named; -{ - int bytes = ((mode == BLKmode) - ? int_size_in_bytes (type) - : (int) 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'. - - If this macro is not defined, the compiler will output an ordinary call to - the library function `__builtin_saveregs'. */ - -rtx -d30v_expand_builtin_saveregs () -{ - 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, type, pretend_size, second_time) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type ATTRIBUTE_UNUSED; - int *pretend_size ATTRIBUTE_UNUSED; - int second_time; -{ - if (TARGET_DEBUG_ARG) - fprintf (stderr, - "setup_vararg: words = %2d, mode = %4s, second_time = %d\n", - *cum, GET_MODE_NAME (mode), second_time); -} - - -/* Create the va_list data type. */ - -tree -d30v_build_va_list () -{ - tree f_arg_ptr, f_arg_num, record, type_decl; - tree int_type_node; - - record = make_lang_type (RECORD_TYPE); - type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); - int_type_node = make_signed_type (INT_TYPE_SIZE); - - f_arg_ptr = build_decl (FIELD_DECL, get_identifier ("__va_arg_ptr"), - ptr_type_node); - f_arg_num = build_decl (FIELD_DECL, get_identifier ("__va_arg_num"), - int_type_node); - - DECL_FIELD_CONTEXT (f_arg_ptr) = record; - DECL_FIELD_CONTEXT (f_arg_num) = record; - - TREE_CHAIN (record) = type_decl; - TYPE_NAME (record) = type_decl; - TYPE_FIELDS (record) = f_arg_ptr; - TREE_CHAIN (f_arg_ptr) = f_arg_num; - - layout_type (record); - - /* The correct type is an array type of one element. */ - return build_array_type (record, build_index_type (size_zero_node)); -} - - -/* Expand __builtin_va_start to do the va_start macro. */ - -void -d30v_expand_builtin_va_start (stdarg_p, valist, nextarg) - int stdarg_p ATTRIBUTE_UNUSED; - tree valist; - rtx nextarg ATTRIBUTE_UNUSED; -{ - HOST_WIDE_INT words; - tree f_arg_ptr, f_arg_num; - tree arg_ptr, arg_num, saveregs, t; - - f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); - f_arg_num = TREE_CHAIN (f_arg_ptr); - - valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); - arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr); - arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num); - - words = current_function_args_info; /* __builtin_args_info (0) */ - - /* (AP)->__va_arg_ptr = (int *) __builtin_saveregs (); */ - saveregs = make_tree (TREE_TYPE (arg_ptr), d30v_expand_builtin_saveregs ()); - t = build (MODIFY_EXPR, TREE_TYPE (arg_ptr), arg_ptr, saveregs); - TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - - /* (AP)->__va_arg_num = __builtin_args_info (0) - 2; */ - t = build (PLUS_EXPR, TREE_TYPE (arg_num), build_int_2 (words, 0), - build_int_2 (-GPR_ARG_FIRST, 0)); - t = build (MODIFY_EXPR, TREE_TYPE (arg_num), arg_num, t); - TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); -} - - -/* Expand __builtin_va_arg to do the va_arg macro. */ - -rtx -d30v_expand_builtin_va_arg(valist, type) - tree valist; - tree type; -{ - tree f_arg_ptr, f_arg_num; - tree arg_ptr, arg_num, t, ptr; - int num, size; - rtx lab_false, ptr_rtx, r; - - f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); - f_arg_num = TREE_CHAIN (f_arg_ptr); - - valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); - arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr); - arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num); - - size = int_size_in_bytes (type); - - lab_false = gen_label_rtx (); - ptr_rtx = gen_reg_rtx (Pmode); - - /* if (sizeof (TYPE) > 4 && ((AP)->__va_arg_num & 1) != 0) - (AP)->__va_arg_num++; */ - - if (size > UNITS_PER_WORD) - { - t = build (BIT_AND_EXPR, TREE_TYPE (arg_num), arg_num, - build_int_2 (1, 0)); - - emit_cmp_and_jump_insns (expand_expr (t, NULL_RTX, QImode, EXPAND_NORMAL), - GEN_INT (0), EQ, const1_rtx, QImode, 1, - lab_false); - - t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num, - build_int_2 (1, 0)); - TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - - emit_label (lab_false); - } - - - /* __ptr = (TYPE *)(((char *)(void *)((AP)->__va_arg_ptr - + (AP)->__va_arg_num))); */ - - t = build (MULT_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (4, 0)); - t = build (PLUS_EXPR, ptr_type_node, arg_ptr, t); - - /* if (sizeof (TYPE) < 4) - __ptr = (void *)__ptr + 4 - sizeof (TYPE); */ - - if (size < UNITS_PER_WORD) - t = build (PLUS_EXPR, ptr_type_node, t, - build_int_2 (UNITS_PER_WORD - size, 0)); - - TREE_SIDE_EFFECTS (t) = 1; - - ptr = build1 (NOP_EXPR, build_pointer_type (type), t); - t = build (MODIFY_EXPR, type, ptr, t); - - r = expand_expr (t, ptr_rtx, Pmode, EXPAND_NORMAL); - if (r != ptr_rtx) - emit_move_insn (ptr_rtx, r); - - - /* (AP)->__va_arg_num += (sizeof (TYPE) + 3) / 4; */ - num = (size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; - t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num, - build_int_2 (num, 0)); - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - - return ptr_rtx; -} - -/* Generate the assembly code for function entry. FILE is a stdio - stream to output the code to. SIZE is an int: how many units of - temporary storage to allocate. - - Refer to the array `regs_ever_live' to determine which registers to - save; `regs_ever_live[I]' is nonzero if register number I is ever - used in the function. This function is responsible for knowing - which registers should not be saved even if used. */ - -static void -d30v_output_function_prologue (stream, size) - FILE *stream ATTRIBUTE_UNUSED; - HOST_WIDE_INT size ATTRIBUTE_UNUSED; -{ - /* For the d30v, move all of the prologue processing into separate - insns. */ -} - - -/* 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 output_function_prologue (), 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 ()); -} - - -/* This function generates the assembly code for function exit. - Args are as for output_function_prologue (). - - The function epilogue should not depend on the current stack - pointer! It should use the frame pointer only. This is mandatory - because of alloca; we also take advantage of it to omit stack - adjustments before returning. */ - -static void -d30v_output_function_epilogue (stream, size) - FILE *stream ATTRIBUTE_UNUSED; - HOST_WIDE_INT size ATTRIBUTE_UNUSED; -{ - /* 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. */ - d30v_stack_cache = (d30v_stack_t *)0; /* reset stack cache */ -} - - - -/* Called after register allocation to add any instructions needed for - the epilogue. Using an epilogue insn is favored compared to putting - all of the instructions in output_function_prologue(), 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; - 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. */ - extra_stack = 0; - for (i = GPR_FIRST; i <= GPR_LAST; i++) - if (info->save_p[i] == 1) - { - if (cfun->machine->eh_epilogue_sp_ofs && i == GPR_LINK) - extra_stack = 4; - else - { - if (extra_stack) - { - emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack))); - extra_stack = 0; - } - 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. */ - if ((info->memrefs_1word & 1) != 0) - extra_stack += UNITS_PER_WORD; - extra_stack += current_function_pretend_args_size + info->varargs_size; - - if (extra_stack) - { - if (cfun->machine->eh_epilogue_sp_ofs) - emit_insn (gen_addsi3 (cfun->machine->eh_epilogue_sp_ofs, - cfun->machine->eh_epilogue_sp_ofs, - GEN_INT (extra_stack))); - else - emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack))); - } - if (cfun->machine->eh_epilogue_sp_ofs) - emit_insn (gen_addsi3 (sp, sp, cfun->machine->eh_epilogue_sp_ofs)); - - /* Now emit the return instruction. */ - emit_jump_insn (gen_rtx_RETURN (VOIDmode)); -} - - -/* 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 ATTRIBUTE_UNUSED; -{ - 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: - if (GET_CODE (SUBREG_REG (value)) != REG) - abort (); - offset = subreg_regno_offset (REGNO (SUBREG_REG (value)), - GET_MODE (SUBREG_REG (value)), - SUBREG_BYTE (value), - GET_MODE (value)); - value = SUBREG_REG (value); - - /* 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 - { - const char *suffix = ""; - int offset0 = 0; - - if (GET_CODE (x0) == SUBREG) - { - offset0 = subreg_regno_offset (REGNO (SUBREG_REG (x0)), - GET_MODE (SUBREG_REG (x0)), - SUBREG_BYTE (x0), - GET_MODE (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_regno_offset (REGNO (SUBREG_REG (x1)), - GET_MODE (SUBREG_REG (x1)), - SUBREG_BYTE (x1), - GET_MODE (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 ':': /* Output the condition for the current insn. */ - x = current_insn_predicate; - if (x == NULL_RTX) - break; - letter = 'T'; - /* FALLTHRU */ - - 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 'z': /* If arg is 0 or 0.0, print r0, otherwise print as normal */ - if ((GET_CODE (x) == CONST_INT && INTVAL (x) == 0) - || (GET_CODE (x) == CONST_DOUBLE && CONST_DOUBLE_LOW (x) == 0 - && CONST_DOUBLE_HIGH (x) == 0)) - { - fputs (reg_names[GPR_FIRST], stream); - return; - } - - /* fall through */ - - 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, - jmp */ - 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, x, strict_p) - enum machine_mode mode; - rtx x; - int strict_p; -{ - 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, strict_p) - rtx x; - rtx oldx ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; - int strict_p ATTRIBUTE_UNUSED; -{ - 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. */ - -const 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); - int two_mem_moves_p = FALSE; - - if (GET_CODE (dest) == MEM) - { - if (!reg_or_0_operand (true_value, mode)) - return FALSE; - - if (rtx_equal_p (dest, false_value)) - two_mem_moves_p = TRUE; - - else if (!reg_or_0_operand (false_value, mode)) - return FALSE; - } - - /* We used to try to optimize setting 0/1 by using mvfsys, but that turns out - to be slower than just doing the conditional execution. */ - - br_reg = gen_reg_rtx (CCmode); - emit_insn (d30v_emit_comparison (GET_CODE (test), br_reg, - d30v_compare_op0, d30v_compare_op1)); - - if (!two_mem_moves_p) - 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))); - else - { - /* Emit conditional stores as two separate stores. This avoids a problem - where you have a conditional store, and one of the arms of the - conditional store is spilled to memory. */ - emit_insn (gen_rtx_SET (VOIDmode, - dest, - gen_rtx_IF_THEN_ELSE (mode, - gen_rtx_NE (CCmode, br_reg, - const0_rtx), - true_value, - dest))); - - emit_insn (gen_rtx_SET (VOIDmode, - dest, - gen_rtx_IF_THEN_ELSE (mode, - gen_rtx_EQ (CCmode, br_reg, - const0_rtx), - false_value, - dest))); - - } - - return TRUE; -} - - -/* 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 ATTRIBUTE_UNUSED; -{ -} - - -/* 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. */ - -static int -d30v_adjust_cost (insn, link, dep_insn, cost) - rtx insn; - rtx link ATTRIBUTE_UNUSED; - 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 + 2; - } - } - - return cost; -} - -/* Function which returns the number of insns that can be - scheduled in the same machine cycle. This must be constant - over an entire compilation. The default is 1. */ -static int -d30v_issue_rate () -{ - return 2; -} - - -/* Routine to allocate, mark and free a per-function, - machine specific structure. */ - -static void -d30v_init_machine_status (p) - struct function *p; -{ - p->machine = - (machine_function *) xcalloc (1, sizeof (machine_function)); -} - -static void -d30v_mark_machine_status (p) - struct function * p; -{ - if (p->machine == NULL) - return; - - ggc_mark_rtx (p->machine->eh_epilogue_sp_ofs); -} - -static void -d30v_free_machine_status (p) - struct function *p; -{ - struct machine_function *machine = p->machine; - - if (machine == NULL) - return; - - free (machine); - p->machine = NULL; -} - -/* Do anything needed before RTL is emitted for each function. */ - -void -d30v_init_expanders () -{ - /* Arrange to save and restore machine status around nested functions. */ - init_machine_status = d30v_init_machine_status; - mark_machine_status = d30v_mark_machine_status; - free_machine_status = d30v_free_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 () -{ - return get_hard_reg_initial_val (Pmode, GPR_LINK); -} - -/* Called to register all of our global variables with the garbage - collector. */ - -static void -d30v_add_gc_roots () -{ - ggc_add_rtx_root (&d30v_compare_op0, 1); - ggc_add_rtx_root (&d30v_compare_op1, 1); -}