blob: 674a3f08eba2edcaa50d72e849be4e6f749f909d [file] [log] [blame]
/* Subroutines for assembler code output on the DSP1610.
Copyright (C) 1994, 1995, 1997, 1998, 2001 Free Software Foundation, Inc.
Contributed by Michael Collison (collison@isisinc.net).
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. */
/* Some output-actions in dsp1600.md need these. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "tree.h"
#include "expr.h"
#include "function.h"
#include "flags.h"
#include "ggc.h"
#include "toplev.h"
#include "recog.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
const char *text_seg_name;
const char *rsect_text;
const char *data_seg_name;
const char *rsect_data;
const char *bss_seg_name;
const char *rsect_bss;
const char *const_seg_name;
const char *rsect_const;
const char *chip_name;
const 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;
rtx (*dsp16xx_compare_gen) PARAMS (());
static const char *fp;
static const char *sp;
static const char *rr;
static const 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;
static const char *const 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 const char *const ashift_right_asm[] =
{
"%0=%0>>1",
"%0=%0>>4",
"%0=%0>>8",
"%0=%0>>16"
};
static const char *const ashift_right_asm_first[] =
{
"%0=%1>>1",
"%0=%1>>4",
"%0=%1>>8",
"%0=%1>>16"
};
static const char *const ashift_left_asm[] =
{
"%0=%0<<1",
"%0=%0<<4",
"%0=%0<<8",
"%0=%0<<16"
};
static const char *const ashift_left_asm_first[] =
{
"%0=%1<<1",
"%0=%1<<4",
"%0=%1<<8",
"%0=%1<<16"
};
static const char *const 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 const char *const 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"
};
static int reg_save_size PARAMS ((void));
static void dsp16xx_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
static void dsp16xx_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
/* Initialize the GCC target structure. */
#undef TARGET_ASM_BYTE_OP
#define TARGET_ASM_BYTE_OP "\tint\t"
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP NULL
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP NULL
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE dsp16xx_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE dsp16xx_output_function_epilogue
struct gcc_target targetm = TARGET_INITIALIZER;
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 'l':
return A0_REG;
case 'C':
return A1_REG;
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 '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:
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 ATTRIBUTE_UNUSED;
enum machine_mode mode;
{
return (GET_MODE_SIZE(mode));
}
enum reg_class
limit_reload_class (mode, class)
enum machine_mode mode ATTRIBUTE_UNUSED;
enum reg_class class;
{
return class;
}
int
dsp16xx_register_move_cost (from, to)
enum reg_class from, to;
{
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 (from == YBASE_VIRT_REGS)
{
if (to == YBASE_VIRT_REGS)
return 16;
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 8;
}
else
return 10;
}
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 8;
}
else
return 10;
}
return 8;
}
/* 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))
{
switch ((int) class)
{
case YBASE_VIRT_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_YBASE_REGS:
return ACCUM_LOW_REGS;
case ACCUM_OR_YBASE_REGS:
return ACCUM_REGS;
case X_OR_YBASE_REGS:
return X_REG;
case Y_OR_YBASE_REGS:
return Y_REG;
case ACCUM_LOW_YL_PL_OR_YBASE_REGS:
return YL_OR_PL_OR_ACCUM_LOW_REGS;
case P_OR_YBASE_REGS:
return P_REG;
case ACCUM_Y_P_OR_YBASE_REGS:
return ACCUM_Y_OR_P_REGS;
case Y_ADDR_OR_YBASE_REGS:
return Y_ADDR_REGS;
case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS:
return NON_HIGH_YBASE_ELIGIBLE_REGS;;
case YBASE_OR_YBASE_ELIGIBLE_REGS:
return YBASE_ELIGIBLE_REGS;
case NO_HIGH_ALL_REGS:
return NOHIGH_NON_YBASE_REGS;
case ALL_REGS:
return NON_YBASE_REGS;
default:
return class;
}
}
/* If x is not an accumulator or a ybase register, restrict the class of registers
we can copy the register into. */
if (REG_P (x) && !IS_ACCUM_REG (REGNO (x)) && !IS_YBASE_REGISTER_WINDOW (REGNO (x)))
{
switch ((int) class)
{
case NO_REGS:
case A0H_REG: case A0L_REG: case A0_REG: case A1H_REG:
case ACCUM_HIGH_REGS: case A1L_REG: case ACCUM_LOW_REGS:
case A1_REG: case ACCUM_REGS:
return class;
case X_REG:
return (!reload_in_progress ? NO_REGS : class);
case X_OR_ACCUM_LOW_REGS:
return ACCUM_LOW_REGS;
case X_OR_ACCUM_REGS:
return ACCUM_REGS;
case YH_REG:
return (!reload_in_progress ? NO_REGS : class);
case YH_OR_ACCUM_HIGH_REGS:
return ACCUM_HIGH_REGS;
case X_OR_YH_REGS:
case YL_REG:
return (!reload_in_progress ? NO_REGS : class);
case YL_OR_ACCUM_LOW_REGS:
return ACCUM_LOW_REGS;
case X_OR_YL_REGS:
case X_OR_Y_REGS: case Y_REG:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_OR_Y_REGS:
return ACCUM_REGS;
case PH_REG:
case X_OR_PH_REGS: case PL_REG:
return (!reload_in_progress ? NO_REGS : class);
case PL_OR_ACCUM_LOW_REGS:
return ACCUM_LOW_REGS;
case X_OR_PL_REGS:
return (!reload_in_progress ? NO_REGS : class);
case YL_OR_PL_OR_ACCUM_LOW_REGS:
return ACCUM_LOW_REGS;
case P_REG:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_OR_P_REGS:
return ACCUM_REGS;
case YL_OR_P_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_YL_OR_P_REGS:
return ACCUM_LOW_REGS;
case Y_OR_P_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_Y_OR_P_REGS:
return ACCUM_REGS;
case NO_FRAME_Y_ADDR_REGS:
case Y_ADDR_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_Y_ADDR_REGS:
return ACCUM_LOW_REGS;
case ACCUM_OR_Y_ADDR_REGS:
return ACCUM_REGS;
case X_OR_Y_ADDR_REGS:
case Y_OR_Y_ADDR_REGS:
case P_OR_Y_ADDR_REGS:
return (!reload_in_progress ? NO_REGS : class);
case NON_HIGH_YBASE_ELIGIBLE_REGS:
return ACCUM_LOW_REGS;
case YBASE_ELIGIBLE_REGS:
return ACCUM_REGS;
case J_REG:
case J_OR_DAU_16_BIT_REGS:
case BMU_REGS:
return (!reload_in_progress ? NO_REGS : class);
case YBASE_VIRT_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return class;
else
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return class;
else
return ACCUM_LOW_REGS;
case ACCUM_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return class;
else
return ACCUM_REGS;
case X_OR_YBASE_REGS:
case Y_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return YBASE_VIRT_REGS;
else
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_YL_PL_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_LOW_OR_YBASE_REGS;
else
return ACCUM_LOW_REGS;
case P_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return YBASE_VIRT_REGS;
else
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_Y_P_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_OR_YBASE_REGS;
else
return ACCUM_REGS;
case Y_ADDR_OR_YBASE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return YBASE_VIRT_REGS;
else
return (!reload_in_progress ? NO_REGS : class);
case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_LOW_OR_YBASE_REGS;
else
return ACCUM_LOW_REGS;
case YBASE_OR_YBASE_ELIGIBLE_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_OR_YBASE_REGS;
else
return ACCUM_REGS;
case NO_HIGH_ALL_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_LOW_OR_YBASE_REGS;
else
return ACCUM_LOW_REGS;
case ALL_REGS:
if (IS_YBASE_ELIGIBLE_REG (REGNO (x)))
return ACCUM_OR_YBASE_REGS;
else
return ACCUM_REGS;
case NOHIGH_NON_ADDR_REGS:
return ACCUM_LOW_REGS;
case NON_ADDR_REGS:
case SLOW_MEM_LOAD_REGS:
return ACCUM_REGS;
case NOHIGH_NON_YBASE_REGS:
return ACCUM_LOW_REGS;
case NO_ACCUM_NON_YBASE_REGS:
return (!reload_in_progress ? NO_REGS : class);
case NON_YBASE_REGS:
return ACCUM_REGS;
default:
return class;
}
}
/* If x (the input) is a ybase register, restrict the class of registers
we can copy the register into. */
if (REG_P (x) && !TARGET_RESERVE_YBASE
&& IS_YBASE_REGISTER_WINDOW (REGNO(x)))
{
switch ((int) class)
{
case NO_REGS:
case A0H_REG: case A0L_REG: case A0_REG: case A1H_REG:
case ACCUM_HIGH_REGS: case A1L_REG: case ACCUM_LOW_REGS:
case A1_REG: case ACCUM_REGS: case X_REG:
case X_OR_ACCUM_LOW_REGS: case X_OR_ACCUM_REGS:
case YH_REG: case YH_OR_ACCUM_HIGH_REGS:
case X_OR_YH_REGS: case YL_REG:
case YL_OR_ACCUM_LOW_REGS: case X_OR_YL_REGS:
case X_OR_Y_REGS: case Y_REG:
case ACCUM_OR_Y_REGS: case PH_REG:
case X_OR_PH_REGS: case PL_REG:
case PL_OR_ACCUM_LOW_REGS: case X_OR_PL_REGS:
case YL_OR_PL_OR_ACCUM_LOW_REGS: case P_REG:
case ACCUM_OR_P_REGS: case YL_OR_P_REGS:
case ACCUM_LOW_OR_YL_OR_P_REGS: case Y_OR_P_REGS:
case ACCUM_Y_OR_P_REGS: case NO_FRAME_Y_ADDR_REGS:
case Y_ADDR_REGS: case ACCUM_LOW_OR_Y_ADDR_REGS:
case ACCUM_OR_Y_ADDR_REGS: case X_OR_Y_ADDR_REGS:
case Y_OR_Y_ADDR_REGS: case P_OR_Y_ADDR_REGS:
case NON_HIGH_YBASE_ELIGIBLE_REGS: case YBASE_ELIGIBLE_REGS:
default:
return class;
case J_REG:
return (!reload_in_progress ? NO_REGS : class);
case J_OR_DAU_16_BIT_REGS:
return ACCUM_HIGH_REGS;
case BMU_REGS:
case YBASE_VIRT_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_YBASE_REGS:
return ACCUM_LOW_REGS;
case ACCUM_OR_YBASE_REGS:
return ACCUM_REGS;
case X_OR_YBASE_REGS:
return X_REG;
case Y_OR_YBASE_REGS:
return Y_REG;
case ACCUM_LOW_YL_PL_OR_YBASE_REGS:
return YL_OR_PL_OR_ACCUM_LOW_REGS;
case P_OR_YBASE_REGS:
return P_REG;
case ACCUM_Y_P_OR_YBASE_REGS:
return ACCUM_Y_OR_P_REGS;
case Y_ADDR_OR_YBASE_REGS:
return Y_ADDR_REGS;
case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS:
return NON_HIGH_YBASE_ELIGIBLE_REGS;
case YBASE_OR_YBASE_ELIGIBLE_REGS:
return YBASE_ELIGIBLE_REGS;
case NO_HIGH_ALL_REGS:
return NON_HIGH_YBASE_ELIGIBLE_REGS;
case ALL_REGS:
return YBASE_ELIGIBLE_REGS;
case NOHIGH_NON_ADDR_REGS:
return ACCUM_LOW_OR_YL_OR_P_REGS;
case NON_ADDR_REGS:
return ACCUM_Y_OR_P_REGS;
case SLOW_MEM_LOAD_REGS:
return ACCUM_OR_Y_ADDR_REGS;
case NOHIGH_NON_YBASE_REGS:
return NON_HIGH_YBASE_ELIGIBLE_REGS;
case NO_ACCUM_NON_YBASE_REGS:
return Y_ADDR_REGS;
case NON_YBASE_REGS:
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 the accumulators are not part of the class
being reloaded into, return NO_REGS. */
#if 0
if (!reg_class_subset_p (ACCUM_REGS, class))
return (!reload_in_progress ? NO_REGS : class);
#endif
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)
{
/* We can't copy from a memory location into a
ybase register. */
if (reg_class_subset_p(YBASE_VIRT_REGS, class))
{
switch ((int) class)
{
case YBASE_VIRT_REGS:
return (!reload_in_progress ? NO_REGS : class);
case ACCUM_LOW_OR_YBASE_REGS:
return ACCUM_LOW_REGS;
case ACCUM_OR_YBASE_REGS:
return ACCUM_REGS;
case X_OR_YBASE_REGS:
return X_REG;
case Y_OR_YBASE_REGS:
return Y_REG;
case ACCUM_LOW_YL_PL_OR_YBASE_REGS:
return YL_OR_PL_OR_ACCUM_LOW_REGS;
case P_OR_YBASE_REGS:
return P_REG;
case ACCUM_Y_P_OR_YBASE_REGS:
return ACCUM_Y_OR_P_REGS;
case Y_ADDR_OR_YBASE_REGS:
return Y_ADDR_REGS;
case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS:
return NON_HIGH_YBASE_ELIGIBLE_REGS;
case YBASE_OR_YBASE_ELIGIBLE_REGS:
return YBASE_ELIGIBLE_REGS;
case NO_HIGH_ALL_REGS:
return NOHIGH_NON_YBASE_REGS;
case ALL_REGS:
return NON_YBASE_REGS;
default:
return class;
}
}
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 we are reloading a plus into a high accumulator register,
we need a scratch low accumulator, because the low half gets
clobbered. */
if (class == ACCUM_HIGH_REGS
|| class == A1H_REG
|| class == A0H_REG)
{
if (GET_CODE (in) == PLUS && mode == QImode)
return ACCUM_LOW_REGS;
}
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;
if (class == ACCUM_OR_YBASE_REGS && REG_P(in)
&& IS_YBASE_ELIGIBLE_REG(regno))
{
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 ATTRIBUTE_UNUSED;
{
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++), and (*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 ATTRIBUTE_UNUSED;
{
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 ATTRIBUTE_UNUSED;
{
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 ATTRIBUTE_UNUSED;
{
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 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;
/* This value is needed to compute reg_size. */
current_frame_info.function_makes_calls = !leaf_function_p ();
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;
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;
{
#if 0
if (regno == REG_PR && current_frame_info.function_makes_calls)
return 1;
#endif
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;
}
static void
dsp16xx_output_function_prologue (file, size)
FILE *file;
HOST_WIDE_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=%ld, vars= %ld, regs= %d, args=%d, extra= %ld */\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= %ld, sp save_offset= %ld */\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 (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=%ld\n\t*%s++%s\n", reg_names[REG_J], current_frame_info.var_size, sp, reg_names[REG_J]);
else
fatal_error ("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))
{
fprintf (file, "\tpush(*%s)=%s\n", sp, reg_names[regno]);
}
/* For debugging purposes, we want the return address to be at a predictable
location. */
if (current_frame_info.function_makes_calls)
fprintf (file, "\tpush(*%s)=%s\n", sp, reg_names[RETURN_ADDRESS_REGNUM]);
if (current_frame_info.args_size)
{
if (current_frame_info.args_size == 1)
fprintf (file, "\t*%s++\n", sp);
else
error ("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=%ld\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;
}
static void
dsp16xx_output_function_epilogue (file, size)
FILE *file;
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
int regno;
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=%ld\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);
}
if (current_frame_info.function_makes_calls)
fprintf (file, "\t%s=pop(*%s)\n", reg_names[RETURN_ADDRESS_REGNUM], sp);
for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno)
if (dsp16xx_call_saved_register(regno))
{
fprintf (file, "\t%s=pop(*%s)\n", reg_names[regno], sp);
}
if (current_frame_info.var_size)
{
if (current_frame_info.var_size == 1)
fprintf (file, "\t*%s--\n", sp);
else
{
fprintf (file, "\t%s=%ld\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;
int offset = 0;
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 = 0;
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_error ("invalid 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 ()
{
char *tmp;
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 = xstrdup (chip_name);
rsect_text = tmp = (char *) xmalloc (strlen(".rsect ") +
strlen(text_seg_name) + 3);
sprintf (tmp, ".rsect \"%s\"", text_seg_name);
rsect_data = tmp = (char *) xmalloc (strlen(".rsect ") +
strlen(data_seg_name) + 3);
sprintf (tmp, ".rsect \"%s\"", data_seg_name);
rsect_bss = tmp = (char *) xmalloc (strlen(".rsect ") +
strlen(bss_seg_name) + 3);
sprintf (tmp, ".rsect \"%s\"", bss_seg_name);
rsect_const = tmp = (char *) xmalloc (strlen(".rsect ") +
strlen(const_seg_name) + 3);
sprintf (tmp, ".rsect \"%s\"", const_seg_name);
/* Mark our global variables for GC. */
ggc_add_rtx_root (&dsp16xx_addhf3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_subhf3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_mulhf3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_divhf3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_cmphf3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_fixhfhi2_libcall, 1);
ggc_add_rtx_root (&dsp16xx_floathihf2_libcall, 1);
ggc_add_rtx_root (&dsp16xx_neghf2_libcall, 1);
ggc_add_rtx_root (&dsp16xx_mulhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_udivqi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_udivhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_divqi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_divhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_modqi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_modhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_umodqi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_umodhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_ashrhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_ashlhi3_libcall, 1);
ggc_add_rtx_root (&dsp16xx_ucmphi2_libcall, 1);
ggc_add_rtx_root (&dsp16xx_lshrhi3_libcall, 1);
}
int
next_cc_user_unsigned (insn)
rtx insn;
{
switch (next_cc_user_code (insn))
{
case GTU:
case GEU:
case LTU:
case LEU:
return 1;
default:
return 0;
}
}
enum rtx_code
next_cc_user_code (insn)
rtx insn;
{
/* If no insn could be found we assume that the jump has been
deleted and the compare will be deleted later. */
if (!(insn = next_cc0_user (insn)))
return (enum rtx_code) 0;
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
output_operand_lossage ("bad register extension code");
}
else if (code == MEM)
output_address (XEXP(op,0));
else if (code == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (op);
if (letter == 'H')
fprintf (file, HOST_WIDE_INT_PRINT_HEX, val & 0xffff);
else if (letter == 'h')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, val);
else if (letter == 'U')
fprintf (file, HOST_WIDE_INT_PRINT_HEX, (val >> 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 if (code == CONST)
{
rtx addr = XEXP (op, 0);
if (GET_CODE (addr) != PLUS)
{
output_addr_const(file, op);
return;
}
if ((GET_CODE (XEXP (addr, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (addr, 0)) == LABEL_REF)
&& (GET_CODE (XEXP (addr, 1)) == CONST_INT))
{
int n = INTVAL (XEXP(addr, 1));
output_addr_const (file, XEXP (addr, 0));
if (n >= 0)
fprintf (file, "+");
n = (int) (short) n;
fprintf (file, "%d", n);
}
else if ((GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
|| GET_CODE (XEXP (addr, 1)) == LABEL_REF)
&& (GET_CODE (XEXP (addr, 0)) == CONST_INT))
{
int n = INTVAL (XEXP(addr, 0));
output_addr_const (file, XEXP (addr, 1));
if (n >= 0)
fprintf (file, "+");
n = (int) (short) n;
fprintf (file, "%d", n);
}
else
output_addr_const(file, op);
}
else
output_addr_const (file, op);
}
void
print_operand_address(file, addr)
FILE *file;
rtx addr;
{
rtx base;
int offset = 0;;
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);
else
abort();
if (GET_CODE (base) == REG && REGNO(base) == STACK_POINTER_REGNUM)
{
if (offset >= -31 && offset <= 0)
offset = 31 + offset;
else
fatal_error ("invalid offset in ybase addressing");
}
else
fatal_error ("invalid register in ybase addressing");
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 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_INT (value);
output_asm_insn ("%u0=%U1\n\t%w0=%H1", operands);
#else
fatal_error ("inline float constants not supported on this host");
#endif
}
static 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;
}
/* If the function makes calls we will save need to save the 'pr' register. */
if (current_frame_info.function_makes_calls)
reg_save_size += 1;
return (reg_save_size);
}
#if 0
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);
}
#endif
int
initial_frame_pointer_offset()
{
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_INT (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_INT (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_INT (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_INT (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;
const char * const *shift_asm_ptr;
const char * const *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_error ("invalid 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
int
num_1600_core_shifts (shift_amount)
int shift_amount;
{
int quotient;
int i;
int first_shift_emitted = 0;
int num_shifts = 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++)
num_shifts++;
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++)
num_shifts++;
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++)
num_shifts++;
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++)
num_shifts++;
first_shift_emitted = 1;
}
}
return num_shifts;
}
void
asm_output_common(file, name, size, rounded)
FILE *file;
const char *name;
int size ATTRIBUTE_UNUSED;
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;
const char *name;
int size ATTRIBUTE_UNUSED;
int rounded;
{
bss_section ();
assemble_name (file, name);
fputs (":", file);
if (rounded > 1)
fprintf (file, "%d * int\n", rounded);
else
fprintf (file, "int\n");
}
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 an 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 (type == void_type_node)
return (struct rtx_def *) 0;
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 ATTRIBUTE_UNUSED;/* 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
coff_dsp16xx_file_start (file)
FILE *file;
{
fprintf (file, "#include <%s.h>\n", save_chip_name);
}
void
luxworks_dsp16xx_file_start (file)
FILE *file;
{
char *temp_filename;
int len, err_code;
fprintf (file, "\t.debug ");
err_code = (TARGET_DEBUG) ? fprintf (file, "yes, ") : fprintf (file, "no, ");
err_code = (TARGET_SAVE_TEMPS) ? fprintf (file, "asm, ") : fprintf (file, "temp, ");
len = strlen (main_input_filename);
temp_filename = (char *) xmalloc (len + 2);
strcpy (temp_filename, main_input_filename);
#ifdef __CYGWIN32__
p = temp_filename;
while (*p != '\0') {
if (*p == '\\')
*p = '/';
p++;
}
#endif
fprintf (file, "\"%s\"\n", temp_filename);
fprintf (file, "#include <%s.h>\n", save_chip_name);
/*
* Add dummy sections, so that they always exist in the
* object code. These have been created so that the number and
* type of sections remain consistent with and without -g option. Note
* that the .data, .text, .const and .bss are always created when -g
* is provided as an option. */
fprintf (file, "\t.rsect \".text\" , nodelete\n");
fprintf (file, "\t.rsect \".data\" , nodelete\n");
fprintf (file, "\t.rsect \".const\" , nodelete\n");
fprintf (file, "\t.rsect \".bss\" , nodelete\n");
}
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)))));
else if (mode == HImode)
emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx, x));
else
fatal_error ("invalid 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)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)))));
}
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)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)))));
}
}
else if (mode == HImode)
{
if (code == GTU || code == GEU
|| code == LTU || code == LEU)
{
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)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)))));
}
else
emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
gen_rtx_COMPARE (VOIDmode,
force_reg (HImode, x),
force_reg (HImode,y))));
}
else
fatal_error ("invalid mode for integer comparison in gen_compare_reg");
return cc0_rtx;
}
const 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 "";
}
int
uns_comparison_operator (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode == VOIDmode || GET_MODE (op) == mode)
{
enum rtx_code code;
code = GET_CODE(op);
if (code == LEU || code == LTU || code == GEU
|| code == GTU)
{
return 1;
}
else
return 0;
}
return 0;
}
int
signed_comparison_operator (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode == VOIDmode || GET_MODE (op) == mode)
{
enum rtx_code code;
code = GET_CODE(op);
if (!(code == LEU || code == LTU || code == GEU
|| code == GTU))
{
return 1;
}
else
return 0;
}
return 0;
}