summaryrefslogtreecommitdiff
path: root/gcc/config/crx/crx.c
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /gcc/config/crx/crx.c
downloadcbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2
cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository.
Diffstat (limited to 'gcc/config/crx/crx.c')
-rw-r--r--gcc/config/crx/crx.c1466
1 files changed, 1466 insertions, 0 deletions
diff --git a/gcc/config/crx/crx.c b/gcc/config/crx/crx.c
new file mode 100644
index 000000000..79d341c47
--- /dev/null
+++ b/gcc/config/crx/crx.c
@@ -0,0 +1,1466 @@
+/* Output routines for GCC for CRX.
+ Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+
+ This file is part of GCC.
+
+ GCC 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 3, or (at your
+ option) any later version.
+
+ GCC 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 GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+/*****************************************************************************/
+/* HEADER INCLUDES */
+/*****************************************************************************/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "tree.h"
+#include "tm_p.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "output.h"
+#include "insn-codes.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "except.h"
+#include "function.h"
+#include "recog.h"
+#include "expr.h"
+#include "optabs.h"
+#include "diagnostic-core.h"
+#include "basic-block.h"
+#include "df.h"
+#include "target.h"
+#include "target-def.h"
+
+/*****************************************************************************/
+/* DEFINITIONS */
+/*****************************************************************************/
+
+/* Maximum number of register used for passing parameters. */
+#define MAX_REG_FOR_PASSING_ARGS 6
+
+/* Minimum number register used for passing parameters. */
+#define MIN_REG_FOR_PASSING_ARGS 2
+
+/* The maximum count of words supported in the assembly of the architecture in
+ * a push/pop instruction. */
+#define MAX_COUNT 8
+
+/* Predicate is true if the current function is a 'noreturn' function, i.e. it
+ * is qualified as volatile. */
+#define FUNC_IS_NORETURN_P(decl) (TREE_THIS_VOLATILE (decl))
+
+/* The following macros are used in crx_decompose_address () */
+
+/* Returns the factor of a scaled index address or -1 if invalid. */
+#define SCALE_FOR_INDEX_P(X) \
+ (GET_CODE (X) == CONST_INT ? \
+ (INTVAL (X) == 1 ? 1 : \
+ INTVAL (X) == 2 ? 2 : \
+ INTVAL (X) == 4 ? 4 : \
+ INTVAL (X) == 8 ? 8 : \
+ -1) : \
+ -1)
+
+/* Nonzero if the rtx X is a signed const int of n bits */
+#define RTX_SIGNED_INT_FITS_N_BITS(X,n) \
+ ((GET_CODE (X) == CONST_INT \
+ && SIGNED_INT_FITS_N_BITS (INTVAL (X), n)) ? 1 : 0)
+
+/* Nonzero if the rtx X is an unsigned const int of n bits. */
+#define RTX_UNSIGNED_INT_FITS_N_BITS(X, n) \
+ ((GET_CODE (X) == CONST_INT \
+ && UNSIGNED_INT_FITS_N_BITS (INTVAL (X), n)) ? 1 : 0)
+
+/*****************************************************************************/
+/* STATIC VARIABLES */
+/*****************************************************************************/
+
+/* Nonzero if the last param processed is passed in a register. */
+static int last_parm_in_reg;
+
+/* Will hold the number of the last register the prologue saves, -1 if no
+ * register is saved. */
+static int last_reg_to_save;
+
+/* Each object in the array is a register number. Mark 1 for registers that
+ * need to be saved. */
+static int save_regs[FIRST_PSEUDO_REGISTER];
+
+/* Number of bytes saved on the stack for non-scratch registers */
+static int sum_regs = 0;
+
+/* Number of bytes saved on the stack for local variables. */
+static int local_vars_size;
+
+/* The sum of 2 sizes: locals vars and padding byte for saving the registers.
+ * Used in expand_prologue () and expand_epilogue (). */
+static int size_for_adjusting_sp;
+
+/* In case of a POST_INC or POST_DEC memory reference, we must report the mode
+ * of the memory reference from PRINT_OPERAND to PRINT_OPERAND_ADDRESS. */
+static enum machine_mode output_memory_reference_mode;
+
+/*****************************************************************************/
+/* TARGETM FUNCTION PROTOTYPES */
+/*****************************************************************************/
+
+static bool crx_fixed_condition_code_regs (unsigned int *, unsigned int *);
+static rtx crx_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED);
+static bool crx_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED);
+static int crx_address_cost (rtx, bool);
+static bool crx_legitimate_address_p (enum machine_mode, rtx, bool);
+static bool crx_can_eliminate (const int, const int);
+static rtx crx_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static void crx_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+
+/*****************************************************************************/
+/* RTL VALIDITY */
+/*****************************************************************************/
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P crx_legitimate_address_p
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE crx_can_eliminate
+
+/*****************************************************************************/
+/* STACK LAYOUT AND CALLING CONVENTIONS */
+/*****************************************************************************/
+
+#undef TARGET_FIXED_CONDITION_CODE_REGS
+#define TARGET_FIXED_CONDITION_CODE_REGS crx_fixed_condition_code_regs
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX crx_struct_value_rtx
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY crx_return_in_memory
+
+/*****************************************************************************/
+/* PASSING FUNCTION ARGUMENTS */
+/*****************************************************************************/
+
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG crx_function_arg
+
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE crx_function_arg_advance
+
+/*****************************************************************************/
+/* RELATIVE COSTS OF OPERATIONS */
+/*****************************************************************************/
+
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST crx_address_cost
+
+/*****************************************************************************/
+/* TARGET-SPECIFIC USES OF `__attribute__' */
+/*****************************************************************************/
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE crx_attribute_table
+
+static const struct attribute_spec crx_attribute_table[] = {
+ /* ISRs have special prologue and epilogue requirements. */
+ {"interrupt", 0, 0, false, true, true, NULL},
+ {NULL, 0, 0, false, false, false, NULL}
+};
+
+/* Option handling. */
+
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE crx_option_optimization_table
+
+static const struct default_options crx_option_optimization_table[] =
+ {
+ /* Put each function in its own section so that PAGE-instruction
+ relaxation can do its best. */
+ { OPT_LEVELS_1_PLUS, OPT_ffunction_sections, NULL, 1 },
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+
+/* Initialize 'targetm' variable which contains pointers to functions and data
+ * relating to the target machine. */
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+
+/*****************************************************************************/
+/* TARGET HOOK IMPLEMENTATIONS */
+/*****************************************************************************/
+
+/* Return the fixed registers used for condition codes. */
+
+static bool
+crx_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
+{
+ *p1 = CC_REGNUM;
+ *p2 = INVALID_REGNUM;
+ return true;
+}
+
+/* Implements hook TARGET_STRUCT_VALUE_RTX. */
+
+static rtx
+crx_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, CRX_STRUCT_VALUE_REGNUM);
+}
+
+/* Implements hook TARGET_RETURN_IN_MEMORY. */
+
+static bool
+crx_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+ if (TYPE_MODE (type) == BLKmode)
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (type);
+ return (size == -1 || size > 8);
+ }
+ else
+ return false;
+}
+
+
+/*****************************************************************************/
+/* MACRO IMPLEMENTATIONS */
+/*****************************************************************************/
+
+/* STACK LAYOUT AND CALLING CONVENTIONS ROUTINES */
+/* --------------------------------------------- */
+
+/* Return nonzero if the current function being compiled is an interrupt
+ * function as specified by the "interrupt" attribute. */
+
+int
+crx_interrupt_function_p (void)
+{
+ tree attributes;
+
+ attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ return lookup_attribute ("interrupt", attributes) != NULL_TREE;
+}
+
+/* Compute values for the array save_regs and the variable sum_regs. The index
+ * of save_regs is numbers of register, each will get 1 if we need to save it
+ * in the current function, 0 if not. sum_regs is the total sum of the
+ * registers being saved. */
+
+static void
+crx_compute_save_regs (void)
+{
+ unsigned int regno;
+
+ /* initialize here so in case the function is no-return it will be -1. */
+ last_reg_to_save = -1;
+
+ /* No need to save any registers if the function never returns. */
+ if (FUNC_IS_NORETURN_P (current_function_decl))
+ return;
+
+ /* Initialize the number of bytes to be saved. */
+ sum_regs = 0;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (fixed_regs[regno])
+ {
+ save_regs[regno] = 0;
+ continue;
+ }
+
+ /* If this reg is used and not call-used (except RA), save it. */
+ if (crx_interrupt_function_p ())
+ {
+ if (!current_function_is_leaf && call_used_regs[regno])
+ /* this is a volatile reg in a non-leaf interrupt routine - save it
+ * for the sake of its sons. */
+ save_regs[regno] = 1;
+
+ else if (df_regs_ever_live_p (regno))
+ /* This reg is used - save it. */
+ save_regs[regno] = 1;
+ else
+ /* This reg is not used, and is not a volatile - don't save. */
+ save_regs[regno] = 0;
+ }
+ else
+ {
+ /* If this reg is used and not call-used (except RA), save it. */
+ if (df_regs_ever_live_p (regno)
+ && (!call_used_regs[regno] || regno == RETURN_ADDRESS_REGNUM))
+ save_regs[regno] = 1;
+ else
+ save_regs[regno] = 0;
+ }
+ }
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (save_regs[regno] == 1)
+ {
+ last_reg_to_save = regno;
+ sum_regs += UNITS_PER_WORD;
+ }
+}
+
+/* Compute the size of the local area and the size to be adjusted by the
+ * prologue and epilogue. */
+
+static void
+crx_compute_frame (void)
+{
+ /* For aligning the local variables. */
+ int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
+ int padding_locals;
+
+ /* Padding needed for each element of the frame. */
+ local_vars_size = get_frame_size ();
+
+ /* Align to the stack alignment. */
+ padding_locals = local_vars_size % stack_alignment;
+ if (padding_locals)
+ padding_locals = stack_alignment - padding_locals;
+
+ local_vars_size += padding_locals;
+
+ size_for_adjusting_sp = local_vars_size + (ACCUMULATE_OUTGOING_ARGS ?
+ crtl->outgoing_args_size : 0);
+}
+
+/* Worker function for TARGET_CAN_ELIMINATE. */
+
+bool
+crx_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+ return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
+}
+
+/* Implements the macro INITIAL_ELIMINATION_OFFSET, return the OFFSET. */
+
+int
+crx_initial_elimination_offset (int from, int to)
+{
+ /* Compute this since we need to use sum_regs. */
+ crx_compute_save_regs ();
+
+ /* Compute this since we need to use local_vars_size. */
+ crx_compute_frame ();
+
+ if ((from) == FRAME_POINTER_REGNUM && (to) == STACK_POINTER_REGNUM)
+ return (ACCUMULATE_OUTGOING_ARGS ?
+ crtl->outgoing_args_size : 0);
+ else if ((from) == ARG_POINTER_REGNUM && (to) == FRAME_POINTER_REGNUM)
+ return (sum_regs + local_vars_size);
+ else if ((from) == ARG_POINTER_REGNUM && (to) == STACK_POINTER_REGNUM)
+ return (sum_regs + local_vars_size +
+ (ACCUMULATE_OUTGOING_ARGS ?
+ crtl->outgoing_args_size : 0));
+ else
+ abort ();
+}
+
+/* REGISTER USAGE */
+/* -------------- */
+
+/* Return the class number of the smallest class containing reg number REGNO.
+ * This could be a conditional expression or could index an array. */
+
+enum reg_class
+crx_regno_reg_class (int regno)
+{
+ if (regno >= 0 && regno < SP_REGNUM)
+ return NOSP_REGS;
+
+ if (regno == SP_REGNUM)
+ return GENERAL_REGS;
+
+ if (regno == LO_REGNUM)
+ return LO_REGS;
+ if (regno == HI_REGNUM)
+ return HI_REGS;
+
+ return NO_REGS;
+}
+
+/* Transfer between HILO_REGS and memory via secondary reloading. */
+
+enum reg_class
+crx_secondary_reload_class (enum reg_class rclass,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED)
+{
+ if (reg_classes_intersect_p (rclass, HILO_REGS)
+ && true_regnum (x) == -1)
+ return GENERAL_REGS;
+
+ return NO_REGS;
+}
+
+/* Return 1 if hard register REGNO can hold a value of machine-mode MODE. */
+
+int
+crx_hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+ /* CC can only hold CCmode values. */
+ if (regno == CC_REGNUM)
+ return GET_MODE_CLASS (mode) == MODE_CC;
+ if (GET_MODE_CLASS (mode) == MODE_CC)
+ return 0;
+ /* HILO registers can only hold SImode and DImode */
+ if (HILO_REGNO_P (regno))
+ return mode == SImode || mode == DImode;
+ return 1;
+}
+
+/* PASSING FUNCTION ARGUMENTS */
+/* -------------------------- */
+
+/* If enough param regs are available for passing the param of type TYPE return
+ * the number of registers needed else 0. */
+
+static int
+enough_regs_for_param (CUMULATIVE_ARGS * cum, const_tree type,
+ enum machine_mode mode)
+{
+ int type_size;
+ int remaining_size;
+
+ if (mode != BLKmode)
+ type_size = GET_MODE_BITSIZE (mode);
+ else
+ type_size = int_size_in_bytes (type) * BITS_PER_UNIT;
+
+ remaining_size =
+ BITS_PER_WORD * (MAX_REG_FOR_PASSING_ARGS -
+ (MIN_REG_FOR_PASSING_ARGS + cum->ints) + 1);
+
+ /* Any variable which is too big to pass in two registers, will pass on
+ * stack. */
+ if ((remaining_size >= type_size) && (type_size <= 2 * BITS_PER_WORD))
+ return (type_size + BITS_PER_WORD - 1) / BITS_PER_WORD;
+
+ return 0;
+}
+
+/* Implements TARGET_FUNCTION_ARG. */
+
+static rtx
+crx_function_arg (CUMULATIVE_ARGS * cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ last_parm_in_reg = 0;
+
+ /* Function_arg () is called with this type just after all the args have had
+ * their registers assigned. The rtx that function_arg returns from this type
+ * is supposed to pass to 'gen_call' but currently it is not implemented (see
+ * macro GEN_CALL). */
+ if (type == void_type_node)
+ return NULL_RTX;
+
+ if (targetm.calls.must_pass_in_stack (mode, type) || (cum->ints < 0))
+ return NULL_RTX;
+
+ if (mode == BLKmode)
+ {
+ /* Enable structures that need padding bytes at the end to pass to a
+ * function in registers. */
+ if (enough_regs_for_param (cum, type, mode) != 0)
+ {
+ last_parm_in_reg = 1;
+ return gen_rtx_REG (mode, MIN_REG_FOR_PASSING_ARGS + cum->ints);
+ }
+ }
+
+ if (MIN_REG_FOR_PASSING_ARGS + cum->ints > MAX_REG_FOR_PASSING_ARGS)
+ return NULL_RTX;
+ else
+ {
+ if (enough_regs_for_param (cum, type, mode) != 0)
+ {
+ last_parm_in_reg = 1;
+ return gen_rtx_REG (mode, MIN_REG_FOR_PASSING_ARGS + cum->ints);
+ }
+ }
+
+ return NULL_RTX;
+}
+
+/* Implements the macro INIT_CUMULATIVE_ARGS defined in crx.h. */
+
+void
+crx_init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype,
+ rtx libfunc ATTRIBUTE_UNUSED)
+{
+ tree param, next_param;
+
+ cum->ints = 0;
+
+ /* Determine if this function has variable arguments. This is indicated by
+ * the last argument being 'void_type_mode' if there are no variable
+ * arguments. Change here for a different vararg. */
+ for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
+ param != (tree) 0; param = next_param)
+ {
+ next_param = TREE_CHAIN (param);
+ if (next_param == (tree) 0 && TREE_VALUE (param) != void_type_node)
+ {
+ cum->ints = -1;
+ return;
+ }
+ }
+}
+
+/* Implements TARGET_FUNCTION_ARG_ADVANCE. */
+
+static void
+crx_function_arg_advance (CUMULATIVE_ARGS * cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ /* l holds the number of registers required */
+ int l = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+
+ /* If the parameter isn't passed on a register don't advance cum. */
+ if (!last_parm_in_reg)
+ return;
+
+ if (targetm.calls.must_pass_in_stack (mode, type) || (cum->ints < 0))
+ return;
+
+ if (mode == SImode || mode == HImode || mode == QImode || mode == DImode)
+ {
+ if (l <= 1)
+ cum->ints += 1;
+ else
+ cum->ints += l;
+ }
+ else if (mode == SFmode || mode == DFmode)
+ cum->ints += l;
+ else if ((mode) == BLKmode)
+ {
+ if ((l = enough_regs_for_param (cum, type, mode)) != 0)
+ cum->ints += l;
+ }
+
+}
+
+/* Implements the macro FUNCTION_ARG_REGNO_P defined in crx.h. Return nonzero
+ * if N is a register used for passing parameters. */
+
+int
+crx_function_arg_regno_p (int n)
+{
+ return (n <= MAX_REG_FOR_PASSING_ARGS && n >= MIN_REG_FOR_PASSING_ARGS);
+}
+
+/* ADDRESSING MODES */
+/* ---------------- */
+
+/* Implements the hook for TARGET_LEGITIMATE_ADDRESS_P defined in crx.h.
+ * The following addressing modes are supported on CRX:
+ *
+ * Relocations --> const | symbol_ref | label_ref
+ * Absolute address --> 32-bit absolute
+ * Post increment --> reg + 12-bit disp.
+ * Post modify --> reg + 12-bit disp.
+ * Register relative --> reg | 32-bit disp. + reg | 4 bit + reg
+ * Scaled index --> reg + reg | 22-bit disp. + reg + reg |
+ * 22-disp. + reg + reg + (2 | 4 | 8) */
+
+static rtx
+crx_addr_reg (rtx addr_reg)
+{
+ if (GET_MODE (addr_reg) != Pmode)
+ return NULL_RTX;
+
+ if (REG_P (addr_reg))
+ return addr_reg;
+ else if (GET_CODE (addr_reg) == SUBREG
+ && REG_P (SUBREG_REG (addr_reg))
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (addr_reg)))
+ <= UNITS_PER_WORD))
+ return SUBREG_REG (addr_reg);
+ else
+ return NULL_RTX;
+}
+
+enum crx_addrtype
+crx_decompose_address (rtx addr, struct crx_address *out)
+{
+ rtx base = NULL_RTX, index = NULL_RTX, disp = NULL_RTX;
+ rtx scale_rtx = NULL_RTX, side_effect = NULL_RTX;
+ int scale = -1;
+
+ enum crx_addrtype retval = CRX_INVALID;
+
+ switch (GET_CODE (addr))
+ {
+ case CONST_INT:
+ /* Absolute address (known at compile time) */
+ retval = CRX_ABSOLUTE;
+ disp = addr;
+ if (!UNSIGNED_INT_FITS_N_BITS (INTVAL (disp), GET_MODE_BITSIZE (Pmode)))
+ return CRX_INVALID;
+ break;
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ /* Absolute address (known at link time) */
+ retval = CRX_ABSOLUTE;
+ disp = addr;
+ break;
+
+ case REG:
+ case SUBREG:
+ /* Register relative address */
+ retval = CRX_REG_REL;
+ base = addr;
+ break;
+
+ case PLUS:
+ switch (GET_CODE (XEXP (addr, 0)))
+ {
+ case REG:
+ case SUBREG:
+ if (REG_P (XEXP (addr, 1)))
+ {
+ /* Scaled index with scale = 1 and disp. = 0 */
+ retval = CRX_SCALED_INDX;
+ base = XEXP (addr, 1);
+ index = XEXP (addr, 0);
+ scale = 1;
+ }
+ else if (RTX_SIGNED_INT_FITS_N_BITS (XEXP (addr, 1), 28))
+ {
+ /* Register relative address and <= 28-bit disp. */
+ retval = CRX_REG_REL;
+ base = XEXP (addr, 0);
+ disp = XEXP (addr, 1);
+ }
+ else
+ return CRX_INVALID;
+ break;
+
+ case PLUS:
+ /* Scaled index and <= 22-bit disp. */
+ retval = CRX_SCALED_INDX;
+ base = XEXP (XEXP (addr, 0), 1);
+ disp = XEXP (addr, 1);
+ if (!RTX_SIGNED_INT_FITS_N_BITS (disp, 22))
+ return CRX_INVALID;
+ switch (GET_CODE (XEXP (XEXP (addr, 0), 0)))
+ {
+ case REG:
+ /* Scaled index with scale = 0 and <= 22-bit disp. */
+ index = XEXP (XEXP (addr, 0), 0);
+ scale = 1;
+ break;
+
+ case MULT:
+ /* Scaled index with scale >= 0 and <= 22-bit disp. */
+ index = XEXP (XEXP (XEXP (addr, 0), 0), 0);
+ scale_rtx = XEXP (XEXP (XEXP (addr, 0), 0), 1);
+ if ((scale = SCALE_FOR_INDEX_P (scale_rtx)) == -1)
+ return CRX_INVALID;
+ break;
+
+ default:
+ return CRX_INVALID;
+ }
+ break;
+
+ case MULT:
+ /* Scaled index with scale >= 0 */
+ retval = CRX_SCALED_INDX;
+ base = XEXP (addr, 1);
+ index = XEXP (XEXP (addr, 0), 0);
+ scale_rtx = XEXP (XEXP (addr, 0), 1);
+ /* Scaled index with scale >= 0 and <= 22-bit disp. */
+ if ((scale = SCALE_FOR_INDEX_P (scale_rtx)) == -1)
+ return CRX_INVALID;
+ break;
+
+ default:
+ return CRX_INVALID;
+ }
+ break;
+
+ case POST_INC:
+ case POST_DEC:
+ /* Simple post-increment */
+ retval = CRX_POST_INC;
+ base = XEXP (addr, 0);
+ side_effect = addr;
+ break;
+
+ case POST_MODIFY:
+ /* Generic post-increment with <= 12-bit disp. */
+ retval = CRX_POST_INC;
+ base = XEXP (addr, 0);
+ side_effect = XEXP (addr, 1);
+ if (base != XEXP (side_effect, 0))
+ return CRX_INVALID;
+ switch (GET_CODE (side_effect))
+ {
+ case PLUS:
+ case MINUS:
+ disp = XEXP (side_effect, 1);
+ if (!RTX_SIGNED_INT_FITS_N_BITS (disp, 12))
+ return CRX_INVALID;
+ break;
+
+ default:
+ /* CRX only supports PLUS and MINUS */
+ return CRX_INVALID;
+ }
+ break;
+
+ default:
+ return CRX_INVALID;
+ }
+
+ if (base)
+ {
+ base = crx_addr_reg (base);
+ if (!base)
+ return CRX_INVALID;
+ }
+ if (index)
+ {
+ index = crx_addr_reg (index);
+ if (!index)
+ return CRX_INVALID;
+ }
+
+ out->base = base;
+ out->index = index;
+ out->disp = disp;
+ out->scale = scale;
+ out->side_effect = side_effect;
+
+ return retval;
+}
+
+bool
+crx_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx addr, bool strict)
+{
+ enum crx_addrtype addrtype;
+ struct crx_address address;
+
+ if (TARGET_DEBUG_ADDR)
+ {
+ fprintf (stderr,
+ "\n======\nGO_IF_LEGITIMATE_ADDRESS, mode = %s, strict = %d\n",
+ GET_MODE_NAME (mode), strict);
+ debug_rtx (addr);
+ }
+
+ addrtype = crx_decompose_address (addr, &address);
+
+ if (addrtype == CRX_POST_INC && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ return FALSE;
+
+ if (TARGET_DEBUG_ADDR)
+ {
+ const char *typestr;
+ switch (addrtype)
+ {
+ case CRX_INVALID:
+ typestr = "Invalid";
+ break;
+ case CRX_REG_REL:
+ typestr = "Register relative";
+ break;
+ case CRX_POST_INC:
+ typestr = "Post-increment";
+ break;
+ case CRX_SCALED_INDX:
+ typestr = "Scaled index";
+ break;
+ case CRX_ABSOLUTE:
+ typestr = "Absolute";
+ break;
+ default:
+ abort ();
+ }
+ fprintf (stderr, "CRX Address type: %s\n", typestr);
+ }
+
+ if (addrtype == CRX_INVALID)
+ return FALSE;
+
+ if (strict)
+ {
+ if (address.base && !REGNO_OK_FOR_BASE_P (REGNO (address.base)))
+ {
+ if (TARGET_DEBUG_ADDR)
+ fprintf (stderr, "Base register not strict\n");
+ return FALSE;
+ }
+ if (address.index && !REGNO_OK_FOR_INDEX_P (REGNO (address.index)))
+ {
+ if (TARGET_DEBUG_ADDR)
+ fprintf (stderr, "Index register not strict\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* ROUTINES TO COMPUTE COSTS */
+/* ------------------------- */
+
+/* Return cost of the memory address x. */
+
+static int
+crx_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+ enum crx_addrtype addrtype;
+ struct crx_address address;
+
+ int cost = 2;
+
+ addrtype = crx_decompose_address (addr, &address);
+
+ gcc_assert (addrtype != CRX_INVALID);
+
+ /* An absolute address causes a 3-word instruction */
+ if (addrtype == CRX_ABSOLUTE)
+ cost+=2;
+
+ /* Post-modifying addresses are more powerful. */
+ if (addrtype == CRX_POST_INC)
+ cost-=2;
+
+ /* Attempt to minimize number of registers in the address. */
+ if (address.base)
+ cost++;
+
+ if (address.index && address.scale == 1)
+ cost+=5;
+
+ if (address.disp && !INT_CST4 (INTVAL (address.disp)))
+ cost+=2;
+
+ if (TARGET_DEBUG_ADDR)
+ {
+ fprintf (stderr, "\n======\nTARGET_ADDRESS_COST = %d\n", cost);
+ debug_rtx (addr);
+ }
+
+ return cost;
+}
+
+/* Return the cost of moving data of mode MODE between a register of class
+ * RCLASS and memory; IN is zero if the value is to be written to memory,
+ * nonzero if it is to be read in. This cost is relative to those in
+ * REGISTER_MOVE_COST. */
+
+int
+crx_memory_move_cost (enum machine_mode mode,
+ enum reg_class rclass ATTRIBUTE_UNUSED,
+ int in ATTRIBUTE_UNUSED)
+{
+ /* One LD or ST takes twice the time of a simple reg-reg move */
+ if (reg_classes_intersect_p (rclass, GENERAL_REGS))
+ {
+ /* printf ("GENERAL_REGS LD/ST = %d\n", 4 * HARD_REGNO_NREGS (0, mode));*/
+ return 4 * HARD_REGNO_NREGS (0, mode);
+ }
+ else if (reg_classes_intersect_p (rclass, HILO_REGS))
+ {
+ /* HILO to memory and vice versa */
+ /* printf ("HILO_REGS %s = %d\n", in ? "LD" : "ST",
+ (REGISTER_MOVE_COST (mode,
+ in ? GENERAL_REGS : HILO_REGS,
+ in ? HILO_REGS : GENERAL_REGS) + 4)
+ * HARD_REGNO_NREGS (0, mode)); */
+ return (REGISTER_MOVE_COST (mode,
+ in ? GENERAL_REGS : HILO_REGS,
+ in ? HILO_REGS : GENERAL_REGS) + 4)
+ * HARD_REGNO_NREGS (0, mode);
+ }
+ else /* default (like in i386) */
+ {
+ /* printf ("ANYREGS = 100\n"); */
+ return 100;
+ }
+}
+
+/* INSTRUCTION OUTPUT */
+/* ------------------ */
+
+/* Check if a const_double is ok for crx store-immediate instructions */
+
+int
+crx_const_double_ok (rtx op)
+{
+ if (GET_MODE (op) == DFmode)
+ {
+ REAL_VALUE_TYPE r;
+ long l[2];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ REAL_VALUE_TO_TARGET_DOUBLE (r, l);
+ return (UNSIGNED_INT_FITS_N_BITS (l[0], 4) &&
+ UNSIGNED_INT_FITS_N_BITS (l[1], 4)) ? 1 : 0;
+ }
+
+ if (GET_MODE (op) == SFmode)
+ {
+ REAL_VALUE_TYPE r;
+ long l;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ return UNSIGNED_INT_FITS_N_BITS (l, 4) ? 1 : 0;
+ }
+
+ return (UNSIGNED_INT_FITS_N_BITS (CONST_DOUBLE_LOW (op), 4) &&
+ UNSIGNED_INT_FITS_N_BITS (CONST_DOUBLE_HIGH (op), 4)) ? 1 : 0;
+}
+
+/* Implements the macro PRINT_OPERAND defined in crx.h. */
+
+void
+crx_print_operand (FILE * file, rtx x, int code)
+{
+ switch (code)
+ {
+ case 'p' :
+ if (GET_CODE (x) == REG) {
+ if (GET_MODE (x) == DImode || GET_MODE (x) == DFmode)
+ {
+ int regno = REGNO (x);
+ if (regno + 1 >= SP_REGNUM) abort ();
+ fprintf (file, "{%s, %s}", reg_names[regno], reg_names[regno + 1]);
+ return;
+ }
+ else
+ {
+ if (REGNO (x) >= SP_REGNUM) abort ();
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ return;
+ }
+ }
+
+ case 'd' :
+ {
+ const char *crx_cmp_str;
+ switch (GET_CODE (x))
+ { /* MD: compare (reg, reg or imm) but CRX: cmp (reg or imm, reg)
+ * -> swap all non symmetric ops */
+ case EQ : crx_cmp_str = "eq"; break;
+ case NE : crx_cmp_str = "ne"; break;
+ case GT : crx_cmp_str = "lt"; break;
+ case GTU : crx_cmp_str = "lo"; break;
+ case LT : crx_cmp_str = "gt"; break;
+ case LTU : crx_cmp_str = "hi"; break;
+ case GE : crx_cmp_str = "le"; break;
+ case GEU : crx_cmp_str = "ls"; break;
+ case LE : crx_cmp_str = "ge"; break;
+ case LEU : crx_cmp_str = "hs"; break;
+ default : abort ();
+ }
+ fprintf (file, "%s", crx_cmp_str);
+ return;
+ }
+
+ case 'H':
+ /* Print high part of a double precision value. */
+ switch (GET_CODE (x))
+ {
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == SFmode) abort ();
+ if (GET_MODE (x) == DFmode)
+ {
+ /* High part of a DF const. */
+ REAL_VALUE_TYPE r;
+ long l[2];
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (r, l);
+
+ fprintf (file, "$0x%lx", l[1]);
+ return;
+ }
+
+ /* -- Fallthrough to handle DI consts -- */
+
+ case CONST_INT:
+ {
+ rtx high, low;
+ split_double (x, &low, &high);
+ putc ('$', file);
+ output_addr_const (file, high);
+ return;
+ }
+
+ case REG:
+ if (REGNO (x) + 1 >= FIRST_PSEUDO_REGISTER) abort ();
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ return;
+
+ case MEM:
+ /* Adjust memory address to high part. */
+ {
+ rtx adj_mem = x;
+ adj_mem = adjust_address (adj_mem, GET_MODE (adj_mem), 4);
+
+ output_memory_reference_mode = GET_MODE (adj_mem);
+ output_address (XEXP (adj_mem, 0));
+ return;
+ }
+
+ default:
+ abort ();
+ }
+
+ case 'L':
+ /* Print low part of a double precision value. */
+ switch (GET_CODE (x))
+ {
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == SFmode) abort ();
+ if (GET_MODE (x) == DFmode)
+ {
+ /* High part of a DF const. */
+ REAL_VALUE_TYPE r;
+ long l[2];
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (r, l);
+
+ fprintf (file, "$0x%lx", l[0]);
+ return;
+ }
+
+ /* -- Fallthrough to handle DI consts -- */
+
+ case CONST_INT:
+ {
+ rtx high, low;
+ split_double (x, &low, &high);
+ putc ('$', file);
+ output_addr_const (file, low);
+ return;
+ }
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ return;
+
+ case MEM:
+ output_memory_reference_mode = GET_MODE (x);
+ output_address (XEXP (x, 0));
+ return;
+
+ default:
+ abort ();
+ }
+
+ case 0 : /* default */
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ return;
+
+ case MEM:
+ output_memory_reference_mode = GET_MODE (x);
+ output_address (XEXP (x, 0));
+ return;
+
+ case CONST_DOUBLE:
+ {
+ REAL_VALUE_TYPE r;
+ long l;
+
+ /* Always use H and L for double precision - see above */
+ gcc_assert (GET_MODE (x) == SFmode);
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+
+ fprintf (file, "$0x%lx", l);
+ return;
+ }
+
+ default:
+ putc ('$', file);
+ output_addr_const (file, x);
+ return;
+ }
+
+ default:
+ output_operand_lossage ("invalid %%xn code");
+ }
+
+ abort ();
+}
+
+/* Implements the macro PRINT_OPERAND_ADDRESS defined in crx.h. */
+
+void
+crx_print_operand_address (FILE * file, rtx addr)
+{
+ enum crx_addrtype addrtype;
+ struct crx_address address;
+
+ int offset;
+
+ addrtype = crx_decompose_address (addr, &address);
+
+ if (address.disp)
+ offset = INTVAL (address.disp);
+ else
+ offset = 0;
+
+ switch (addrtype)
+ {
+ case CRX_REG_REL:
+ fprintf (file, "%d(%s)", offset, reg_names[REGNO (address.base)]);
+ return;
+
+ case CRX_POST_INC:
+ switch (GET_CODE (address.side_effect))
+ {
+ case PLUS:
+ break;
+ case MINUS:
+ offset = -offset;
+ break;
+ case POST_INC:
+ offset = GET_MODE_SIZE (output_memory_reference_mode);
+ break;
+ case POST_DEC:
+ offset = -GET_MODE_SIZE (output_memory_reference_mode);
+ break;
+ default:
+ abort ();
+ }
+ fprintf (file, "%d(%s)+", offset, reg_names[REGNO (address.base)]);
+ return;
+
+ case CRX_SCALED_INDX:
+ fprintf (file, "%d(%s, %s, %d)", offset, reg_names[REGNO (address.base)],
+ reg_names[REGNO (address.index)], address.scale);
+ return;
+
+ case CRX_ABSOLUTE:
+ output_addr_const (file, address.disp);
+ return;
+
+ default:
+ abort ();
+ }
+}
+
+
+/*****************************************************************************/
+/* MACHINE DESCRIPTION HELPER-FUNCTIONS */
+/*****************************************************************************/
+
+void crx_expand_movmem_single (rtx src, rtx srcbase, rtx dst, rtx dstbase,
+ rtx tmp_reg, unsigned HOST_WIDE_INT *offset_p)
+{
+ rtx addr, mem;
+ unsigned HOST_WIDE_INT offset = *offset_p;
+
+ /* Load */
+ addr = plus_constant (src, offset);
+ mem = adjust_automodify_address (srcbase, SImode, addr, offset);
+ emit_move_insn (tmp_reg, mem);
+
+ /* Store */
+ addr = plus_constant (dst, offset);
+ mem = adjust_automodify_address (dstbase, SImode, addr, offset);
+ emit_move_insn (mem, tmp_reg);
+
+ *offset_p = offset + 4;
+}
+
+int
+crx_expand_movmem (rtx dstbase, rtx srcbase, rtx count_exp, rtx align_exp)
+{
+ unsigned HOST_WIDE_INT count = 0, offset, si_moves, i;
+ HOST_WIDE_INT align = 0;
+
+ rtx src, dst;
+ rtx tmp_reg;
+
+ if (GET_CODE (align_exp) == CONST_INT)
+ { /* Only if aligned */
+ align = INTVAL (align_exp);
+ if (align & 3)
+ return 0;
+ }
+
+ if (GET_CODE (count_exp) == CONST_INT)
+ { /* No more than 16 SImode moves */
+ count = INTVAL (count_exp);
+ if (count > 64)
+ return 0;
+ }
+
+ tmp_reg = gen_reg_rtx (SImode);
+
+ /* Create psrs for the src and dest pointers */
+ dst = copy_to_mode_reg (Pmode, XEXP (dstbase, 0));
+ if (dst != XEXP (dstbase, 0))
+ dstbase = replace_equiv_address_nv (dstbase, dst);
+ src = copy_to_mode_reg (Pmode, XEXP (srcbase, 0));
+ if (src != XEXP (srcbase, 0))
+ srcbase = replace_equiv_address_nv (srcbase, src);
+
+ offset = 0;
+
+ /* Emit SImode moves */
+ si_moves = count >> 2;
+ for (i = 0; i < si_moves; i++)
+ crx_expand_movmem_single (src, srcbase, dst, dstbase, tmp_reg, &offset);
+
+ /* Special cases */
+ if (count & 3)
+ {
+ offset = count - 4;
+ crx_expand_movmem_single (src, srcbase, dst, dstbase, tmp_reg, &offset);
+ }
+
+ gcc_assert (offset == count);
+
+ return 1;
+}
+
+static void
+mpushpop_str (char *stringbuffer, const char *mnemonic, char *mask)
+{
+ if (strlen (mask) > 2 || crx_interrupt_function_p ()) /* needs 2-word instr. */
+ sprintf (stringbuffer, "\n\t%s\tsp, {%s}", mnemonic, mask);
+ else /* single word instruction */
+ sprintf (stringbuffer, "\n\t%s\t%s", mnemonic, mask);
+}
+
+/* Called from crx.md. The return value depends on the parameter push_or_pop:
+ * When push_or_pop is zero -> string for push instructions of prologue.
+ * When push_or_pop is nonzero -> string for pop/popret/retx in epilogue.
+ * Relies on the assumptions:
+ * 1. RA is the last register to be saved.
+ * 2. The maximal value of the counter is MAX_COUNT. */
+
+char *
+crx_prepare_push_pop_string (int push_or_pop)
+{
+ /* j is the number of registers being saved, takes care that there won't be
+ * more than 8 in one push/pop instruction */
+
+ /* For the register mask string */
+ static char mask_str[50];
+
+ /* i is the index of save_regs[], going from 0 until last_reg_to_save */
+ int i = 0;
+
+ int ra_in_bitmask = 0;
+
+ char *return_str;
+
+ /* For reversing on the push instructions if there are more than one. */
+ char *temp_str;
+
+ return_str = (char *) xmalloc (120);
+ temp_str = (char *) xmalloc (120);
+
+ /* Initialize */
+ memset (return_str, 0, 3);
+
+ while (i <= last_reg_to_save)
+ {
+ /* Prepare mask for one instruction. */
+ mask_str[0] = 0;
+
+ if (i <= SP_REGNUM)
+ { /* Add regs unit full or SP register reached */
+ int j = 0;
+ while (j < MAX_COUNT && i <= SP_REGNUM)
+ {
+ if (save_regs[i])
+ {
+ /* TODO to use ra_in_bitmask for detecting last pop is not
+ * smart it prevents things like: popret r5 */
+ if (i == RETURN_ADDRESS_REGNUM) ra_in_bitmask = 1;
+ if (j > 0) strcat (mask_str, ", ");
+ strcat (mask_str, reg_names[i]);
+ ++j;
+ }
+ ++i;
+ }
+ }
+ else
+ {
+ /* Handle hi/lo savings */
+ while (i <= last_reg_to_save)
+ {
+ if (save_regs[i])
+ {
+ strcat (mask_str, "lo, hi");
+ i = last_reg_to_save + 1;
+ break;
+ }
+ ++i;
+ }
+ }
+
+ if (strlen (mask_str) == 0) continue;
+
+ if (push_or_pop == 1)
+ {
+ if (crx_interrupt_function_p ())
+ mpushpop_str (temp_str, "popx", mask_str);
+ else
+ {
+ if (ra_in_bitmask)
+ {
+ mpushpop_str (temp_str, "popret", mask_str);
+ ra_in_bitmask = 0;
+ }
+ else mpushpop_str (temp_str, "pop", mask_str);
+ }
+
+ strcat (return_str, temp_str);
+ }
+ else
+ {
+ /* push - We need to reverse the order of the instructions if there
+ * are more than one. (since the pop will not be reversed in the
+ * epilogue */
+ if (crx_interrupt_function_p ())
+ mpushpop_str (temp_str, "pushx", mask_str);
+ else
+ mpushpop_str (temp_str, "push", mask_str);
+ strcat (temp_str, return_str);
+ strcpy (strcat (return_str, "\t"), temp_str);
+ }
+
+ }
+
+ if (push_or_pop == 1)
+ {
+ /* pop */
+ if (crx_interrupt_function_p ())
+ strcat (return_str, "\n\tretx\n");
+
+ else if (!FUNC_IS_NORETURN_P (current_function_decl)
+ && !save_regs[RETURN_ADDRESS_REGNUM])
+ strcat (return_str, "\n\tjump\tra\n");
+ }
+
+ /* Skip the newline and the tab in the start of return_str. */
+ return_str += 2;
+ return return_str;
+}
+
+/* CompactRISC CRX Architecture stack layout:
+
+ 0 +---------------------
+ |
+ .
+ .
+ |
+ +==================== Sp(x)=Ap(x+1)
+ A | Args for functions
+ | | called by X and Dynamically
+ | | Dynamic allocations allocated and
+ | | (alloca, variable deallocated
+ Stack | length arrays).
+ grows +-------------------- Fp(x)
+ down| | Local variables of X
+ ward| +--------------------
+ | | Regs saved for X-1
+ | +==================== Sp(x-1)=Ap(x)
+ | Args for func X
+ | pushed by X-1
+ +-------------------- Fp(x-1)
+ |
+ |
+ V
+
+*/
+
+void
+crx_expand_prologue (void)
+{
+ crx_compute_frame ();
+ crx_compute_save_regs ();
+
+ /* If there is no need in push and adjustment to sp, return. */
+ if (size_for_adjusting_sp + sum_regs == 0)
+ return;
+
+ if (last_reg_to_save != -1)
+ /* If there are registers to push. */
+ emit_insn (gen_push_for_prologue (GEN_INT (sum_regs)));
+
+ if (size_for_adjusting_sp > 0)
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-size_for_adjusting_sp)));
+
+ if (frame_pointer_needed)
+ /* Initialize the frame pointer with the value of the stack pointer
+ * pointing now to the locals. */
+ emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+}
+
+/* Generate insn that updates the stack for local variables and padding for
+ * registers we save. - Generate the appropriate return insn. */
+
+void
+crx_expand_epilogue (void)
+{
+ /* Nonzero if we need to return and pop only RA. This will generate a
+ * different insn. This differentiate is for the peepholes for call as last
+ * statement in function. */
+ int only_popret_RA = (save_regs[RETURN_ADDRESS_REGNUM]
+ && (sum_regs == UNITS_PER_WORD));
+
+ if (frame_pointer_needed)
+ /* Restore the stack pointer with the frame pointers value */
+ emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+
+ if (size_for_adjusting_sp > 0)
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (size_for_adjusting_sp)));
+
+ if (crx_interrupt_function_p ())
+ emit_jump_insn (gen_interrupt_return ());
+ else if (last_reg_to_save == -1)
+ /* Nothing to pop */
+ /* Don't output jump for interrupt routine, only retx. */
+ emit_jump_insn (gen_indirect_jump_return ());
+ else if (only_popret_RA)
+ emit_jump_insn (gen_popret_RA_return ());
+ else
+ emit_jump_insn (gen_pop_and_popret_return (GEN_INT (sum_regs)));
+}