From c83c42d9a87921d377f82e8588e6d86d61162247 Mon Sep 17 00:00:00 2001 From: Richard Kenner Date: Fri, 9 Sep 1994 13:28:38 -0400 Subject: [PATCH] Initial revision From-SVN: r8049 --- gcc/config/dsp16xx/dsp16xx.c | 2226 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2226 insertions(+) create mode 100644 gcc/config/dsp16xx/dsp16xx.c diff --git a/gcc/config/dsp16xx/dsp16xx.c b/gcc/config/dsp16xx/dsp16xx.c new file mode 100644 index 0000000..08e8653 --- /dev/null +++ b/gcc/config/dsp16xx/dsp16xx.c @@ -0,0 +1,2226 @@ +/* Subroutines for assembler code output on the DSP1610. + Copyright (C) 1994 Free Software Foundation, Inc. + Contributed by Michael Collison (collison@world.std.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Some output-actions in dsp1600.md need these. */ +#include +#include "config.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "tree.h" +#include "expr.h" +#include "flags.h" + +char *text_seg_name; +char *rsect_text; +char *data_seg_name; +char *rsect_data; +char *bss_seg_name; +char *rsect_bss; +char *const_seg_name; +char *rsect_const; + +char *chip_name; +char *save_chip_name; + +/* Save the operands of a compare. The 16xx has not lt or gt, so + in these cases we swap the operands and reverse the condition */ + +rtx dsp16xx_compare_op0; +rtx dsp16xx_compare_op1; +struct rtx_def *(*dsp16xx_compare_gen)(); + +static char *fp; +static char *sp; +static char *rr; +static char *a1h; + +struct dsp16xx_frame_info current_frame_info; +struct dsp16xx_frame_info zero_frame_info; + +rtx dsp16xx_addhf3_libcall = (rtx) 0; +rtx dsp16xx_subhf3_libcall = (rtx) 0; +rtx dsp16xx_mulhf3_libcall = (rtx) 0; +rtx dsp16xx_divhf3_libcall = (rtx) 0; +rtx dsp16xx_cmphf3_libcall = (rtx) 0; +rtx dsp16xx_fixhfhi2_libcall = (rtx) 0; +rtx dsp16xx_floathihf2_libcall = (rtx) 0; +rtx dsp16xx_neghf2_libcall = (rtx) 0; + +rtx dsp16xx_mulhi3_libcall = (rtx) 0; +rtx dsp16xx_udivqi3_libcall = (rtx) 0; +rtx dsp16xx_udivhi3_libcall = (rtx) 0; +rtx dsp16xx_divqi3_libcall = (rtx) 0; +rtx dsp16xx_divhi3_libcall = (rtx) 0; +rtx dsp16xx_modqi3_libcall = (rtx) 0; +rtx dsp16xx_modhi3_libcall = (rtx) 0; +rtx dsp16xx_umodqi3_libcall = (rtx) 0; +rtx dsp16xx_umodhi3_libcall = (rtx) 0; +rtx dsp16xx_ashrhi3_libcall = (rtx) 0; +rtx dsp16xx_ashlhi3_libcall = (rtx) 0; +rtx dsp16xx_ucmphi2_libcall = (rtx) 0; +rtx dsp16xx_lshrhi3_libcall = (rtx) 0; + +char *himode_reg_name[] = HIMODE_REGISTER_NAMES; + +#define SHIFT_INDEX_1 0 +#define SHIFT_INDEX_4 1 +#define SHIFT_INDEX_8 2 +#define SHIFT_INDEX_16 3 + +static char *ashift_right_asm[] = +{ + "%0=%0>>1", + "%0=%0>>4", + "%0=%0>>8", + "%0=%0>>16" +}; + +static char *ashift_right_asm_first[] = +{ + "%0=%1>>1", + "%0=%1>>4", + "%0=%1>>8", + "%0=%1>>16" +}; + +static char *ashift_left_asm[] = +{ + "%0=%0<<1", + "%0=%0<<4", + "%0=%0<<8", + "%0=%0<<16" +}; + +static char *ashift_left_asm_first[] = +{ + "%0=%1<<1", + "%0=%1<<4", + "%0=%1<<8", + "%0=%1<<16" +}; + +static char *lshift_right_asm[] = +{ + "%0=%0>>1\n\t%0=%b0&0x7fff", + "%0=%0>>4\n\t%0=%b0&0x0fff", + "%0=%0>>8\n\t%0=%b0&0x00ff", + "%0=%0>>16\n\t%0=%b0&0x0000" +}; + +static char *lshift_right_asm_first[] = +{ + "%0=%1>>1\n\t%0=%b0&0x7fff", + "%0=%1>>4\n\t%0=%b0&0x0fff", + "%0=%1>>8\n\t%0=%b0&0x00ff", + "%0=%1>>16\n\t%0=%b0&0x0000" +}; + +int +hard_regno_mode_ok (regno, mode) +int regno; +enum machine_mode mode; +{ + switch ((int) mode) + { + case VOIDmode: + return 1; + + /* + We can't use the c0-c2 for QImode, since they are only + 8 bits in length */ + + case QImode: + if (regno != REG_C0 && regno != REG_C1 && regno != REG_C2) + return 1; + else + return 0; + + /* We only allow a0, a1, y, and p to be allocated for 32-bit modes. + Additionally we allow the virtual ybase registers to be used for 32-bit + modes. */ + + case HFmode: + case SFmode: + case DFmode: + case XFmode: + case HImode: + case SImode: + case DImode: + if (regno == REG_A0 || regno == REG_A1 || regno == REG_Y || regno == REG_PROD + || (IS_YBASE_REGISTER_WINDOW(regno) && ((regno & 1) == 0))) + return 1; + else + return 0; + + default: + return 0; + } +} + +enum reg_class +dsp16xx_reg_class_from_letter (c) +int c; +{ + switch (c) + { + case 'A': + return ACCUM_REGS; + + case 'h': + return ACCUM_HIGH_REGS; + + case 'j': + return A0H_REG; + + case 'k': + return A0L_REG; + + case 'q': + return A1H_REG; + + case 'u': + return A1L_REG; + + case 'x': + return X_REG; + + case 'y': + return YH_REG; + + case 'z': + return YL_REG; + + case 't': + return P_REG; + + case 'Z': + return Y_OR_P_REGS; + + case 'd': + return ACCUM_Y_OR_P_REGS; + + case 'C': + return NO_FRAME_Y_ADDR_REGS; + + case 'a': + return Y_ADDR_REGS; + + case 'B': + return (TARGET_BMU ? BMU_REGS : NO_REGS); + + case 'Y': + return YBASE_VIRT_REGS; + + case 'v': + return PH_REG; + + case 'w': + return PL_REG; + + case 'W': + return J_REG; + + case 'e': + return YBASE_ELIGIBLE_REGS; + + case 'b': + return ACCUM_LOW_REGS; + + case 'c': + return NON_YBASE_REGS; + + case 'f': + return Y_REG; + + case 'D': + return SLOW_MEM_LOAD_REGS; + + default: + fatal ("Illegal register class letter %c", c); + return NO_REGS; + } +} +/* Return the class number of the smallest class containing + reg number REGNO. */ + +int +regno_reg_class(regno) +int regno; +{ + switch (regno) + { + case REG_A0L: + return (int) A0L_REG; + case REG_A1L: + return (int) A1L_REG; + + case REG_A0: + return (int) A0H_REG; + case REG_A1: + return (int) A1H_REG; + + case REG_X: + return (int) X_REG; + + case REG_Y: + return (int) YH_REG; + case REG_YL: + return (int) YL_REG; + + case REG_PROD: + return (int) PH_REG; + case REG_PRODL: + return (int) PL_REG; + + case REG_R0: case REG_R1: case REG_R2: case REG_R3: + return (int) Y_ADDR_REGS; + + case REG_J: + return (int) J_REG; + case REG_K: + return (int) GENERAL_REGS; + + case REG_YBASE: + return (int) GENERAL_REGS; + + case REG_PT: + return (int) GENERAL_REGS; + + case REG_AR0: case REG_AR1: case REG_AR2: case REG_AR3: + return (int) BMU_REGS; + + case REG_C0: case REG_C1: case REG_C2: + return (int) GENERAL_REGS; + + case REG_PR: + return (int) GENERAL_REGS; + + case REG_RB: + return (int) GENERAL_REGS; + + case REG_YBASE0: case REG_YBASE1: case REG_YBASE2: case REG_YBASE3: + case REG_YBASE4: case REG_YBASE5: case REG_YBASE6: case REG_YBASE7: + case REG_YBASE8: case REG_YBASE9: case REG_YBASE10: case REG_YBASE11: + case REG_YBASE12: case REG_YBASE13: case REG_YBASE14: case REG_YBASE15: + case REG_YBASE16: case REG_YBASE17: case REG_YBASE18: case REG_YBASE19: + case REG_YBASE20: case REG_YBASE21: case REG_YBASE22: case REG_YBASE23: + case REG_YBASE24: case REG_YBASE25: case REG_YBASE26: case REG_YBASE27: + case REG_YBASE28: case REG_YBASE29: case REG_YBASE30: case REG_YBASE31: + return (int) YBASE_VIRT_REGS; + + default: + return (int) NO_REGS; + } +} + +/* A C expression for the maximum number of consecutive registers of class CLASS + needed to hold a value of mode MODE */ + +int +class_max_nregs(class, mode) +enum reg_class class; +enum machine_mode mode; +{ + return (GET_MODE_SIZE(mode)); +} + +enum reg_class +limit_reload_class (mode, class) +enum machine_mode mode; +enum reg_class class; +{ + switch ((int) class) + { + case NO_REGS: + case A0H_REG: + case A0L_REG: + case A0_REG: + case A1H_REG: + return class; + + case ACCUM_HIGH_REGS: + fatal ("ACCUM_HIGH_REGS class in limit_reload_class"); + + case A1L_REG: + case ACCUM_LOW_REGS: + case A1_REG: + return class; + + case ACCUM_REGS: + if (GET_MODE_SIZE(mode) == 1) + return ACCUM_LOW_REGS; + else + return class; + + case X_REG: + case X_OR_ACCUM_LOW_REGS: + return class; + + case X_OR_ACCUM_REGS: + if (GET_MODE_SIZE(mode) == 1) + return X_OR_ACCUM_LOW_REGS; + else + return class; + + case YH_REG: + return class; + + case YH_OR_ACCUM_HIGH_REGS: + fatal ("YH_OR_ACCUM_HIGH_REGS found in limit_reload_class"); + + case X_OR_YH_REGS: + return class; + + case YL_REG: + /* Register 'yl' is illegal for QImode, so we should never + see it. */ + + fatal ("YL found in limit_reload_class"); + + case YL_OR_ACCUM_LOW_REGS: + case X_OR_YL_REGS: + return class; + + case Y_REG: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return YH_REG; + + case ACCUM_OR_Y_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return YL_OR_ACCUM_LOW_REGS; + + case PH_REG: + case X_OR_PH_REGS: + case PL_REG: + case PL_OR_ACCUM_LOW_REGS: + case X_OR_PL_REGS: + return class; + + case P_REG: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return PL_REG; + + case ACCUM_OR_P_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return PL_OR_ACCUM_LOW_REGS; + + case YL_OR_P_REGS: + case ACCUM_LOW_OR_YL_OR_P_REGS: + return class; + + case Y_OR_P_REGS: + return class; + + case ACCUM_Y_OR_P_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return ACCUM_LOW_OR_YL_OR_P_REGS; + + case NO_FRAME_Y_ADDR_REGS: + case Y_ADDR_REGS: + case ACCUM_LOW_OR_Y_ADDR_REGS: + return class; + + case ACCUM_OR_Y_ADDR_REGS: + if (GET_MODE_SIZE(mode) > 1) + return ACCUM_REGS; + else + return ACCUM_LOW_OR_Y_ADDR_REGS; + + case X_OR_Y_ADDR_REGS: + return class; + + case Y_OR_Y_ADDR_REGS: + case P_OR_Y_ADDR_REGS: + case NON_HIGH_YBASE_ELIGIBLE_REGS: + + case J_REG: + return class; + + case YBASE_ELIGIBLE_REGS: + if (GET_MODE_SIZE(mode) > 1) + return ACCUM_Y_P_OR_YBASE_REGS; + else + return NON_HIGH_YBASE_ELIGIBLE_REGS; + + case J_OR_DAU_16_BIT_REGS: + if (GET_MODE_SIZE(mode) == 1) + return J_REG; + else + return class; + + case BMU_REGS: + case NOHIGH_NON_ADDR_REGS: + return class; + + case NON_ADDR_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return NOHIGH_NON_ADDR_REGS; + + case NOHIGH_NON_YBASE_REGS: + return class; + + case NON_YBASE_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return NOHIGH_NON_YBASE_REGS; + + case YBASE_VIRT_REGS: + case ACCUM_LOW_OR_YBASE_REGS: + return class; + + case ACCUM_OR_YBASE_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return ACCUM_LOW_OR_YBASE_REGS; + + case X_OR_YBASE_REGS: + return class; + + case Y_OR_YBASE_REGS: + case ACCUM_LOW_YL_PL_OR_YBASE_REGS: + case P_OR_YBASE_REGS: + return class; + + case ACCUM_Y_P_OR_YBASE_REGS: + return ACCUM_LOW_YL_PL_OR_YBASE_REGS; + + case Y_ADDR_OR_YBASE_REGS: + case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS: + return class; + + case YBASE_OR_YBASE_ELIGIBLE_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS; + + case NO_HIGH_ALL_REGS: + return class; + + case ALL_REGS: + if (GET_MODE_SIZE(mode) > 1) + return class; + else + return NO_HIGH_ALL_REGS; + + default: + return class; + } +} + +int +dsp16xx_register_move_cost (from, to) +enum reg_class from, to; +{ +#if 0 + if (from == NO_REGS || to == NO_REGS || (from == to)) + return 2; +#endif + + if (from == A0H_REG || from == A0L_REG || from == A0_REG || + from == A1H_REG || from == ACCUM_HIGH_REGS || from == A1L_REG || + from == ACCUM_LOW_REGS || from == A1_REG || from == ACCUM_REGS) + { + if (to == Y_REG || to == P_REG) + return 4; + else + return 2; + } + + if (to == A0H_REG || to == A0L_REG || to == A0_REG || + to == A1H_REG || to == ACCUM_HIGH_REGS || to == A1L_REG || + to == ACCUM_LOW_REGS || to == A1_REG || to == ACCUM_REGS) + { + return 2; + } + +#if 0 + if (from == YBASE_VIRT_REGS) + { + if (to == X_REG || to == YH_REG || to == YL_REG || + to == Y_REG || to == PL_REG || to == PH_REG || + to == P_REG || to == Y_ADDR_REGS || to == YBASE_ELIGIBLE_REGS || + to == Y_OR_P_REGS) + { + return 2; + } + else + return 4; + } + + if (to == YBASE_VIRT_REGS) + { + if (from == X_REG || from == YH_REG || from == YL_REG || + from == Y_REG || from == PL_REG || from == PH_REG || + from == P_REG || from == Y_ADDR_REGS || from == YBASE_ELIGIBLE_REGS || + from == Y_OR_P_REGS) + { + return 2; + } + else + return 4; + } +#endif + return 4; +} + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS; but on some machines + in some cases it is preferable to use a more restrictive class. + Also, we must ensure that a PLUS is reloaded either + into an accumulator or an address register. */ + +enum reg_class +preferred_reload_class (x, class) + rtx x; + enum reg_class class; +{ + /* The ybase registers cannot have constants copied directly + to them. */ + + if (CONSTANT_P (x)) + { + if (class == ALL_REGS) + return NON_YBASE_REGS; + } + + if (class == ALL_REGS && REG_P (x) && !TARGET_RESERVE_YBASE + && IS_YBASE_REGISTER_WINDOW (REGNO(x))) + return YBASE_ELIGIBLE_REGS; + + if (GET_CODE (x) == PLUS) + { + if (GET_MODE (x) == QImode + && REG_P (XEXP (x,0)) + && (XEXP (x,0) == frame_pointer_rtx + || XEXP (x,0) == stack_pointer_rtx) + && (GET_CODE (XEXP (x,1)) == CONST_INT)) + { + if (class == ACCUM_HIGH_REGS) + return class; + + if (reg_class_subset_p (ACCUM_HIGH_REGS, class)) + return ACCUM_HIGH_REGS; + + /* We will use accumulator 'a1l' for reloading a + PLUS. We can only use one accumulator because + 'reload_inqi' only allows one alternative to be + used. */ + + else if (class == ACCUM_LOW_REGS) + return A1L_REG; + else if (class == A0L_REG) + return NO_REGS; + else + return class; + } + + if (class == NON_YBASE_REGS || class == YBASE_ELIGIBLE_REGS) + return Y_ADDR_REGS; + else + return class; + } + else if (GET_CODE (x) == MEM) + { + if (class == ALL_REGS) + { +#if 0 + if (GET_MODE(x) == HImode) + return NO_ACCUM_NON_YBASE_REGS; + else +#endif + return NON_YBASE_REGS; + } + else + return class; + } + else + return class; +} + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ + +enum reg_class +secondary_reload_class (class, mode, in) + enum reg_class class; + enum machine_mode mode; + rtx in; +{ + int regno = -1; + + if (GET_CODE (in) == REG || GET_CODE (in) == SUBREG) + regno = true_regnum (in); + + if (class == ACCUM_HIGH_REGS + || class == ACCUM_LOW_REGS + || class == A1L_REG + || class == A0L_REG + || class == A1H_REG + || class == A0H_REG) + { + if (GET_CODE (in) == PLUS && mode == QImode) + { + rtx addr0 = XEXP (in, 0); + rtx addr1 = XEXP (in, 1); + + /* If we are reloading a plus (reg:QI) (reg:QI) + we need an additional register. */ + if (REG_P (addr0) && REG_P (addr1)) + return NO_REGS; + } + } + + /* We can place anything into ACCUM_REGS and can put ACCUM_REGS + into anything. */ + + if ((class == ACCUM_REGS || class == ACCUM_HIGH_REGS || + class == ACCUM_LOW_REGS || class == A0H_REG || class == A0L_REG || + class == A1H_REG || class == A1_REG) || + (regno >= REG_A0 && regno < REG_A1L + 1)) + return NO_REGS; + + /* We can copy the ybase registers into: + r0-r3, a0-a1, y, p, & x or the union of + any of these. */ + + if (!TARGET_RESERVE_YBASE && IS_YBASE_REGISTER_WINDOW(regno)) + { + switch ((int) class) + { + case (int) X_REG: + case (int) X_OR_ACCUM_LOW_REGS: + case (int) X_OR_ACCUM_REGS: + case (int) YH_REG: + case (int) YH_OR_ACCUM_HIGH_REGS: + case (int) X_OR_YH_REGS: + case (int) YL_REG: + case (int) YL_OR_ACCUM_LOW_REGS: + case (int) X_OR_Y_REGS: + case (int) X_OR_YL_REGS: + case (int) Y_REG: + case (int) ACCUM_OR_Y_REGS: + case (int) PH_REG: + case (int) X_OR_PH_REGS: + case (int) PL_REG: + case (int) PL_OR_ACCUM_LOW_REGS: + case (int) X_OR_PL_REGS: + case (int) YL_OR_PL_OR_ACCUM_LOW_REGS: + case (int) P_REG: + case (int) ACCUM_OR_P_REGS: + case (int) YL_OR_P_REGS: + case (int) ACCUM_LOW_OR_YL_OR_P_REGS: + case (int) Y_OR_P_REGS: + case (int) ACCUM_Y_OR_P_REGS: + case (int) Y_ADDR_REGS: + case (int) ACCUM_LOW_OR_Y_ADDR_REGS: + case (int) ACCUM_OR_Y_ADDR_REGS: + case (int) X_OR_Y_ADDR_REGS: + case (int) Y_OR_Y_ADDR_REGS: + case (int) P_OR_Y_ADDR_REGS: + case (int) YBASE_ELIGIBLE_REGS: + return NO_REGS; + + default: + return ACCUM_HIGH_REGS; + } + } + + /* We can copy r0-r3, a0-a1, y, & p + directly to the ybase registers. In addition + we can use any of the ybase virtual registers + as the secondary reload registers when copying + between any of these registers. */ + + if (!TARGET_RESERVE_YBASE && regno != -1) + { + switch (regno) + { + case REG_A0: + case REG_A0L: + case REG_A1: + case REG_A1L: + case REG_X: + case REG_Y: + case REG_YL: + case REG_PROD: + case REG_PRODL: + case REG_R0: + case REG_R1: + case REG_R2: + case REG_R3: + if (class == YBASE_VIRT_REGS) + return NO_REGS; + else + { + switch ((int) class) + { + case (int) X_REG: + case (int) X_OR_ACCUM_LOW_REGS: + case (int) X_OR_ACCUM_REGS: + case (int) YH_REG: + case (int) YH_OR_ACCUM_HIGH_REGS: + case (int) X_OR_YH_REGS: + case (int) YL_REG: + case (int) YL_OR_ACCUM_LOW_REGS: + case (int) X_OR_Y_REGS: + case (int) X_OR_YL_REGS: + case (int) Y_REG: + case (int) ACCUM_OR_Y_REGS: + case (int) PH_REG: + case (int) X_OR_PH_REGS: + case (int) PL_REG: + case (int) PL_OR_ACCUM_LOW_REGS: + case (int) X_OR_PL_REGS: + case (int) YL_OR_PL_OR_ACCUM_LOW_REGS: + case (int) P_REG: + case (int) ACCUM_OR_P_REGS: + case (int) YL_OR_P_REGS: + case (int) ACCUM_LOW_OR_YL_OR_P_REGS: + case (int) Y_OR_P_REGS: + case (int) ACCUM_Y_OR_P_REGS: + case (int) Y_ADDR_REGS: + case (int) ACCUM_LOW_OR_Y_ADDR_REGS: + case (int) ACCUM_OR_Y_ADDR_REGS: + case (int) X_OR_Y_ADDR_REGS: + case (int) Y_OR_Y_ADDR_REGS: + case (int) P_OR_Y_ADDR_REGS: + case (int) YBASE_ELIGIBLE_REGS: + return YBASE_VIRT_REGS; + + default: + break; + } + } + } + } + + /* Memory or constants can be moved from or to any register + except the ybase virtual registers */ + if (regno == -1 && GET_CODE(in) != PLUS) + { + if (class == YBASE_VIRT_REGS) + return NON_YBASE_REGS; + else + return NO_REGS; + } + + if (GET_CODE (in) == PLUS && mode == QImode) + { + rtx addr0 = XEXP (in, 0); + rtx addr1 = XEXP (in, 1); + + /* If we are reloading a plus (reg:QI) (reg:QI) + we need a low accumulator, not a high one. */ + if (REG_P (addr0) && REG_P (addr1)) + return ACCUM_LOW_REGS; + } + +#if 0 + if (REG_P(in)) + return ACCUM_REGS; +#endif + + /* Otherwise, we need a high accumulator(s). */ + return ACCUM_HIGH_REGS; +} + +int +symbolic_address_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + return (symbolic_address_p (op)); + +} + +int symbolic_address_p (op) +rtx op; +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case LABEL_REF: + return 1; + + case CONST: + op = XEXP (op, 0); + return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF + || GET_CODE (XEXP (op, 0)) == LABEL_REF) + && GET_CODE (XEXP (op, 1)) == CONST_INT + && INTVAL (XEXP (op,1)) < 0x20); + + default: + return 0; + } +} + +/* For a Y address space operand we allow only *rn, *rn++, *rn--. + This routine only recognizes *rn, the '<>' constraints recognize + *rn++, *rn-- */ + +int +Y_address_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + return (memory_address_p (mode, op) && !symbolic_address_p (op)); +} + +int +sp_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + return (GET_CODE (op) == PLUS + && (XEXP (op, 0) == stack_pointer_rtx + || XEXP (op, 0) == frame_pointer_rtx) + && GET_CODE (XEXP (op,1)) == CONST_INT); +} + +int +sp_operand2 (op, mode) +rtx op; +enum machine_mode mode; +{ + if ((GET_CODE (op) == PLUS + && (XEXP (op, 0) == stack_pointer_rtx + || XEXP (op, 0) == frame_pointer_rtx) + && (REG_P (XEXP (op,1)) + && IS_ADDRESS_REGISTER (REGNO (XEXP(op, 1)))))) + return 1; + else if ((GET_CODE (op) == PLUS + && (XEXP (op, 1) == stack_pointer_rtx + || XEXP (op, 1) == frame_pointer_rtx) + && (REG_P (XEXP (op,0)) + && IS_ADDRESS_REGISTER (REGNO (XEXP(op, 1)))))) + return 1; + else + return 0; +} + +int +nonmemory_arith_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + return (immediate_operand (op, mode) || arith_reg_operand (op, mode)); +} + +int +arith_reg_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + return (register_operand (op, mode) + && (GET_CODE (op) != REG + || REGNO (op) >= FIRST_PSEUDO_REGISTER + || (!(IS_YBASE_REGISTER_WINDOW (REGNO (op))) + && REGNO (op) != FRAME_POINTER_REGNUM))); +} + +int +call_address_operand (op, mode) +rtx op; +enum machine_mode mode; +{ + if (symbolic_address_p (op) || REG_P(op)) + { + return 1; + } + + return 0; +} + +int +dsp16xx_comparison_operator (op, mode) + register rtx op; + enum machine_mode mode; +{ + return ((mode == VOIDmode || GET_MODE (op) == mode) + && GET_RTX_CLASS (GET_CODE (op)) == '<' + && (GET_CODE(op) != GE && GET_CODE (op) != LT && + GET_CODE (op) != GEU && GET_CODE (op) != LTU)); +} + +void +notice_update_cc(exp) +rtx exp; +{ + if (GET_CODE (exp) == SET) + { + /* Jumps do not alter the cc's. */ + + if (SET_DEST (exp) == pc_rtx) + return; + + /* Moving register or memory into a register: + it doesn't alter the cc's, but it might invalidate + the RTX's which we remember the cc's came from. + (Note that moving a constant 0 or 1 MAY set the cc's). */ + if (REG_P (SET_DEST (exp)) + && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM)) + { + if (cc_status.value1 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)) + cc_status.value1 = 0; + if (cc_status.value2 + && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)) + cc_status.value2 = 0; + return; + } + /* Moving register into memory doesn't alter the cc's. + It may invalidate the RTX's which we remember the cc's came from. */ + if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp))) + { + if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM) + cc_status.value1 = 0; + if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM) + cc_status.value2 = 0; + return; + } + /* Function calls clobber the cc's. */ + else if (GET_CODE (SET_SRC (exp)) == CALL) + { + CC_STATUS_INIT; + return; + } + /* Tests and compares set the cc's in predictable ways. */ + else if (SET_DEST (exp) == cc0_rtx) + { + CC_STATUS_INIT; + cc_status.value1 = SET_SRC (exp); + return; + } + /* Certain instructions effect the condition codes. */ + else if (GET_MODE_CLASS (GET_MODE (SET_SRC (exp))) == MODE_INT) + switch( GET_CODE (SET_SRC (exp)) ) + { + case PLUS: + case MINUS: + if (REG_P (SET_DEST (exp))) + { + /* Address registers don't set the condition codes */ + if (IS_ADDRESS_REGISTER (REGNO (SET_DEST (exp)))) + { + CC_STATUS_INIT; + break; + } + } + case ASHIFTRT: + case LSHIFTRT: + case ASHIFT: + case LSHIFT: + case AND: + case IOR: + case XOR: + case MULT: + case NEG: + case NOT: + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = SET_DEST (exp); + break; + + default: + CC_STATUS_INIT; + } + else + { + CC_STATUS_INIT; + } + } + else if (GET_CODE (exp) == PARALLEL + && GET_CODE (XVECEXP (exp, 0, 0)) == SET) + { + if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx) + return; + + if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx) + { + CC_STATUS_INIT; + cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0)); + return; + } + + CC_STATUS_INIT; + } + else + { + CC_STATUS_INIT; + } +} + +int +dsp16xx_makes_calls () +{ + rtx insn; + + for (insn = get_insns (); insn; insn = next_insn (insn)) + if (GET_CODE (insn) == CALL_INSN) + return (1); + + return 0; +} + +long compute_frame_size (size) +int size; +{ + long total_size; + long var_size; + long args_size; + long extra_size; + long reg_size; + + reg_size = 0; + extra_size = 0; + var_size = size; + args_size = current_function_outgoing_args_size; + reg_size = reg_save_size (); + + total_size = var_size + args_size + extra_size + reg_size; + + + /* Save other computed information. */ + current_frame_info.total_size = total_size; + current_frame_info.var_size = var_size; + current_frame_info.args_size = args_size; + current_frame_info.extra_size = extra_size; + current_frame_info.reg_size = reg_size; + current_frame_info.initialized = reload_completed; + current_frame_info.reg_size = reg_size / UNITS_PER_WORD; + current_frame_info.function_makes_calls = dsp16xx_makes_calls (); + + if (reg_size) + { + unsigned long offset = args_size + var_size + reg_size; + current_frame_info.sp_save_offset = offset; + current_frame_info.fp_save_offset = offset - total_size; + } + + return total_size; +} + +int +dsp16xx_call_saved_register (regno) +int regno; +{ + return (regs_ever_live[regno] && !call_used_regs[regno] && + !IS_YBASE_REGISTER_WINDOW(regno)); + +} + +int +ybase_regs_ever_used () +{ + int regno; + int live = 0; + + for (regno = REG_YBASE0; regno <= REG_YBASE31; regno++) + if (regs_ever_live[regno]) + { + live = 1; + break; + } + + return live; +} + +void +function_prologue (file, size) +FILE *file; +int size; +{ + int regno; + long total_size; + fp = reg_names[FRAME_POINTER_REGNUM]; + sp = reg_names[STACK_POINTER_REGNUM]; + rr = reg_names[RETURN_ADDRESS_REGNUM]; /* return address register */ + a1h = reg_names[REG_A1]; + + total_size = compute_frame_size (size); + + fprintf( file, "\t/* FUNCTION PROLOGUE: */\n" ); + fprintf (file, "\t/* total=%d, vars= %d, regs= %d, args=%d, extra= %d */\n", + current_frame_info.total_size, + current_frame_info.var_size, + current_frame_info.reg_size, + current_function_outgoing_args_size, + current_frame_info.extra_size); + + fprintf (file, "\t/* fp save offset= %d, sp save_offset= %d */\n\n", + current_frame_info.fp_save_offset, + current_frame_info.sp_save_offset); + /* Set up the 'ybase' register window. */ + + if (ybase_regs_ever_used()) + { + fprintf (file, "\t%s=%s\n", a1h, reg_names[REG_YBASE]); + if (TARGET_YBASE_HIGH) + fprintf (file, "\t%s=%sh-32\n", reg_names[REG_A1], a1h); + else + fprintf (file, "\t%s=%sh+32\n", reg_names[REG_A1], a1h); + fprintf (file, "\t%s=%s\n", reg_names[REG_YBASE], a1h); + } + +#if 0 + if (current_frame_info.function_makes_calls) + fprintf( file, "\t*%s++=%s\n", sp, rr ); /* Push return address */ +#endif + + + if (current_frame_info.var_size) + { + if (current_frame_info.var_size == 1) + fprintf (file, "\t*%s++\n", sp); + else + { + if(SMALL_INTVAL(current_frame_info.var_size) && ((current_frame_info.var_size & 0x8000) == 0)) + fprintf (file, "\t%s=%d\n\t*%s++%s\n", reg_names[REG_J], current_frame_info.var_size, sp, reg_names[REG_J]); + else + fatal ("Stack size > 32k"); + } + } + + /* Save any registers this function uses, unless they are + * used in a call, in which case we don't need to + */ + + for( regno = 0; regno < FIRST_PSEUDO_REGISTER; ++ regno ) + if (dsp16xx_call_saved_register (regno)) + { +#if OLD_REGISTER_SAVE + fprintf( file, "\t*%s++=%s\n", sp, reg_names[regno] ); +#else + fprintf( file, "\tpush(*%s)=%s\n", sp, reg_names[regno] ); +#endif + } + + if (current_frame_info.args_size) + { + if (current_frame_info.args_size == 1) + fprintf (file, "\t*%s++\n", sp); + else + { + if(SMALL_INTVAL(current_frame_info.args_size) && ((current_frame_info.args_size & 0x8000) == 0)) + fprintf (file, "\t%s=%d\n\t*%s++%s\n", reg_names[REG_J], current_frame_info.args_size, sp, reg_names[REG_J]); + else + fatal ("Stack size > 32k"); + } + } + + if (frame_pointer_needed) + { + fprintf( file, "\t%s=%s\n", a1h, sp ); + fprintf( file, "\t%s=%s\n", fp, a1h ); /* Establish new base frame */ + fprintf( file, "\t%s=%d\n", reg_names[REG_J], -total_size); + fprintf( file, "\t*%s++%s\n", fp, reg_names[REG_J]); + } + + fprintf( file, "\t/* END FUNCTION PROLOGUE: */\n\n" ); +} + +void +init_emulation_routines () +{ + dsp16xx_addhf3_libcall = (rtx) 0; + dsp16xx_subhf3_libcall = (rtx) 0; + dsp16xx_mulhf3_libcall = (rtx) 0; + dsp16xx_divhf3_libcall = (rtx) 0; + dsp16xx_cmphf3_libcall = (rtx) 0; + dsp16xx_fixhfhi2_libcall = (rtx) 0; + dsp16xx_floathihf2_libcall = (rtx) 0; + dsp16xx_neghf2_libcall = (rtx) 0; + + dsp16xx_mulhi3_libcall = (rtx) 0; + dsp16xx_udivqi3_libcall = (rtx) 0; + dsp16xx_udivhi3_libcall = (rtx) 0; + dsp16xx_divqi3_libcall = (rtx) 0; + dsp16xx_divhi3_libcall = (rtx) 0; + dsp16xx_modqi3_libcall = (rtx) 0; + dsp16xx_modhi3_libcall = (rtx) 0; + dsp16xx_umodqi3_libcall = (rtx) 0; + dsp16xx_umodhi3_libcall = (rtx) 0; + dsp16xx_ashrhi3_libcall = (rtx) 0; + dsp16xx_ashlhi3_libcall = (rtx) 0; + dsp16xx_ucmphi2_libcall = (rtx) 0; + dsp16xx_lshrhi3_libcall = (rtx) 0; + +} +void +function_epilogue (file, size) +FILE *file; +int size; +{ + int regno; + int initial_stack_dec = 0; + + fp = reg_names[FRAME_POINTER_REGNUM]; + sp = reg_names[STACK_POINTER_REGNUM]; + rr = reg_names[RETURN_ADDRESS_REGNUM]; /* return address register */ + a1h = reg_names[REG_A1]; + + fprintf( file, "\n\t/* FUNCTION EPILOGUE: */\n" ); + + if (current_frame_info.args_size) + { + if (current_frame_info.args_size == 1) + fprintf (file, "\t*%s--\n", sp); + else + { + fprintf (file, "\t%s=%d\n\t*%s++%s\n", + reg_names[REG_J], -current_frame_info.args_size, sp, reg_names[REG_J]); + } + } + + if (ybase_regs_ever_used()) + { + fprintf (file, "\t%s=%s\n", a1h, reg_names[REG_YBASE]); + if (TARGET_YBASE_HIGH) + fprintf (file, "\t%s=%sh+32\n", reg_names[REG_A1], a1h); + else + fprintf (file, "\t%s=%sh-32\n", reg_names[REG_A1], a1h); + fprintf (file, "\t%s=%s\n", reg_names[REG_YBASE], a1h); + } + + for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno) + if (dsp16xx_call_saved_register(regno)) + { +#if OLD_REGISTER_SAVE + if (!initial_stack_dec) + { + initial_stack_dec = 1; + fprintf (file, "\t*%s--\n", sp); + } +#endif + +#if OLD_REGISTER_SAVE + fprintf( file, "\t%s=*%s--\n", reg_names[regno], sp ); +#else + fprintf( file, "\t%s=pop(*%s)\n", reg_names[regno], sp ); +#endif + } + + /* If we restored any registers we have to account for the + initial pre-decrement. But only if we had any local variables + or spills. */ +#if OLD_REGISTER_SAVE + if (initial_stack_dec) + fprintf (file, "\t*%s++\n", sp); +#endif + + if (current_frame_info.var_size) + { + if (current_frame_info.var_size == 1) + fprintf (file, "\t*%s--\n", sp); + else + { + fprintf (file, "\t%s=%d\n\t*%s++%s\n", + reg_names[REG_J], -current_frame_info.var_size, sp, reg_names[REG_J]); + } + } + + fprintf (file, "\treturn\n"); + /* Reset the frame info for the next function */ + current_frame_info = zero_frame_info; + init_emulation_routines (); +} + +/* Emit insns to move operands[1] into operands[0]. + + Return 1 if we have written out everything that needs to be done to + do the move. Otherwise, return 0 and the caller will emit the move + normally. */ + +int +emit_move_sequence (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + register rtx operand0 = operands[0]; + register rtx operand1 = operands[1]; + + /* We can only store registers to memory. */ + + if (GET_CODE (operand0) == MEM && GET_CODE (operand1) != REG) + operands[1] = force_reg (mode, operand1); + + return 0; +} + +void +double_reg_from_memory (operands) +rtx operands[]; +{ + rtx xoperands[4]; + + if (GET_CODE(XEXP(operands[1],0)) == POST_INC) + { + output_asm_insn ("%u0=%1", operands); + output_asm_insn ("%w0=%1", operands); + } + else if (GET_CODE(XEXP(operands[1],0)) == POST_DEC) + { + xoperands[1] = XEXP (XEXP (operands[1], 0), 0); + xoperands[0] = operands[0]; + + /* We can't use j anymore since the compiler can allocate it. */ +/* output_asm_insn ("j=-3\n\t%u0=*%1++\n\t%w0=*%1++j", xoperands); */ + output_asm_insn ("%u0=*%1++\n\t%w0=*%1--\n\t*%1--\n\t*%1--", xoperands); + } + else if (GET_CODE(XEXP(operands[1],0)) == PLUS) + { + rtx addr; + rtx base; + int offset; + + output_asm_insn ("%u0=%1", operands); + + + /* In order to print out the least significant word we must + use 'offset + 1'. */ + addr = XEXP (operands[1], 0); + if (GET_CODE (XEXP(addr,0)) == CONST_INT) + offset = INTVAL(XEXP(addr,0)) + 1; + else if (GET_CODE (XEXP(addr,1)) == CONST_INT) + offset = INTVAL(XEXP(addr,1)) + 1; + + fprintf (asm_out_file, "\t%s=*(%d)\n", reg_names[REGNO(operands[0]) + 1], offset + 31); + } + else + { + xoperands[1] = XEXP(operands[1],0); + xoperands[0] = operands[0]; + + output_asm_insn ("%u0=*%1++\n\t%w0=*%1--", xoperands); + } +} + + +void +double_reg_to_memory (operands) +rtx operands[]; +{ + rtx xoperands[4]; + + if (GET_CODE(XEXP(operands[0],0)) == POST_INC) + { + output_asm_insn ("%0=%u1", operands); + output_asm_insn ("%0=%w1", operands); + } + else if (GET_CODE(XEXP(operands[0],0)) == POST_DEC) + { + xoperands[0] = XEXP (XEXP (operands[0], 0), 0); + xoperands[1] = operands[1]; + + /* We can't use j anymore since the compiler can allocate it. */ + +/* output_asm_insn ("j=-3\n\t*%0++=%u1\n\t*%0++j=%w1", xoperands); */ + output_asm_insn ("*%0++=%u1\n\t*%0--=%w1\n\t*%0--\n\t*%0--", xoperands); + + } + else if (GET_CODE(XEXP(operands[0],0)) == PLUS) + { + rtx addr; + int offset; + + output_asm_insn ("%0=%u1", operands); + + /* In order to print out the least significant word we must + use 'offset + 1'. */ + addr = XEXP (operands[0], 0); + if (GET_CODE (XEXP(addr,0)) == CONST_INT) + offset = INTVAL(XEXP(addr,0)) + 1; + else if (GET_CODE (XEXP(addr,1)) == CONST_INT) + offset = INTVAL(XEXP(addr,1)) + 1; + else + fatal ("Illegal addressing mode"); + + fprintf (asm_out_file, "\t*(%d)=%s\n", offset + 31, reg_names[REGNO(operands[1]) + 1]); + } + else + { + xoperands[0] = XEXP(operands[0],0); + xoperands[1] = operands[1]; + + output_asm_insn ("*%0++=%u1\n\t*%0--=%w1", xoperands); + } +} + +void +override_options () +{ + if (chip_name == (char *) 0) + chip_name = DEFAULT_CHIP_NAME; + + if (text_seg_name == (char *) 0) + text_seg_name = DEFAULT_TEXT_SEG_NAME; + + if (data_seg_name == (char *) 0) + data_seg_name = DEFAULT_DATA_SEG_NAME; + + if (bss_seg_name == (char *) 0) + bss_seg_name = DEFAULT_BSS_SEG_NAME; + + if (const_seg_name == (char *) 0) + const_seg_name = DEFAULT_CONST_SEG_NAME; + + save_chip_name = (char *) xmalloc (strlen(chip_name) + 1); + strcpy (save_chip_name, chip_name); + + rsect_text = (char *) xmalloc (strlen(".rsect ") + + strlen(text_seg_name) + 3); + rsect_data = (char *) xmalloc (strlen(".rsect ") + + strlen(data_seg_name) + 3); + rsect_bss = (char *) xmalloc (strlen(".rsect ") + + strlen(bss_seg_name) + 3); + rsect_const = (char *) xmalloc (strlen(".rsect ") + + strlen(const_seg_name) + 3); + + sprintf (rsect_text, ".rsect \"%s\"", text_seg_name); + sprintf (rsect_data, ".rsect \"%s\"", data_seg_name); + sprintf (rsect_bss, ".rsect \"%s\"", bss_seg_name); + sprintf (rsect_const, ".rsect \"%s\"", const_seg_name); + + if (optimize) + { + if (TARGET_OPTIMIZE_SPEED) + { + flag_unroll_loops = 1; + flag_inline_functions = 1; + } + } +} + +enum rtx_code save_next_cc_user_code; + +enum rtx_code +next_cc_user_code (insn) +rtx insn; +{ + if ( !(insn = next_cc0_user (insn))) + abort (); + else if (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE) + return GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0)); + else if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && comparison_operator (SET_SRC (PATTERN (insn)), VOIDmode)) + return GET_CODE (SET_SRC (PATTERN (insn))); + else + abort (); +} + +void +print_operand(file, op, letter) +FILE *file; +rtx op; +int letter; +{ + enum rtx_code code; + + code = GET_CODE(op); + + switch (letter) + { + case 'I': + code = reverse_condition (code); + /* Fallthrough */ + + case 'C': + if (code == EQ) + { + fputs ("eq", file); + return; + } + else if (code == NE) + { + fputs ("ne", file); + return; + } + else if (code == GT || code == GTU) + { + fputs ("gt", file); + return; + } + else if (code == LT || code == LTU) + { + fputs ("mi", file); + return; + } + else if (code == GE || code == GEU) + { + fputs ("pl", file); + return; + } + else if (code == LE || code == LEU) + { + fputs ("le", file); + return; + } + else + abort (); + break; + + default: + break; + } + + if( code == REG ) + { + /* Print the low half of a 32-bit register pair */ + if (letter == 'w') + fprintf( file, "%s", reg_names[REGNO(op)+1] ); + else if (letter == 'u' || !letter) + fprintf( file, "%s", reg_names[REGNO(op)]); + else if (letter == 'b') + fprintf ( file, "%sh", reg_names[REGNO(op)]); + else if (letter == 'm') + fprintf (file, "%s", himode_reg_name[REGNO(op)]); + else + fatal("Bad register extension code"); + } + else if( code == MEM ) + output_address( XEXP(op,0) ); + else if( code == CONST_INT ) + { + if( letter == 'H' ) + fprintf( file, "0x%x", (INTVAL(op) & 0xffff) ); + else if (letter == 'h') + fprintf( file, "%d", INTVAL (op) ); + else if( letter == 'U' ) + fprintf( file, "0x%x", ((INTVAL(op) & 0xffff0000) >> 16) & 0xffff ); + else + output_addr_const( file, op ); + } + else if( code == CONST_DOUBLE && GET_MODE(op) != DImode ) + { + union { double d; int i[2]; } u; + union { float f; int i; } u1; + u.i[0] = CONST_DOUBLE_LOW (op); + u.i[1] = CONST_DOUBLE_HIGH (op); + u1.f = u.d; + fprintf( file, "0x%x", u1.i ); + } + else output_addr_const( file, op); +} + + +void +print_operand_address(file, addr) +FILE *file; +rtx addr; +{ + rtx base; + int offset; + + switch (GET_CODE (addr)) + { + case REG: + fprintf (file, "*%s", reg_names[REGNO (addr)]); + break; + case POST_DEC: + fprintf (file, "*%s--", reg_names[REGNO (XEXP (addr, 0))]); + break; + case POST_INC: + fprintf (file, "*%s++", reg_names[REGNO (XEXP (addr, 0))]); + break; + case PLUS: + if (GET_CODE (XEXP(addr,0)) == CONST_INT) + offset = INTVAL(XEXP(addr,0)), base = XEXP(addr,1); + else if (GET_CODE (XEXP(addr,1)) == CONST_INT) + offset = INTVAL(XEXP(addr,1)), base = XEXP(addr,0); + if (GET_CODE (base) == REG && REGNO(base) == STACK_POINTER_REGNUM) + { + if (offset >= -31 && offset <= 0) + offset = 31 + offset; + else + fatal ("Illegal offset in ybase addressing"); + } + else + fatal ("Illegal register in ybase addresing"); + + fprintf (file, "*(%d)", offset); + break; + + default: + if( FITS_5_BITS( addr ) ) + fprintf( file, "*(0x%x)", (INTVAL(addr) & 0x20) ); + else + output_addr_const(file, addr); + } +} + +void +output_dsp16xx_float_const(operands) +rtx *operands; +{ + rtx dst = operands[0]; + rtx src = operands[1]; + +#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + REAL_VALUE_TYPE d; + long value; + + REAL_VALUE_FROM_CONST_DOUBLE (d, src); + REAL_VALUE_TO_TARGET_SINGLE (d, value); + + operands[1] = gen_rtx (CONST_INT, VOIDmode, value); + output_asm_insn ("%u0=%U1\n\t%w0=%H1", operands); +#else + fatal ("inline float constants not supported on this host"); +#endif +} + +int +reg_save_size () +{ + int reg_save_size = 0; + int regno; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (dsp16xx_call_saved_register (regno)) + { + reg_save_size += UNITS_PER_WORD; + } + + return (reg_save_size); +} + +int +dsp16xx_starting_frame_offset() +{ + int reg_save_size = 0; + int regno; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (dsp16xx_call_saved_register (regno)) + { + reg_save_size += UNITS_PER_WORD; + } + + return (reg_save_size); +} + +int +initial_frame_pointer_offset() +{ + int frame_size; + int regno; + int offset = 0; + + offset = compute_frame_size (get_frame_size()); + +#ifdef STACK_GROWS_DOWNWARD + return (offset); +#else + return (-offset); +#endif +} + +/* Generate the minimum number of 1600 core shift instructions + to shift by 'shift_amount'. */ + +#if 0 +void +emit_1600_core_shift (shift_op, operands, shift_amount, mode) +enum rtx_code shift_op; +rtx *operands; +int shift_amount; +enum machine_mode mode; +{ + int quotient; + int i; + int first_shift_emitted = 0; + + while (shift_amount != 0) + { + if (shift_amount/16) + { + quotient = shift_amount/16; + shift_amount = shift_amount - (quotient * 16); + for (i = 0; i < quotient; i++) + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (shift_op, mode, + first_shift_emitted ? operands[0] : operands[1], + gen_rtx (CONST_INT, VOIDmode, 16)))); + first_shift_emitted = 1; + } + else if (shift_amount/8) + { + quotient = shift_amount/8; + shift_amount = shift_amount - (quotient * 8); + for (i = 0; i < quotient; i++) + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (shift_op, mode, + first_shift_emitted ? operands[0] : operands[1], + gen_rtx (CONST_INT, VOIDmode, 8)))); + first_shift_emitted = 1; + } + else if (shift_amount/4) + { + quotient = shift_amount/4; + shift_amount = shift_amount - (quotient * 4); + for (i = 0; i < quotient; i++) + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (shift_op, mode, + first_shift_emitted ? operands[0] : operands[1], + gen_rtx (CONST_INT, VOIDmode, 4)))); + first_shift_emitted = 1; + } + else if (shift_amount/1) + { + quotient = shift_amount/1; + shift_amount = shift_amount - (quotient * 1); + for (i = 0; i < quotient; i++) + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (shift_op, mode, + first_shift_emitted ? operands[0] : operands[1], + gen_rtx (CONST_INT, VOIDmode, 1)))); + first_shift_emitted = 1; + } + } +} +#else +void +emit_1600_core_shift (shift_op, operands, shift_amount) +enum rtx_code shift_op; +rtx *operands; +int shift_amount; +{ + int quotient; + int i; + int first_shift_emitted = 0; + char **shift_asm_ptr; + char **shift_asm_ptr_first; + + if (shift_op == ASHIFT) + { + shift_asm_ptr = ashift_left_asm; + shift_asm_ptr_first = ashift_left_asm_first; + } + else if (shift_op == ASHIFTRT) + { + shift_asm_ptr = ashift_right_asm; + shift_asm_ptr_first = ashift_right_asm_first; + } + else if (shift_op == LSHIFTRT) + { + shift_asm_ptr = lshift_right_asm; + shift_asm_ptr_first = lshift_right_asm_first; + } + else + fatal ("Illegal shift operator in emit_1600_core_shift"); + + while (shift_amount != 0) + { + if (shift_amount/16) + { + quotient = shift_amount/16; + shift_amount = shift_amount - (quotient * 16); + for (i = 0; i < quotient; i++) + output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_16] + : shift_asm_ptr_first[SHIFT_INDEX_16]), operands); + first_shift_emitted = 1; + } + else if (shift_amount/8) + { + quotient = shift_amount/8; + shift_amount = shift_amount - (quotient * 8); + for (i = 0; i < quotient; i++) + output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_8] + : shift_asm_ptr_first[SHIFT_INDEX_8]), operands); + first_shift_emitted = 1; + } + else if (shift_amount/4) + { + quotient = shift_amount/4; + shift_amount = shift_amount - (quotient * 4); + for (i = 0; i < quotient; i++) + output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_4] + : shift_asm_ptr_first[SHIFT_INDEX_4]), operands); + first_shift_emitted = 1; + } + else if (shift_amount/1) + { + quotient = shift_amount/1; + shift_amount = shift_amount - (quotient * 1); + for (i = 0; i < quotient; i++) + output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_1] + : shift_asm_ptr_first[SHIFT_INDEX_1]), operands); + first_shift_emitted = 1; + } + } +} +#endif +void + asm_output_common(file, name, size, rounded) +FILE *file; +char *name; +int size; +int rounded; +{ + bss_section (); + ASM_GLOBALIZE_LABEL (file, name); + assemble_name (file, name); + fputs (":", file); + if (rounded > 1) + fprintf (file, "%d * int\n", rounded); + else + fprintf (file, "int\n"); +} + +void +asm_output_local(file, name, size, rounded) +FILE *file; +char *name; +int size; +int rounded; +{ + bss_section (); + assemble_name (file, name); + fputs (":", file); + if (rounded > 1) + fprintf (file, "%d * int\n", rounded); + else + fprintf (file, "int\n"); +} + +void +asm_output_float (file, fp_const) +FILE *file; +double fp_const; +{ +#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + REAL_VALUE_TYPE d = fp_const; + long value; + + REAL_VALUE_TO_TARGET_SINGLE (d, value); + fputs ("\tint ", file); +#ifdef WORDS_BIG_ENDIAN + fprintf (file, "0x%-4.4x, 0x%-4.4x", (value >> 16) & 0xffff, (value & 0xffff)); +#else + fprintf (file, "0x%-4.4x, 0x%-4.4x", (value & 0xffff), (value >> 16) & 0xffff); +#endif + fputs ("\n", file); +#else + fatal ("inline float constants not supported on this host"); +#endif +} + +void +asm_output_long (file, value) +FILE *file; +long value; +{ + fputs ("\tint ", file); +#ifdef WORDS_BIG_ENDIAN + fprintf (file, "0x%-4.4x, 0x%-4.4x", (value >> 16) & 0xffff, (value & 0xffff)); +#else + fprintf (file, "0x%-4.4x, 0x%-4.4x", (value & 0xffff), (value >> 16) & 0xffff); +#endif + fputs ("\n", file); +} + +int +dsp16xx_address_cost (addr) +rtx addr; +{ + switch (GET_CODE (addr)) + { + default: + break; + + case REG: + return 1; + + case CONST: + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (addr, &offset); + + if (GET_CODE (addr) == LABEL_REF) + return 2; + + if (GET_CODE (addr) != SYMBOL_REF) + return 4; + + if (INTVAL (offset) == 0) + return 2; + } + /* fall through */ + + case POST_INC: case POST_DEC: + return (GET_MODE (addr) == QImode ? 1 : 2); + + case SYMBOL_REF: case LABEL_REF: + return 2; + + case PLUS: + { + register rtx plus0 = XEXP (addr, 0); + register rtx plus1 = XEXP (addr, 1); + + if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG) + { + plus0 = XEXP (addr, 1); + plus1 = XEXP (addr, 0); + } + + if (GET_CODE (plus0) != REG) + break; + + switch (GET_CODE (plus1)) + { + default: + break; + + case CONST_INT: + return 4; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return dsp16xx_address_cost (plus1) + 1; + } + } + } + + return 4; +} + + +/* Determine 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. + + On the dsp1610 the first four words of args are normally in registers + and the rest are pushed. If we a long or on float mode, the argument + must begin on a even register boundary + + Note that FUNCTION_ARG and FUNCTION_INCOMING_ARG were different. + For structures that are passed in memory, but could have been + passed in registers, we first load the structure into the + register, and then when the last argument is passed, we store + the registers into the stack locations. This fixes some bugs + where GCC did not expect to have register arguments, followed */ + + +struct rtx_def * +dsp16xx_function_arg (args_so_far, mode, type, named) + CUMULATIVE_ARGS args_so_far; + enum machine_mode mode; + tree type; + int named; +{ + if (TARGET_REGPARM) + { + if ((args_so_far & 1) != 0 + && (mode == HImode || GET_MODE_CLASS(mode) == MODE_FLOAT)) + args_so_far++; + + if (named && args_so_far < 4 && !MUST_PASS_IN_STACK (mode,type)) + return gen_rtx (REG, mode, args_so_far + FIRST_REG_FOR_FUNCTION_ARG); + else + return (struct rtx_def *) 0; + } + else + return (struct rtx_def *) 0; +} + +/* Advance the argument to the next argument position. */ + +void +dsp16xx_function_arg_advance (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* whether or not the argument was named */ +{ + if (TARGET_REGPARM) + { + if ((*cum & 1) != 0 + && (mode == HImode || GET_MODE_CLASS(mode) == MODE_FLOAT)) + *cum += 1; + + if (mode != BLKmode) + *cum += GET_MODE_SIZE (mode); + else + *cum += int_size_in_bytes (type); + } +} + +void +dsp16xx_file_start () +{ + fprintf (asm_out_file, "#include <%s.h>\n", save_chip_name); +#if 0 + if (TARGET_BMU) + fprintf (asm_out_file, "#include <1610.h>\n"); +#endif +} + +rtx +gen_tst_reg (x) + rtx x; +{ + enum machine_mode mode; + + mode = GET_MODE (x); + + if (mode == QImode) + { + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + gen_rtx (SET, VOIDmode, cc0_rtx, x), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, QImode, 0))))); + } + else if (mode == HImode) + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, x)); + else + fatal ("Illegal mode for gen_tst_reg"); + + return cc0_rtx; +} + +rtx +gen_compare_reg (code, x, y) + enum rtx_code code; + rtx x, y; +{ + enum machine_mode mode; + + mode = GET_MODE (x); + /* For floating point compare insns, a call is generated so don't + do anything here. */ + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return cc0_rtx; + + if (mode == QImode) + { + if (code == GTU || code == GEU || + code == LTU || code == LEU) + { + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (3, + gen_rtx (SET, VOIDmode, cc0_rtx, + gen_rtx (COMPARE, mode, x, y)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, QImode, 0)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, QImode, 0))))); + } + else + { + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (3, + gen_rtx (SET, VOIDmode, cc0_rtx, + gen_rtx (COMPARE, mode, x, y)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, QImode, 0)), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, QImode, 0))))); + } + } + else if (mode == HImode) + { + if (code == GTU || code == GEU || + code == LTU || code == LEU) + { +#if 1 + emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5, + gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, x, y)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0))))); +#else + if (!dsp16xx_ucmphi2_libcall) + dsp16xx_ucmphi2_libcall = gen_rtx (SYMBOL_REF, Pmode, UCMPHI2_LIBCALL); + emit_library_call (dsp16xx_ucmphi2_libcall, 1, HImode, 2, + x, HImode, y, HImode); + emit_insn (gen_tsthi_1 (copy_to_reg(hard_libcall_value (HImode)))); +#endif + } + else + emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, + gen_rtx (COMPARE, VOIDmode, force_reg(HImode, x), + force_reg(HImode,y)))); + } + else + fatal ("Illegal mode for integer comparison in gen_compare_reg"); + + return cc0_rtx; +} + +char * +output_block_move (operands) + rtx operands[]; +{ + int loop_count = INTVAL(operands[2]); + rtx xoperands[4]; + + fprintf (asm_out_file, "\tdo %d {\n", loop_count); + xoperands[0] = operands[4]; + xoperands[1] = operands[1]; + output_asm_insn ("%0=*%1++", xoperands); + + xoperands[0] = operands[0]; + xoperands[1] = operands[4]; + output_asm_insn ("*%0++=%1", xoperands); + + fprintf (asm_out_file, "\t}\n"); + return ""; +} -- 2.7.4